@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,317 @@
1
+ import { withOverview, withPan, withPin } from "@graview/layout";
2
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
3
+ /**
4
+ * THE DISTRICT UNDER THE HAND, placed in the scene while it is dragged —
5
+ * at most once a frame, since pointer events arrive faster than frames are
6
+ * drawn — and handed back on release to be written into the view once.
7
+ */
8
+ export function useHeldDistrict() {
9
+ const [held, setHeld] = useState(null);
10
+ const placing = useRef(null);
11
+ const frame = useRef(0);
12
+ const place = useCallback((next) => {
13
+ placing.current = next;
14
+ if (frame.current !== 0)
15
+ return;
16
+ frame.current = requestAnimationFrame(() => {
17
+ frame.current = 0;
18
+ setHeld(placing.current);
19
+ });
20
+ }, []);
21
+ /** Letting go: where the hand last put it, if it put it anywhere. */
22
+ const release = useCallback(() => {
23
+ if (frame.current !== 0)
24
+ cancelAnimationFrame(frame.current);
25
+ frame.current = 0;
26
+ const put = placing.current;
27
+ placing.current = null;
28
+ if (put)
29
+ setHeld(null);
30
+ return put;
31
+ }, []);
32
+ return { held, place, release };
33
+ }
34
+ /**
35
+ * THE HAND MOVES THE PICTURE; ONLY LETTING GO MOVES THE WORLD.
36
+ *
37
+ * While a hand is on it, the offset it has made is painted straight onto
38
+ * the world's layers as a transform, once per animation frame, outside
39
+ * React entirely. Letting go writes it into the view as a pan, and the
40
+ * transform is cleared once that has been committed, so the picture never
41
+ * jumps back for a frame. `settled` is told when the gesture is over — the
42
+ * world already where the hand left it, so nothing tweens its way there.
43
+ *
44
+ * The GPU path keeps the old road: its stage is a `layoutsubtree` canvas,
45
+ * and combining that with CSS transforms crashes the renderer. It is not
46
+ * the default path and it is not the measured one.
47
+ */
48
+ export function useWorldShift({ stage, pan, setView, settled, }) {
49
+ const live = useRef({ x: 0, y: 0 });
50
+ const painting = useRef(0);
51
+ const settling = useRef(false);
52
+ const settledLive = useRef(settled);
53
+ settledLive.current = settled;
54
+ const layers = useCallback(() => [...(stage.current?.querySelectorAll("[data-graview-world]") ?? [])], [stage]);
55
+ const paint = useCallback(() => {
56
+ if (painting.current !== 0)
57
+ return;
58
+ painting.current = requestAnimationFrame(() => {
59
+ painting.current = 0;
60
+ const { x, y } = live.current;
61
+ const move = x === 0 && y === 0 ? "" : `translate3d(${x}px, ${y}px, 0)`;
62
+ for (const layer of layers())
63
+ layer.style.transform = move;
64
+ });
65
+ }, [layers]);
66
+ const clear = useCallback(() => {
67
+ settling.current = false;
68
+ for (const layer of layers())
69
+ layer.style.transform = "";
70
+ settledLive.current();
71
+ }, [layers]);
72
+ /** Where the hand has taken the picture, against where the view says it is. */
73
+ const shiftTo = useCallback((wanted, from) => {
74
+ live.current = { x: wanted.x - from.x, y: wanted.y - from.y };
75
+ paint();
76
+ }, [paint]);
77
+ /** Letting go: the offset becomes the view's own pan. False when there was none. */
78
+ const settle = useCallback(() => {
79
+ const { x, y } = live.current;
80
+ if (x === 0 && y === 0)
81
+ return false;
82
+ live.current = { x: 0, y: 0 };
83
+ settling.current = true;
84
+ setView((current) => {
85
+ const at = current.pan ?? { x: 0, y: 0 };
86
+ return withPan(current, { x: at.x + x, y: at.y + y });
87
+ });
88
+ /*
89
+ * AND IF THE VIEW NEVER ARRIVES, LET GO ANYWAY. A scene whose view is
90
+ * controlled from outside may decline the change, and then nothing
91
+ * would clear the transform. LATE ON PURPOSE: a `requestAnimationFrame`
92
+ * here beat React to the commit, and every release flashed the whole
93
+ * city back to where the drag had started for one frame.
94
+ */
95
+ window.setTimeout(() => {
96
+ if (settling.current)
97
+ clear();
98
+ }, 300);
99
+ return true;
100
+ }, [setView, clear]);
101
+ /*
102
+ * CLEARED ONLY ONCE THE WORLD HAS ACTUALLY MOVED. Clearing the transform
103
+ * in the same breath as the state change put the picture back where it
104
+ * started for one frame, because React had not committed the new pan yet.
105
+ */
106
+ useLayoutEffect(() => {
107
+ if (settling.current)
108
+ clear();
109
+ }, [pan, clear]);
110
+ /** The offset the hand has made so far, not yet in the view. */
111
+ const offset = useCallback(() => live.current, []);
112
+ return { shiftTo, settle, settling, offset };
113
+ }
114
+ /** How far a pointer has to travel before a press is a drag: below it, a click is a click. */
115
+ const DRAG_THRESHOLD = 4;
116
+ /**
117
+ * THE GESTURES THEMSELVES. The threshold is what keeps a click a click:
118
+ * below it nothing has happened and the pointer-up is an ordinary
119
+ * selection; above it the gesture owns the pointer and the click that
120
+ * follows is swallowed.
121
+ */
122
+ export function useSceneDrag({ view, panned, limit, panWithin, moveByTransform, setView, setDragging, noteMoved, shift, district, }) {
123
+ /** Set for the click a drag is about to produce, so a drag that ends on a card does not also select it. */
124
+ const swallow = useRef(false);
125
+ const gesture = useRef(null);
126
+ const panFrom = (event) => {
127
+ const pan = view.pan ?? { x: 0, y: 0 };
128
+ gesture.current = { kind: "pan", fromX: event.clientX, fromY: event.clientY, baseX: pan.x, baseY: pan.y, moved: false };
129
+ };
130
+ const onGroundDown = (event) => {
131
+ if (event.button !== 0)
132
+ return;
133
+ // Only the ground itself. A card, a chip or anything a view drew keeps
134
+ // whatever meaning it already had.
135
+ if (event.target.closest("[data-graview-view]"))
136
+ return;
137
+ panFrom(event);
138
+ };
139
+ const onCardDown = (node, event) => {
140
+ if (event.button !== 0)
141
+ return;
142
+ /*
143
+ * A PICTURE IS NOT A THING YOU REARRANGE — you look around it. A drag
144
+ * that starts on a billboard moves the view, unless it started on the
145
+ * rail, which is the one part of a billboard that means "move the
146
+ * board". A district's own card keeps its drag, because placing a
147
+ * district by hand is a real gesture with a dashed kerb to show for it.
148
+ */
149
+ if (node.screenOf !== undefined && !event.target.closest("[data-graview-grip]")) {
150
+ panFrom(event);
151
+ return;
152
+ }
153
+ gesture.current = {
154
+ kind: "card",
155
+ id: node.id,
156
+ fromX: event.clientX,
157
+ fromY: event.clientY,
158
+ // Unpanned, because that is the space a pin is stored in.
159
+ baseX: node.x - panned.x,
160
+ baseY: node.y - panned.y,
161
+ moved: false,
162
+ };
163
+ };
164
+ const onDragMove = (event) => {
165
+ const drag = gesture.current;
166
+ if (!drag)
167
+ return;
168
+ const dx = event.clientX - drag.fromX;
169
+ const dy = event.clientY - drag.fromY;
170
+ if (!drag.moved) {
171
+ if (Math.hypot(dx, dy) < DRAG_THRESHOLD)
172
+ return;
173
+ drag.moved = true;
174
+ setDragging(true);
175
+ /*
176
+ * Capture only once it IS a drag. Taken on pointer-down it redirected
177
+ * the click and double-click that followed to the host, and
178
+ * double-clicking a task opened the card instead of travelling into it.
179
+ */
180
+ event.currentTarget.setPointerCapture(event.pointerId);
181
+ }
182
+ if (drag.kind === "pan") {
183
+ // A little way over a picture that fits, and as far as the city reaches when it is bigger.
184
+ const wanted = panWithin(limit(), { x: drag.baseX + dx, y: drag.baseY + dy });
185
+ if (moveByTransform)
186
+ shift.shiftTo(wanted, view.pan ?? { x: 0, y: 0 });
187
+ else
188
+ setView((current) => withPan(current, wanted));
189
+ }
190
+ else if (drag.id) {
191
+ district.place({ id: drag.id, x: drag.baseX + dx, y: drag.baseY + dy });
192
+ }
193
+ };
194
+ const onDragUp = () => {
195
+ shift.settle();
196
+ // The district that was under the hand goes into the view, once, where the hand last put it.
197
+ const put = district.release();
198
+ if (put)
199
+ setView((current) => withPin(current, put.id, { x: put.x, y: put.y }));
200
+ if (gesture.current?.moved) {
201
+ swallow.current = true;
202
+ setTimeout(() => (swallow.current = false), 0);
203
+ // And say a hand moved something, so the bar can offer to put it back.
204
+ noteMoved();
205
+ }
206
+ gesture.current = null;
207
+ /*
208
+ * THE COMMIT A HAND MADE IS NOT A TRANSITION EITHER. The drag stays open
209
+ * until the pan it made has been drawn — the world shift closes it, in
210
+ * the same commit that moves the cards — or the whole city would fly
211
+ * from where the drag started to where it ended, after the hand had
212
+ * already put it there.
213
+ */
214
+ if (shift.settling.current)
215
+ return;
216
+ setDragging(false);
217
+ };
218
+ return { onGroundDown, onCardDown, onDragMove, onDragUp, swallow };
219
+ }
220
+ /**
221
+ * THE WHEEL AND THE PINCH. `zoomAbout` and `panBy` are filled in by the
222
+ * scene once it knows its city; the listeners call through them, so they
223
+ * are attached once rather than on every zoom.
224
+ */
225
+ export function useWheelAndPinch({ stage, view, setView, }) {
226
+ /*
227
+ * PINCH IS ALTITUDE. The camera has one axis, so the universal zoom
228
+ * gesture maps to it: fingers together rises to the Graview, fingers
229
+ * apart descends — one discrete step per gesture, with a cooldown so a
230
+ * long pinch does not bounce. Chromium and Firefox hand a trackpad
231
+ * pinch over as ctrl+wheel; Safari speaks GestureEvent. Both are
232
+ * claimed here so the browser's own page zoom never fires on the scene.
233
+ */
234
+ const altitude = useRef({ view, charge: 0, coolUntil: 0, lastScale: 1 });
235
+ altitude.current.view = view;
236
+ /*
237
+ * FROM ALTITUDE, PINCH AND CTRL+WHEEL ZOOM THE CITY — continuously, about
238
+ * the pointer, the way every map does — and the plain wheel pans the
239
+ * ground. Stepping the altitude once per gesture with a cooldown read as
240
+ * a zoom that sticks. From the ground, fingers together still rise: the
241
+ * way up is a gesture, the way down is the picture's own control.
242
+ */
243
+ const zoomAbout = useRef(() => { });
244
+ const panBy = useRef(() => { });
245
+ useEffect(() => {
246
+ const element = stage.current;
247
+ if (!element)
248
+ return;
249
+ const step = (rising, stamp) => {
250
+ const held = altitude.current;
251
+ if (stamp < held.coolUntil)
252
+ return;
253
+ const up = held.view.overview ?? false;
254
+ if (rising === up)
255
+ return;
256
+ held.coolUntil = stamp + 600;
257
+ held.charge = 0;
258
+ setView(withOverview(held.view, rising));
259
+ };
260
+ const onWheel = (event) => {
261
+ const held = altitude.current;
262
+ const up = held.view.overview ?? false;
263
+ if (event.ctrlKey) {
264
+ event.preventDefault();
265
+ if (!up) {
266
+ held.charge += event.deltaY;
267
+ if (Math.abs(held.charge) < 60)
268
+ return;
269
+ if (held.charge > 0)
270
+ step(true, performance.now());
271
+ else
272
+ held.charge = 0;
273
+ return;
274
+ }
275
+ // A mouse notch (a hundred) is a step and a half; a trackpad's few units are a nudge.
276
+ zoomAbout.current(Math.exp(-event.deltaY * 0.004), event.clientX, event.clientY);
277
+ return;
278
+ }
279
+ if (!up)
280
+ return;
281
+ // Over the ground only: a lens, a scroll region or a pane keeps its own wheel.
282
+ const target = event.target;
283
+ if (target?.closest("[data-graview-view], .graview-scroll, [data-graview-overlay], .graview-zoom"))
284
+ return;
285
+ event.preventDefault();
286
+ panBy.current(-event.deltaX, -event.deltaY);
287
+ };
288
+ const onGestureStart = (event) => {
289
+ event.preventDefault();
290
+ altitude.current.lastScale = 1;
291
+ };
292
+ const onGesture = (event) => {
293
+ event.preventDefault();
294
+ const held = altitude.current;
295
+ const scale = event.scale ?? 1;
296
+ if (!(held.view.overview ?? false)) {
297
+ if (scale < 0.72)
298
+ step(true, performance.now());
299
+ return;
300
+ }
301
+ const ratio = scale / (held.lastScale || 1);
302
+ held.lastScale = scale;
303
+ const at = event;
304
+ zoomAbout.current(ratio, at.clientX, at.clientY);
305
+ };
306
+ element.addEventListener("wheel", onWheel, { passive: false });
307
+ element.addEventListener("gesturestart", onGestureStart);
308
+ element.addEventListener("gesturechange", onGesture);
309
+ return () => {
310
+ element.removeEventListener("wheel", onWheel);
311
+ element.removeEventListener("gesturestart", onGestureStart);
312
+ element.removeEventListener("gesturechange", onGesture);
313
+ };
314
+ }, [setView, stage]);
315
+ return { zoomAbout, panBy };
316
+ }
317
+ //# sourceMappingURL=scene-hand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-hand.js","sourceRoot":"","sources":["../src/scene-hand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { Matrix4 } from "@graview/render";
2
+ import type { SceneNode } from "./scene-root.js";
3
+ /**
4
+ * What a click on a view selects.
5
+ *
6
+ * Selecting a GROUP selects its members, because a group is a view of a set
7
+ * of nodes rather than a node itself — "select the People block" means the
8
+ * people. Everything downstream then works unchanged: the affordance layer
9
+ * sees a selection of real nodes and can say what is true about them.
10
+ */
11
+ export declare function selectionFor(node: SceneNode, current: readonly string[], additive: boolean): string[];
12
+ /**
13
+ * WHAT ONE `rem` IS WORTH RIGHT NOW — the reader's own text size, watched.
14
+ *
15
+ * The scene's cards hold text sized in `rem` and were laid out in pixels,
16
+ * so a reader who asked for bigger words got them inside a city that had
17
+ * not moved: a headline in a glyph. The layout takes this as its unit, so
18
+ * the picture grows with the words.
19
+ *
20
+ * Read from the root rather than from a setting's name, because the scene
21
+ * has no business knowing what an app called its text-size control — the
22
+ * root font size is where every such control lands, including the browser's
23
+ * own, which no app declares at all.
24
+ */
25
+ export declare function useRootUnit(): number;
26
+ /**
27
+ * The element's size, tracked. Returns null until it has been measured, so a
28
+ * first render never lays out against a guess.
29
+ */
30
+ export declare function useElementSize(ref: {
31
+ current: HTMLElement | null;
32
+ }): {
33
+ width: number;
34
+ height: number;
35
+ } | null;
36
+ /**
37
+ * A plane's elevation, in the idiom of its scheme.
38
+ *
39
+ * Dark separates by luminance, so one soft dark pool is right. Light
40
+ * separates the way objects on a desk do — a tight contact shadow plus a
41
+ * long diffuse one — so it gets both, and the further plane casts the
42
+ * longer, weaker one.
43
+ */
44
+ export declare function planeShadow(shadow: number, scheme: "light" | "dark"): string;
45
+ export declare function cssTransform(transform: Matrix4): string;
46
+ //# sourceMappingURL=scene-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-helpers.d.ts","sourceRoot":"","sources":["../src/scene-helpers.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,QAAQ,EAAE,OAAO,GAChB,MAAM,EAAE,CAOV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAyBpC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE;IAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,GACnC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuB1C;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAO5E;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAGvD"}
@@ -0,0 +1,108 @@
1
+ import { useEffect, useState } from "react";
2
+ /**
3
+ * What a click on a view selects.
4
+ *
5
+ * Selecting a GROUP selects its members, because a group is a view of a set
6
+ * of nodes rather than a node itself — "select the People block" means the
7
+ * people. Everything downstream then works unchanged: the affordance layer
8
+ * sees a selection of real nodes and can say what is true about them.
9
+ */
10
+ export function selectionFor(node, current, additive) {
11
+ const ids = node.aggregate ? [...node.aggregate.memberIds] : [node.id];
12
+ if (!additive)
13
+ return ids;
14
+ const alreadyIn = ids.every((id) => current.includes(id));
15
+ return alreadyIn
16
+ ? current.filter((id) => !ids.includes(id))
17
+ : [...current, ...ids.filter((id) => !current.includes(id))];
18
+ }
19
+ /**
20
+ * WHAT ONE `rem` IS WORTH RIGHT NOW — the reader's own text size, watched.
21
+ *
22
+ * The scene's cards hold text sized in `rem` and were laid out in pixels,
23
+ * so a reader who asked for bigger words got them inside a city that had
24
+ * not moved: a headline in a glyph. The layout takes this as its unit, so
25
+ * the picture grows with the words.
26
+ *
27
+ * Read from the root rather than from a setting's name, because the scene
28
+ * has no business knowing what an app called its text-size control — the
29
+ * root font size is where every such control lands, including the browser's
30
+ * own, which no app declares at all.
31
+ */
32
+ export function useRootUnit() {
33
+ const [unit, setUnit] = useState(16);
34
+ useEffect(() => {
35
+ if (typeof window === "undefined")
36
+ return;
37
+ const root = document.documentElement;
38
+ const read = () => {
39
+ const now = parseFloat(getComputedStyle(root).fontSize);
40
+ if (Number.isFinite(now))
41
+ setUnit((held) => (Math.abs(held - now) < 0.5 ? held : now));
42
+ };
43
+ read();
44
+ /*
45
+ * The setting writes the root's own `style`, and a reader changing the
46
+ * browser's default changes the computed size without touching it — so
47
+ * both are watched: the attribute for the app's control, and a resize
48
+ * for the browser's.
49
+ */
50
+ const observer = new MutationObserver(read);
51
+ observer.observe(root, { attributes: true, attributeFilter: ["style"] });
52
+ window.addEventListener("resize", read);
53
+ return () => {
54
+ observer.disconnect();
55
+ window.removeEventListener("resize", read);
56
+ };
57
+ }, []);
58
+ return unit;
59
+ }
60
+ /**
61
+ * The element's size, tracked. Returns null until it has been measured, so a
62
+ * first render never lays out against a guess.
63
+ */
64
+ export function useElementSize(ref) {
65
+ const [size, setSize] = useState(null);
66
+ useEffect(() => {
67
+ const element = ref.current;
68
+ if (!element || typeof ResizeObserver === "undefined")
69
+ return;
70
+ const observer = new ResizeObserver((entries) => {
71
+ const box = entries[0]?.contentRect;
72
+ if (!box || box.width === 0 || box.height === 0)
73
+ return;
74
+ // Round to whole pixels: a fractional width would recompute the layout
75
+ // on every sub-pixel wobble and never settle.
76
+ setSize((current) => {
77
+ const next = { width: Math.round(box.width), height: Math.round(box.height) };
78
+ return current && current.width === next.width && current.height === next.height
79
+ ? current
80
+ : next;
81
+ });
82
+ });
83
+ observer.observe(element);
84
+ return () => observer.disconnect();
85
+ }, [ref]);
86
+ return size;
87
+ }
88
+ /**
89
+ * A plane's elevation, in the idiom of its scheme.
90
+ *
91
+ * Dark separates by luminance, so one soft dark pool is right. Light
92
+ * separates the way objects on a desk do — a tight contact shadow plus a
93
+ * long diffuse one — so it gets both, and the further plane casts the
94
+ * longer, weaker one.
95
+ */
96
+ export function planeShadow(shadow, scheme) {
97
+ if (scheme === "dark") {
98
+ return `0 ${(10 * shadow).toFixed(1)}px ${(34 * shadow).toFixed(1)}px rgba(0,0,0,${(shadow + 0.12).toFixed(2)})`;
99
+ }
100
+ const contact = `0 ${(1 + 2 * shadow).toFixed(1)}px ${(2 + 5 * shadow).toFixed(1)}px rgba(20,30,32,${(0.03 + 0.06 * shadow).toFixed(3)})`;
101
+ const cast = `0 ${(6 + 26 * shadow).toFixed(1)}px ${(18 + 60 * shadow).toFixed(1)}px -${(10 + 10 * shadow).toFixed(1)}px rgba(20,30,32,${(0.1 + 0.3 * shadow).toFixed(3)})`;
102
+ return `${contact}, ${cast}`;
103
+ }
104
+ export function cssTransform(transform) {
105
+ // Column-major 4x4 into CSS matrix3d, which is also column-major.
106
+ return `matrix3d(${transform.join(",")})`;
107
+ }
108
+ //# sourceMappingURL=scene-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-helpers.js","sourceRoot":"","sources":["../src/scene-helpers.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { AnySchema, GraphReader, NodeOfSchema } from "@graview/core";
2
+ import { type InterpolatedLayout } from "@graview/layout";
3
+ import type { ActivityMark } from "./activity.js";
4
+ import type { SceneNode } from "./scene-root.js";
5
+ /**
6
+ * EVERY LINE IN THE SCENE, measured against the DOM it is drawn over.
7
+ *
8
+ * Both line layers measure elements — panels, spans, chips — and anything
9
+ * measured during a render reads the PREVIOUS commit's geometry. The scene
10
+ * used to live with that: a tween paints sixty frames so one stale frame
11
+ * is invisible, and a settle tick after the last one catches the rest. A
12
+ * cut had no such cover, and a drag left every line one pointer event
13
+ * behind the card it was tied to.
14
+ *
15
+ * So the lines are their own component, and after each commit that could
16
+ * have moved anything — a new frame, a new selection, a graph edit that
17
+ * re-flowed a view — they re-render ONCE from a layout effect, before the
18
+ * browser paints. Only this subtree renders twice; the hosts do not.
19
+ *
20
+ * The strands are resolved here rather than inside a layer because two
21
+ * layers need the same answer: the connector layer draws them, and the
22
+ * ties layer yields to exactly the relations that already have a line.
23
+ * Deciding that from the layout alone was the bug: a session's line into
24
+ * the week was CLAIMED as drawn while it actually rose from the panel's
25
+ * centre, so selecting the session lit three of its four drills and left
26
+ * the fourth to a faint line from nowhere.
27
+ */
28
+ export declare function Lines<S extends AnySchema>({ frame, width, height, scheme, overview, selection, emphasis, stageRef, store, graphNodes, onPickEdge, liveOf, holding, }: {
29
+ readonly frame: InterpolatedLayout;
30
+ readonly width: number;
31
+ readonly height: number;
32
+ readonly scheme: "light" | "dark";
33
+ readonly overview: boolean;
34
+ readonly selection: readonly string[];
35
+ readonly emphasis: string | null;
36
+ readonly stageRef: {
37
+ current: HTMLElement | null;
38
+ };
39
+ readonly store: {
40
+ graph: GraphReader<NodeOfSchema<S>>;
41
+ };
42
+ readonly graphNodes: unknown;
43
+ readonly onPickEdge: (edgeId: string, at?: {
44
+ x: number;
45
+ y: number;
46
+ }) => void;
47
+ readonly liveOf: (connector: {
48
+ from: string;
49
+ to: string;
50
+ }) => ActivityMark | undefined;
51
+ /** A card is held by the hand: draw from the layout, measure when it lets go. */
52
+ readonly holding?: boolean;
53
+ }): import("react").JSX.Element | null;
54
+ /**
55
+ * What plane 1 is, said in words, over the run of cards it applies to.
56
+ *
57
+ * The schema has always carried a description on every edge — "who does the
58
+ * run", "a nap that must not be interrupted" — and nothing ever showed them.
59
+ * A row of anonymous cards under the thing you clicked is a puzzle; the same
60
+ * row under "who does the run" is an answer. Layout groups the neighbourhood
61
+ * by edge kind, so each caption spans one contiguous run rather than
62
+ * repeating itself once per card.
63
+ */
64
+ export declare function RelationCaptions({ nodes, scheme, width: stageWidth, stageRef, moving, holding, }: {
65
+ readonly nodes: readonly SceneNode[];
66
+ readonly scheme: "light" | "dark";
67
+ readonly width: number;
68
+ readonly stageRef: {
69
+ current: HTMLElement | null;
70
+ };
71
+ /** In flight: captions are measured where the transition lands, not on every frame of it. */
72
+ readonly moving?: boolean;
73
+ /** A card is held: nothing the captions stand over moves, so they are not measured again. */
74
+ readonly holding?: boolean;
75
+ }): import("react").JSX.Element | null;
76
+ //# sourceMappingURL=scene-lines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-lines.d.ts","sourceRoot":"","sources":["../src/scene-lines.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,KAAK,kBAAkB,EAAoC,MAAM,iBAAiB,CAAC;AAG5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAKlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,EACzC,KAAK,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,UAAU,EACV,UAAU,EACV,MAAM,EACN,OAAe,GAChB,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7E,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,YAAY,GAAG,SAAS,CAAC;IACvF,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,sCAwHA;AAydD;;;;;;;;;GASG;AAEH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,MAAM,EACN,KAAK,EAAE,UAAU,EACjB,QAAQ,EACR,MAAc,EACd,OAAe,GAChB,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;IACnD,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,sCAgGA"}