@kanzo-tech/graph 0.14.0 → 0.15.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 (113) hide show
  1. package/README.md +12 -15
  2. package/dist/core/categories.d.ts +18 -6
  3. package/dist/core/categories.d.ts.map +1 -1
  4. package/dist/core/categories.js +41 -10
  5. package/dist/core/categories.js.map +1 -1
  6. package/dist/core/channels.d.ts +10 -5
  7. package/dist/core/channels.d.ts.map +1 -1
  8. package/dist/core/channels.js +11 -13
  9. package/dist/core/channels.js.map +1 -1
  10. package/dist/core/detail.d.ts +14 -8
  11. package/dist/core/detail.d.ts.map +1 -1
  12. package/dist/core/detail.js +33 -15
  13. package/dist/core/detail.js.map +1 -1
  14. package/dist/core/filter.d.ts +3 -1
  15. package/dist/core/filter.d.ts.map +1 -1
  16. package/dist/core/filter.js +54 -50
  17. package/dist/core/filter.js.map +1 -1
  18. package/dist/core/load.d.ts +58 -0
  19. package/dist/core/load.d.ts.map +1 -0
  20. package/dist/core/load.js +115 -0
  21. package/dist/core/load.js.map +1 -0
  22. package/dist/core/state.d.ts +27 -55
  23. package/dist/core/state.d.ts.map +1 -1
  24. package/dist/core/store.d.ts +1 -3
  25. package/dist/core/store.d.ts.map +1 -1
  26. package/dist/core/store.js +160 -175
  27. package/dist/core/store.js.map +1 -1
  28. package/dist/core/types.d.ts +21 -17
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/index.d.ts +5 -7
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +19 -25
  33. package/dist/index.js.map +1 -1
  34. package/dist/parts/gesture.d.ts +1 -10
  35. package/dist/parts/gesture.d.ts.map +1 -1
  36. package/dist/parts/gesture.js +28 -28
  37. package/dist/parts/gesture.js.map +1 -1
  38. package/dist/parts/graph-canvas.d.ts.map +1 -1
  39. package/dist/parts/graph-canvas.js +119 -108
  40. package/dist/parts/graph-canvas.js.map +1 -1
  41. package/dist/parts/graph-inspector.d.ts +4 -4
  42. package/dist/parts/graph-inspector.d.ts.map +1 -1
  43. package/dist/parts/graph-inspector.js +55 -57
  44. package/dist/parts/graph-inspector.js.map +1 -1
  45. package/dist/parts/graph-legend.d.ts +4 -4
  46. package/dist/parts/graph-legend.d.ts.map +1 -1
  47. package/dist/parts/graph-legend.js +21 -21
  48. package/dist/parts/graph-legend.js.map +1 -1
  49. package/dist/parts/overlays.d.ts +2 -4
  50. package/dist/parts/overlays.d.ts.map +1 -1
  51. package/dist/parts/overlays.js +45 -45
  52. package/dist/parts/overlays.js.map +1 -1
  53. package/dist/react/use-graph-state.d.ts +4 -2
  54. package/dist/react/use-graph-state.d.ts.map +1 -1
  55. package/dist/react/use-graph-state.js +10 -6
  56. package/dist/react/use-graph-state.js.map +1 -1
  57. package/dist/react/use-graph.d.ts +1 -4
  58. package/dist/react/use-graph.d.ts.map +1 -1
  59. package/dist/react/use-graph.js +38 -49
  60. package/dist/react/use-graph.js.map +1 -1
  61. package/dist/render/graph-looks.d.ts.map +1 -1
  62. package/dist/render/graph-looks.js.map +1 -1
  63. package/dist/render/graph-model.d.ts +6 -8
  64. package/dist/render/graph-model.d.ts.map +1 -1
  65. package/dist/render/graph-model.js +51 -57
  66. package/dist/render/graph-model.js.map +1 -1
  67. package/dist/render/graph-sim.d.ts +2 -3
  68. package/dist/render/graph-sim.d.ts.map +1 -1
  69. package/dist/render/graph-sim.js.map +1 -1
  70. package/dist/render/renderer.d.ts +6 -11
  71. package/dist/render/renderer.d.ts.map +1 -1
  72. package/dist/render/renderer.js +120 -173
  73. package/dist/render/renderer.js.map +1 -1
  74. package/dist/section.d.ts.map +1 -1
  75. package/dist/section.js +3 -7
  76. package/dist/section.js.map +1 -1
  77. package/package.json +11 -11
  78. package/dist/core/refine.d.ts +0 -14
  79. package/dist/core/refine.d.ts.map +0 -1
  80. package/dist/core/refine.js +0 -20
  81. package/dist/core/refine.js.map +0 -1
  82. package/dist/core/resident.d.ts +0 -79
  83. package/dist/core/resident.d.ts.map +0 -1
  84. package/dist/core/resident.js +0 -44
  85. package/dist/core/resident.js.map +0 -1
  86. package/dist/core/scheduler.d.ts +0 -42
  87. package/dist/core/scheduler.d.ts.map +0 -1
  88. package/dist/core/scheduler.js +0 -77
  89. package/dist/core/scheduler.js.map +0 -1
  90. package/dist/core/tile-matrix.d.ts +0 -37
  91. package/dist/core/tile-matrix.d.ts.map +0 -1
  92. package/dist/core/tile-matrix.js +0 -49
  93. package/dist/core/tile-matrix.js.map +0 -1
  94. package/dist/core/tile.d.ts +0 -54
  95. package/dist/core/tile.d.ts.map +0 -1
  96. package/dist/core/tile.js +0 -76
  97. package/dist/core/tile.js.map +0 -1
  98. package/dist/core/tileset.d.ts +0 -59
  99. package/dist/core/tileset.d.ts.map +0 -1
  100. package/dist/core/tileset.js +0 -177
  101. package/dist/core/tileset.js.map +0 -1
  102. package/dist/render/adaptive.d.ts +0 -27
  103. package/dist/render/adaptive.d.ts.map +0 -1
  104. package/dist/render/adaptive.js +0 -25
  105. package/dist/render/adaptive.js.map +0 -1
  106. package/dist/render/compose.d.ts +0 -51
  107. package/dist/render/compose.d.ts.map +0 -1
  108. package/dist/render/compose.js +0 -107
  109. package/dist/render/compose.js.map +0 -1
  110. package/dist/render/encode.d.ts +0 -42
  111. package/dist/render/encode.d.ts.map +0 -1
  112. package/dist/render/encode.js +0 -68
  113. package/dist/render/encode.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"graph-canvas.js","sources":["../../src/parts/graph-canvas.tsx"],"sourcesContent":["\"use client\";\n\nimport { categoricalCapacity, cn, Show, useThemeTick } from \"@kanzo-tech/ui\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { nameOf } from \"../core/categories\";\nimport { denseOf, type VertexId } from \"../core/resident\";\nimport type { GraphOptions } from \"../core/state\";\nimport { useGraphContext } from \"../react/graph-root\";\nimport { internalsOf } from \"../react/use-graph\";\nimport { useGraphState } from \"../react/use-graph-state\";\nimport type { Composition } from \"../render/compose\";\nimport { resolveLook } from \"../render/graph-looks\";\nimport { scaleOf } from \"../render/graph-model\";\nimport { cursorChip, useGesture } from \"./gesture\";\nimport { useOverlays } from \"./overlays\";\nimport { ShapeGlyph } from \"./shape-glyph\";\n\nexport interface GraphCanvasProps extends React.ComponentProps<\"div\"> {\n /** The chrome — a toolbar, a legend, an inspector — positioned over the surface. */\n children?: ReactNode;\n}\n\nconst WASH = \"var(--brand-a5)\";\n\ninterface Label {\n vertex: VertexId;\n text: string;\n}\n\n/** The biggest `budget` vertices by the ramp, the focused one first, each with its `title`. */\nfunction labelsOf(composition: Composition, budget: number, focus: VertexId | null): Label[] {\n const titles = composition.titles;\n if (!titles || budget <= 0) return [];\n const order = Array.from({ length: composition.vertices }, (_, i) => i);\n const ramp = composition.sizes;\n if (ramp) order.sort((a, b) => (ramp[b] ?? 0) - (ramp[a] ?? 0));\n const chosen = order.slice(0, budget);\n const focused = focus === null ? undefined : composition.resident.indexOf(focus);\n if (focused !== undefined && !chosen.includes(focused)) chosen.unshift(focused);\n return chosen.flatMap((i) => {\n const vertex = composition.resident.at(i);\n const text = titles[i];\n return vertex === undefined || !text ? [] : [{ vertex, text }];\n });\n}\n\nconst WAITING: Partial<Record<string, string>> = {\n opening: \"Opening the corpus…\",\n reading: \"Reading the corpus…\",\n};\n\n/**\n * **The element, and what the look declares over it** — the grid, the vignette, the labels and the\n * hover card are fields of `Look`, so drawing them is the canvas's; the marquee and lasso gesture is\n * too. Two layers: cosmos.gl fills the surface with a canvas of its own, so chrome parented there\n * would be a sibling inside an element the renderer resizes. The frame is `isolate`, so a host's\n * `z-index` on a child cannot escape into the page.\n *\n * The surface is never gated on the first answer: rendered behind a placeholder it deadlocks — no\n * element, no renderer, no camera, no question. Waiting is an overlay over an empty canvas.\n */\nexport function GraphCanvas({ children, className, slot, ...rest }: GraphCanvasProps) {\n const api = useGraphContext();\n const { attach, renderer } = internalsOf(api);\n const focus = useGraphState((s) => s.focus);\n const tool = useGraphState((s) => s.tool);\n const options = useGraphState((s) => s.options);\n const domain = useGraphState((s) => s.drawn?.domain ?? s.domain);\n const waiting = useGraphState((s) => (s.drawn === null ? WAITING[s.status] : undefined));\n const look = useMemo(() => resolveLook(options.look), [options.look]);\n const getGraph = useCallback(() => renderer()?.graph ?? null, [renderer]);\n const overlays = useOverlays({ getGraph, getResident: api.getResident });\n const { cardRef, gridRef, hostRef, hoverAt, labelRef, schedule, setHovered, setLabelOrder, track } = overlays;\n const surfaceRef = useRef<HTMLDivElement>(null);\n const [composition, setComposition] = useState<Composition | null>(null);\n\n useEffect(() => {\n const surface = surfaceRef.current;\n if (!surface) return;\n return attach(surface, { onFrame: schedule, onComposed: setComposition, onHover: hoverAt });\n }, [attach, hoverAt, schedule]);\n\n const themeTick = useThemeTick();\n useEffect(() => {\n renderer()?.repaint();\n }, [renderer, themeTick]);\n\n const labels = useMemo(\n () => (composition ? labelsOf(composition, look.labels, focus) : []),\n [composition, focus, look.labels],\n );\n useEffect(() => {\n setLabelOrder(labels.map((label) => label.vertex));\n track();\n schedule();\n }, [labels, schedule, setLabelOrder, track, look.grid]);\n\n const gesture = useGesture({\n getGraph,\n getResident: api.getResident,\n getSelection: useCallback(() => api.getState().selection, [api]),\n commit: (vertices, source, label) => api.select(vertices ? [...vertices] : null, source, label),\n setTool: api.setTool,\n tool,\n });\n const { active, drag, preview } = gesture;\n\n const capacity = hostRef.current ? categoricalCapacity(hostRef.current) : undefined;\n const scale = useMemo(() => scaleOf(options, capacity), [options, capacity]);\n\n return (\n <div\n {...rest}\n className={cn(\"relative isolate size-full overflow-hidden bg-background\", className)}\n data-slot={slot ?? \"graph-canvas\"}\n ref={hostRef}\n >\n <div className=\"size-full\" data-slot=\"graph-canvas-surface\" ref={surfaceRef} />\n <Show when={look.grid}>\n <div\n className=\"pointer-events-none absolute inset-0\"\n ref={gridRef}\n style={{ backgroundImage: \"radial-gradient(var(--border) 1px, transparent 1px)\" }}\n />\n </Show>\n <Show when={look.vignette}>\n <div\n className=\"pointer-events-none absolute inset-0\"\n style={{ background: \"radial-gradient(ellipse 75% 65% at 50% 45%, transparent 30%, var(--background) 100%)\" }}\n />\n </Show>\n <div className=\"pointer-events-none absolute inset-0 overflow-hidden\">\n {labels.map((label) => (\n <span\n className=\"absolute top-0 left-0 whitespace-nowrap font-medium text-[10px] text-foreground leading-none opacity-0 transition-opacity [text-shadow:0_0_3px_var(--background),0_0_6px_var(--background)]\"\n key={String(label.vertex)}\n ref={labelRef(label.vertex)}\n >\n {label.text}\n </span>\n ))}\n <HoverCard\n cardRef={cardRef}\n composition={composition}\n domain={domain}\n options={options}\n scale={scale}\n schedule={schedule}\n setHovered={setHovered}\n />\n </div>\n <Show when={active !== null}>\n <div className=\"absolute inset-0 cursor-crosshair\" data-slot=\"graph-canvas-gesture\" {...gesture.handlers}>\n {drag && (\n <svg className=\"pointer-events-none size-full\">\n {drag.tool === \"rect\" ? (\n <rect\n className=\"stroke-primary\"\n height={Math.abs(drag.to[1] - drag.from[1])}\n strokeDasharray=\"4 3\"\n strokeWidth={1}\n style={{ fill: WASH }}\n width={Math.abs(drag.to[0] - drag.from[0])}\n x={Math.min(drag.from[0], drag.to[0])}\n y={Math.min(drag.from[1], drag.to[1])}\n />\n ) : (\n drag.path.length > 1 && (\n <polygon\n className=\"stroke-primary\"\n points={drag.path.map(([x, y]) => `${x},${y}`).join(\" \")}\n strokeWidth={1}\n style={{ fill: WASH }}\n />\n )\n )}\n </svg>\n )}\n <Show when={preview !== null}>\n <span\n className=\"pointer-events-none absolute rounded-sm bg-primary px-1.5 py-0.5 font-medium text-[10px] text-primary-foreground tabular-nums\"\n style={cursorChip(drag)}\n >\n {preview} node{preview === 1 ? \"\" : \"s\"}\n </span>\n </Show>\n </div>\n </Show>\n <Show when={waiting !== undefined}>\n <p\n className=\"pointer-events-none absolute inset-0 grid place-items-center text-muted-foreground text-xs\"\n data-slot=\"graph-canvas-waiting\"\n >\n {waiting}\n </p>\n </Show>\n {children}\n </div>\n );\n}\n\ninterface HoverCardProps {\n cardRef: React.RefObject<HTMLDivElement | null>;\n composition: Composition | null;\n domain: readonly unknown[];\n options: GraphOptions;\n scale: ReturnType<typeof scaleOf>;\n schedule: () => void;\n setHovered: (vertex: VertexId | null) => void;\n}\n\n/** The only part that re-renders on a hover: the canvas and its labels do not. */\nfunction HoverCard({ cardRef, composition, domain, options, scale, schedule, setHovered }: HoverCardProps) {\n const hovered = useGraphState((s) => s.hovered);\n useEffect(() => {\n setHovered(hovered);\n schedule();\n }, [hovered, schedule, setHovered]);\n\n const index = hovered === null ? undefined : composition?.resident.indexOf(hovered);\n if (hovered === null || !composition || index === undefined) return null;\n const rank = composition.categories[index] ?? 0;\n const bound = options.fill !== undefined || options.symbol !== undefined;\n const category = bound && rank < domain.length ? nameOf(domain[rank], options.categories) : null;\n return (\n <div\n className=\"absolute top-0 left-0 flex w-max max-w-60 items-center gap-2 rounded-lg border bg-popover px-2.5 py-2 opacity-0 shadow-lg\"\n data-slot=\"graph-canvas-card\"\n ref={cardRef}\n >\n <ShapeGlyph className=\"size-2.5 shrink-0\" color={scale.color(rank)} shape={scale.shape(rank)} />\n <span className=\"truncate font-medium text-popover-foreground text-sm leading-none\">\n {composition.titles?.[index] || `#${denseOf(hovered)}`}\n </span>\n <Show when={category !== null}>\n <span className=\"text-muted-foreground text-xs\">{category}</span>\n </Show>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAsBA;AAQA;AACE;AACA;AACA;AAEA;AACA;AAEA;AAEE;AAEA;AAA6D;AAEjE;AAEA;AAAiD;AACtC;AAEX;AAYO;AACL;;AAKoC;AAAqB;;AAGtB;AAAqB;AAMxD;AACE;AACA;AACA;AAA0F;AAG5F;AACA;;AACE;AAAY;AAGd;AAAe;AACoD;AACjC;AAElC;AACE;AAEA;AAGF;AAA2B;AACzB;AACiB;AAC8C;AAC+B;AACjF;AACb;AAOF;AACE;AAAC;AAAA;AACK;AAC+E;AAChE;AACd;AAEL;AAA6E;AAE3E;AAAC;AAAA;AACW;AACL;AACqB;AAAsD;AAEpF;AAEE;AAAC;AAAA;AACW;AACW;AAAuF;AAEhH;AAEG;AACC;AAAC;AAAA;AACW;AAEgB;AAEnB;AAAA;AAHiB;AAK3B;AACD;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEJ;AAGK;AAGK;AAAC;AAAA;AACW;AACgC;AAC1B;AACH;AACE;AAC0B;AACL;AACA;AAAA;AAIpC;AAAC;AAAA;AACW;AAC6C;AAC1C;AACE;AAAK;AAI5B;AAGA;AAAC;AAAA;AACW;AACY;AAErB;AAAA;AAAQ;AAA2B;AAAA;AAAA;AAExC;AAEJ;AAEE;AAAC;AAAA;AACW;AACA;AAET;AAAA;AAEL;AACC;AAAA;AAAA;AAGP;AAaA;;AACE;AACA;AACE;AACA;AAGF;AACA;AACA;AAGA;AACE;AAAC;AAAA;AACW;AACA;AACL;AAEL;AAA8F;AAG9F;AAGA;AAAA;AAAA;AAGN;;;;"}
1
+ {"version":3,"file":"graph-canvas.js","sources":["../../src/parts/graph-canvas.tsx"],"sourcesContent":["\"use client\";\n\nimport { categoricalCapacity, cn, Show, useThemeTick } from \"@kanzo-tech/ui\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { nameOf } from \"../core/categories\";\nimport { readTitles } from \"../core/detail\";\nimport type { Encoding, Geometry } from \"../core/load\";\nimport type { GraphOptions } from \"../core/state\";\nimport type { VertexId } from \"../core/types\";\nimport { useGraphContext } from \"../react/graph-root\";\nimport { internalsOf } from \"../react/use-graph\";\nimport { useGraphSnapshot, useGraphState } from \"../react/use-graph-state\";\nimport { resolveLook } from \"../render/graph-looks\";\nimport { scaleOf } from \"../render/graph-model\";\nimport { cursorChip, useGesture } from \"./gesture\";\nimport { useOverlays } from \"./overlays\";\nimport { ShapeGlyph } from \"./shape-glyph\";\n\nexport interface GraphCanvasProps extends React.ComponentProps<\"div\"> {\n /** The chrome — a toolbar, a legend, an inspector — positioned over the surface. */\n children?: ReactNode;\n}\n\nconst WASH = \"color-mix(in oklab, var(--primary) 17%, transparent)\";\n\n/**\n * The biggest `budget` surviving vertices by the ramp, the focused one first. One pass, keeping the\n * best few in order: a sort of every vertex to name twenty-six of them is the cost this avoids.\n */\nfunction labelled(geometry: Geometry, encoding: Encoding, mask: Uint8Array | null, budget: number, focus: VertexId | null): VertexId[] {\n const ramp = encoding.sizes;\n const best: VertexId[] = [];\n if (ramp && budget > 0) {\n for (let id = 0; id < geometry.size; id++) {\n const value = ramp[id] as number;\n if ((mask && !mask[id]) || Number.isNaN(value)) continue;\n if (best.length === budget && value <= (ramp[best[budget - 1] as number] as number)) continue;\n let at = best.length;\n while (at > 0 && (ramp[best[at - 1] as number] as number) < value) at--;\n best.splice(at, 0, id);\n if (best.length > budget) best.pop();\n }\n }\n return focus === null || best.includes(focus) ? best : [focus, ...best];\n}\n\n/** A label's text is read for the vertices that carry one, never carried for every vertex. */\nfunction useTitles(vertices: readonly VertexId[]): ReadonlyMap<VertexId, string> {\n const api = useGraphContext();\n const corpus = useGraphState((s) => s.corpus);\n const title = useGraphState((s) => s.options.title);\n const geometry = useGraphSnapshot((s) => s.geometry);\n const [titles, setTitles] = useState<ReadonlyMap<VertexId, string>>(() => new Map());\n const key = vertices.join(\",\");\n useEffect(() => {\n if (!corpus || !geometry || key === \"\") return;\n const aborter = new AbortController();\n readTitles(corpus, geometry, key.split(\",\").map(Number), title, aborter.signal).then(\n (found) => !aborter.signal.aborted && setTitles(found),\n (error: unknown) => !aborter.signal.aborted && api.getState().options.onFailure(error instanceof Error ? error.message : String(error)),\n );\n return () => aborter.abort();\n }, [api, corpus, geometry, key, title]);\n return titles;\n}\n\nconst WAITING: Partial<Record<string, string>> = {\n opening: \"Opening the corpus…\",\n loading: \"Loading the graph…\",\n};\n\n/**\n * **The element, and what the look declares over it** — the grid, the vignette, the labels and the\n * hover card are fields of `Look`, so drawing them is the canvas's; the marquee and lasso gesture is\n * too. Two layers: cosmos.gl fills the surface with a canvas of its own, so chrome parented there\n * would be a sibling inside an element the renderer resizes. The frame is `isolate`, so a host's\n * `z-index` on a child cannot escape into the page.\n *\n * The surface is never gated on the first answer: rendered behind a placeholder it deadlocks — no\n * element, no renderer, no camera, no question. Waiting is an overlay over an empty canvas.\n */\nexport function GraphCanvas({ children, className, slot, ...rest }: GraphCanvasProps) {\n const api = useGraphContext();\n const { attach, renderer } = internalsOf(api);\n const focus = useGraphState((s) => s.focus);\n const tool = useGraphState((s) => s.tool);\n const options = useGraphState((s) => s.options);\n const domain = useGraphState((s) => s.drawn?.domain ?? s.domain);\n const waiting = useGraphState((s) => (s.drawn === null ? WAITING[s.status] : undefined));\n const look = useMemo(() => resolveLook(options.look), [options.look]);\n const getGraph = useCallback(() => renderer()?.graph ?? null, [renderer]);\n const overlays = useOverlays({ getGraph });\n const { cardRef, gridRef, hostRef, hoverAt, labelRef, schedule, setHovered, setLabelOrder, track } = overlays;\n const surfaceRef = useRef<HTMLDivElement>(null);\n const geometry = useGraphSnapshot((s) => s.geometry);\n const encoding = useGraphSnapshot((s) => s.encoding);\n const mask = useGraphSnapshot((s) => s.mask);\n\n useEffect(() => {\n const surface = surfaceRef.current;\n if (!surface) return;\n return attach(surface, { onFrame: schedule, onHover: hoverAt });\n }, [attach, hoverAt, schedule]);\n\n const themeTick = useThemeTick();\n useEffect(() => {\n renderer()?.repaint();\n }, [renderer, themeTick]);\n\n const labelledIds = useMemo(\n () => (geometry && encoding ? labelled(geometry, encoding, mask, look.labels, focus) : []),\n [geometry, encoding, mask, focus, look.labels],\n );\n const titles = useTitles(labelledIds);\n const labels = useMemo(\n () => labelledIds.flatMap((vertex) => (titles.get(vertex) ? [{ vertex, text: titles.get(vertex) as string }] : [])),\n [labelledIds, titles],\n );\n useEffect(() => {\n setLabelOrder(labels.map((label) => label.vertex));\n track();\n schedule();\n }, [labels, schedule, setLabelOrder, track, look.grid]);\n\n const gesture = useGesture({\n getGraph,\n getSelection: useCallback(() => api.getState().selection, [api]),\n commit: (vertices, source, label) => api.select(vertices ? [...vertices] : null, source, label),\n setTool: api.setTool,\n tool,\n });\n const { active, drag, preview } = gesture;\n\n const capacity = hostRef.current ? categoricalCapacity(hostRef.current) : undefined;\n const scale = useMemo(() => scaleOf(options, capacity), [options, capacity]);\n\n return (\n <div\n {...rest}\n className={cn(\"relative isolate size-full overflow-hidden bg-background\", className)}\n data-slot={slot ?? \"graph-canvas\"}\n ref={hostRef}\n >\n <div className=\"size-full\" data-slot=\"graph-canvas-surface\" ref={surfaceRef} />\n <Show when={look.grid}>\n <div\n className=\"pointer-events-none absolute inset-0\"\n ref={gridRef}\n style={{ backgroundImage: \"radial-gradient(var(--border) 1px, transparent 1px)\" }}\n />\n </Show>\n <Show when={look.vignette}>\n <div\n className=\"pointer-events-none absolute inset-0\"\n style={{ background: \"radial-gradient(ellipse 75% 65% at 50% 45%, transparent 30%, var(--background) 100%)\" }}\n />\n </Show>\n <div className=\"pointer-events-none absolute inset-0 overflow-hidden\">\n {labels.map((label) => (\n <span\n className=\"absolute top-0 left-0 whitespace-nowrap font-medium text-[10px] text-foreground leading-none opacity-0 transition-opacity [text-shadow:0_0_3px_var(--background),0_0_6px_var(--background)]\"\n key={String(label.vertex)}\n ref={labelRef(label.vertex)}\n >\n {label.text}\n </span>\n ))}\n <HoverCard\n cardRef={cardRef}\n domain={domain}\n encoding={encoding}\n options={options}\n scale={scale}\n schedule={schedule}\n setHovered={setHovered}\n />\n </div>\n <Show when={active !== null}>\n <div className=\"absolute inset-0 cursor-crosshair\" data-slot=\"graph-canvas-gesture\" {...gesture.handlers}>\n {drag && (\n <svg className=\"pointer-events-none size-full\">\n {drag.tool === \"rect\" ? (\n <rect\n className=\"stroke-primary\"\n height={Math.abs(drag.to[1] - drag.from[1])}\n strokeDasharray=\"4 3\"\n strokeWidth={1}\n style={{ fill: WASH }}\n width={Math.abs(drag.to[0] - drag.from[0])}\n x={Math.min(drag.from[0], drag.to[0])}\n y={Math.min(drag.from[1], drag.to[1])}\n />\n ) : (\n drag.path.length > 1 && (\n <polygon\n className=\"stroke-primary\"\n points={drag.path.map(([x, y]) => `${x},${y}`).join(\" \")}\n strokeWidth={1}\n style={{ fill: WASH }}\n />\n )\n )}\n </svg>\n )}\n <Show when={preview !== null}>\n <span\n className=\"pointer-events-none absolute rounded-sm bg-primary px-1.5 py-0.5 font-medium text-[10px] text-primary-foreground tabular-nums\"\n style={cursorChip(drag)}\n >\n {preview} node{preview === 1 ? \"\" : \"s\"}\n </span>\n </Show>\n </div>\n </Show>\n <Show when={waiting !== undefined}>\n <p\n className=\"pointer-events-none absolute inset-0 grid place-items-center text-muted-foreground text-xs\"\n data-slot=\"graph-canvas-waiting\"\n >\n {waiting}\n </p>\n </Show>\n {children}\n </div>\n );\n}\n\ninterface HoverCardProps {\n cardRef: React.RefObject<HTMLDivElement | null>;\n domain: readonly unknown[];\n encoding: Encoding | null;\n options: GraphOptions;\n scale: ReturnType<typeof scaleOf>;\n schedule: () => void;\n setHovered: (vertex: VertexId | null) => void;\n}\n\n/** The only part that re-renders on a hover: the canvas and its labels do not. */\nfunction HoverCard({ cardRef, domain, encoding, options, scale, schedule, setHovered }: HoverCardProps) {\n const hovered = useGraphState((s) => s.hovered);\n const titles = useTitles(useMemo(() => (hovered === null ? [] : [hovered]), [hovered]));\n useEffect(() => {\n setHovered(hovered);\n schedule();\n }, [hovered, schedule, setHovered, titles]);\n\n if (hovered === null || !encoding || hovered >= encoding.ranks.length) return null;\n const rank = encoding.ranks[hovered] ?? 0;\n const category = rank < domain.length ? nameOf(domain[rank], options.categories) : null;\n return (\n <div\n className=\"absolute top-0 left-0 flex w-max max-w-60 items-center gap-2 rounded-lg border bg-popover px-2.5 py-2 opacity-0 shadow-lg\"\n data-slot=\"graph-canvas-card\"\n ref={cardRef}\n >\n <ShapeGlyph className=\"size-2.5 shrink-0\" color={scale.color(rank)} shape={scale.shape(rank)} />\n <span className=\"truncate font-medium text-popover-foreground text-sm leading-none\">\n {titles.get(hovered) || `#${hovered}`}\n </span>\n <Show when={category !== null}>\n <span className=\"text-muted-foreground text-xs\">{category}</span>\n </Show>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAuBA;AAMA;AACE;AAEA;AACE;AACE;AAEA;AACA;AACA;AACA;AAC+B;AAGnC;AACF;AAGA;AACE;AAMA;AACE;AACA;AACA;AAAgF;AACzB;AACiF;AAEnH;AAGzB;AAEA;AAAiD;AACtC;AAEX;AAYO;AACL;;AAKoC;AAAqB;;AAGtB;AAAqB;AAQxD;AACE;AACA;AACA;AAA8D;AAGhE;AACA;;AACE;AAAY;AAGd;AAAoB;AACqE;AAC1C;AAGhC;AACqG;AAC9F;AAEtB;AACE;AAEA;AAGF;AAA2B;AACzB;AAC+D;AAC+B;AACjF;AACb;AAOF;AACE;AAAC;AAAA;AACK;AAC+E;AAChE;AACd;AAEL;AAA6E;AAE3E;AAAC;AAAA;AACW;AACL;AACqB;AAAsD;AAEpF;AAEE;AAAC;AAAA;AACW;AACW;AAAuF;AAEhH;AAEG;AACC;AAAC;AAAA;AACW;AAEgB;AAEnB;AAAA;AAHiB;AAK3B;AACD;AAAC;AAAA;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAEJ;AAGK;AAGK;AAAC;AAAA;AACW;AACgC;AAC1B;AACH;AACE;AAC0B;AACL;AACA;AAAA;AAIpC;AAAC;AAAA;AACW;AAC6C;AAC1C;AACE;AAAK;AAI5B;AAGA;AAAC;AAAA;AACW;AACY;AAErB;AAAA;AAAQ;AAA2B;AAAA;AAAA;AAExC;AAEJ;AAEE;AAAC;AAAA;AACW;AACA;AAET;AAAA;AAEL;AACC;AAAA;AAAA;AAGP;AAaA;AACE;AAOA;AAJE;AACA;AAIF;AAEA;AACE;AAAC;AAAA;AACW;AACA;AACL;AAEL;AAA8F;AAG9F;AAGA;AAAA;AAAA;AAGN;;;;"}
@@ -9,10 +9,10 @@ export interface GraphInspectorProps extends Omit<React.ComponentProps<"div">, "
9
9
  children?: (detail: VertexDetail) => ReactNode;
10
10
  }
11
11
  /**
12
- * **The focused vertex, fetched and laid out by the corpus's own types.** A tile carries what the
13
- * channels project and nothing else, so the rest of the row is read when a reader focuses it — one
14
- * payload tile, filtered to one `dense_id`, cancelled when the focus moves on. The fields are the
15
- * type's, in the order the corpus declares them; the address and the position are the canvas's.
12
+ * **The focused vertex, fetched and laid out by the corpus's own tables.** The loaded graph carries
13
+ * what the channels project and nothing else, so the rest of the row is read when a reader focuses
14
+ * it — a scan of its table, filtered to its key, cancelled when the focus moves on. The fields are the
15
+ * table's, in the order the manifest declares them; the key and the position are the canvas's.
16
16
  */
17
17
  export declare function GraphInspector({ children, className, slot, ...rest }: GraphInspectorProps): import("react").JSX.Element;
18
18
  //# sourceMappingURL=graph-inspector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-inspector.d.ts","sourceRoot":"","sources":["../../src/parts/graph-inspector.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrE,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ/D,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACxF;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,SAAS,CAAC;CAChD;AAWD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,mBAAmB,+BAoFzF"}
1
+ {"version":3,"file":"graph-inspector.d.ts","sourceRoot":"","sources":["../../src/parts/graph-inspector.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrE,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO/D,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACxF;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,SAAS,CAAC;CAChD;AAWD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,mBAAmB,+BAuFzF"}
@@ -1,84 +1,82 @@
1
1
  "use client";
2
- import { jsxs as s, jsx as e, Fragment as k } from "react/jsx-runtime";
3
- import { useChartCapacity as I, Show as p, Skeleton as D, Badge as L, Button as z, DataList as G, DataListItem as B, DataListItemLabel as F, DataListItemValue as M, cn as j } from "@kanzo-tech/ui";
4
- import { CrosshairIcon as A } from "lucide-react";
5
- import { useMemo as E, useState as V, useEffect as T } from "react";
6
- import { nameOf as $ } from "../core/categories.js";
7
- import { bindingOf as q } from "../core/channels.js";
8
- import { readVertex as H } from "../core/detail.js";
9
- import { denseOf as J } from "../core/resident.js";
10
- import { useGraphContext as K } from "../react/graph-root.js";
11
- import { internalsOf as P } from "../react/use-graph.js";
12
- import { useGraphState as d } from "../react/use-graph-state.js";
13
- import { scaleOf as Q } from "../render/graph-model.js";
14
- import { ShapeGlyph as R } from "./shape-glyph.js";
15
- const w = (a) => a instanceof Date ? a.toISOString().slice(0, 10) : a == null || a === "" ? "—" : String(a);
16
- function it({ children: a, className: S, slot: y, ...C }) {
17
- const c = K(), o = d((t) => t.focus), h = d((t) => t.corpus), l = d((t) => t.options), O = d((t) => {
18
- var r;
19
- return ((r = t.drawn) == null ? void 0 : r.domain) ?? t.domain;
20
- }), u = I(), x = E(() => Q(l, u), [l, u]), [f, g] = V(null);
21
- T(() => {
22
- const t = P(c).store.getSnapshot().matrix;
23
- if (o === null || !h || !t) return;
24
- const r = new AbortController();
25
- return H(h, t, o, r.signal).then(
26
- (i) => !r.signal.aborted && g({ vertex: o, detail: i }),
27
- (i) => {
28
- r.signal.aborted || (g({ vertex: o, detail: null }), c.getState().options.onFailure(i instanceof Error ? i.message : String(i)));
2
+ import { jsxs as i, jsx as n, Fragment as O } from "react/jsx-runtime";
3
+ import { useChartCapacity as B, Show as m, Skeleton as F, Badge as M, Button as T, DataList as j, DataListItem as A, DataListItemLabel as E, DataListItemValue as V, cn as $ } from "@kanzo-tech/ui";
4
+ import { CrosshairIcon as q } from "lucide-react";
5
+ import { useMemo as H, useState as J, useEffect as K } from "react";
6
+ import { nameOf as P } from "../core/categories.js";
7
+ import { bindingOf as Q } from "../core/channels.js";
8
+ import { readVertex as R } from "../core/detail.js";
9
+ import { useGraphContext as U } from "../react/graph-root.js";
10
+ import { useGraphState as d, useGraphSnapshot as W } from "../react/use-graph-state.js";
11
+ import { scaleOf as X } from "../render/graph-model.js";
12
+ import { ShapeGlyph as Y } from "./shape-glyph.js";
13
+ const w = (s) => s instanceof Date ? s.toISOString().slice(0, 10) : s == null || s === "" ? "—" : String(s);
14
+ function ct({ children: s, className: C, slot: I, ...k }) {
15
+ var y;
16
+ const p = U(), o = d((t) => t.focus), h = d((t) => t.corpus), r = W((t) => t.geometry), l = d((t) => t.options), D = d((t) => {
17
+ var a;
18
+ return ((a = t.drawn) == null ? void 0 : a.domain) ?? t.domain;
19
+ }), g = B(), v = H(() => X(l, g), [l, g]), [f, b] = J(null);
20
+ K(() => {
21
+ if (o === null || !h || !r) return;
22
+ const t = new AbortController();
23
+ return R(h, r, o, t.signal).then(
24
+ (a) => !t.signal.aborted && b({ vertex: o, detail: a }),
25
+ (a) => {
26
+ t.signal.aborted || (b({ vertex: o, detail: null }), p.getState().options.onFailure(a instanceof Error ? a.message : String(a)));
29
27
  }
30
- ), () => r.abort();
31
- }, [c, h, o]);
32
- const n = f !== null && f.vertex === o ? f.detail : void 0, v = (t) => {
33
- var r;
34
- return (r = n == null ? void 0 : n.fields.find((i) => i.name === t)) == null ? void 0 : r.value;
35
- }, N = q(l).category, m = v(N), b = O.indexOf(typeof m == "bigint" ? Number(m) : m);
36
- return /* @__PURE__ */ s("div", { ...C, className: j("space-y-3 text-sm", S), "data-slot": y ?? "graph-inspector", children: [
37
- /* @__PURE__ */ e(p, { when: o === null, children: /* @__PURE__ */ e("p", { className: "text-muted-foreground text-xs", children: "Click a vertex on the canvas to inspect it." }) }),
38
- /* @__PURE__ */ e(p, { when: o !== null && n === void 0, children: /* @__PURE__ */ e(D, { className: "h-24 w-full" }) }),
39
- /* @__PURE__ */ e(p, { when: o !== null && n === null, children: /* @__PURE__ */ e("p", { className: "text-muted-foreground text-xs", children: "This vertex could not be read." }) }),
40
- n && /* @__PURE__ */ s(k, { children: [
41
- /* @__PURE__ */ s("div", { children: [
42
- /* @__PURE__ */ e("p", { className: "truncate font-medium", "data-slot": "graph-inspector-title", children: l.title === void 0 ? `#${J(n.vertex)}` : w(v(l.title)) }),
43
- /* @__PURE__ */ s("div", { className: "mt-1 flex items-center gap-1.5", children: [
44
- /* @__PURE__ */ e(p, { when: N !== void 0, children: /* @__PURE__ */ s(L, { className: "gap-1 text-[10px]", size: "xs", variant: "outline", children: [
45
- /* @__PURE__ */ e(
46
- R,
28
+ ), () => t.abort();
29
+ }, [p, h, o, r]);
30
+ const e = f !== null && f.vertex === o ? f.detail : void 0, u = (t) => {
31
+ var a;
32
+ return (a = e == null ? void 0 : e.fields.find((z) => z.name === t)) == null ? void 0 : a.value;
33
+ }, c = Q(l), N = c.byTable ? e == null ? void 0 : e.table : u(c.category), S = D.findIndex((t) => String(t) === String(N)), G = c.byTable || c.category !== void 0, L = (y = r == null ? void 0 : r.tables.find((t) => t.name === (e == null ? void 0 : e.table))) == null ? void 0 : y.identity, x = u(l.title) ?? u(L);
34
+ return /* @__PURE__ */ i("div", { ...k, className: $("space-y-3 text-sm", C), "data-slot": I ?? "graph-inspector", children: [
35
+ /* @__PURE__ */ n(m, { when: o === null, children: /* @__PURE__ */ n("p", { className: "text-muted-foreground text-xs", children: "Click a vertex on the canvas to inspect it." }) }),
36
+ /* @__PURE__ */ n(m, { when: o !== null && e === void 0, children: /* @__PURE__ */ n(F, { className: "h-24 w-full" }) }),
37
+ /* @__PURE__ */ n(m, { when: o !== null && e === null, children: /* @__PURE__ */ n("p", { className: "text-muted-foreground text-xs", children: "This vertex could not be read." }) }),
38
+ e && /* @__PURE__ */ i(O, { children: [
39
+ /* @__PURE__ */ i("div", { children: [
40
+ /* @__PURE__ */ n("p", { className: "truncate font-medium", "data-slot": "graph-inspector-title", children: x == null ? `#${e.vertex}` : w(x) }),
41
+ /* @__PURE__ */ i("div", { className: "mt-1 flex items-center gap-1.5", children: [
42
+ /* @__PURE__ */ n(m, { when: G, children: /* @__PURE__ */ i(M, { className: "gap-1 text-[10px]", size: "xs", variant: "outline", children: [
43
+ /* @__PURE__ */ n(
44
+ Y,
47
45
  {
48
46
  className: "size-2 shrink-0",
49
- color: x.color(Math.max(0, b)),
50
- shape: x.shape(Math.max(0, b))
47
+ color: v.color(Math.max(0, S)),
48
+ shape: v.shape(Math.max(0, S))
51
49
  }
52
50
  ),
53
- $(m, l.categories)
51
+ P(N, l.categories)
54
52
  ] }) }),
55
- /* @__PURE__ */ s(
56
- z,
53
+ /* @__PURE__ */ i(
54
+ T,
57
55
  {
58
56
  className: "h-5 gap-1 text-[10px]",
59
- onClick: () => c.reveal(n.vertex),
57
+ onClick: () => p.reveal(e.vertex),
60
58
  size: "sm",
61
59
  title: "Bring this vertex into view on the canvas",
62
60
  variant: "ghost",
63
61
  children: [
64
- /* @__PURE__ */ e(A, { className: "size-3" }),
62
+ /* @__PURE__ */ n(q, { className: "size-3" }),
65
63
  "Find on canvas"
66
64
  ]
67
65
  }
68
66
  )
69
67
  ] })
70
68
  ] }),
71
- /* @__PURE__ */ s(G, { orientation: "vertical", children: [
72
- n.fields.map((t) => /* @__PURE__ */ s(B, { className: "gap-0.5 py-0", children: [
73
- /* @__PURE__ */ e(F, { className: "text-xs", children: t.name }),
74
- /* @__PURE__ */ e(M, { className: "break-all", children: w(t.value) })
69
+ /* @__PURE__ */ i(j, { orientation: "vertical", children: [
70
+ e.fields.map((t) => /* @__PURE__ */ i(A, { className: "gap-0.5 py-0", children: [
71
+ /* @__PURE__ */ n(E, { className: "text-xs", children: t.name }),
72
+ /* @__PURE__ */ n(V, { className: "break-all", children: w(t.value) })
75
73
  ] }, t.name)),
76
- a == null ? void 0 : a(n)
74
+ s == null ? void 0 : s(e)
77
75
  ] })
78
76
  ] })
79
77
  ] });
80
78
  }
81
79
  export {
82
- it as GraphInspector
80
+ ct as GraphInspector
83
81
  };
84
82
  //# sourceMappingURL=graph-inspector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-inspector.js","sources":["../../src/parts/graph-inspector.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Badge,\n Button,\n cn,\n DataList,\n DataListItem,\n DataListItemLabel,\n DataListItemValue,\n Show,\n Skeleton,\n useChartCapacity,\n} from \"@kanzo-tech/ui\";\nimport { CrosshairIcon } from \"lucide-react\";\nimport { useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport { nameOf } from \"../core/categories\";\nimport { bindingOf } from \"../core/channels\";\nimport { readVertex, type VertexDetail } from \"../core/detail\";\nimport { denseOf } from \"../core/resident\";\nimport { useGraphContext } from \"../react/graph-root\";\nimport { internalsOf } from \"../react/use-graph\";\nimport { useGraphState } from \"../react/use-graph-state\";\nimport { scaleOf } from \"../render/graph-model\";\nimport { ShapeGlyph } from \"./shape-glyph\";\n\nexport interface GraphInspectorProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /**\n * More fields for the focused vertex, drawn inside its list after the corpus's own — a\n * `DataListItem` each. A render prop rather than a table of formatters: what a product adds is\n * markup, and the part never learns what any of it means.\n */\n children?: (detail: VertexDetail) => ReactNode;\n}\n\n/** A cell, as text — a `DATE` arrives as a `Date`, and a 64-bit column as a `bigint`. */\nconst text = (value: unknown): string => {\n if (value instanceof Date) return value.toISOString().slice(0, 10);\n if (value === null || value === undefined || value === \"\") return \"—\";\n return String(value);\n};\n\ntype Answer = { vertex: bigint; detail: VertexDetail | null } | null;\n\n/**\n * **The focused vertex, fetched and laid out by the corpus's own types.** A tile carries what the\n * channels project and nothing else, so the rest of the row is read when a reader focuses it — one\n * payload tile, filtered to one `dense_id`, cancelled when the focus moves on. The fields are the\n * type's, in the order the corpus declares them; the address and the position are the canvas's.\n */\nexport function GraphInspector({ children, className, slot, ...rest }: GraphInspectorProps) {\n const api = useGraphContext();\n const focus = useGraphState((s) => s.focus);\n const corpus = useGraphState((s) => s.corpus);\n const options = useGraphState((s) => s.options);\n const domain = useGraphState((s) => s.drawn?.domain ?? s.domain);\n const capacity = useChartCapacity();\n const scale = useMemo(() => scaleOf(options, capacity), [options, capacity]);\n const [answer, setAnswer] = useState<Answer>(null);\n\n useEffect(() => {\n const matrix = internalsOf(api).store.getSnapshot().matrix;\n if (focus === null || !corpus || !matrix) return;\n const aborter = new AbortController();\n readVertex(corpus, matrix, focus, aborter.signal).then(\n (detail) => !aborter.signal.aborted && setAnswer({ vertex: focus, detail }),\n (error: unknown) => {\n if (aborter.signal.aborted) return;\n setAnswer({ vertex: focus, detail: null });\n api.getState().options.onFailure(error instanceof Error ? error.message : String(error));\n },\n );\n return () => aborter.abort();\n }, [api, corpus, focus]);\n\n const current = answer !== null && answer.vertex === focus ? answer.detail : undefined;\n const field = (name: string | undefined) => current?.fields.find((f) => f.name === name)?.value;\n const category = bindingOf(options).category;\n const value = field(category);\n const rank = domain.indexOf(typeof value === \"bigint\" ? Number(value) : value);\n\n return (\n <div {...rest} className={cn(\"space-y-3 text-sm\", className)} data-slot={slot ?? \"graph-inspector\"}>\n <Show when={focus === null}>\n <p className=\"text-muted-foreground text-xs\">Click a vertex on the canvas to inspect it.</p>\n </Show>\n <Show when={focus !== null && current === undefined}>\n <Skeleton className=\"h-24 w-full\" />\n </Show>\n <Show when={focus !== null && current === null}>\n <p className=\"text-muted-foreground text-xs\">This vertex could not be read.</p>\n </Show>\n {current && (\n <>\n <div>\n <p className=\"truncate font-medium\" data-slot=\"graph-inspector-title\">\n {options.title === undefined ? `#${denseOf(current.vertex)}` : text(field(options.title))}\n </p>\n <div className=\"mt-1 flex items-center gap-1.5\">\n <Show when={category !== undefined}>\n <Badge className=\"gap-1 text-[10px]\" size=\"xs\" variant=\"outline\">\n <ShapeGlyph\n className=\"size-2 shrink-0\"\n color={scale.color(Math.max(0, rank))}\n shape={scale.shape(Math.max(0, rank))}\n />\n {nameOf(value, options.categories)}\n </Badge>\n </Show>\n <Button\n className=\"h-5 gap-1 text-[10px]\"\n onClick={() => api.reveal(current.vertex)}\n size=\"sm\"\n title=\"Bring this vertex into view on the canvas\"\n variant=\"ghost\"\n >\n <CrosshairIcon className=\"size-3\" />\n Find on canvas\n </Button>\n </div>\n </div>\n <DataList orientation=\"vertical\">\n {current.fields.map((f) => (\n <DataListItem className=\"gap-0.5 py-0\" key={f.name}>\n <DataListItemLabel className=\"text-xs\">{f.name}</DataListItemLabel>\n <DataListItemValue className=\"break-all\">{text(f.value)}</DataListItemValue>\n </DataListItem>\n ))}\n {children?.(current)}\n </DataList>\n </>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAoCA;AAcO;AACL;;AAIoC;AAAqB;AAKzD;AACE;AACA;AACA;AACA;AAAkD;AAC0B;AAExE;AAEuF;AACzF;AAEmB;AAGvB;;AAC4C;AAA8C;AAK1F;AAEI;AAEA;AAGA;AAGA;AAGI;AACE;AAEA;AAEE;AAEI;AAAA;AAAC;AAAA;AACW;AAC0B;AACA;AAAA;AAAA;AAEL;AAErC;AACA;AAAC;AAAA;AACW;AAC8B;AACnC;AACC;AACE;AAER;AAAkC;AAAE;AAAA;AAAA;AAAA;AAGxC;AACF;AAEG;AAEG;AAA+C;AACS;AAE3D;AACW;AACd;AACF;AAIR;;;;"}
1
+ {"version":3,"file":"graph-inspector.js","sources":["../../src/parts/graph-inspector.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Badge,\n Button,\n cn,\n DataList,\n DataListItem,\n DataListItemLabel,\n DataListItemValue,\n Show,\n Skeleton,\n useChartCapacity,\n} from \"@kanzo-tech/ui\";\nimport { CrosshairIcon } from \"lucide-react\";\nimport { useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport { nameOf } from \"../core/categories\";\nimport { bindingOf } from \"../core/channels\";\nimport { readVertex, type VertexDetail } from \"../core/detail\";\nimport type { VertexId } from \"../core/types\";\nimport { useGraphContext } from \"../react/graph-root\";\nimport { useGraphSnapshot, useGraphState } from \"../react/use-graph-state\";\nimport { scaleOf } from \"../render/graph-model\";\nimport { ShapeGlyph } from \"./shape-glyph\";\n\nexport interface GraphInspectorProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /**\n * More fields for the focused vertex, drawn inside its list after the corpus's own — a\n * `DataListItem` each. A render prop rather than a table of formatters: what a product adds is\n * markup, and the part never learns what any of it means.\n */\n children?: (detail: VertexDetail) => ReactNode;\n}\n\n/** A cell, as text — a `DATE` arrives as a `Date`, and a 64-bit column as a `bigint`. */\nconst text = (value: unknown): string => {\n if (value instanceof Date) return value.toISOString().slice(0, 10);\n if (value === null || value === undefined || value === \"\") return \"—\";\n return String(value);\n};\n\ntype Answer = { vertex: VertexId; detail: VertexDetail | null } | null;\n\n/**\n * **The focused vertex, fetched and laid out by the corpus's own tables.** The loaded graph carries\n * what the channels project and nothing else, so the rest of the row is read when a reader focuses\n * it — a scan of its table, filtered to its key, cancelled when the focus moves on. The fields are the\n * table's, in the order the manifest declares them; the key and the position are the canvas's.\n */\nexport function GraphInspector({ children, className, slot, ...rest }: GraphInspectorProps) {\n const api = useGraphContext();\n const focus = useGraphState((s) => s.focus);\n const corpus = useGraphState((s) => s.corpus);\n const geometry = useGraphSnapshot((s) => s.geometry);\n const options = useGraphState((s) => s.options);\n const domain = useGraphState((s) => s.drawn?.domain ?? s.domain);\n const capacity = useChartCapacity();\n const scale = useMemo(() => scaleOf(options, capacity), [options, capacity]);\n const [answer, setAnswer] = useState<Answer>(null);\n\n useEffect(() => {\n if (focus === null || !corpus || !geometry) return;\n const aborter = new AbortController();\n readVertex(corpus, geometry, focus, aborter.signal).then(\n (detail) => !aborter.signal.aborted && setAnswer({ vertex: focus, detail }),\n (error: unknown) => {\n if (aborter.signal.aborted) return;\n setAnswer({ vertex: focus, detail: null });\n api.getState().options.onFailure(error instanceof Error ? error.message : String(error));\n },\n );\n return () => aborter.abort();\n }, [api, corpus, focus, geometry]);\n\n const current = answer !== null && answer.vertex === focus ? answer.detail : undefined;\n const field = (name: string | undefined) => current?.fields.find((f) => f.name === name)?.value;\n const binding = bindingOf(options);\n const category = binding.byTable ? current?.table : field(binding.category);\n const rank = domain.findIndex((value) => String(value) === String(category));\n const bound = binding.byTable || binding.category !== undefined;\n const identity = geometry?.tables.find((table) => table.name === current?.table)?.identity;\n const heading = field(options.title) ?? field(identity);\n\n return (\n <div {...rest} className={cn(\"space-y-3 text-sm\", className)} data-slot={slot ?? \"graph-inspector\"}>\n <Show when={focus === null}>\n <p className=\"text-muted-foreground text-xs\">Click a vertex on the canvas to inspect it.</p>\n </Show>\n <Show when={focus !== null && current === undefined}>\n <Skeleton className=\"h-24 w-full\" />\n </Show>\n <Show when={focus !== null && current === null}>\n <p className=\"text-muted-foreground text-xs\">This vertex could not be read.</p>\n </Show>\n {current && (\n <>\n <div>\n <p className=\"truncate font-medium\" data-slot=\"graph-inspector-title\">\n {heading === undefined || heading === null ? `#${current.vertex}` : text(heading)}\n </p>\n <div className=\"mt-1 flex items-center gap-1.5\">\n <Show when={bound}>\n <Badge className=\"gap-1 text-[10px]\" size=\"xs\" variant=\"outline\">\n <ShapeGlyph\n className=\"size-2 shrink-0\"\n color={scale.color(Math.max(0, rank))}\n shape={scale.shape(Math.max(0, rank))}\n />\n {nameOf(category, options.categories)}\n </Badge>\n </Show>\n <Button\n className=\"h-5 gap-1 text-[10px]\"\n onClick={() => api.reveal(current.vertex)}\n size=\"sm\"\n title=\"Bring this vertex into view on the canvas\"\n variant=\"ghost\"\n >\n <CrosshairIcon className=\"size-3\" />\n Find on canvas\n </Button>\n </div>\n </div>\n <DataList orientation=\"vertical\">\n {current.fields.map((f) => (\n <DataListItem className=\"gap-0.5 py-0\" key={f.name}>\n <DataListItemLabel className=\"text-xs\">{f.name}</DataListItemLabel>\n <DataListItemValue className=\"break-all\">{text(f.value)}</DataListItemValue>\n </DataListItem>\n ))}\n {children?.(current)}\n </DataList>\n </>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAmCA;AAcO;;AACL;;AAKoC;AAAqB;AAKzD;AACE;AACA;AACA;AAAoD;AACwB;AAExE;AAEuF;AACzF;AAEmB;AAGvB;;AAC4C;AAA8C;AAQ1F;AAEI;AAEA;AAGA;AAGA;AAGI;AACE;AAEA;AAEE;AAEI;AAAA;AAAC;AAAA;AACW;AAC0B;AACA;AAAA;AAAA;AAEF;AAExC;AACA;AAAC;AAAA;AACW;AAC8B;AACnC;AACC;AACE;AAER;AAAkC;AAAE;AAAA;AAAA;AAAA;AAGxC;AACF;AAEG;AAEG;AAA+C;AACS;AAE3D;AACW;AACd;AACF;AAIR;;;;"}
@@ -1,9 +1,9 @@
1
1
  export type GraphLegendProps = React.ComponentProps<"div">;
2
2
  /**
3
- * **The categorical scale the canvas draws, as rows** — glyph, name and how many vertices the marks of
4
- * that category stand for in view, then what is drawn of the whole. The rows are the domain fixed
5
- * before any tile arrived, so a row never moves and a colour never changes hands; a category past the
6
- * palette's capacity is Other, as it is on the canvas.
3
+ * **The categorical scale the canvas draws, as rows** — glyph, name and how many vertices of that
4
+ * category are drawn, then what is drawn of the whole. Unbound, the categories are the vertex types.
5
+ * The rows are the domain fixed before the graph loaded, so a row never moves and a colour never
6
+ * changes hands; a category past the palette's capacity is Other, as it is on the canvas.
7
7
  *
8
8
  * Names are the root's `categories`, never this part's: a legend that renamed a category would be
9
9
  * encoding one product's policy, which is the parts rule's reversal.
@@ -1 +1 @@
1
- {"version":3,"file":"graph-legend.d.ts","sourceRoot":"","sources":["../../src/parts/graph-legend.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AAI3D;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,+BA6CzE"}
1
+ {"version":3,"file":"graph-legend.d.ts","sourceRoot":"","sources":["../../src/parts/graph-legend.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AAI3D;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,+BA8CzE"}
@@ -1,52 +1,52 @@
1
1
  "use client";
2
2
  import { jsxs as s, jsx as a } from "react/jsx-runtime";
3
- import { useChartCapacity as S, Show as f, cn as x } from "@kanzo-tech/ui";
3
+ import { useChartCapacity as S, Show as x, cn as b } from "@kanzo-tech/ui";
4
4
  import { useMemo as O } from "react";
5
- import { nameOf as v } from "../core/categories.js";
6
- import { bindingOf as G } from "../core/channels.js";
5
+ import { nameOf as G } from "../core/categories.js";
6
+ import { bindingOf as j } from "../core/channels.js";
7
7
  import { useGraphState as r } from "../react/use-graph-state.js";
8
- import { scaleOf as j } from "../render/graph-model.js";
9
- import { ShapeGlyph as N } from "./shape-glyph.js";
10
- const z = (l, c) => l.length === c.length && l.every((p, n) => p === c[n]);
8
+ import { scaleOf as z } from "../render/graph-model.js";
9
+ import { ShapeGlyph as y } from "./shape-glyph.js";
10
+ const C = (l, c) => l.length === c.length && l.every((p, n) => p === c[n]);
11
11
  function E({ className: l, slot: c, ...p }) {
12
12
  const n = r((e) => e.options), u = r((e) => {
13
13
  var t;
14
14
  return ((t = e.drawn) == null ? void 0 : t.domain) ?? e.domain;
15
- }, z), d = r((e) => {
15
+ }, C), i = r((e) => {
16
16
  var t;
17
17
  return ((t = e.drawn) == null ? void 0 : t.tally) ?? null;
18
- }), w = r((e) => {
18
+ }), N = r((e) => {
19
19
  var t;
20
- return ((t = e.drawn) == null ? void 0 : t.represented) ?? null;
21
- }), y = r((e) => e.total), o = S(), i = O(() => j(n, o), [n, o]), g = G(n).category !== void 0, h = u.slice(0, o), b = d ? d.slice(o).reduce((e, t) => e + t, 0) : null, m = (e) => e == null ? "—" : e.toLocaleString();
20
+ return ((t = e.drawn) == null ? void 0 : t.vertices) ?? null;
21
+ }), w = r((e) => e.total), o = S(), d = O(() => z(n, o), [n, o]), g = j(n), f = g.byTable || g.category !== void 0, h = u.slice(0, o), v = i ? i.slice(o).reduce((e, t) => e + t, 0) : null, m = (e) => e == null ? "—" : e.toLocaleString();
22
22
  return /* @__PURE__ */ s(
23
23
  "div",
24
24
  {
25
25
  ...p,
26
- className: x("rounded-lg border bg-card px-2.5 py-1.5 text-xs", l),
26
+ className: b("rounded-lg border bg-card px-2.5 py-1.5 text-xs", l),
27
27
  "data-slot": c ?? "graph-legend",
28
28
  children: [
29
- /* @__PURE__ */ a(f, { when: g && h.length > 0, children: /* @__PURE__ */ s("ul", { className: "space-y-1", "data-slot": "graph-legend-items", children: [
29
+ /* @__PURE__ */ a(x, { when: f && h.length > 0, children: /* @__PURE__ */ s("ul", { className: "space-y-1", "data-slot": "graph-legend-items", children: [
30
30
  h.map((e, t) => /* @__PURE__ */ s("li", { className: "flex items-center gap-2", "data-slot": "graph-legend-item", children: [
31
- /* @__PURE__ */ a(N, { className: "size-2.5 shrink-0", color: i.color(t), shape: i.shape(t) }),
32
- /* @__PURE__ */ a("span", { className: "truncate", children: v(e, n.categories) }),
33
- /* @__PURE__ */ a("span", { className: "ms-auto ps-4 text-muted-foreground tabular-nums", children: m(d ? d[t] ?? 0 : null) })
31
+ /* @__PURE__ */ a(y, { className: "size-2.5 shrink-0", color: d.color(t), shape: d.shape(t) }),
32
+ /* @__PURE__ */ a("span", { className: "truncate", children: G(e, n.categories) }),
33
+ /* @__PURE__ */ a("span", { className: "ms-auto ps-4 text-muted-foreground tabular-nums", children: m(i ? i[t] ?? 0 : null) })
34
34
  ] }, String(e))),
35
- /* @__PURE__ */ a(f, { when: u.length > o, children: /* @__PURE__ */ s("li", { className: "flex items-center gap-2", "data-slot": "graph-legend-item", children: [
36
- /* @__PURE__ */ a(N, { className: "size-2.5 shrink-0", color: i.color(o), shape: i.shape(o) }),
35
+ /* @__PURE__ */ a(x, { when: u.length > o, children: /* @__PURE__ */ s("li", { className: "flex items-center gap-2", "data-slot": "graph-legend-item", children: [
36
+ /* @__PURE__ */ a(y, { className: "size-2.5 shrink-0", color: d.color(o), shape: d.shape(o) }),
37
37
  /* @__PURE__ */ a("span", { children: "Other" }),
38
- /* @__PURE__ */ a("span", { className: "ms-auto ps-4 text-muted-foreground tabular-nums", children: m(b) })
38
+ /* @__PURE__ */ a("span", { className: "ms-auto ps-4 text-muted-foreground tabular-nums", children: m(v) })
39
39
  ] }) })
40
40
  ] }) }),
41
41
  /* @__PURE__ */ s(
42
42
  "p",
43
43
  {
44
- className: x("text-muted-foreground tabular-nums", g && h.length > 0 && "mt-1 border-t pt-1"),
44
+ className: b("text-muted-foreground tabular-nums", f && h.length > 0 && "mt-1 border-t pt-1"),
45
45
  "data-slot": "graph-legend-count",
46
46
  children: [
47
- m(w),
47
+ m(N),
48
48
  " of ",
49
- m(y),
49
+ m(w),
50
50
  " drawn"
51
51
  ]
52
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"graph-legend.js","sources":["../../src/parts/graph-legend.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn, Show, useChartCapacity } from \"@kanzo-tech/ui\";\nimport { useMemo } from \"react\";\nimport { nameOf } from \"../core/categories\";\nimport { bindingOf } from \"../core/channels\";\nimport { useGraphState } from \"../react/use-graph-state\";\nimport { scaleOf } from \"../render/graph-model\";\nimport { ShapeGlyph } from \"./shape-glyph\";\n\nexport type GraphLegendProps = React.ComponentProps<\"div\">;\n\nconst shallow = (a: readonly unknown[], b: readonly unknown[]) => a.length === b.length && a.every((v, i) => v === b[i]);\n\n/**\n * **The categorical scale the canvas draws, as rows** — glyph, name and how many vertices the marks of\n * that category stand for in view, then what is drawn of the whole. The rows are the domain fixed\n * before any tile arrived, so a row never moves and a colour never changes hands; a category past the\n * palette's capacity is Other, as it is on the canvas.\n *\n * Names are the root's `categories`, never this part's: a legend that renamed a category would be\n * encoding one product's policy, which is the parts rule's reversal.\n */\nexport function GraphLegend({ className, slot, ...rest }: GraphLegendProps) {\n const options = useGraphState((s) => s.options);\n const domain = useGraphState((s) => s.drawn?.domain ?? s.domain, shallow);\n const tally = useGraphState((s) => s.drawn?.tally ?? null);\n const represented = useGraphState((s) => s.drawn?.represented ?? null);\n const total = useGraphState((s) => s.total);\n const capacity = useChartCapacity();\n const scale = useMemo(() => scaleOf(options, capacity), [options, capacity]);\n const bound = bindingOf(options).category !== undefined;\n const shown = domain.slice(0, capacity);\n const other = tally ? tally.slice(capacity).reduce((sum, n) => sum + n, 0) : null;\n const count = (n: number | null | undefined) => (n === null || n === undefined ? \"—\" : n.toLocaleString());\n\n return (\n <div\n {...rest}\n className={cn(\"rounded-lg border bg-card px-2.5 py-1.5 text-xs\", className)}\n data-slot={slot ?? \"graph-legend\"}\n >\n <Show when={bound && shown.length > 0}>\n <ul className=\"space-y-1\" data-slot=\"graph-legend-items\">\n {shown.map((value, rank) => (\n <li className=\"flex items-center gap-2\" data-slot=\"graph-legend-item\" key={String(value)}>\n <ShapeGlyph className=\"size-2.5 shrink-0\" color={scale.color(rank)} shape={scale.shape(rank)} />\n <span className=\"truncate\">{nameOf(value, options.categories)}</span>\n <span className=\"ms-auto ps-4 text-muted-foreground tabular-nums\">{count(tally ? (tally[rank] ?? 0) : null)}</span>\n </li>\n ))}\n <Show when={domain.length > capacity}>\n <li className=\"flex items-center gap-2\" data-slot=\"graph-legend-item\">\n <ShapeGlyph className=\"size-2.5 shrink-0\" color={scale.color(capacity)} shape={scale.shape(capacity)} />\n <span>Other</span>\n <span className=\"ms-auto ps-4 text-muted-foreground tabular-nums\">{count(other)}</span>\n </li>\n </Show>\n </ul>\n </Show>\n <p\n className={cn(\"text-muted-foreground tabular-nums\", bound && shown.length > 0 && \"mt-1 border-t pt-1\")}\n data-slot=\"graph-legend-count\"\n >\n {count(represented)} of {count(total)} drawn\n </p>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAYA;AAWO;AACL;;AACoC;AAAqB;;AACtB;AAAkB;;AACZ;AAAwB;AASjE;AACE;AAAC;AAAA;AACK;AACsE;AACvD;AAEnB;AAEK;AAEG;AAA8F;AAChC;AAC8C;AAE/G;AAGG;AAAsG;AAC3F;AACqE;AAEpF;AAEJ;AACA;AAAC;AAAA;AACsG;AAC3F;AAET;AAAiB;AAAE;AAAgB;AAAE;AAAA;AAAA;AAAA;AACxC;AAAA;AAGN;;;;"}
1
+ {"version":3,"file":"graph-legend.js","sources":["../../src/parts/graph-legend.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn, Show, useChartCapacity } from \"@kanzo-tech/ui\";\nimport { useMemo } from \"react\";\nimport { nameOf } from \"../core/categories\";\nimport { bindingOf } from \"../core/channels\";\nimport { useGraphState } from \"../react/use-graph-state\";\nimport { scaleOf } from \"../render/graph-model\";\nimport { ShapeGlyph } from \"./shape-glyph\";\n\nexport type GraphLegendProps = React.ComponentProps<\"div\">;\n\nconst shallow = (a: readonly unknown[], b: readonly unknown[]) => a.length === b.length && a.every((v, i) => v === b[i]);\n\n/**\n * **The categorical scale the canvas draws, as rows** — glyph, name and how many vertices of that\n * category are drawn, then what is drawn of the whole. Unbound, the categories are the vertex types.\n * The rows are the domain fixed before the graph loaded, so a row never moves and a colour never\n * changes hands; a category past the palette's capacity is Other, as it is on the canvas.\n *\n * Names are the root's `categories`, never this part's: a legend that renamed a category would be\n * encoding one product's policy, which is the parts rule's reversal.\n */\nexport function GraphLegend({ className, slot, ...rest }: GraphLegendProps) {\n const options = useGraphState((s) => s.options);\n const domain = useGraphState((s) => s.drawn?.domain ?? s.domain, shallow);\n const tally = useGraphState((s) => s.drawn?.tally ?? null);\n const vertices = useGraphState((s) => s.drawn?.vertices ?? null);\n const total = useGraphState((s) => s.total);\n const capacity = useChartCapacity();\n const scale = useMemo(() => scaleOf(options, capacity), [options, capacity]);\n const binding = bindingOf(options);\n const bound = binding.byTable || binding.category !== undefined;\n const shown = domain.slice(0, capacity);\n const other = tally ? tally.slice(capacity).reduce((sum, n) => sum + n, 0) : null;\n const count = (n: number | null | undefined) => (n === null || n === undefined ? \"—\" : n.toLocaleString());\n\n return (\n <div\n {...rest}\n className={cn(\"rounded-lg border bg-card px-2.5 py-1.5 text-xs\", className)}\n data-slot={slot ?? \"graph-legend\"}\n >\n <Show when={bound && shown.length > 0}>\n <ul className=\"space-y-1\" data-slot=\"graph-legend-items\">\n {shown.map((value, rank) => (\n <li className=\"flex items-center gap-2\" data-slot=\"graph-legend-item\" key={String(value)}>\n <ShapeGlyph className=\"size-2.5 shrink-0\" color={scale.color(rank)} shape={scale.shape(rank)} />\n <span className=\"truncate\">{nameOf(value, options.categories)}</span>\n <span className=\"ms-auto ps-4 text-muted-foreground tabular-nums\">{count(tally ? (tally[rank] ?? 0) : null)}</span>\n </li>\n ))}\n <Show when={domain.length > capacity}>\n <li className=\"flex items-center gap-2\" data-slot=\"graph-legend-item\">\n <ShapeGlyph className=\"size-2.5 shrink-0\" color={scale.color(capacity)} shape={scale.shape(capacity)} />\n <span>Other</span>\n <span className=\"ms-auto ps-4 text-muted-foreground tabular-nums\">{count(other)}</span>\n </li>\n </Show>\n </ul>\n </Show>\n <p\n className={cn(\"text-muted-foreground tabular-nums\", bound && shown.length > 0 && \"mt-1 border-t pt-1\")}\n data-slot=\"graph-legend-count\"\n >\n {count(vertices)} of {count(total)} drawn\n </p>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAYA;AAWO;AACL;;AACoC;AAAqB;;AACtB;AAAkB;;AACf;AAAqB;AAU3D;AACE;AAAC;AAAA;AACK;AACsE;AACvD;AAEnB;AAEK;AAEG;AAA8F;AAChC;AAC8C;AAE/G;AAGG;AAAsG;AAC3F;AACqE;AAEpF;AAEJ;AACA;AAAC;AAAA;AACsG;AAC3F;AAET;AAAc;AAAE;AAAgB;AAAE;AAAA;AAAA;AAAA;AACrC;AAAA;AAGN;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { Graph } from '@cosmos.gl/graph';
2
- import { Resident, VertexId } from '../core/resident';
2
+ import { VertexId } from '../core/types';
3
3
  export interface GraphOverlays {
4
4
  /** The box the overlays are positioned within — the canvas' own bounds. */
5
5
  hostRef: React.RefObject<HTMLDivElement | null>;
@@ -26,11 +26,9 @@ export interface GraphOverlays {
26
26
  schedule: () => void;
27
27
  }
28
28
  /**
29
- * The labels, the hover card and the grid, positioned from the renderer every frame: points are
30
- * tracked by index through the resident map, and every overlay is held by identity.
29
+ * The labels, the hover card and the grid, positioned from the renderer every frame.
31
30
  */
32
31
  export declare function useOverlays(api: {
33
32
  getGraph: () => Graph | null;
34
- getResident: () => Resident;
35
33
  }): GraphOverlays;
36
34
  //# sourceMappingURL=overlays.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/parts/overlays.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA6C3D,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,mDAAmD;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACtE,gFAAgF;IAChF,aAAa,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAC9C,8EAA8E;IAC9E,UAAU,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,8FAA8F;IAC9F,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACrD;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE;IAAE,QAAQ,EAAE,MAAM,KAAK,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,QAAQ,CAAA;CAAE,GAAG,aAAa,CAgO7G"}
1
+ {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/parts/overlays.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AA0C9C,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,mDAAmD;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACtE,gFAAgF;IAChF,aAAa,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAC9C,8EAA8E;IAC9E,UAAU,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9C,8FAA8F;IAC9F,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACrD;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE;IAAE,QAAQ,EAAE,MAAM,KAAK,GAAG,IAAI,CAAA;CAAE,GAAG,aAAa,CAuNhF"}
@@ -1,100 +1,100 @@
1
1
  "use client";
2
- import { useRef as c, useCallback as d, useEffect as O } from "react";
3
- const S = 22, W = 14, m = 8, V = 10, X = 12, M = 3, q = (R, i, h) => Math.min(Math.max(R, i), Math.max(i, h));
4
- function tt(R) {
5
- const { getGraph: i, getResident: h } = R, T = c(null), A = c(null), _ = c(null), P = c(/* @__PURE__ */ new Map()), $ = c(/* @__PURE__ */ new Map()), L = c([]), B = c(null), D = c(null), k = c(null), H = c(null), f = c(0), Z = d(() => {
2
+ import { useRef as c, useCallback as h, useEffect as S } from "react";
3
+ const T = 22, C = 14, m = 8, Q = 10, U = 12, M = 3, F = (A, i, y) => Math.min(Math.max(A, i), Math.max(i, y));
4
+ function X(A) {
5
+ const { getGraph: i } = A, y = c(null), R = c(null), _ = c(null), P = c(/* @__PURE__ */ new Map()), $ = c(/* @__PURE__ */ new Map()), L = c([]), B = c(null), D = c(null), k = c(null), H = c(null), f = c(0), N = h(() => {
6
6
  const t = i();
7
- t && t.trackPointPositionsByIndices(h().indicesOf(L.current));
8
- }, [i, h]), I = d(() => {
7
+ t && t.trackPointPositionsByIndices(L.current);
8
+ }, [i]), I = h(() => {
9
9
  const t = i();
10
10
  if (!t) return;
11
- const s = h();
12
- let p = null;
13
- const x = (e) => {
14
- const n = s.indexOf(e);
15
- return n === void 0 ? null : (p ?? (p = t.getTrackedPointPositionsMap()), p.get(n) ?? null);
16
- }, C = [], g = H.current;
11
+ let s = null;
12
+ const g = (e) => {
13
+ s ?? (s = t.getTrackedPointPositionsMap());
14
+ const n = s.get(e);
15
+ return n && !Number.isNaN(n[0]) ? n : null;
16
+ }, d = [], p = H.current;
17
17
  for (const e of L.current) {
18
18
  const n = P.current.get(e);
19
19
  if (!n) continue;
20
- const u = x(e);
20
+ const u = g(e);
21
21
  if (!u) {
22
22
  n.style.opacity = "0";
23
23
  continue;
24
24
  }
25
- const [o, l] = t.spaceToScreenPosition(u);
25
+ const [o, a] = t.spaceToScreenPosition(u);
26
26
  let r = $.current.get(e);
27
27
  r === void 0 && (r = n.offsetWidth, $.current.set(e, r));
28
- const a = o - r / 2, G = a + r, b = l - V, v = b - X, Q = g !== null && (G < 0 || b < 0 || a > g.width || v > g.height), U = C.some((w) => a < w[2] && G > w[0] && v < w[3] && b > w[1]);
29
- if (Q || U) {
28
+ const l = o - r / 2, G = l + r, b = a - Q, w = b - U, J = p !== null && (G < 0 || b < 0 || l > p.width || w > p.height), K = d.some((v) => l < v[2] && G > v[0] && w < v[3] && b > v[1]);
29
+ if (J || K) {
30
30
  n.style.opacity = "0";
31
31
  continue;
32
32
  }
33
- C.push([a - M, v - M, G + M, b + M]), n.style.transform = `translate(${Math.round(a)}px, ${Math.round(v)}px)`, n.style.opacity = "1";
33
+ d.push([l - M, w - M, G + M, b + M]), n.style.transform = `translate(${Math.round(l)}px, ${Math.round(w)}px)`, n.style.opacity = "1";
34
34
  }
35
- const z = A.current;
35
+ const z = R.current;
36
36
  if (z) {
37
37
  const e = t.getZoomLevel();
38
38
  if (e > 0) {
39
- const n = S * e / 2 ** Math.floor(Math.log2(e)), [u, o] = t.spaceToScreenPosition([0, 0]), l = (r) => (r % n + n) % n;
40
- z.style.backgroundSize = `${n}px ${n}px`, z.style.backgroundPosition = `${l(u)}px ${l(o)}px`;
39
+ const n = T * e / 2 ** Math.floor(Math.log2(e)), [u, o] = t.spaceToScreenPosition([0, 0]), a = (r) => (r % n + n) % n;
40
+ z.style.backgroundSize = `${n}px ${n}px`, z.style.backgroundPosition = `${a(u)}px ${a(o)}px`;
41
41
  }
42
42
  }
43
- const y = _.current, F = B.current;
44
- if (y && F !== null && g) {
45
- const e = s.indexOf(F) === void 0 ? null : D.current;
43
+ const x = _.current, j = B.current;
44
+ if (x && j !== null && p) {
45
+ const e = D.current;
46
46
  if (e) {
47
47
  const [n, u] = t.spaceToScreenPosition(e);
48
48
  let o = k.current;
49
- o || (o = { width: y.offsetWidth, height: y.offsetHeight }, k.current = o);
50
- const l = u - W - o.height, r = u + W, a = l >= m ? l : r;
51
- y.style.transform = `translate(${Math.round(
52
- q(n - o.width / 2, m, g.width - o.width - m)
53
- )}px, ${Math.round(q(a, m, g.height - o.height - m))}px)`, y.style.opacity = "1";
49
+ o || (o = { width: x.offsetWidth, height: x.offsetHeight }, k.current = o);
50
+ const a = u - C - o.height, r = u + C, l = a >= m ? a : r;
51
+ x.style.transform = `translate(${Math.round(
52
+ F(n - o.width / 2, m, p.width - o.width - m)
53
+ )}px, ${Math.round(F(l, m, p.height - o.height - m))}px)`, x.style.opacity = "1";
54
54
  }
55
55
  }
56
- }, [i, h]);
57
- O(() => {
58
- const t = T.current;
56
+ }, [i]);
57
+ S(() => {
58
+ const t = y.current;
59
59
  if (!t) return;
60
- const s = new ResizeObserver(([p]) => {
61
- const x = p == null ? void 0 : p.contentRect;
62
- x && (H.current = { width: x.width, height: x.height });
60
+ const s = new ResizeObserver(([g]) => {
61
+ const d = g == null ? void 0 : g.contentRect;
62
+ d && (H.current = { width: d.width, height: d.height });
63
63
  });
64
64
  return s.observe(t), () => s.disconnect();
65
- }, []), O(() => {
66
- const t = A.current;
67
- t && (t.style.backgroundSize = `${S}px ${S}px`);
65
+ }, []), S(() => {
66
+ const t = R.current;
67
+ t && (t.style.backgroundSize = `${T}px ${T}px`);
68
68
  }, []);
69
- const E = d(() => {
69
+ const E = h(() => {
70
70
  f.current || (f.current = requestAnimationFrame(() => {
71
71
  f.current = 0, I();
72
72
  }));
73
73
  }, [I]);
74
- O(
74
+ S(
75
75
  () => () => {
76
76
  f.current && cancelAnimationFrame(f.current), f.current = 0;
77
77
  },
78
78
  []
79
79
  );
80
- const j = d(
80
+ const O = h(
81
81
  (t) => (s) => {
82
82
  s ? P.current.set(t, s) : P.current.delete(t);
83
83
  },
84
84
  []
85
- ), J = d((t) => {
85
+ ), W = h((t) => {
86
86
  L.current = t, $.current.clear();
87
- }, []), K = d((t) => {
87
+ }, []), q = h((t) => {
88
88
  B.current = t, k.current = null;
89
- }, []), N = d(
89
+ }, []), Z = h(
90
90
  (t) => {
91
91
  D.current = t, E();
92
92
  },
93
93
  [E]
94
94
  );
95
- return { hostRef: T, gridRef: A, cardRef: _, labelRef: j, setLabelOrder: J, setHovered: K, hoverAt: N, track: Z, schedule: E };
95
+ return { hostRef: y, gridRef: R, cardRef: _, labelRef: O, setLabelOrder: W, setHovered: q, hoverAt: Z, track: N, schedule: E };
96
96
  }
97
97
  export {
98
- tt as useOverlays
98
+ X as useOverlays
99
99
  };
100
100
  //# sourceMappingURL=overlays.js.map