@graview/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/LICENSE +96 -0
  2. package/README.md +20 -0
  3. package/dist/activity.d.ts +113 -0
  4. package/dist/activity.d.ts.map +1 -0
  5. package/dist/activity.js +207 -0
  6. package/dist/activity.js.map +1 -0
  7. package/dist/animation.d.ts +64 -0
  8. package/dist/animation.d.ts.map +1 -0
  9. package/dist/animation.js +245 -0
  10. package/dist/animation.js.map +1 -0
  11. package/dist/captions.d.ts +43 -0
  12. package/dist/captions.d.ts.map +1 -0
  13. package/dist/captions.js +61 -0
  14. package/dist/captions.js.map +1 -0
  15. package/dist/channels.d.ts +15 -0
  16. package/dist/channels.d.ts.map +1 -0
  17. package/dist/channels.js +114 -0
  18. package/dist/channels.js.map +1 -0
  19. package/dist/connectors.d.ts +176 -0
  20. package/dist/connectors.d.ts.map +1 -0
  21. package/dist/connectors.js +837 -0
  22. package/dist/connectors.js.map +1 -0
  23. package/dist/context.d.ts +348 -0
  24. package/dist/context.d.ts.map +1 -0
  25. package/dist/context.js +568 -0
  26. package/dist/context.js.map +1 -0
  27. package/dist/drawn.d.ts +60 -0
  28. package/dist/drawn.d.ts.map +1 -0
  29. package/dist/drawn.js +58 -0
  30. package/dist/drawn.js.map +1 -0
  31. package/dist/edge-signs.d.ts +13 -0
  32. package/dist/edge-signs.d.ts.map +1 -0
  33. package/dist/edge-signs.js +76 -0
  34. package/dist/edge-signs.js.map +1 -0
  35. package/dist/hooks.d.ts +160 -0
  36. package/dist/hooks.d.ts.map +1 -0
  37. package/dist/hooks.js +457 -0
  38. package/dist/hooks.js.map +1 -0
  39. package/dist/index.d.ts +37 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +22 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/keyboard.d.ts +5 -0
  44. package/dist/keyboard.d.ts.map +1 -0
  45. package/dist/keyboard.js +178 -0
  46. package/dist/keyboard.js.map +1 -0
  47. package/dist/kit.d.ts +10 -0
  48. package/dist/kit.d.ts.map +1 -0
  49. package/dist/kit.js +32 -0
  50. package/dist/kit.js.map +1 -0
  51. package/dist/local-intelligence.d.ts +31 -0
  52. package/dist/local-intelligence.d.ts.map +1 -0
  53. package/dist/local-intelligence.js +66 -0
  54. package/dist/local-intelligence.js.map +1 -0
  55. package/dist/motion.d.ts +17 -0
  56. package/dist/motion.d.ts.map +1 -0
  57. package/dist/motion.js +26 -0
  58. package/dist/motion.js.map +1 -0
  59. package/dist/occupants.d.ts +45 -0
  60. package/dist/occupants.d.ts.map +1 -0
  61. package/dist/occupants.js +57 -0
  62. package/dist/occupants.js.map +1 -0
  63. package/dist/parallel.d.ts +19 -0
  64. package/dist/parallel.d.ts.map +1 -0
  65. package/dist/parallel.js +30 -0
  66. package/dist/parallel.js.map +1 -0
  67. package/dist/picking.d.ts +30 -0
  68. package/dist/picking.d.ts.map +1 -0
  69. package/dist/picking.js +140 -0
  70. package/dist/picking.js.map +1 -0
  71. package/dist/plots.d.ts +106 -0
  72. package/dist/plots.d.ts.map +1 -0
  73. package/dist/plots.js +306 -0
  74. package/dist/plots.js.map +1 -0
  75. package/dist/pointer.d.ts +27 -0
  76. package/dist/pointer.d.ts.map +1 -0
  77. package/dist/pointer.js +39 -0
  78. package/dist/pointer.js.map +1 -0
  79. package/dist/presence.d.ts +110 -0
  80. package/dist/presence.d.ts.map +1 -0
  81. package/dist/presence.js +280 -0
  82. package/dist/presence.js.map +1 -0
  83. package/dist/rails.d.ts +11 -0
  84. package/dist/rails.d.ts.map +1 -0
  85. package/dist/rails.js +15 -0
  86. package/dist/rails.js.map +1 -0
  87. package/dist/resolved-view.d.ts +78 -0
  88. package/dist/resolved-view.d.ts.map +1 -0
  89. package/dist/resolved-view.js +365 -0
  90. package/dist/resolved-view.js.map +1 -0
  91. package/dist/robot.d.ts +114 -0
  92. package/dist/robot.d.ts.map +1 -0
  93. package/dist/robot.js +93 -0
  94. package/dist/robot.js.map +1 -0
  95. package/dist/routes.d.ts +50 -0
  96. package/dist/routes.d.ts.map +1 -0
  97. package/dist/routes.js +150 -0
  98. package/dist/routes.js.map +1 -0
  99. package/dist/scene-camera.d.ts +25 -0
  100. package/dist/scene-camera.d.ts.map +1 -0
  101. package/dist/scene-camera.js +130 -0
  102. package/dist/scene-camera.js.map +1 -0
  103. package/dist/scene-hand.d.ts +110 -0
  104. package/dist/scene-hand.d.ts.map +1 -0
  105. package/dist/scene-hand.js +317 -0
  106. package/dist/scene-hand.js.map +1 -0
  107. package/dist/scene-helpers.d.ts +46 -0
  108. package/dist/scene-helpers.d.ts.map +1 -0
  109. package/dist/scene-helpers.js +108 -0
  110. package/dist/scene-helpers.js.map +1 -0
  111. package/dist/scene-lines.d.ts +76 -0
  112. package/dist/scene-lines.d.ts.map +1 -0
  113. package/dist/scene-lines.js +578 -0
  114. package/dist/scene-lines.js.map +1 -0
  115. package/dist/scene-root.d.ts +71 -0
  116. package/dist/scene-root.d.ts.map +1 -0
  117. package/dist/scene-root.js +790 -0
  118. package/dist/scene-root.js.map +1 -0
  119. package/dist/scene.d.ts +17 -0
  120. package/dist/scene.d.ts.map +1 -0
  121. package/dist/scene.js +17 -0
  122. package/dist/scene.js.map +1 -0
  123. package/dist/seat-marks.d.ts +35 -0
  124. package/dist/seat-marks.d.ts.map +1 -0
  125. package/dist/seat-marks.js +43 -0
  126. package/dist/seat-marks.js.map +1 -0
  127. package/dist/settings.d.ts +30 -0
  128. package/dist/settings.d.ts.map +1 -0
  129. package/dist/settings.js +73 -0
  130. package/dist/settings.js.map +1 -0
  131. package/dist/view-boundary.d.ts +33 -0
  132. package/dist/view-boundary.d.ts.map +1 -0
  133. package/dist/view-boundary.js +53 -0
  134. package/dist/view-boundary.js.map +1 -0
  135. package/dist/view-host.d.ts +57 -0
  136. package/dist/view-host.d.ts.map +1 -0
  137. package/dist/view-host.js +474 -0
  138. package/dist/view-host.js.map +1 -0
  139. package/dist/view-registry.d.ts +122 -0
  140. package/dist/view-registry.d.ts.map +1 -0
  141. package/dist/view-registry.js +20 -0
  142. package/dist/view-registry.js.map +1 -0
  143. package/dist/where-drawn.d.ts +151 -0
  144. package/dist/where-drawn.d.ts.map +1 -0
  145. package/dist/where-drawn.js +231 -0
  146. package/dist/where-drawn.js.map +1 -0
  147. package/package.json +60 -0
@@ -0,0 +1,39 @@
1
+ export function createPointerStore() {
2
+ let point = null;
3
+ const listeners = new Set();
4
+ const watchers = new Set();
5
+ const tell = (active) => {
6
+ for (const watcher of watchers)
7
+ watcher(active);
8
+ };
9
+ return {
10
+ subscribe(listener) {
11
+ listeners.add(listener);
12
+ if (listeners.size === 1)
13
+ tell(true);
14
+ return () => {
15
+ listeners.delete(listener);
16
+ if (listeners.size === 0) {
17
+ point = null;
18
+ tell(false);
19
+ }
20
+ };
21
+ },
22
+ snapshot: () => point,
23
+ set(next) {
24
+ if (point === next || (point && next && point.x === next.x && point.y === next.y))
25
+ return;
26
+ point = next;
27
+ for (const listener of listeners)
28
+ listener();
29
+ },
30
+ get listeners() {
31
+ return listeners.size;
32
+ },
33
+ onActive(watcher) {
34
+ watchers.add(watcher);
35
+ return () => void watchers.delete(watcher);
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=pointer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointer.js","sourceRoot":"","sources":["../src/pointer.ts"],"names":[],"mappings":"AA2BA,MAAM,UAAU,kBAAkB;IAChC,IAAI,KAAK,GAAsB,IAAI,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;IACtD,MAAM,IAAI,GAAG,CAAC,MAAe,EAAE,EAAE;QAC/B,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC,CAAC;IACF,OAAO;QACL,SAAS,CAAC,QAAQ;YAChB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,OAAO,GAAG,EAAE;gBACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC3B,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBACzB,KAAK,GAAG,IAAI,CAAC;oBACb,IAAI,CAAC,KAAK,CAAC,CAAC;gBACd,CAAC;YACH,CAAC,CAAC;QACJ,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK;QACrB,GAAG,CAAC,IAAI;YACN,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;gBAAE,OAAO;YAC1F,KAAK,GAAG,IAAI,CAAC;YACb,KAAK,MAAM,QAAQ,IAAI,SAAS;gBAAE,QAAQ,EAAE,CAAC;QAC/C,CAAC;QACD,IAAI,SAAS;YACX,OAAO,SAAS,CAAC,IAAI,CAAC;QACxB,CAAC;QACD,QAAQ,CAAC,OAAO;YACd,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { type AnySchema, type Presence, type PresenceChannel, type Principal, type SettingDeclaration, type Store } from "@graview/core";
2
+ import { type ViewState } from "@graview/layout";
3
+ import type { DrawnBox } from "./context.js";
4
+ import { type RobotState } from "./robot.js";
5
+ /**
6
+ * ONE ID PER TAB, kept for the tab's life. It fills `Author.session` on
7
+ * this tab's ops AND the presence key, so the log's `participantOf(op)`
8
+ * and the figure standing on the map are the same person by construction.
9
+ * (Before this, every seat's session was "ui", and two tabs of one seat
10
+ * were one participant.)
11
+ */
12
+ export declare function tabSession(): string;
13
+ /**
14
+ * PRIVACY IS A READER SETTING, in the profile pane with the others and kept
15
+ * in the browser, never the graph. Where you are is shared by default for
16
+ * a named seat and never for an anonymous one; what you point at is
17
+ * private until you say otherwise.
18
+ */
19
+ export declare const SHARE_WHERE: SettingDeclaration;
20
+ export declare const SHARE_OVER: SettingDeclaration;
21
+ export declare const PRESENCE_SETTINGS: readonly SettingDeclaration[];
22
+ /** How often a tab says where it is, unprompted: the served poll's own pace. */
23
+ export declare const HEARTBEAT_MS = 800;
24
+ export type Placed = {
25
+ readonly kind: "person";
26
+ readonly presence: Presence;
27
+ readonly point: {
28
+ x: number;
29
+ y: number;
30
+ };
31
+ /** What they stand at: a plot, or the audience row of a screen. */
32
+ readonly at: string;
33
+ readonly index: number;
34
+ readonly of: number;
35
+ readonly audience: boolean;
36
+ } | {
37
+ readonly kind: "edge";
38
+ readonly presence: Presence;
39
+ readonly point: {
40
+ x: number;
41
+ y: number;
42
+ };
43
+ } | {
44
+ readonly kind: "count";
45
+ readonly at: string;
46
+ readonly point: {
47
+ x: number;
48
+ y: number;
49
+ };
50
+ readonly n: number;
51
+ } | {
52
+ readonly kind: "robot";
53
+ readonly presence: Presence;
54
+ readonly point: {
55
+ x: number;
56
+ y: number;
57
+ };
58
+ readonly mode: string;
59
+ } | {
60
+ readonly kind: "over";
61
+ readonly presence: Presence;
62
+ readonly box: DrawnBox;
63
+ };
64
+ /** How many stand in a row before the rest are a number. */
65
+ export declare const AUDIENCE_ROW = 3;
66
+ /** Where a stop puts somebody: the audience row of the showing they are watching, else the plot their focus names. */
67
+ export declare function anchorOf(stop: string): {
68
+ readonly at: string;
69
+ readonly audience: boolean;
70
+ } | null;
71
+ /**
72
+ * THE OTHERS, PLACED. Pure — a test can ask what a viewer would draw from
73
+ * a list of presences and a `whereIs`. People at one anchor stand in a
74
+ * row; past `AUDIENCE_ROW` the rest are "+n". Anonymous viewers are a count
75
+ * at their plot, never a figure. Somebody whose stop is nowhere on this
76
+ * map is an indicator at the edge, named. A robot stands where its owner's
77
+ * presence says, captioned as theirs; a shared hover is an outline.
78
+ */
79
+ export declare function placeOthers(who: readonly Presence[], whereIs: (id: string) => DrawnBox | null, width: number): Placed[];
80
+ export interface PresenceInputs<S extends AnySchema> {
81
+ readonly channel: PresenceChannel | undefined;
82
+ readonly store: Store<S>;
83
+ readonly view: ViewState;
84
+ readonly principal: Principal;
85
+ readonly session: string;
86
+ readonly robots: ReadonlyMap<string, RobotState>;
87
+ readonly seatWho: string | null;
88
+ readonly settingValues: Readonly<Record<string, string>>;
89
+ setView(next: ViewState | ((current: ViewState) => ViewState)): void;
90
+ }
91
+ export interface PresenceState {
92
+ /** The others, by participant. Empty without a channel. */
93
+ readonly who: ReadonlyMap<string, Presence>;
94
+ /** How this tab is seen, or null when it broadcasts nothing. */
95
+ readonly sharing: {
96
+ readonly participant: string;
97
+ readonly name: string;
98
+ } | null;
99
+ /** Whose stop this tab is adopting, if anybody's. */
100
+ readonly following: Presence | null;
101
+ follow(participant: string | null): void;
102
+ }
103
+ /**
104
+ * Owned by the provider beside the activity and the robots. Publishes on
105
+ * view change, robot change and heartbeat; folds what the channel says
106
+ * into `who`; adopts a followed person's stop until you move yourself or
107
+ * press Escape. Nothing ever writes presence to the store.
108
+ */
109
+ export declare function usePresenceState<S extends AnySchema>(inputs: PresenceInputs<S>): PresenceState;
110
+ //# sourceMappingURL=presence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,280 @@
1
+ import { hueFor } from "@graview/core";
2
+ import { fromUrl, kindsOfAggregate, sameView, toUrl } from "@graview/layout";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { pickedFrom } from "./picking.js";
5
+ import { participantOf as keyOf } from "./robot.js";
6
+ /*
7
+ * WHO IS WHERE — the others, on your own map.
8
+ *
9
+ * Presence is a payload and never a pixel: a participant, the stop they are
10
+ * on (`toUrl(view)`, the complete answer), what they are hovering if they
11
+ * chose to share it, where their robot stands, and when they last said so.
12
+ * Each viewer places the others with its own `whereIs`, so two windows of
13
+ * different sizes agree about who is at the tasks plot while agreeing about
14
+ * nothing else. Nothing here writes to the store: a person standing
15
+ * somewhere is a fact about that person at that screen, like their text
16
+ * size — the recorded precedent is `settings.ts`.
17
+ */
18
+ const SESSION_KEY = "graview:session";
19
+ /**
20
+ * ONE ID PER TAB, kept for the tab's life. It fills `Author.session` on
21
+ * this tab's ops AND the presence key, so the log's `participantOf(op)`
22
+ * and the figure standing on the map are the same person by construction.
23
+ * (Before this, every seat's session was "ui", and two tabs of one seat
24
+ * were one participant.)
25
+ */
26
+ export function tabSession() {
27
+ const fresh = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID().slice(0, 8) : Math.random().toString(36).slice(2, 10);
28
+ try {
29
+ const held = sessionStorage.getItem(SESSION_KEY);
30
+ if (held)
31
+ return held;
32
+ const made = fresh();
33
+ sessionStorage.setItem(SESSION_KEY, made);
34
+ return made;
35
+ }
36
+ catch {
37
+ // A private window or a test: this visit still has a session, it is just not remembered.
38
+ return fresh();
39
+ }
40
+ }
41
+ /**
42
+ * PRIVACY IS A READER SETTING, in the profile pane with the others and kept
43
+ * in the browser, never the graph. Where you are is shared by default for
44
+ * a named seat and never for an anonymous one; what you point at is
45
+ * private until you say otherwise.
46
+ */
47
+ export const SHARE_WHERE = {
48
+ name: "share-where",
49
+ title: "Where you are",
50
+ description: "Whether the others here see which stop you are on.",
51
+ honoured: "root-attribute",
52
+ options: [
53
+ { value: "shared", label: "Shown" },
54
+ { value: "private", label: "Hidden" },
55
+ ],
56
+ initial: "shared",
57
+ };
58
+ export const SHARE_OVER = {
59
+ name: "share-over",
60
+ title: "What you point at",
61
+ description: "Whether the others see the thing under your pointer, outlined in your colour.",
62
+ honoured: "root-attribute",
63
+ // Four labels, all different: two pairs reading "Seen / Private" put the
64
+ // same word twice on the pane, which the audit counts as the repeat it is.
65
+ options: [
66
+ { value: "private", label: "Kept to yourself" },
67
+ { value: "shared", label: "Outlined for others" },
68
+ ],
69
+ initial: "private",
70
+ };
71
+ export const PRESENCE_SETTINGS = [SHARE_WHERE, SHARE_OVER];
72
+ /** How often a tab says where it is, unprompted: the served poll's own pace. */
73
+ export const HEARTBEAT_MS = 800;
74
+ /** How many stand in a row before the rest are a number. */
75
+ export const AUDIENCE_ROW = 3;
76
+ /** Where a stop puts somebody: the audience row of the showing they are watching, else the plot their focus names. */
77
+ export function anchorOf(stop) {
78
+ const view = fromUrl(stop);
79
+ const focus = view.focusId;
80
+ if (!focus)
81
+ return null;
82
+ const showing = view.within?.["view"];
83
+ if (showing) {
84
+ const [kind] = kindsOfAggregate(focus);
85
+ if (kind)
86
+ return { at: `screen:${kind}`, audience: true };
87
+ }
88
+ return { at: focus, audience: false };
89
+ }
90
+ /**
91
+ * THE OTHERS, PLACED. Pure — a test can ask what a viewer would draw from
92
+ * a list of presences and a `whereIs`. People at one anchor stand in a
93
+ * row; past `AUDIENCE_ROW` the rest are "+n". Anonymous viewers are a count
94
+ * at their plot, never a figure. Somebody whose stop is nowhere on this
95
+ * map is an indicator at the edge, named. A robot stands where its owner's
96
+ * presence says, captioned as theirs; a shared hover is an outline.
97
+ */
98
+ export function placeOthers(who, whereIs, width) {
99
+ const placed = [];
100
+ const rows = new Map();
101
+ const counts = new Map();
102
+ let edges = 0;
103
+ for (const presence of who) {
104
+ const anchor = anchorOf(presence.stop);
105
+ const box = anchor ? whereIs(anchor.at) : null;
106
+ if (!anchor || !box) {
107
+ if (presence.name) {
108
+ placed.push({ kind: "edge", presence, point: { x: width - 8, y: 40 + edges++ * 28 } });
109
+ }
110
+ continue;
111
+ }
112
+ if (!presence.name) {
113
+ const held = counts.get(anchor.at);
114
+ counts.set(anchor.at, { n: (held?.n ?? 0) + 1, box });
115
+ continue;
116
+ }
117
+ const row = rows.get(anchor.at) ?? [];
118
+ row.push(presence);
119
+ rows.set(anchor.at, row);
120
+ if (row.length > AUDIENCE_ROW)
121
+ continue;
122
+ const anchorBox = box;
123
+ const index = row.length - 1;
124
+ // A row in front of the screen; a huddle at a plot's foot, a little left of its robot.
125
+ const point = anchor.audience
126
+ ? { x: anchorBox.x + anchorBox.width * 0.5 + (index - 1) * 26, y: anchorBox.y + Math.min(anchorBox.height, 24) }
127
+ : { x: anchorBox.x + anchorBox.width * 0.5 - 26 - index * 22, y: anchorBox.y + anchorBox.height - 4 };
128
+ placed.push({ kind: "person", presence, point, at: anchor.at, index, of: row.length, audience: anchor.audience });
129
+ }
130
+ for (const [at, row] of rows) {
131
+ if (row.length <= AUDIENCE_ROW)
132
+ continue;
133
+ const box = whereIs(at);
134
+ if (!box)
135
+ continue;
136
+ placed.push({ kind: "count", at, point: { x: box.x + box.width * 0.5 + 2 * 26 + 18, y: box.y + box.height - 4 }, n: row.length - AUDIENCE_ROW });
137
+ }
138
+ for (const [at, { n, box }] of counts) {
139
+ placed.push({ kind: "count", at, point: { x: box.x + box.width * 0.5 + 40, y: box.y + box.height - 4 }, n });
140
+ }
141
+ for (const presence of who) {
142
+ if (!presence.name)
143
+ continue;
144
+ if (presence.robot?.at && presence.robot.mode !== "docked") {
145
+ const box = whereIs(presence.robot.at);
146
+ if (box)
147
+ placed.push({ kind: "robot", presence, point: { x: box.x + box.width / 2 + 22, y: box.y + box.height - 4 }, mode: presence.robot.mode });
148
+ }
149
+ if (presence.over) {
150
+ const box = whereIs(presence.over);
151
+ if (box)
152
+ placed.push({ kind: "over", presence, box });
153
+ }
154
+ }
155
+ return placed;
156
+ }
157
+ /**
158
+ * Owned by the provider beside the activity and the robots. Publishes on
159
+ * view change, robot change and heartbeat; folds what the channel says
160
+ * into `who`; adopts a followed person's stop until you move yourself or
161
+ * press Escape. Nothing ever writes presence to the store.
162
+ */
163
+ export function usePresenceState(inputs) {
164
+ const { channel, store, view, principal, session, robots, seatWho, settingValues, setView } = inputs;
165
+ const [who, setWho] = useState(() => new Map());
166
+ const [over, setOver] = useState(null);
167
+ const [followingId, setFollowingId] = useState(null);
168
+ const named = typeof principal.id === "string" && principal.id.length > 0;
169
+ const shareWhere = named && (settingValues[SHARE_WHERE.name] ?? SHARE_WHERE.initial) === "shared";
170
+ const shareOver = shareWhere && (settingValues[SHARE_OVER.name] ?? SHARE_OVER.initial) === "shared";
171
+ const participant = keyOf({ kind: principal.kind, ...(principal.id ? { id: principal.id } : {}), session });
172
+ const name = named ? String(store.graph.getNode(principal.id)?.label ?? principal.id) : "";
173
+ /* What this tab says about itself — recomputed when anything in it moves. */
174
+ const robot = seatWho ? robots.get(`agent:${seatWho}:${session}`) : undefined;
175
+ const mine = useMemo(() => {
176
+ if (!channel || !shareWhere)
177
+ return null;
178
+ return {
179
+ participant,
180
+ name,
181
+ hue: hueFor(principal.id ?? "nobody"),
182
+ stop: toUrl(view),
183
+ ...(shareOver ? { over } : {}),
184
+ ...(robot ? { robot: { at: robot.at, mode: robot.mode } } : {}),
185
+ at: new Date().toISOString(),
186
+ };
187
+ }, [channel, shareWhere, shareOver, participant, name, principal.id, view, over, robot?.at, robot?.mode]);
188
+ /* Say it: on change, and on the heartbeat while it stands. */
189
+ useEffect(() => {
190
+ if (!channel)
191
+ return;
192
+ if (!mine)
193
+ return;
194
+ channel.here(mine);
195
+ const beat = setInterval(() => channel.here({ ...mine, at: new Date().toISOString() }), HEARTBEAT_MS);
196
+ return () => clearInterval(beat);
197
+ }, [channel, mine]);
198
+ /* Hear it. */
199
+ useEffect(() => {
200
+ if (!channel)
201
+ return;
202
+ const stop = channel.onWho((others) => setWho(new Map(others.map((presence) => [presence.participant, presence]))));
203
+ return stop;
204
+ }, [channel]);
205
+ /* Leave when the page goes, not only when React unmounts. */
206
+ useEffect(() => {
207
+ if (!channel || typeof window === "undefined")
208
+ return;
209
+ const gone = () => channel.leave();
210
+ window.addEventListener("pagehide", gone);
211
+ return () => window.removeEventListener("pagehide", gone);
212
+ }, [channel]);
213
+ /* What you point at, only while you share it: read the way a click reads it. */
214
+ useEffect(() => {
215
+ if (!shareOver || typeof document === "undefined")
216
+ return;
217
+ let last = null;
218
+ const moved = (event) => {
219
+ const picked = pickedFrom(event.target);
220
+ if (picked === last)
221
+ return;
222
+ last = picked;
223
+ setOver(picked);
224
+ };
225
+ document.addEventListener("pointermove", moved, { passive: true });
226
+ return () => document.removeEventListener("pointermove", moved);
227
+ }, [shareOver]);
228
+ /* FOLLOW A PERSON: adopt their stop as it changes, until you move yourself or press Escape. */
229
+ const adopted = useRef(null);
230
+ const following = followingId ? (who.get(followingId) ?? null) : null;
231
+ const follow = useCallback((next) => {
232
+ adopted.current = null;
233
+ setFollowingId(next);
234
+ }, []);
235
+ useEffect(() => {
236
+ if (!followingId)
237
+ return;
238
+ if (!following) {
239
+ // They left. Nothing to follow.
240
+ setFollowingId(null);
241
+ return;
242
+ }
243
+ if (following.stop === adopted.current?.stop)
244
+ return;
245
+ adopted.current = { stop: following.stop, at: Date.now() };
246
+ setView(fromUrl(following.stop));
247
+ }, [followingId, following, setView]);
248
+ useEffect(() => {
249
+ const held = adopted.current;
250
+ if (!followingId || held === null)
251
+ return;
252
+ /*
253
+ * A stop of your own is you leaving: the mirror of navigating away from
254
+ * a robot. Compared as views, not strings, and not in the moment of
255
+ * landing — the provider resolves an adopted stop against what this
256
+ * seat may see, and that resolution is arrival, not departure.
257
+ */
258
+ if (Date.now() - held.at < 400)
259
+ return;
260
+ if (!sameView(view, fromUrl(held.stop)))
261
+ setFollowingId(null);
262
+ }, [view, followingId]);
263
+ useEffect(() => {
264
+ if (!followingId || typeof document === "undefined")
265
+ return;
266
+ const onKey = (event) => {
267
+ if (event.key === "Escape")
268
+ setFollowingId(null);
269
+ };
270
+ document.addEventListener("keydown", onKey);
271
+ return () => document.removeEventListener("keydown", onKey);
272
+ }, [followingId]);
273
+ return useMemo(() => ({
274
+ who,
275
+ sharing: mine ? { participant, name } : null,
276
+ following,
277
+ follow,
278
+ }), [who, mine, participant, name, following, follow]);
279
+ }
280
+ //# sourceMappingURL=presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The rail the layout keeps clear on each side of the picture, by width.
3
+ * A full-size scene reserves 264 and 128; a narrow one, where the panes
4
+ * are sheets rather than rails, keeps only the corner the altitude control
5
+ * stands in.
6
+ */
7
+ export declare function railInset(width: number): {
8
+ left: number;
9
+ right: number;
10
+ };
11
+ //# sourceMappingURL=rails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rails.d.ts","sourceRoot":"","sources":["../src/rails.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAMxE"}
package/dist/rails.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The rail the layout keeps clear on each side of the picture, by width.
3
+ * A full-size scene reserves 264 and 128; a narrow one, where the panes
4
+ * are sheets rather than rails, keeps only the corner the altitude control
5
+ * stands in.
6
+ */
7
+ export function railInset(width) {
8
+ if (width < 640)
9
+ return { left: 8, right: 56 };
10
+ return {
11
+ left: Math.round(Math.min(264, width * 0.22)),
12
+ right: Math.round(Math.min(128, width * 0.107)),
13
+ };
14
+ }
15
+ //# sourceMappingURL=rails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rails.js","sourceRoot":"","sources":["../src/rails.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,KAAK,GAAG,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC/C,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;QAC7C,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;KAChD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ import type { AnySchema, Fidelity } from "@graview/core";
2
+ import { type ReactNode } from "react";
3
+ import { type ViewMode } from "./context.js";
4
+ import type { SceneNode } from "./scene-root.js";
5
+ export interface ResolvedViewProps {
6
+ readonly node: SceneNode;
7
+ readonly mode: ViewMode;
8
+ readonly selected: boolean;
9
+ /** Overrides the fidelity the plane would ask for. Jack-in uses this. */
10
+ readonly fidelity?: Fidelity;
11
+ }
12
+ /**
13
+ * THE DISTRICTS THE ROW COULD NOT HOLD, NAMED — BEHIND ONE PRESS.
14
+ *
15
+ * A district is read rather than glanced at, so the row never squeezes a
16
+ * name below a word: past what it can hold at a legible width it keeps the
17
+ * ones that fit and hands the rest to this. Not a district — it has no
18
+ * members, no figure and no count of its own — a card that says how many
19
+ * are missing and takes you to any of them.
20
+ *
21
+ * It used to list every name inside its own box, which is a district
22
+ * card's height: room for the count and nothing else, the names scrolled
23
+ * away under a fade, and "+6" over one clipped word read as a broken
24
+ * placeholder rather than the only way to five districts. So the card IS
25
+ * the control now — "+6 more" is a button — and pressing it opens a panel
26
+ * above the row that names every district with what it holds, each name
27
+ * a press to that district. The district you are already in, which lands
28
+ * here when the row has room for nothing else, is marked as here rather
29
+ * than offered as somewhere to go.
30
+ */
31
+ export declare function BeyondCard({ kinds }: {
32
+ kinds: readonly string[];
33
+ }): import("react").JSX.Element;
34
+ /**
35
+ * THE PICTURE DOES NOT REDRAW ITSELF BECAUSE THE WORLD MOVED.
36
+ *
37
+ * `panLayout` hands every node a new object per frame — it has moved, after
38
+ * all — and React took that at face value and re-rendered every lens in the
39
+ * scene on every pointer move of a drag. A profile of one drag across rota's
40
+ * city put a fifth of the main thread inside the calendar, the coverage
41
+ * matrix and the timeline redrawing themselves, none of which had anything
42
+ * new to say: a lens is drawn in its host's own coordinates and does not
43
+ * know where the host is.
44
+ *
45
+ * So a view redraws when its picture changes, not when its position does.
46
+ */
47
+ export declare const SettledView: typeof ResolvedView;
48
+ /**
49
+ * Picks the view for a cell of the matrix — cardinality by whether this is an
50
+ * aggregate, fidelity by plane depth — and renders it.
51
+ *
52
+ * A kind with no registered view falls back to the primitive the app
53
+ * supplied, which is why a new node kind renders sensibly before anyone
54
+ * writes a view for it.
55
+ */
56
+ export declare function ResolvedView<S extends AnySchema>({ node, mode, selected, fidelity, }: ResolvedViewProps): import("react").JSX.Element;
57
+ /**
58
+ * One view, redrawn when its signature changes and not otherwise. The
59
+ * element is built inside a memo, so a parent re-render with the same
60
+ * signature hands React the same element and it skips the subtree.
61
+ */
62
+ export declare const Drawn: import("react").NamedExoticComponent<{
63
+ readonly signature: string;
64
+ readonly graph: unknown;
65
+ readonly draw: () => ReactNode;
66
+ }>;
67
+ /**
68
+ * A RELATION THE BAND COULD NOT HOLD, drawn as what it is (docs/scale.md):
69
+ * a group in the graph's own words with a true count and the first names
70
+ * in it — the most relevant — or the "+N more" door to the kind's picture.
71
+ * Not the kind's own lens squeezed into a card: a coverage matrix at 180
72
+ * pixels says nothing and costs thousands of elements. Opened by the host's
73
+ * own press (a double-click, or Enter), like every card.
74
+ */
75
+ export declare const BandCard: import("react").NamedExoticComponent<{
76
+ readonly node: SceneNode;
77
+ }>;
78
+ //# sourceMappingURL=resolved-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolved-view.d.ts","sourceRoot":"","sources":["../src/resolved-view.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAgB,MAAM,eAAe,CAAC;AAGvE,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGnF,OAAO,EAAoD,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG/F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,+BA4IjE;AAyCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,EAKnB,OAAO,YAAY,CAAC;AAEzB;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,EAChD,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,QAAQ,GACT,EAAE,iBAAiB,+BAoInB;AAED;;;;GAIG;AACH,eAAO,MAAM,KAAK;wBAC+B,MAAM;oBAAkB,OAAO;mBAAiB,MAAM,SAAS;EAI/G,CAAC;AA0BF;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;mBAAqD,SAAS;EAgDjF,CAAC"}