@graview/react 0.1.28 → 0.1.29

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 (106) hide show
  1. package/dist/affordances.d.ts +2 -1
  2. package/dist/affordances.d.ts.map +1 -1
  3. package/dist/affordances.js +2 -1
  4. package/dist/affordances.js.map +1 -1
  5. package/dist/beside-the-map.d.ts +13 -0
  6. package/dist/beside-the-map.d.ts.map +1 -0
  7. package/dist/beside-the-map.js +14 -0
  8. package/dist/beside-the-map.js.map +1 -0
  9. package/dist/channels.d.ts +2 -1
  10. package/dist/channels.d.ts.map +1 -1
  11. package/dist/channels.js +121 -22
  12. package/dist/channels.js.map +1 -1
  13. package/dist/connectors.d.ts +27 -2
  14. package/dist/connectors.d.ts.map +1 -1
  15. package/dist/connectors.js +135 -21
  16. package/dist/connectors.js.map +1 -1
  17. package/dist/context.d.ts +38 -2
  18. package/dist/context.d.ts.map +1 -1
  19. package/dist/context.js +63 -14
  20. package/dist/context.js.map +1 -1
  21. package/dist/drawn.d.ts +1 -1
  22. package/dist/drawn.d.ts.map +1 -1
  23. package/dist/drawn.js +1 -1
  24. package/dist/drawn.js.map +1 -1
  25. package/dist/edge-signs.d.ts +1 -1
  26. package/dist/edge-signs.d.ts.map +1 -1
  27. package/dist/edge-signs.js +52 -7
  28. package/dist/edge-signs.js.map +1 -1
  29. package/dist/index.d.ts +6 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/last-visit.d.ts +18 -0
  34. package/dist/last-visit.d.ts.map +1 -0
  35. package/dist/last-visit.js +79 -0
  36. package/dist/last-visit.js.map +1 -0
  37. package/dist/map-choice.d.ts +55 -0
  38. package/dist/map-choice.d.ts.map +1 -0
  39. package/dist/map-choice.js +73 -0
  40. package/dist/map-choice.js.map +1 -0
  41. package/dist/plots.d.ts +30 -24
  42. package/dist/plots.d.ts.map +1 -1
  43. package/dist/plots.js +168 -149
  44. package/dist/plots.js.map +1 -1
  45. package/dist/provider.d.ts +2 -1
  46. package/dist/provider.d.ts.map +1 -1
  47. package/dist/provider.js +3 -1
  48. package/dist/provider.js.map +1 -1
  49. package/dist/record-links-layer.d.ts +80 -0
  50. package/dist/record-links-layer.d.ts.map +1 -0
  51. package/dist/record-links-layer.js +127 -0
  52. package/dist/record-links-layer.js.map +1 -0
  53. package/dist/record-links.d.ts +58 -0
  54. package/dist/record-links.d.ts.map +1 -0
  55. package/dist/record-links.js +72 -0
  56. package/dist/record-links.js.map +1 -0
  57. package/dist/resolved-view.d.ts.map +1 -1
  58. package/dist/resolved-view.js +4 -0
  59. package/dist/resolved-view.js.map +1 -1
  60. package/dist/ribbon-width.d.ts +8 -0
  61. package/dist/ribbon-width.d.ts.map +1 -0
  62. package/dist/ribbon-width.js +15 -0
  63. package/dist/ribbon-width.js.map +1 -0
  64. package/dist/ribbons-layer.d.ts +56 -0
  65. package/dist/ribbons-layer.d.ts.map +1 -0
  66. package/dist/ribbons-layer.js +167 -0
  67. package/dist/ribbons-layer.js.map +1 -0
  68. package/dist/ribbons.d.ts +70 -0
  69. package/dist/ribbons.d.ts.map +1 -0
  70. package/dist/ribbons.js +400 -0
  71. package/dist/ribbons.js.map +1 -0
  72. package/dist/scene-camera.d.ts.map +1 -1
  73. package/dist/scene-camera.js +10 -4
  74. package/dist/scene-camera.js.map +1 -1
  75. package/dist/scene-covered.d.ts +8 -0
  76. package/dist/scene-covered.d.ts.map +1 -0
  77. package/dist/scene-covered.js +118 -0
  78. package/dist/scene-covered.js.map +1 -0
  79. package/dist/scene-hand.d.ts +15 -0
  80. package/dist/scene-hand.d.ts.map +1 -1
  81. package/dist/scene-hand.js +45 -1
  82. package/dist/scene-hand.js.map +1 -1
  83. package/dist/scene-lines.d.ts +15 -2
  84. package/dist/scene-lines.d.ts.map +1 -1
  85. package/dist/scene-lines.js +215 -85
  86. package/dist/scene-lines.js.map +1 -1
  87. package/dist/scene-options.d.ts +7 -2
  88. package/dist/scene-options.d.ts.map +1 -1
  89. package/dist/scene-options.js +22 -8
  90. package/dist/scene-options.js.map +1 -1
  91. package/dist/scene-root.d.ts +1 -0
  92. package/dist/scene-root.d.ts.map +1 -1
  93. package/dist/scene-root.js +197 -154
  94. package/dist/scene-root.js.map +1 -1
  95. package/dist/view-host.d.ts.map +1 -1
  96. package/dist/view-host.js +6 -1
  97. package/dist/view-host.js.map +1 -1
  98. package/dist/view-registry.d.ts +5 -0
  99. package/dist/view-registry.d.ts.map +1 -1
  100. package/dist/view-registry.js +2 -6
  101. package/dist/view-registry.js.map +1 -1
  102. package/dist/where-drawn.d.ts +3 -1
  103. package/dist/where-drawn.d.ts.map +1 -1
  104. package/dist/where-drawn.js +10 -16
  105. package/dist/where-drawn.js.map +1 -1
  106. package/package.json +5 -5
@@ -1,5 +1,8 @@
1
- import { beginning, orderKinds, touchWeights } from "@graview/core";
2
- import { useMemo } from "react";
1
+ import { orderKinds } from "@graview/core";
2
+ import { beginning } from "@graview/core/seat";
3
+ import { touchWeights } from "@graview/core/search";
4
+ import { useContext, useMemo } from "react";
5
+ import { BesideTheMap } from "./beside-the-map.js";
3
6
  import { foundIds, useFound, useGraview } from "./context.js";
4
7
  import { useViolations } from "./hooks.js";
5
8
  import { railInset, SEAT_FOOT } from "./rails.js";
@@ -15,13 +18,17 @@ const NO_HITS = new Set();
15
18
  * dead ground on a wide screen and clips on a narrow one, and the plane
16
19
  * bands are proportions rather than pixels, so they follow.
17
20
  */
18
- export function useLayoutOptions(options, size, unit, nameWidth,
21
+ export function useLayoutOptions(options, size, unit,
19
22
  /** Whether an ask field stands at the picture's foot: the layout keeps that strip clear of cards. */
20
- seated, cityZoom, screenHeight,
23
+ seated, cityZoom,
24
+ /** What a panel floating over the scene covers, in pixels (`useDock`): the city is fitted in what is left. */
25
+ dock,
21
26
  /** The graph's tick: the log only grows when the graph changes. */
22
27
  nodes) {
23
28
  const { store, views, hiddenKinds } = useGraview();
24
29
  const found = useFound();
30
+ // A picture opened from altitude stands beside the map, where a panel is drawn.
31
+ const besideTheMap = useContext(BesideTheMap);
25
32
  // What each subject's violations name, so a rule's neighborhood is what it judges.
26
33
  const violations = useViolations();
27
34
  const judged = useMemo(() => {
@@ -53,7 +60,7 @@ nodes) {
53
60
  return useMemo(() => ({
54
61
  ...options,
55
62
  cityZoom,
56
- ...(screenHeight !== undefined ? { screenHeight } : {}),
63
+ ...(besideTheMap ? { besideTheMap: true } : {}),
57
64
  // What is not drawn for this seat at this stop: a workspace's disabled
58
65
  // modules, and the administered ones this seat may not see or has not
59
66
  // asked to — one set, from the provider, so every surface agrees.
@@ -67,7 +74,15 @@ nodes) {
67
74
  * the altitude control and the key stand in, in proportion, so an
68
75
  * embed a paragraph wide does not give a third of itself to chrome.
69
76
  */
70
- inset: railInset(size?.width ?? 1200),
77
+ /*
78
+ * THE CORNER THE ALTITUDE CONTROL AND THE KEY STAND IN, and nothing of
79
+ * the city opened under them: the districts and their names open
80
+ * clear of the chrome; the zoom at the foot when no ask field keeps it.
81
+ */
82
+ inset: { ...railInset((size?.width ?? 1200) - dock.right), top: 8, bottom: seated ? 0 : 56 },
83
+ // Up and the Key stand clear of a panel too, at the corner of what the reader sees.
84
+ chrome: [{ x: (size?.width ?? 1200) - dock.right - 176, y: 0, width: 176, height: 100 }],
85
+ ...(dock.right > 0 || dock.bottom > 0 ? { dock } : {}),
71
86
  // The reader's own text size, which the cards are sized in: the city
72
87
  // grows with the words rather than holding them at a fixed 230×97.
73
88
  unit,
@@ -94,7 +109,6 @@ nodes) {
94
109
  (held[place.kind] ??= []).push({ as: place.as, title: place.title, ...(place.across ? { across: place.across } : {}) });
95
110
  return held;
96
111
  }, {}),
97
- nameWidth,
98
112
  ...(size
99
113
  ? {
100
114
  width: size.width,
@@ -111,6 +125,6 @@ nodes) {
111
125
  height: seated ? Math.max(0, size.height - SEAT_FOOT) : size.height,
112
126
  }
113
127
  : {}),
114
- }), [options, size, unit, store, views, hiddenKinds, judged, relevance, cityZoom, screenHeight, nameWidth, seated]);
128
+ }), [options, size, unit, store, views, hiddenKinds, judged, relevance, cityZoom, seated, besideTheMap, dock.right, dock.bottom]);
115
129
  }
116
130
  //# sourceMappingURL=scene-options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scene-options.js","sourceRoot":"","sources":["../src/scene-options.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scene-options.js","sourceRoot":"","sources":["../src/scene-options.ts"],"names":[],"mappings":"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"}
@@ -47,6 +47,7 @@ export interface SceneProps {
47
47
  /** Rendered over the scene — an affordance surface, a header, a legend. */
48
48
  readonly children?: ReactNode;
49
49
  }
50
+ export declare const cityGround: import("./lazy-part.js").LazyModule<typeof import("./plots.js")>;
50
51
  /**
51
52
  * The spatial scene: one `<canvas layoutsubtree>` with the views as its
52
53
  * IMMEDIATE children.
@@ -1 +1 @@
1
- {"version":3,"file":"scene-root.d.ts","sourceRoot":"","sources":["../src/scene-root.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAS,MAAM,eAAe,CAAC;AACtD,OAAO,EAcL,KAAK,kBAAkB,EAEvB,KAAK,aAAa,EAOnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAiBf,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,EAAE,SAAS,EAAE,CAAC;AAKrB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAChC,MAAM,EAAE,iBAAiB,CAAC;QAC1B;;;WAGG;QACH,MAAM,EAAE,kBAAkB,CAAC;QAC3B;;;;;;WAMG;QACH,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3B,kEAAkE;QAClE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;KACxC,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B;AAkmBD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,EACzC,OAAO,EACP,QAAiB,EACjB,cAAc,EACd,SAAS,EACT,KAAK,EACL,OAAc,EACd,QAAQ,GACT,EAAE,UAAU,+BAkXZ"}
1
+ {"version":3,"file":"scene-root.d.ts","sourceRoot":"","sources":["../src/scene-root.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAS,MAAM,eAAe,CAAC;AACtD,OAAO,EAWL,KAAK,kBAAkB,EAEvB,KAAK,aAAa,EAOnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAQL,KAAK,aAAa,EAClB,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AA0Bf,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,EAAE,SAAS,EAAE,CAAC;AAKrB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAChC,MAAM,EAAE,iBAAiB,CAAC;QAC1B;;;WAGG;QACH,MAAM,EAAE,kBAAkB,CAAC;QAC3B;;;;;;WAMG;QACH,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3B,kEAAkE;QAClE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;KACxC,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B;AA+iBD,eAAO,MAAM,UAAU,kEAAyD,CAAC;AAiDjF;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,EACzC,OAAO,EACP,QAAiB,EACjB,cAAc,EACd,SAAS,EACT,KAAK,EACL,OAAc,EACd,QAAQ,GACT,EAAE,UAAU,+BAuaZ"}
@@ -1,22 +1,30 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { SCENE_LAYERS } from "@graview/core";
3
- import { toIso } from "@graview/core/scene";
4
- import { aggregateId, cameraLimit, panForZoom, kindOfCard, kindsOf, layout, marqueeHeightFor, withFocus, withOverview, withPan, withPin, panLayout, withRelation, withJackIn, holdLayout, isBandAggregate, toggleExpanded, withWithin, } from "@graview/layout";
5
- import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
3
+ import { toIso, TOP_DOWN } from "@graview/core/city";
4
+ import { aggregateId, cameraLimit, panForZoom, kindOfCard, kindsOf, layout, withFocus, withOverview, panLayout, withRelation, withJackIn, holdLayout, isBandAggregate, toggleExpanded, withWithin, } from "@graview/layout";
5
+ import { withPan, withPin } from "@graview/layout/view-moves";
6
+ import { Suspense, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
7
+ import { retryingImport } from "@graview/core/retry";
8
+ import { lazyModule } from "./lazy-part.js";
9
+ import { BesideTheMap } from "./beside-the-map.js";
10
+ import { mapChoice } from "./map-choice.js";
6
11
  import { useActivity } from "./attention.js";
7
12
  import { useAnimatedLayout, useSeatWork, useTouched } from "./animation.js";
8
13
  import { SeatMarks } from "./seat-marks.js";
9
- import { useFound, useGraph, useGraview } from "./context.js";
10
- import { MarqueeRoomContext, useMarqueeNameWidth } from "./drawn.js";
11
- import { Plots } from "./plots.js";
14
+ import { matchWith, useFound, useGraph, useGraview } from "./context.js";
15
+ import { search, touchWeights } from "@graview/core/search";
16
+ import { kitConnector, useKit } from "./kit.js";
17
+ // The scene searches: it hands the provider the matcher as it is fetched (FR-179).
18
+ matchWith({ search, touchWeights });
12
19
  import { Occupants } from "./occupants.js";
13
20
  import { EdgeSigns } from "./edge-signs.js";
14
21
  import { useCameraFlights } from "./scene-camera.js";
15
- import { useHeldDistrict, useSceneDrag, useWheelAndPinch, useWorldShift } from "./scene-hand.js";
22
+ import { useKeyboardKeepsToWhatIsSeen } from "./scene-covered.js";
23
+ import { useDock, useHeldDistrict, useSceneDrag, useWheelAndPinch, useWorldShift } from "./scene-hand.js";
16
24
  import { whereIsIn } from "./where-drawn.js";
17
25
  import { BandCard, BeyondCard, SettledView } from "./resolved-view.js";
18
26
  import { selectionFor, useElementSize, useRootUnit } from "./scene-helpers.js";
19
- import { Lines, RelationCaptions } from "./scene-lines.js";
27
+ import { Lines, provisionalLines, RelationCaptions } from "./scene-lines.js";
20
28
  import { railInset } from "./rails.js";
21
29
  import { useSeatDrawn } from "./seat-talk.js";
22
30
  import { useLayoutOptions } from "./scene-options.js";
@@ -66,25 +74,6 @@ function useHandZoom(overview) {
66
74
  }, [overview]);
67
75
  return [zoom, zoomLive, setZoom];
68
76
  }
69
- /**
70
- * THE BILLBOARD IS CUT TO ITS PICTURE. The lens draws in a box as tall as
71
- * the window; the screen's host reports how much of it the lens actually
72
- * used, and the layout sizes the billboard to that — so the picture's foot
73
- * is on the curb instead of a village's height above it. Whole pixels,
74
- * and only a change re-lays the city.
75
- *
76
- * Reported by the layout's CURRENT screen only. A billboard on its way
77
- * into its village is still drawn as one, but for the length of the tween
78
- * two hosts would report, the layout would flip between their two heights,
79
- * and every flip restarted the tween from where it was — a crawl of a
80
- * pixel a frame until the old picture had faded.
81
- */
82
- function useScreenHeight() {
83
- const [screenHeight, setScreenHeight] = useState(undefined);
84
- // Whole pixels: React keeps the height it has when the new one is the same, so only a change re-lays the city.
85
- const noteScreenHeight = useCallback((height) => setScreenHeight(height && Math.round(height)), []);
86
- return [screenHeight, noteScreenHeight];
87
- }
88
77
  /**
89
78
  * THE CAMERA IS NOT A MOVE. A drive-in on the far side of a large city
90
79
  * lights up off-screen unless the camera goes to it, and the camera's
@@ -373,6 +362,8 @@ const toggled = (current, id) => current.includes(id) ? current.filter((other) =
373
362
  function useCardActs() {
374
363
  const { views, view, setView, setSelection } = useGraview();
375
364
  const found = useFound();
365
+ // Whether a panel beside the map lists a district opened from altitude.
366
+ const besideTheMap = useContext(BesideTheMap);
376
367
  /*
377
368
  * Whether this card stands for a kind the app gave a picture of its own —
378
369
  * the same question `ResolvedView` asks to draw the ◆, asked here so the
@@ -481,6 +472,20 @@ function useCardActs() {
481
472
  // A district the search lit opens narrowed by the same words.
482
473
  const card = kindOfCard(node.id);
483
474
  const lit = card !== null && (found?.byKind[card] ?? 0) > 0;
475
+ /*
476
+ * A DISTRICT WITH NO PICTURE OF ITS OWN, OPENED FROM ALTITUDE, OPENS
477
+ * TO ITS STOP BESIDE THE MAP, in the panel a lens opens in: its own
478
+ * focus, at altitude, so it takes the place of a lens or another
479
+ * kind's stop open there (one panel at a time), the address says so,
480
+ * and Back returns to what was open before. It used to be chosen,
481
+ * which listed it beside the map only when nothing was open — with a
482
+ * lens open, the lens stayed and the district opened behind it.
483
+ */
484
+ if (besideTheMap && card !== null && view.overview && !ownPictureOf(node) && !(lit && view.q)) {
485
+ setView((current) => withWithin({ ...withFocus(current, `aggregate:${card}`), relation: null }, "view", null));
486
+ setSelection([]);
487
+ return;
488
+ }
484
489
  setView((current) => withJackIn(current, node.id, { ownPicture: ownPictureOf(node), ...(lit && current.q ? { carry: current.q } : {}) }));
485
490
  /*
486
491
  * A zoomed RECORD is selected — reading closely is when you act.
@@ -492,37 +497,47 @@ function useCardActs() {
492
497
  },
493
498
  });
494
499
  }
495
- /**
496
- * THE RAIL YOU MOVE THE BOARD BY.
497
- *
498
- * Dragging the picture itself looks around the city, and must go on
499
- * doing so: the billboard is the biggest thing on screen and the one
500
- * most likely to be under the hand, and taking panning away from it
501
- * was how the far side of a city became unreachable once. So the
502
- * board gets a rail along its top edge, the way a window has a title
503
- * bar — the picture pans, the rail moves the board, and neither
504
- * gesture has to be discovered from the other.
505
- *
506
- * How far it may go is not this rail's business: `layout` leashes
507
- * the pin to its own plot, so a hand and a pasted link are held to
508
- * the same distance.
509
- *
510
- * A TITLE BAR, not a gray strip with a pill floating over it: the
511
- * picture's name on the left, the one way down on the right, and
512
- * the whole bar the handle that moves the board.
500
+ /*
501
+ * THE RELATIONS FROM ALTITUDE, one ribbon each (`ribbons-layer.tsx`),
502
+ * fetched the first time the city is drawn: a page that stays on the
503
+ * ground carries none of them.
513
504
  */
514
- function ScreenGrip({ node }) {
515
- const { views, view, setView } = useGraview();
516
- return (_jsxs("div", { className: "graview-screen-grip", "data-graview-grip": node.id, "data-testid": "screen-grip", title: "Move this picture \u2014 it stays by its own village", children: [_jsx("span", { className: "graview-screen-title", "aria-hidden": "true", children: views.places().find((place) => place.as === view.within?.["view"] && place.kind === node.screenOf)?.title ?? "" }), _jsx("button", { type: "button", className: "graview-screen-fullscreen", "data-testid": "screen-fullscreen", title: "Open this picture on its own \u2014 leave the graview with it", onClick: (event) => {
517
- event.stopPropagation();
518
- setView((current) => withOverview(current, false));
519
- }, onPointerDown: stop, onDoubleClick: stop, children: "Open \u2197" })] }));
505
+ const ribbonsModule = /* @__PURE__ */ lazyModule(retryingImport(() => import("./ribbons-layer.js")));
506
+ const Ribbons = /* @__PURE__ */ ribbonsModule.part((module, props) => _jsx(module.Ribbons, { ...props }), { what: "The relations", quiet: true });
507
+ const ABOVE_KEY = "graview:map-from-above";
508
+ /** Whether this person looks at the city from straight above: their own way of looking, kept in this browser when it can be. */
509
+ function useFromAbove() {
510
+ const [above, setAbove] = useState(() => {
511
+ try {
512
+ return globalThis.localStorage?.getItem(ABOVE_KEY) === "1";
513
+ }
514
+ catch {
515
+ return false;
516
+ }
517
+ });
518
+ const choose = useCallback((next) => {
519
+ setAbove(next);
520
+ try {
521
+ globalThis.localStorage?.setItem(ABOVE_KEY, next ? "1" : "0");
522
+ }
523
+ catch {
524
+ // Not kept: it holds for this visit.
525
+ }
526
+ }, []);
527
+ return [above, choose];
520
528
  }
529
+ /*
530
+ * THE GROUND OF THE CITY — its plots, their blocks, the roads and their
531
+ * rules — fetched the first time the scene rises: a page that never looks
532
+ * from altitude never carries it (`plots.tsx`).
533
+ */
534
+ export const cityGround = lazyModule(retryingImport(() => import("./plots.js")));
535
+ const Plots = cityGround.part((module, props) => _jsx(module.Plots, { ...props }), { quiet: true });
521
536
  /** SCENE FURNITURE in the ground's other corner: the way a map carries its own zoom. */
522
- function ZoomControls({ zoom, zoomBy }) {
537
+ function ZoomControls({ zoom, zoomBy, above, setAbove }) {
523
538
  /** One way to zoom: its sign, the step it takes, and whether the zoom has gone as far as it goes that way. */
524
539
  const button = (way, sign, factor, disabled) => (_jsx("button", { type: "button", className: "graview-zoom-button", "data-testid": `zoom-${way}`, "aria-label": `Zoom ${way}`, title: `Zoom ${way} — or pinch, or ctrl+wheel`, disabled: disabled, onClick: () => zoomBy(factor), children: sign }));
525
- return (_jsxs("div", { className: "graview-zoom", "data-graview-foot": "", role: "group", "aria-label": "Zoom", onPointerDown: stop, onClick: stop, children: [button("out", "−", 1 / 1.25, zoom <= ZOOM_MIN + 1e-6), _jsxs("span", { className: "graview-zoom-level", "data-testid": "zoom-level", "aria-live": "polite", children: [Math.round(zoom * 100), "%"] }), button("in", "+", 1.25, zoom >= ZOOM_MAX - 1e-6)] }));
540
+ return (_jsxs("div", { className: "graview-zoom", "data-graview-foot": "", role: "group", "aria-label": "Zoom", onPointerDown: stop, onClick: stop, children: [button("out", "−", 1 / 1.25, zoom <= ZOOM_MIN + 1e-6), _jsxs("span", { className: "graview-zoom-level", "data-testid": "zoom-level", "aria-live": "polite", children: [Math.round(zoom * 100), "%"] }), button("in", "+", 1.25, zoom >= ZOOM_MAX - 1e-6), _jsx("button", { type: "button", className: "graview-zoom-button graview-zoom-above", "data-testid": "from-above", "aria-pressed": above, title: above ? "See the city at an angle again" : "See the city from straight above", onClick: () => setAbove(!above), children: "From above" })] }));
526
541
  }
527
542
  /**
528
543
  * The spatial scene: one `<canvas layoutsubtree>` with the views as its
@@ -535,16 +550,14 @@ function ZoomControls({ zoom, zoomBy }) {
535
550
  export function Scene({ options, renderer = "auto", attachRenderer, className, style, animate = true, children, }) {
536
551
  const context = useGraview();
537
552
  const { store, scheme, views, view, setView, selection, setSelection, setMenuAt, emphasis, brand, noteMoved, robots, seatTalk, motion } = context;
553
+ // A panel beside the map shows what is chosen from altitude, a record in focus among it (`mapChoice`).
554
+ const besideTheMap = useContext(BesideTheMap);
538
555
  /* Whether an ask field stands at the picture's foot: the layout keeps that strip clear of cards. */
539
556
  const seated = useSeatDrawn(seatTalk);
540
557
  const canvasRef = useRef(null);
541
558
  const wrapperRef = useRef(null);
542
559
  const size = useElementSize(wrapperRef);
543
560
  const unit = useRootUnit();
544
- // The marquee's names measured in the face the scene draws them in (FR-118), for the room under each signpost.
545
- const nameWidth = useMarqueeNameWidth(wrapperRef, unit, brand);
546
- // The room the layout reserves for a marquee of those names, handed to the districts that draw one (132: the district's own width, unused).
547
- const marqueeRoom = useMemo(() => (titles) => marqueeHeightFor(titles, 132, nameWidth), [nameWidth]);
548
561
  // Pinch and ctrl+wheel: altitude from the ground, zoom about the pointer from above; the plain wheel pans. See scene-hand.ts.
549
562
  const { zoomAbout, panBy } = useWheelAndPinch({ stage: wrapperRef, view, setView });
550
563
  // `nodes` is a cached snapshot that only changes when the graph does, so
@@ -556,12 +569,21 @@ export function Scene({ options, renderer = "auto", attachRenderer, className, s
556
569
  * bigger on its plot, and the camera keeps the picture in view. Derived
557
570
  * from the stop, never stored: leaving the picture flies back out.
558
571
  */
559
- const closer = view.overview && view.within?.["view"] !== undefined ? 1.5 : 1;
572
+ /*
573
+ * A PICTURE CHOSEN FROM ALTITUDE OPENS BESIDE THE MAP, at a size a person
574
+ * reads (the lens panel), so the camera no longer flies closer to a
575
+ * billboard on the plot: the city stays where it was, with the district
576
+ * the picture is of lit and the rest receded.
577
+ */
578
+ const closer = 1;
560
579
  const [zoom, zoomLive, setZoom] = useHandZoom(view.overview);
561
- const [screenHeight, noteScreenHeight] = useScreenHeight();
562
- const sized = useLayoutOptions(options, size, unit, nameWidth, seated, closer * zoom, screenHeight, nodes);
580
+ const [above, setAbove] = useFromAbove();
581
+ const seenFrom = useMemo(() => (above ? { ...options, tilt: TOP_DOWN } : options), [options, above]);
582
+ const { probe: dockProbe, dock } = useDock();
583
+ const sized = useLayoutOptions(seenFrom, size, unit, seated, closer * zoom, dock, nodes);
563
584
  const [panned, seen, panWithin, cameraLive, setCamera] = useCamera(view);
564
- const pinnedIds = useMemo(() => new Set(Object.keys(view.pins)), [view.pins]);
585
+ // A district cannot be placed by hand from altitude, so no plot wears the pinned curb for one (a pin in an old bookmark is ignored).
586
+ const pinnedIds = useMemo(() => new Set(Object.keys(view.pins).filter((id) => kindOfCard(id) === null)), [view.pins]);
565
587
  // A district under the hand is placed here while it is dragged, and written into the view on release.
566
588
  const district = useHeldDistrict();
567
589
  const [result, inPlace] = useLaidOut(store, view, district.held, sized, nodes, seen.pan);
@@ -576,8 +598,7 @@ export function Scene({ options, renderer = "auto", attachRenderer, className, s
576
598
  const touched = useTouched();
577
599
  const seatWork = useSeatWork();
578
600
  // Where the camera goes on its own: to a drive-in off the edge, and down into a village. See scene-camera.ts.
579
- const screenId = result.nodes.find((node) => node.screenOf !== undefined)?.id ?? null;
580
- const flights = useCameraFlights({ view, result, frame, panned, closer, screenId, setCamera });
601
+ const flights = useCameraFlights({ view, result, frame, panned, closer, screenId: null, setCamera });
581
602
  useLentWhereIs(context, frame, wrapperRef);
582
603
  usePointerReport(context.pointer, wrapperRef);
583
604
  useAttachedRenderer(renderer, attachRenderer, canvasRef, frame, touched);
@@ -621,7 +642,9 @@ export function Scene({ options, renderer = "auto", attachRenderer, className, s
621
642
  steer();
622
643
  setZoom(next);
623
644
  const box = wrapperRef.current?.getBoundingClientRect();
624
- const center = { x: result.width / 2, y: result.height / 2 };
645
+ // The middle of what the reader sees: a panel over the scene is not part of it.
646
+ const seenBox = result.visible ?? result;
647
+ const center = { x: seenBox.width / 2, y: seenBox.height / 2 };
625
648
  const pointer = box && clientX !== undefined && clientY !== undefined ? { x: clientX - box.left, y: clientY - box.top } : center;
626
649
  const ratio = next / current;
627
650
  setView((current) => withPan(current, panForZoom(current.pan ?? { x: 0, y: 0 }, cameraLive.current, pointer, center, ratio)));
@@ -629,6 +652,8 @@ export function Scene({ options, renderer = "auto", attachRenderer, className, s
629
652
  };
630
653
  /** The view's own pan moved by so much, held within the camera's reach. */
631
654
  const nudge = (dx, dy, limit = cameraLimit(result)) => setView((current) => withPan(current, panWithin(limit, { x: (current.pan?.x ?? 0) + dx, y: (current.pan?.y ?? 0) + dy })));
655
+ // Tab passes over a card the panel covers, and the camera brings the one it lands on into the part that is seen.
656
+ useKeyboardKeepsToWhatIsSeen(wrapperRef, dock, (dx, dy) => nudge(dx, dy));
632
657
  panBy.current = (dx, dy) => {
633
658
  steer();
634
659
  const limit = cameraLimit(result);
@@ -649,114 +674,132 @@ export function Scene({ options, renderer = "auto", attachRenderer, className, s
649
674
  noteMoved();
650
675
  };
651
676
  const actsOn = useCardActs();
677
+ // Whether the kit draws a relation at all, for the ribbons (handed in: see ribbons-layer.tsx).
678
+ const kit = useKit();
679
+ const kitDraws = useCallback((edge) => kitConnector(kit, edge).connector.visible, [kit]);
652
680
  const drawnActivity = (id) => activityOf(frame.nodes.find((node) => node.id === id));
653
681
  /** How far down its card a plot's foot (`share` 1) or middle (2) stands, from the city's own origin and the pan. */
654
- const plotY = (city, node, plot, share) => city.originY + panned.y + toIso(plot.col + plot.side / 2, plot.row + plot.side / share, city.cell).y - node.y;
682
+ const plotY = (city, node, plot, share) => city.originY + panned.y + toIso(plot.col + plot.side / share, plot.row + plot.side / share, city.cell, city.tilt).y - node.y;
655
683
  // The stage's extent: what the ground, its layers and its lines are all drawn over.
656
684
  const extent = { width: result.width, height: result.height };
657
685
  const stage = { position: "relative", zIndex: SCENE_LAYERS.stage, ...extent };
658
686
  const whereIs = (id) => whereIsIn(frame, wrapperRef.current, scheme, views, id);
659
- const hosts = frame.nodes.map((node) => (_jsxs(SceneViewHost, { node: node, crowded: crowded(node), useDom: useDom, ...(node.plot && frame.city && Math.round(node.plane) === 2
687
+ /*
688
+ * THE BILLBOARD IS NOT DRAWN. A picture chosen from altitude was its lens
689
+ * drawn at the city's scale on its plot — eight-pixel text over the
690
+ * districts, unreadable until it was opened. It opens beside the map now,
691
+ * at full size (the lens panel); the stop is the same, so an address, the
692
+ * bar and Back still name it.
693
+ */
694
+ const hosts = frame.nodes.filter((node) => node.screenOf === undefined).map((node) => (_jsx(SceneViewHost, { node: node, crowded: crowded(node), useDom: useDom, ...(node.plot && frame.city && Math.round(node.plane) === 2
660
695
  ? { frontY: plotY(frame.city, node, node.plot, 1), centerY: plotY(frame.city, node, node.plot, 2) }
661
- : {}), ...(node.screenOf !== undefined ? { screen: true, ...(node.id === screenId ? { onDrawnHeight: noteScreenHeight } : {}) } : {}), touched: touched.has(node.id), ...withActivity(activityOf(node)), scheme: scheme, canvasWidth: result.width, canvasHeight: result.height, selected: selection.includes(node.id), ...actsOn(node), onMenu: setMenuAt, selection: selection, onDragStart: (event) => onCardDown(node, event), onDragMove: onDragMove, onDragEnd: onDragUp, swallowClick: swallow, children: [node.screenOf !== undefined ? _jsx(ScreenGrip, { node: node }) : null, node.beyond ? (_jsx(BeyondCard, { kinds: node.beyond })) : node.aggregate?.opens ? (_jsx(BandCard, { node: node })) : (_jsx(SettledView, { node: node, mode: "scene", selected: selection.includes(node.id), ...(crowded(node) ? { fidelity: "glyph" } : {}) }))] }, node.id)));
662
- return (_jsx(MarqueeRoomContext.Provider, { value: marqueeRoom, children: _jsxs("div", { ref: wrapperRef, className: `graview-ground${className ? ` ${className}` : ""}`, "data-graview-altitude": view.overview ? "" : undefined, "data-graview-reach": frame.city ? reachOf(cameraLimit(result)) : undefined, "data-graview-settled": frame.t >= 1 && !flights.gliding() ? "" : undefined, "data-graview-box": size ? `${size.width}x${size.height}` : undefined, onPointerDown: onGroundDown, onPointerMove: onDragMove, onPointerUp: onDragUp, onPointerCancel: onDragUp,
696
+ : {}), touched: touched.has(node.id), ...withActivity(activityOf(node)), scheme: scheme, canvasWidth: result.width, canvasHeight: result.height, selected: selection.includes(node.id), ...actsOn(node), onMenu: setMenuAt, selection: selection, onDragStart: (event) => onCardDown(node, event), onDragMove: onDragMove, onDragEnd: onDragUp, swallowClick: swallow, children: node.beyond ? (_jsx(BeyondCard, { kinds: node.beyond })) : node.aggregate?.opens ? (_jsx(BandCard, { node: node })) : (_jsx(SettledView, { node: node, mode: "scene", selected: selection.includes(node.id), ...(crowded(node) ? { fidelity: "glyph" } : {}) })) }, node.id)));
697
+ return (_jsxs("div", { ref: wrapperRef, className: `graview-ground${className ? ` ${className}` : ""}`, "data-graview-altitude": view.overview ? "" : undefined, "data-graview-reach": frame.city ? reachOf(cameraLimit(result)) : undefined, "data-graview-settled": frame.t >= 1 && !flights.gliding() ? "" : undefined, "data-graview-box": size ? `${size.width}x${size.height}` : undefined, onPointerDown: onGroundDown, onPointerMove: onDragMove, onPointerUp: onDragUp, onPointerCancel: onDragUp,
698
+ /*
699
+ * CLICKING EMPTY GROUND puts the selection down — the gesture every
700
+ * canvas tool teaches, and the graceful half of deselection the ×
701
+ * and Escape were carrying alone. Only the bare ground: a card, a
702
+ * control or a piece of chrome keeps its own meaning, and a drag
703
+ * that ends on the ground is still a pan, not a deselection.
704
+ */
705
+ onClick: (event) => {
706
+ if (swallow.current)
707
+ return;
708
+ const target = event.target;
709
+ // The panes are labeled sections now, not asides (FR-40); chrome is chrome either way.
710
+ if (target.closest("[data-graview-view], button, aside, section[aria-label], [data-graview-offstage], a, input, select"))
711
+ return;
712
+ setSelection([]);
713
+ setMenuAt(null);
714
+ }, style: {
715
+ position: "relative",
663
716
  /*
664
- * CLICKING EMPTY GROUND puts the selection down — the gesture every
665
- * canvas tool teaches, and the graceful half of deselection the ×
666
- * and Escape were carrying alone. Only the bare ground: a card, a
667
- * control or a piece of chrome keeps its own meaning, and a drag
668
- * that ends on the ground is still a pan, not a deselection.
717
+ * THE SCENE FILLS THE WHOLE PICTURE; A PANEL FLOATS OVER IT. The
718
+ * ground, the grid, the depth and the tint run under the panel and
719
+ * show in the gap around it. What a panel takes from the map is the
720
+ * city's fit and the camera's bounds (`useDock`, `--graview-dock`,
721
+ * `--graview-sheet`), never the scene's own box.
669
722
  */
670
- onClick: (event) => {
671
- if (swallow.current)
672
- return;
673
- const target = event.target;
674
- // The panes are labeled sections now, not asides (FR-40); chrome is chrome either way.
675
- if (target.closest("[data-graview-view], button, aside, section[aria-label], [data-graview-offstage], a, input, select"))
676
- return;
677
- setSelection([]);
678
- setMenuAt(null);
679
- }, style: {
680
- position: "relative",
681
- width: "100%",
682
- height: "100%",
683
- cursor: dragging ? "grabbing" : "grab",
684
- touchAction: "none",
685
- ["--graview-altitude"]: view.overview ? 1 : 0,
686
- /*
687
- * THE LATTICE THE CITY STANDS ON. The ground draws its diamonds at
688
- * the cell the map was placed with, anchored where cell (0,0) meets
689
- * the canvas — and the anchor pans with the picture, since the pan
690
- * is baked into every coordinate — so a plot sits on a grid line a
691
- * person can see. The kit's own size stands when no city is drawn.
692
- */
693
- ...(frame.city
694
- ? {
695
- ["--graview-lattice-cell"]: `${frame.city.cell.toFixed(2)}px`,
696
- // The tween's own pan, not the live one: the ground moves with the cards it is under.
697
- ["--graview-lattice-x"]: `${(frame.city.originX + frame.city.pan.x).toFixed(1)}px`,
698
- ["--graview-lattice-y"]: `${(frame.city.originY + frame.city.pan.y).toFixed(1)}px`,
699
- }
700
- : {}),
701
- ...(dragging ? { userSelect: "none" } : {}),
702
- // The stage is sized to the measurement, but a stale measurement
703
- // during a resize can briefly exceed it. Clipping keeps the scene
704
- // inside its own bounds instead of pushing the page taller and
705
- // cutting off anything floating over it.
706
- overflow: "hidden",
707
- ...style,
708
- }, children: [_jsx(Plots, { frame: frame, ...extent,
723
+ width: "100%",
724
+ height: "100%",
725
+ cursor: dragging ? "grabbing" : "grab",
726
+ touchAction: "none",
727
+ ["--graview-altitude"]: view.overview ? 1 : 0,
728
+ /*
729
+ * THE LATTICE THE CITY STANDS ON. The ground draws its diamonds at
730
+ * the cell the map was placed with, anchored where cell (0,0) meets
731
+ * the canvas — and the anchor pans with the picture, since the pan
732
+ * is baked into every coordinate — so a plot sits on a grid line a
733
+ * person can see. The kit's own size stands when no city is drawn.
734
+ */
735
+ ...(frame.city
736
+ ? {
737
+ ["--graview-lattice-cell"]: `${frame.city.cell.toFixed(2)}px`,
738
+ ["--graview-lattice-tilt"]: (frame.city.tilt ?? 0.5).toFixed(3),
739
+ // The tween's own pan, not the live one: the ground moves with the cards it is under.
740
+ ["--graview-lattice-x"]: `${(frame.city.originX + frame.city.pan.x).toFixed(1)}px`,
741
+ ["--graview-lattice-y"]: `${(frame.city.originY + frame.city.pan.y).toFixed(1)}px`,
742
+ }
743
+ : {}),
744
+ ...(dragging ? { userSelect: "none" } : {}),
745
+ // The stage is sized to the measurement, but a stale measurement
746
+ // during a resize can briefly exceed it. Clipping keeps the scene
747
+ // inside its own bounds instead of pushing the page taller and
748
+ // cutting off anything floating over it.
749
+ overflow: "hidden",
750
+ ...style,
751
+ }, children: [view.overview && frame.city ? (_jsx(Suspense, { fallback: null, children: _jsx(Ribbons, { frame: frame, ...extent, pan: frame.city.pan, store: store, graphVersion: nodes, selection: selection, view: { overview: view.overview, focusId: view.focusId, beside: besideTheMap }, emphasis: emphasis, hiddenKinds: context.hiddenKinds, drawn: kitDraws }) })) : null, _jsx("div", { ref: dockProbe, "aria-hidden": "true", style: { position: "absolute", top: 0, right: 0, width: "var(--graview-dock, 0px)", height: "var(--graview-sheet, 0px)", visibility: "hidden", pointerEvents: "none" } }), frame.city ? (_jsx(Suspense, { fallback: null, children: _jsx(Plots, { frame: frame, ...extent,
709
752
  // The tween's own pan: the tiles, the villages and the roads move with the cards on them.
710
- pan: frame.city ? frame.city.pan : panned, brand: brand, pinned: pinnedIds, swallowed: swallow,
753
+ pan: frame.city ? frame.city.pan : panned, brand: brand, pinned: pinnedIds, close: closer * zoom > 1.2, swallowed: swallow, ...(view.overview ? { chosen: mapChoice(selection, { overview: view.overview, focusId: view.focusId, beside: besideTheMap }, (id) => store.graph.getNode(id)?.kind).records } : {}),
711
754
  // A tile is its district: focusing it means the kind's aggregate, never the card's own id.
712
755
  onFocus: (id) => {
713
756
  const kind = kindOfCard(id);
714
757
  if (kind !== null)
715
758
  setView((current) => withFocus(current, aggregateId(kind)));
716
- } }), useDom ? (
759
+ } }) })) : null, useDom ? (
760
+ /*
761
+ * The DOM path uses an ORDINARY container, not a capture canvas.
762
+ *
763
+ * `layoutsubtree` exists so the GPU can capture these elements, and
764
+ * it changes how the browser lays them out. Combining it with the CSS
765
+ * transforms and filters this path applies crashes the renderer
766
+ * process in Chromium 154 — silently, on first paint. Since the DOM
767
+ * path never captures anything, the canvas has no job here, and not
768
+ * creating one removes the whole interaction.
769
+ */
770
+ _jsx("div", { "data-graview-stage": "dom", "data-graview-world": "", style: { ...stage, overflow: "hidden" }, children: hosts })) : (_jsx("canvas", { ref: canvasRef, "data-graview-stage": "gpu", layoutsubtree: "", ...extent, style: { display: "block", ...stage }, children: hosts })), view.overview && !dragging ? (
771
+ /* What the window does not reach, named on the edge it lies past (see edge-signs.tsx). */
772
+ _jsx(EdgeSigns, { result: result, schema: store.schema, onGo: (by) => {
773
+ nudge(by.x, by.y);
774
+ noteMoved();
775
+ } })) : null, view.overview ? _jsx(ZoomControls, { zoom: zoom, zoomBy: (factor) => zoomAbout.current(factor), above: above, setAbove: setAbove }) : null, _jsx(Lines, { frame: frame, focusId: view.focusId, beside: besideTheMap, holding: inPlace, ...extent, scheme: scheme, overview: !!view.overview, selection: selection, emphasis: emphasis, stageRef: wrapperRef, store: store, graphNodes: nodes, ...((provisional) => (provisional ? { provisional } : {}))(provisionalLines(store)),
717
776
  /*
718
- * The DOM path uses an ORDINARY container, not a capture canvas.
719
- *
720
- * `layoutsubtree` exists so the GPU can capture these elements, and
721
- * it changes how the browser lays them out. Combining it with the CSS
722
- * transforms and filters this path applies crashes the renderer
723
- * process in Chromium 154 — silently, on first paint. Since the DOM
724
- * path never captures anything, the canvas has no job here, and not
725
- * creating one removes the whole interaction.
777
+ * A LINE IS A THING. Clicking one that stands for exactly one edge
778
+ * selects the relation itself — the inspector then says what it is
779
+ * and what may lawfully be done to it; right-click opens the same
780
+ * actions at the pointer. Bundled lines stay scenery: "some of
781
+ * these" is not an honest thing to act on.
726
782
  */
727
- _jsx("div", { "data-graview-stage": "dom", "data-graview-world": "", style: { ...stage, overflow: "hidden" }, children: hosts })) : (_jsx("canvas", { ref: canvasRef, "data-graview-stage": "gpu", layoutsubtree: "", ...extent, style: { display: "block", ...stage }, children: hosts })), view.overview && !dragging ? (
728
- /* What the window does not reach, named on the edge it lies past (see edge-signs.tsx). */
729
- _jsx(EdgeSigns, { result: result, schema: store.schema, onGo: (by) => {
730
- nudge(by.x, by.y);
731
- noteMoved();
732
- } })) : null, view.overview ? _jsx(ZoomControls, { zoom: zoom, zoomBy: (factor) => zoomAbout.current(factor) }) : null, _jsx(Lines, { frame: frame, holding: inPlace, ...extent, scheme: scheme, overview: !!view.overview, selection: selection, emphasis: emphasis, stageRef: wrapperRef, store: store, graphNodes: nodes,
783
+ onPickEdge: (edgeId, at) => {
784
+ // A pan that happened to start on a line is a pan, not a pick.
785
+ if (swallow.current)
786
+ return;
787
+ setSelection([edgeId]);
788
+ setMenuAt(at ? { ...at, on: edgeId } : null);
789
+ }, liveOf: (connector) => {
733
790
  /*
734
- * A LINE IS A THING. Clicking one that stands for exactly one edge
735
- * selects the relation itself — the inspector then says what it is
736
- * and what may lawfully be done to it; right-click opens the same
737
- * actions at the pointer. Bundled lines stay scenery: "some of
738
- * these" is not an honest thing to act on.
791
+ * A relation PULSES where it was just made or broken.
792
+ *
793
+ * An edge write touches both of its ends, so a connector is live
794
+ * exactly when both of the things it joins were written in the same
795
+ * window — which is what making or breaking a relation looks like in
796
+ * the log, and is not what changing one node's field looks like.
739
797
  */
740
- onPickEdge: (edgeId, at) => {
741
- // A pan that happened to start on a line is a pan, not a pick.
742
- if (swallow.current)
743
- return;
744
- setSelection([edgeId]);
745
- setMenuAt(at ? { ...at, on: edgeId } : null);
746
- }, liveOf: (connector) => {
747
- /*
748
- * A relation PULSES where it was just made or broken.
749
- *
750
- * An edge write touches both of its ends, so a connector is live
751
- * exactly when both of the things it joins were written in the same
752
- * window — which is what making or breaking a relation looks like in
753
- * the log, and is not what changing one node's field looks like.
754
- */
755
- const from = drawnActivity(connector.from);
756
- const to = drawnActivity(connector.to);
757
- return from?.wrote && to?.wrote ? (from.at > to.at ? from : to) : undefined;
758
- } }), _jsx(Occupants, { width: result.width, whereIs: whereIs }), _jsx(SeatMarks, { marks: seatWork.marks, questions: [...robots.values()]
759
- .filter((one) => one.mode === "asking" && one.at !== null && one.say)
760
- .map((one) => ({ id: one.at, who: one.who, asks: one.say })), whereIs: whereIs, stageRef: wrapperRef, ...extent }), _jsx(RelationCaptions, { nodes: frame.nodes, moving: frame.t < 1, holding: inPlace, scheme: scheme, width: result.width, stageRef: wrapperRef }), children] }) }));
798
+ const from = drawnActivity(connector.from);
799
+ const to = drawnActivity(connector.to);
800
+ return from?.wrote && to?.wrote ? (from.at > to.at ? from : to) : undefined;
801
+ } }), _jsx(Occupants, { width: result.width, whereIs: whereIs }), _jsx(SeatMarks, { marks: seatWork.marks, questions: [...robots.values()]
802
+ .filter((one) => one.mode === "asking" && one.at !== null && one.say)
803
+ .map((one) => ({ id: one.at, who: one.who, asks: one.say })), whereIs: whereIs, stageRef: wrapperRef, ...extent }), _jsx(RelationCaptions, { nodes: frame.nodes, moving: frame.t < 1, holding: inPlace, scheme: scheme, width: result.width, stageRef: wrapperRef }), children] }));
761
804
  }
762
805
  //# sourceMappingURL=scene-root.js.map