@kanzo-tech/graph 0.1.0 → 0.3.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 (56) hide show
  1. package/README.md +15 -7
  2. package/dist/bounded.d.ts +119 -69
  3. package/dist/bounded.d.ts.map +1 -1
  4. package/dist/bounded.js +13 -34
  5. package/dist/bounded.js.map +1 -1
  6. package/dist/duck-source.d.ts +60 -72
  7. package/dist/duck-source.d.ts.map +1 -1
  8. package/dist/duck-source.js +185 -255
  9. package/dist/duck-source.js.map +1 -1
  10. package/dist/graph-canvas.d.ts +1 -1
  11. package/dist/graph-canvas.js.map +1 -1
  12. package/dist/graph-looks.d.ts +63 -18
  13. package/dist/graph-looks.d.ts.map +1 -1
  14. package/dist/graph-looks.js +37 -26
  15. package/dist/graph-looks.js.map +1 -1
  16. package/dist/graph-model.d.ts +2 -2
  17. package/dist/graph-model.d.ts.map +1 -1
  18. package/dist/graph-model.js +19 -14
  19. package/dist/graph-model.js.map +1 -1
  20. package/dist/graph-sim.d.ts +15 -1
  21. package/dist/graph-sim.d.ts.map +1 -1
  22. package/dist/graph-sim.js +17 -13
  23. package/dist/graph-sim.js.map +1 -1
  24. package/dist/index.d.ts +14 -14
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +30 -48
  27. package/dist/index.js.map +1 -1
  28. package/dist/obligations.d.ts +1 -3
  29. package/dist/obligations.d.ts.map +1 -1
  30. package/dist/shape-glyph.d.ts +30 -0
  31. package/dist/shape-glyph.d.ts.map +1 -0
  32. package/dist/shape-glyph.js +15 -0
  33. package/dist/shape-glyph.js.map +1 -0
  34. package/dist/slice-client.d.ts.map +1 -1
  35. package/dist/slice-client.js +36 -36
  36. package/dist/slice-client.js.map +1 -1
  37. package/dist/use-graph-overlays.d.ts +21 -30
  38. package/dist/use-graph-overlays.d.ts.map +1 -1
  39. package/dist/use-graph-overlays.js +39 -37
  40. package/dist/use-graph-overlays.js.map +1 -1
  41. package/dist/use-graph.d.ts +38 -12
  42. package/dist/use-graph.d.ts.map +1 -1
  43. package/dist/use-graph.js +62 -61
  44. package/dist/use-graph.js.map +1 -1
  45. package/dist/use-query-loop.d.ts +8 -4
  46. package/dist/use-query-loop.d.ts.map +1 -1
  47. package/dist/use-query-loop.js +93 -96
  48. package/dist/use-query-loop.js.map +1 -1
  49. package/dist/use-renderer.d.ts.map +1 -1
  50. package/dist/use-renderer.js +69 -66
  51. package/dist/use-renderer.js.map +1 -1
  52. package/package.json +11 -5
  53. package/dist/memory-source.d.ts +0 -32
  54. package/dist/memory-source.d.ts.map +0 -1
  55. package/dist/memory-source.js +0 -134
  56. package/dist/memory-source.js.map +0 -1
package/dist/use-graph.js CHANGED
@@ -1,101 +1,102 @@
1
1
  "use client";
2
- import { useMemo as P, useRef as s, useCallback as R } from "react";
3
- import { DEFAULT_LOOK as j } from "./graph-looks.js";
4
- import { isColour as q } from "./graph-model.js";
5
- import { residentOf as z } from "./resident.js";
6
- import { useQueryLoop as H } from "./use-query-loop.js";
7
- import { useRenderer as I } from "./use-renderer.js";
8
- import { useGraphLook as J } from "./use-graph-look.js";
9
- const S = z(null);
10
- function nr(C) {
2
+ import { useMemo as l, useRef as c, useCallback as b } from "react";
3
+ import { resolveLook as H } from "./graph-looks.js";
4
+ import { isColour as I } from "./graph-model.js";
5
+ import { residentOf as J } from "./resident.js";
6
+ import { resolveSim as K } from "./graph-sim.js";
7
+ import { useQueryLoop as U } from "./use-query-loop.js";
8
+ import { useRenderer as V } from "./use-renderer.js";
9
+ import { useGraphLook as W } from "./use-graph-look.js";
10
+ const X = J(null);
11
+ function cr(B) {
11
12
  const {
12
13
  clusters: D,
13
14
  debounce: E,
14
- events: a,
15
+ events: p,
15
16
  fill: i,
16
- limit: L,
17
- look: b = j,
18
- onFailure: p,
19
- pinned: x,
20
- r: B,
21
- report: G,
17
+ limit: G,
18
+ look: d,
19
+ onFailure: k,
20
+ pinned: L,
21
+ r: x,
22
+ report: y,
22
23
  reportProgress: T,
23
- schedule: y,
24
- sim: F,
25
- simulate: Z = !1,
26
- source: w,
27
- stroke: d,
28
- symbol: l
29
- } = C, A = q(i) ? l : i, K = P(() => ({ fill: i, stroke: d, symbol: l }), [i, d, l]), u = s(null), f = s(null), c = s(S), { explore: M, pending: N, refresh: m, resident: k, slice: v, sliced: Q, total: U } = H({
24
+ schedule: Z,
25
+ sim: v,
26
+ simulate: w = !1,
27
+ source: F,
28
+ stroke: h,
29
+ symbol: m
30
+ } = B, M = l(() => H(d), [d]), N = l(() => K(v), [v]), Q = I(i) ? m : i, S = l(() => ({ fill: i, stroke: h, symbol: m }), [i, h, m]), u = c(null), f = c(null), s = c(X), { pending: Y, refresh: a, resident: g, slice: P, sliced: j, total: q } = U({
30
31
  debounce: E,
31
- fill: A,
32
+ fill: Q,
32
33
  graphRef: f,
33
34
  hostRef: u,
34
- limit: L,
35
- onError: p,
36
- pinned: x,
37
- r: B,
38
- source: w
35
+ limit: G,
36
+ onError: k,
37
+ pinned: L,
38
+ r: x,
39
+ source: F
39
40
  });
40
- c.current = k;
41
- const g = R(() => f.current, []), Y = R(() => c.current, []), t = s(a);
42
- t.current = a;
43
- const h = s(m);
44
- h.current = m;
45
- const _ = P(
41
+ s.current = g;
42
+ const O = b(() => f.current, []), z = b(() => s.current, []), t = c(p);
43
+ t.current = p;
44
+ const R = c(a);
45
+ R.current = a;
46
+ const A = l(
46
47
  () => ({
47
48
  onBackgroundClick: () => {
48
- var r, e;
49
- return (e = (r = t.current) == null ? void 0 : r.onBackgroundClick) == null ? void 0 : e.call(r);
49
+ var r, o;
50
+ return (o = (r = t.current) == null ? void 0 : r.onBackgroundClick) == null ? void 0 : o.call(r);
50
51
  },
51
52
  onDragEnd: (r) => {
52
- var o, n;
53
- const e = c.current.at(r);
54
- e !== void 0 && ((n = (o = t.current) == null ? void 0 : o.onDragEnd) == null || n.call(o, e));
53
+ var e, n;
54
+ const o = s.current.at(r);
55
+ o !== void 0 && ((n = (e = t.current) == null ? void 0 : e.onDragEnd) == null || n.call(e, o));
55
56
  },
56
- onPointClick: (r, e) => {
57
- var n, O;
58
- const o = c.current.at(e);
59
- o !== void 0 && ((O = (n = t.current) == null ? void 0 : n.onPointClick) == null || O.call(n, o, r, e));
57
+ onPointClick: (r, o) => {
58
+ var n, C;
59
+ const e = s.current.at(o);
60
+ e !== void 0 && ((C = (n = t.current) == null ? void 0 : n.onPointClick) == null || C.call(n, e, r, o));
60
61
  },
61
62
  onPointerOut: () => {
62
- var r, e;
63
- return (e = (r = t.current) == null ? void 0 : r.onPointerOut) == null ? void 0 : e.call(r);
63
+ var r, o;
64
+ return (o = (r = t.current) == null ? void 0 : r.onPointerOut) == null ? void 0 : o.call(r);
64
65
  },
65
66
  onPointerOver: (r) => {
66
- var o, n;
67
- const e = c.current.at(r);
68
- e !== void 0 && ((n = (o = t.current) == null ? void 0 : o.onPointerOver) == null || n.call(o, e, r));
67
+ var e, n;
68
+ const o = s.current.at(r);
69
+ o !== void 0 && ((n = (e = t.current) == null ? void 0 : e.onPointerOver) == null || n.call(e, o, r));
69
70
  },
70
71
  onTick: () => {
71
- var r, e;
72
- return (e = (r = t.current) == null ? void 0 : r.onTick) == null ? void 0 : e.call(r);
72
+ var r, o;
73
+ return (o = (r = t.current) == null ? void 0 : r.onTick) == null ? void 0 : o.call(r);
73
74
  },
74
75
  // The camera moved, so the graph is re-asked. A host that forgot this line got a canvas that
75
76
  // drew its first answer and never asked again — which is what `refresh` being the host's
76
77
  // responsibility used to cost.
77
78
  onZoom: () => {
78
- var r, e;
79
- h.current(), (e = (r = t.current) == null ? void 0 : r.onZoom) == null || e.call(r);
79
+ var r, o;
80
+ R.current(), (o = (r = t.current) == null ? void 0 : r.onZoom) == null || o.call(r);
80
81
  }
81
82
  }),
82
83
  // Built once: every reference inside is a ref this hook owns, so there is nothing to depend on.
83
84
  // It used to close over a ref object a host could substitute, which is the prop this file deletes.
84
85
  []
85
86
  );
86
- return I({
87
+ return V({
87
88
  clusters: D,
88
- events: _,
89
+ events: A,
89
90
  graphRef: f,
90
91
  hostRef: u,
91
- onFailure: p,
92
- report: G,
92
+ onFailure: k,
93
+ report: y,
93
94
  reportProgress: T,
94
- sim: F,
95
- simulate: Z
96
- }), J({ channels: K, getGraph: g, hostRef: u, look: b, schedule: y, slice: v }), { explore: M, getGraph: g, getResident: Y, hostRef: u, pending: N, refresh: m, resident: k, slice: v, sliced: Q, total: U };
95
+ sim: N,
96
+ simulate: w
97
+ }), W({ channels: S, getGraph: O, hostRef: u, look: M, schedule: Z, slice: P }), { getGraph: O, getResident: z, hostRef: u, pending: Y, refresh: a, resident: g, slice: P, sliced: j, total: q };
97
98
  }
98
99
  export {
99
- nr as useGraph
100
+ cr as useGraph
100
101
  };
101
102
  //# sourceMappingURL=use-graph.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-graph.js","sources":["../src/use-graph.ts"],"sourcesContent":["\"use client\";\n\nimport type { Graph } from \"@cosmos.gl/graph\";\nimport { useCallback, useMemo, useRef, type RefObject } from \"react\";\nimport type { BoundedSource, Slice } from \"./bounded\";\nimport { DEFAULT_LOOK, type Look } from \"./graph-looks\";\nimport { isColour, type Channels } from \"./graph-model\";\nimport { residentOf, type Resident, type VertexId } from \"./resident\";\nimport type { Sim } from \"./graph-sim\";\nimport type { Motion } from \"./types\";\nimport { useQueryLoop } from \"./use-query-loop\";\nimport { useRenderer, type RendererOptions } from \"./use-renderer\";\nimport { useGraphLook } from \"./use-graph-look\";\n\n/**\n * Everything a graph is, above the element that draws it.\n *\n * **This is Ark's `useX(props) → api`, and the reason it exists is a host that could not adopt\n * `GraphCanvas`.** The component owned the renderer, the query loop and the look, and published them\n * through a context — which a legend or an inspector can read, because those are `children`. What\n * cannot read a context is anything that sits *above* the element: `useGraphOverlays` wants\n * `getGraph` and `getResident`, the `events` block wants the same accessors, and both are arguments\n * to a hook called in the component that renders the canvas rather than inside it. The workspace\n * needed them in thirty places and so kept its own copy of all three.\n *\n * The general answer to that circularity is Ark's, and it is a shape rather than a feature: the\n * factory builds the api where the host can hold it, the provider takes it and renders. What the\n * component version tried instead — `graphRef` and `residentRef` as props, given rather than\n * returned — solved one host's version of it and is deleted by this file.\n *\n * **The structure is copied and the substance is not.** An Ark api's value is its prop getters,\n * which distribute props across many parts. Here there are no parts: a canvas is one element. So\n * there is no `getRootProps()` — `hostRef` is what the provider needs and all it needs — and\n * inventing one for the resemblance would be cargo cult. `CONVENTIONS.md` is where that line is\n * drawn, not here.\n */\n\n/**\n * The gestures, in the terms the rest of this package speaks.\n *\n * cosmos.gl reports a **buffer index**, which names a slot in the answer currently uploaded. Every\n * host then wrote the same three lines — resolve the index through the resident map, return early if\n * it resolves to nothing, carry on with the identity — because an index is not something you can\n * keep. The factory already holds that map, so it does the resolving and a callback that would have\n * been handed a stale slot is simply not called.\n *\n * `onZoom` is the one with a duty attached: the camera moving is how a bounded graph is re-asked,\n * and the loop issues that `refresh` itself. What arrives here is the notification, for a host that\n * has overlays to reposition.\n */\nexport interface GraphEvents {\n onBackgroundClick?: () => void;\n onPointClick?: (vertex: VertexId, graph: Graph, index: number) => void;\n onPointerOver?: (vertex: VertexId, index: number) => void;\n onPointerOut?: () => void;\n onDragEnd?: (vertex: VertexId) => void;\n onTick?: () => void;\n onZoom?: () => void;\n}\n\nexport interface UseGraphProps {\n /** What to draw. `null` renders the frame and asks nothing — a host still resolving its data. */\n source: BoundedSource | null;\n /**\n * Geometry only. Colour comes from the page's categorical scale, never from here.\n *\n * **One appearance input**, where there were two: a `Display` rode beside this with its own\n * defaults, spelling the edge layer, the backdrop and two multipliers over numbers the look\n * already computes. `lookFrom(values)` resolves the whole picture from the axes a person chose.\n */\n look?: Look;\n sim?: Sim;\n /**\n * Off by default, and that is the correct default rather than a cautious one: a bounded source\n * hands back the coordinates its next spatial query is expressed in, so a force moves the picture\n * out from under its own index.\n */\n simulate?: boolean;\n clusters?: (number | undefined)[];\n /**\n * Which column colours a point, **or a CSS colour every point wears** — Plot's channel name,\n * Plot's meaning, and Plot's rule that a colour is a constant and anything else is a column.\n *\n * **A channel is what you want drawn, so it rides the question and not the source.** Baked into a\n * source at construction — which is where this used to live — changing what a graph is coloured by\n * meant building a second source, and *where the bytes are* and *what I want drawn* are not the\n * same question. Changing it here re-asks over the same bytes, which is all it should ever have\n * cost.\n *\n * As a *constant* it costs no query at all: `fill=\"var(--foreground)\"` is the monochrome picture,\n * and it is a binding rather than a look, because a look may not rebind an encoding.\n *\n * Omitted, the source decides which column: it is the only thing that knows what its corpus\n * carries.\n */\n fill?: string;\n /**\n * Which column a point's **shape** carries — Plot's `symbol`, and a peer of colour.\n *\n * Bound to the same column as `fill` this is redundant encoding, which is the point on a colourful\n * document and the only encoding left on a monochrome one. It reads the categorical column the\n * slice already carries, so it costs no query; a *different* column would need a second categorical\n * array in the slice and in every source, and is not paid for.\n *\n * Binding it takes on an obligation — see `gradeComposition`: shape and size spent at once need a\n * radius floor, which is a property of what you composed rather than of the form you picked.\n */\n symbol?: string;\n /**\n * Which column the size ramp is spent on — Plot's `r`.\n *\n * Omitted, every point is drawn at one radius, which is a legitimate picture: without a ramp a\n * look's `form.size` range has only one end.\n */\n r?: string;\n /**\n * What tints a link. **Absent, each link takes the colour of the vertex it leaves**; a constant\n * like `var(--muted-foreground)` makes links plain structure.\n */\n stroke?: string;\n /** Vertices that stay drawn whatever the camera is over. */\n pinned?: VertexId[];\n limit?: number;\n debounce?: number;\n /**\n * Required, and the only one.\n *\n * Its silence is a defect rather than a choice: unhandled, a browser with no WebGL context shows\n * an empty box and says nothing, and what stands in its place is the host's decision.\n */\n onFailure: (message: string) => void;\n events?: GraphEvents;\n report?: (motion: Motion) => void;\n reportProgress?: (value: number) => void;\n /**\n * Ask the overlays to reposition after a look has been uploaded.\n *\n * Optional, and only owed to `useGraphOverlays`: point sizes changed, so labels sit differently,\n * and a look change does not tick — with a simulation off there is nothing to schedule a repaint\n * off. A host drawing no labels passes nothing.\n *\n * It goes through the props rather than being read off the api because `useGraphOverlays` is\n * declared *after* this hook — it needs `getGraph` and `getResident` from it. A host bridges the\n * two with one ref, which is the smallest honest answer to a cycle that is genuinely mutual:\n * overlays need the graph, and the graph's repaint owes the overlays a nudge.\n */\n schedule?: () => void;\n}\n\n/**\n * What a graph publishes — to its own chrome through the provider, and to its host directly.\n *\n * Getters rather than values for the two that change every answer: a legend, an inspector and a\n * hover card all read the graph and the resident map from inside callbacks, and publishing them as\n * values would re-render every consumer on every camera move to hand back a reference they only\n * dereference when something is clicked. `slice` and `resident` ARE values, because a legend counts\n * what is drawn and has to re-render when that changes.\n *\n * **There is no `graphRef` here, and that is deliberate.** `getGraph()` answers every read of it,\n * and a ref object as well would be two ways to express one thing — with the second one writable,\n * which nothing outside `useRenderer` may be.\n */\nexport interface GraphApi {\n /**\n * Where the canvas is drawn. `GraphRootProvider` attaches it; a host that renders its own surface\n * attaches it itself, and nothing works until something does.\n */\n hostRef: RefObject<HTMLDivElement | null>;\n getGraph: () => Graph | null;\n getResident: () => Resident;\n /** The answer currently drawn, or `null` before the first one. */\n slice: Slice | null;\n /** Who is drawn and where, rebuilt with every answer — never build a second one. */\n resident: Resident;\n /** How many vertices there are, when the source knows. */\n total: number | undefined;\n /** Whether a question is outstanding. */\n pending: boolean;\n /** Whether this graph is asked in pieces at all, or fitted under the limit and taken whole. */\n sliced: boolean;\n /** Ask again about wherever the camera is now. Wired to the camera already. */\n refresh: () => void;\n /** Ask a topological question instead of a spatial one, when the source supports one. */\n explore: (seeds: VertexId[], depth: number) => void;\n}\n\n/** The empty answer, built once — a ref has to hold something before the first slice arrives. */\nconst NOBODY = residentOf(null);\n\nexport function useGraph(props: UseGraphProps): GraphApi {\n const {\n clusters,\n debounce,\n events,\n fill,\n limit,\n look = DEFAULT_LOOK,\n onFailure,\n pinned,\n r,\n report,\n reportProgress,\n schedule,\n sim,\n simulate = false,\n source,\n stroke,\n symbol,\n } = props;\n\n /**\n * The two destinations one vocabulary splits into — and the one column they share.\n *\n * A slice carries **one** categorical array, so the query has one column to fetch and the question\n * is which binding names it: `fill` when it is a column, and `symbol` when `fill` is a constant.\n * That is not a fallback, it is the monochrome picture stated exactly — identity has moved to\n * shape, so the column identity lives in is the one `symbol` names.\n *\n * Getting this wrong is not a silent failure, which is the one good thing about it: with `fill` a\n * constant and nothing put in its place, the source fell back to its own default column and DuckDB\n * answered `Referenced column \"community\" not found`. The browser said so on the first Ink render.\n *\n * `stroke` never reaches a query at all — a link's tint is a decision about drawing.\n */\n const asked = isColour(fill) ? symbol : fill;\n const channels = useMemo<Channels>(() => ({ fill, stroke, symbol }), [fill, stroke, symbol]);\n\n const hostRef = useRef<HTMLDivElement>(null);\n const graphRef = useRef<Graph | null>(null);\n const residentRef = useRef<Resident>(NOBODY);\n\n const { explore, pending, refresh, resident, slice, sliced, total } = useQueryLoop({\n debounce,\n fill: asked,\n graphRef,\n hostRef,\n limit,\n onError: onFailure,\n pinned,\n r,\n source,\n });\n\n /**\n * The resident map, readable from a callback that was created once.\n *\n * Written on every render rather than through an effect: a click handler registered with cosmos.gl\n * at construction reads this on the next gesture, and an effect would leave one frame where the map\n * describes buffers that are no longer on screen.\n */\n residentRef.current = resident;\n\n const getGraph = useCallback(() => graphRef.current, []);\n const getResident = useCallback(() => residentRef.current, []);\n\n /**\n * The host's handlers, read through a ref.\n *\n * cosmos.gl takes its callbacks once, at construction, so a fresh `events` object per render would\n * either be ignored or force a rebuild of the WebGL context. The ref is what lets a host pass an\n * inline object without either.\n */\n const live = useRef(events);\n live.current = events;\n\n const refreshRef = useRef(refresh);\n refreshRef.current = refresh;\n\n const wired = useMemo<RendererOptions[\"events\"]>(\n () => ({\n onBackgroundClick: () => live.current?.onBackgroundClick?.(),\n onDragEnd: (index) => {\n const vertex = residentRef.current.at(index);\n if (vertex !== undefined) live.current?.onDragEnd?.(vertex);\n },\n onPointClick: (graph, index) => {\n const vertex = residentRef.current.at(index);\n if (vertex !== undefined) live.current?.onPointClick?.(vertex, graph, index);\n },\n onPointerOut: () => live.current?.onPointerOut?.(),\n onPointerOver: (index) => {\n const vertex = residentRef.current.at(index);\n if (vertex !== undefined) live.current?.onPointerOver?.(vertex, index);\n },\n onTick: () => live.current?.onTick?.(),\n // The camera moved, so the graph is re-asked. A host that forgot this line got a canvas that\n // drew its first answer and never asked again — which is what `refresh` being the host's\n // responsibility used to cost.\n onZoom: () => {\n refreshRef.current();\n live.current?.onZoom?.();\n },\n }),\n // Built once: every reference inside is a ref this hook owns, so there is nothing to depend on.\n // It used to close over a ref object a host could substitute, which is the prop this file deletes.\n [],\n );\n\n /**\n * The renderer, built into an element this hook does not render.\n *\n * That is sound and not a gamble: React attaches refs during the commit phase, before any effect\n * runs, so by the time this effect fires `hostRef.current` is whatever the provider mounted —\n * including when the provider is a child component, since child refs are attached in the same\n * commit. A host that calls `useGraph` and renders no surface at all gets a hook that returns\n * early, forever, which is the honest outcome for a graph with nowhere to go.\n */\n useRenderer({\n clusters,\n events: wired,\n graphRef,\n hostRef,\n onFailure,\n report,\n reportProgress,\n sim,\n simulate,\n });\n\n useGraphLook({ channels, getGraph, hostRef, look, schedule, slice });\n\n return { explore, getGraph, getResident, hostRef, pending, refresh, resident, slice, sliced, total };\n}\n"],"names":[],"mappings":";;;;;;;;AA2LA;AAEO;AACL;AAAM;AACJ;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACA;AACA;AAwBiF;AACjF;AACM;AACN;AACA;AACA;AACS;AACT;AACA;AACA;AAUF;AAEA;AAWA;AAEA;AACA;AAEA;AAAc;AACL;;AACoB;AAAA;AAAA;;AAEvB;AACA;AAAoD;AACtD;;AAEE;AACA;AAAsE;AACxE;;AACoB;AAAA;AAAA;;AAElB;AACA;AAAgE;AAClE;;AACc;AAAA;AAAA;AAAA;AAAA;AAAA;;AAKZ;AACA;AACF;AAAA;AAAA;AAAA;AAIF;AAYF;AAAY;AACV;AACQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AAMJ;;;;"}
1
+ {"version":3,"file":"use-graph.js","sources":["../src/use-graph.ts"],"sourcesContent":["\"use client\";\n\nimport type { Graph } from \"@cosmos.gl/graph\";\nimport { useCallback, useMemo, useRef, type RefObject } from \"react\";\nimport type { BoundedSource, Slice } from \"./bounded\";\nimport { resolveLook, type LookPatch } from \"./graph-looks\";\nimport { isColour, type Channels } from \"./graph-model\";\nimport { residentOf, type Resident, type VertexId } from \"./resident\";\nimport { resolveSim, type Sim } from \"./graph-sim\";\nimport type { Motion } from \"./types\";\nimport { useQueryLoop } from \"./use-query-loop\";\nimport { useRenderer, type RendererOptions } from \"./use-renderer\";\nimport { useGraphLook } from \"./use-graph-look\";\n\n/**\n * Everything a graph is, above the element that draws it.\n *\n * **This is Ark's `useX(props) → api`, and the reason it exists is a host that could not adopt\n * `GraphCanvas`.** The component owned the renderer, the query loop and the look, and published them\n * through a context — which a legend or an inspector can read, because those are `children`. What\n * cannot read a context is anything that sits *above* the element: `useGraphOverlays` wants the api\n * itself, the `events` block wants the accessors on it, and both are arguments to a hook called in\n * the component that renders the canvas rather than inside it. The workspace needed them in thirty\n * places and so kept its own copy of all three.\n *\n * The general answer to that circularity is Ark's, and it is a shape rather than a feature: the\n * factory builds the api where the host can hold it, the provider takes it and renders. What the\n * component version tried instead — `graphRef` and `residentRef` as props, given rather than\n * returned — solved one host's version of it and is deleted by this file.\n *\n * **The structure is copied and the substance is not.** An Ark api's value is its prop getters,\n * which distribute props across many parts. Here there are no parts: a canvas is one element. So\n * there is no `getRootProps()` — `hostRef` is what the provider needs and all it needs — and\n * inventing one for the resemblance would be cargo cult. `CONVENTIONS.md` is where that line is\n * drawn, not here.\n */\n\n/**\n * The gestures, in the terms the rest of this package speaks.\n *\n * cosmos.gl reports a **buffer index**, which names a slot in the answer currently uploaded. Every\n * host then wrote the same three lines — resolve the index through the resident map, return early if\n * it resolves to nothing, carry on with the identity — because an index is not something you can\n * keep. The factory already holds that map, so it does the resolving and a callback that would have\n * been handed a stale slot is simply not called.\n *\n * `onZoom` is the one with a duty attached: the camera moving is how a bounded graph is re-asked,\n * and the loop issues that `refresh` itself. What arrives here is the notification, for a host that\n * has overlays to reposition.\n */\nexport interface GraphEvents {\n onBackgroundClick?: () => void;\n onPointClick?: (vertex: VertexId, graph: Graph, index: number) => void;\n onPointerOver?: (vertex: VertexId, index: number) => void;\n onPointerOut?: () => void;\n onDragEnd?: (vertex: VertexId) => void;\n onTick?: () => void;\n onZoom?: () => void;\n}\n\nexport interface UseGraphProps {\n /** What to draw. `null` renders the frame and asks nothing — a host still resolving its data. */\n source: BoundedSource | null;\n /**\n * Geometry only. Colour comes from the page's categorical scale, never from here.\n *\n * **One appearance input**, where there were two: a `Display` rode beside this with its own\n * defaults, spelling the edge layer, the backdrop and two multipliers over numbers the look\n * already computes. `lookFrom(values)` resolves the whole picture from the axes a person chose.\n *\n * **A patch, not a whole `Look` — which is what `DEFAULT_LOOK` was for.** This took the complete\n * object, so a host that wanted the vignette on wrote `{ ...DEFAULT_LOOK, vignette: true }` and\n * the package exported the default to make that expressible. That spread is a *copy*: the host\n * now holds every number this package chose, and stops tracking any of them the moment one moves\n * here. Passing `{ vignette: true }` says what the host decided and leaves the rest ours. `link`\n * merges one level down, so `{ link: { render: false } }` keeps the measured opacity and width.\n *\n * Memoise it, or pass a literal only when it does not change: the reference is what the buffers\n * are rebuilt on. Omitted entirely, it costs nothing — the shared default is returned by identity.\n */\n look?: LookPatch;\n /**\n * The force coefficients, as a patch over this package's own — `look`'s twin in every respect,\n * including why `DEFAULT_SIM` is no longer exported to spread from.\n */\n sim?: Partial<Sim>;\n /**\n * Off by default, and that is the correct default rather than a cautious one: a bounded source\n * hands back the coordinates its next spatial query is expressed in, so a force moves the picture\n * out from under its own index.\n */\n simulate?: boolean;\n clusters?: (number | undefined)[];\n /**\n * Which column colours a point, **or a CSS colour every point wears** — Plot's channel name,\n * Plot's meaning, and Plot's rule that a colour is a constant and anything else is a column.\n *\n * **A channel is what you want drawn, so it rides the question and not the source.** Baked into a\n * source at construction — which is where this used to live — changing what a graph is coloured by\n * meant building a second source, and *where the bytes are* and *what I want drawn* are not the\n * same question. Changing it here re-asks over the same bytes, which is all it should ever have\n * cost.\n *\n * As a *constant* it costs no query at all: `fill=\"var(--foreground)\"` is the monochrome picture,\n * and it is a binding rather than a look, because a look may not rebind an encoding.\n *\n * Omitted, the source decides which column: it is the only thing that knows what its corpus\n * carries.\n */\n fill?: string;\n /**\n * Which column a point's **shape** carries — Plot's `symbol`, and a peer of colour.\n *\n * Bound to the same column as `fill` this is redundant encoding, which is the point on a colourful\n * document and the only encoding left on a monochrome one. It reads the categorical column the\n * slice already carries, so it costs no query; a *different* column would need a second categorical\n * array in the slice and in every source, and is not paid for.\n *\n * Binding it takes on an obligation — see `gradeComposition`: shape and size spent at once need a\n * radius floor, which is a property of what you composed rather than of the form you picked.\n */\n symbol?: string;\n /**\n * Which column the size ramp is spent on — Plot's `r`.\n *\n * Omitted, every point is drawn at one radius, which is a legitimate picture: without a ramp a\n * look's `form.size` range has only one end.\n */\n r?: string;\n /**\n * What tints a link. **Absent, each link takes the colour of the vertex it leaves**; a constant\n * like `var(--muted-foreground)` makes links plain structure.\n */\n stroke?: string;\n /** Vertices that stay drawn whatever the camera is over. */\n pinned?: VertexId[];\n limit?: number;\n debounce?: number;\n /**\n * Required, and the only one.\n *\n * Its silence is a defect rather than a choice: unhandled, a browser with no WebGL context shows\n * an empty box and says nothing, and what stands in its place is the host's decision.\n */\n onFailure: (message: string) => void;\n events?: GraphEvents;\n report?: (motion: Motion) => void;\n reportProgress?: (value: number) => void;\n /**\n * Ask the overlays to reposition after a look has been uploaded.\n *\n * Optional, and only owed to `useGraphOverlays`: point sizes changed, so labels sit differently,\n * and a look change does not tick — with a simulation off there is nothing to schedule a repaint\n * off. A host drawing no labels passes nothing.\n *\n * It goes through the props rather than being read off the api because `useGraphOverlays` is\n * declared *after* this hook — it takes what this hook returns. A host bridges the two with one\n * ref, which is the smallest honest answer to a cycle that is genuinely mutual: the overlays need\n * the graph, and the graph's repaint owes the overlays a nudge.\n */\n schedule?: () => void;\n}\n\n/**\n * What a graph publishes — to its own chrome through the provider, and to its host directly.\n *\n * Getters rather than values for the two that change every answer: a legend, an inspector and a\n * hover card all read the graph and the resident map from inside callbacks, and publishing them as\n * values would re-render every consumer on every camera move to hand back a reference they only\n * dereference when something is clicked. `slice` and `resident` ARE values, because a legend counts\n * what is drawn and has to re-render when that changes.\n *\n * **There is no `graphRef` here, and that is deliberate.** `getGraph()` answers every read of it,\n * and a ref object as well would be two ways to express one thing — with the second one writable,\n * which nothing outside `useRenderer` may be.\n */\nexport interface GraphApi {\n /**\n * Where the canvas is drawn. `GraphRootProvider` attaches it; a host that renders its own surface\n * attaches it itself, and nothing works until something does.\n */\n hostRef: RefObject<HTMLDivElement | null>;\n /**\n * The renderer and the current map, read from inside a callback that was created once.\n *\n * **These two nearly left with `GraphOverlayOptions`, and the census is why they stayed.** The\n * reason they were on the api was that `useGraphOverlays` asked for them as a pair, so every host\n * copied them out of this object into that hook's argument — which is a re-statement, and that\n * hook takes the api now. What kept them is a different set of readers: `useGraphSelection` takes\n * both (its `commit` is a policy only a product can write, so it cannot be folded into the api the\n * way the overlays were), and a real consumer outside this repository reads `getGraph` and\n * `getResident` off `useGraphContext()` to paint its own mask over the buffers. Both are reads\n * from inside a callback registered once, which is exactly the case `slice` and `resident` cannot\n * serve — a value would re-render every consumer on every camera move to hand back a reference\n * they only dereference when something is clicked.\n */\n getGraph: () => Graph | null;\n getResident: () => Resident;\n /** The answer currently drawn, or `null` before the first one. */\n slice: Slice | null;\n /** Who is drawn and where, rebuilt with every answer — never build a second one. */\n resident: Resident;\n /** How many vertices there are, when the source knows. */\n total: number | undefined;\n /** Whether a question is outstanding. */\n pending: boolean;\n /** Whether this graph is asked in pieces at all, or fitted under the limit and taken whole. */\n sliced: boolean;\n /** Ask again about wherever the camera is now. Wired to the camera already. */\n refresh: () => void;\n}\n\n/** The empty answer, built once — a ref has to hold something before the first slice arrives. */\nconst NOBODY = residentOf(null);\n\nexport function useGraph(props: UseGraphProps): GraphApi {\n const {\n clusters,\n debounce,\n events,\n fill,\n limit,\n look: lookPatch,\n onFailure,\n pinned,\n r,\n report,\n reportProgress,\n schedule,\n sim: simPatch,\n simulate = false,\n source,\n stroke,\n symbol,\n } = props;\n\n /**\n * The patch, merged with what this package chose — once per change of the patch, not per render.\n *\n * The memo is the whole reason this is not inlined. Both resolved values are compared **by\n * reference** downstream: the look's identity is what `useGraphLook` rebuilds the GPU buffers on,\n * and the sim's is what `useRenderer` tests before putting energy back into a settled layout. A\n * merge on every render would upload the same colours sixty times a second and re-heat a graph\n * nobody had touched.\n */\n const look = useMemo(() => resolveLook(lookPatch), [lookPatch]);\n const sim = useMemo(() => resolveSim(simPatch), [simPatch]);\n\n /**\n * The two destinations one vocabulary splits into — and the one column they share.\n *\n * A slice carries **one** categorical array, so the query has one column to fetch and the question\n * is which binding names it: `fill` when it is a column, and `symbol` when `fill` is a constant.\n * That is not a fallback, it is the monochrome picture stated exactly — identity has moved to\n * shape, so the column identity lives in is the one `symbol` names.\n *\n * Getting this wrong is not a silent failure, which is the one good thing about it: with `fill` a\n * constant and nothing put in its place, the source fell back to its own default column and DuckDB\n * answered `Referenced column \"community\" not found`. The browser said so on the first Ink render.\n *\n * `stroke` never reaches a query at all — a link's tint is a decision about drawing.\n */\n const asked = isColour(fill) ? symbol : fill;\n const channels = useMemo<Channels>(() => ({ fill, stroke, symbol }), [fill, stroke, symbol]);\n\n const hostRef = useRef<HTMLDivElement>(null);\n const graphRef = useRef<Graph | null>(null);\n const residentRef = useRef<Resident>(NOBODY);\n\n const { pending, refresh, resident, slice, sliced, total } = useQueryLoop({\n debounce,\n fill: asked,\n graphRef,\n hostRef,\n limit,\n onError: onFailure,\n pinned,\n r,\n source,\n });\n\n /**\n * The resident map, readable from a callback that was created once.\n *\n * Written on every render rather than through an effect: a click handler registered with cosmos.gl\n * at construction reads this on the next gesture, and an effect would leave one frame where the map\n * describes buffers that are no longer on screen.\n */\n residentRef.current = resident;\n\n const getGraph = useCallback(() => graphRef.current, []);\n const getResident = useCallback(() => residentRef.current, []);\n\n /**\n * The host's handlers, read through a ref.\n *\n * cosmos.gl takes its callbacks once, at construction, so a fresh `events` object per render would\n * either be ignored or force a rebuild of the WebGL context. The ref is what lets a host pass an\n * inline object without either.\n */\n const live = useRef(events);\n live.current = events;\n\n const refreshRef = useRef(refresh);\n refreshRef.current = refresh;\n\n const wired = useMemo<RendererOptions[\"events\"]>(\n () => ({\n onBackgroundClick: () => live.current?.onBackgroundClick?.(),\n onDragEnd: (index) => {\n const vertex = residentRef.current.at(index);\n if (vertex !== undefined) live.current?.onDragEnd?.(vertex);\n },\n onPointClick: (graph, index) => {\n const vertex = residentRef.current.at(index);\n if (vertex !== undefined) live.current?.onPointClick?.(vertex, graph, index);\n },\n onPointerOut: () => live.current?.onPointerOut?.(),\n onPointerOver: (index) => {\n const vertex = residentRef.current.at(index);\n if (vertex !== undefined) live.current?.onPointerOver?.(vertex, index);\n },\n onTick: () => live.current?.onTick?.(),\n // The camera moved, so the graph is re-asked. A host that forgot this line got a canvas that\n // drew its first answer and never asked again — which is what `refresh` being the host's\n // responsibility used to cost.\n onZoom: () => {\n refreshRef.current();\n live.current?.onZoom?.();\n },\n }),\n // Built once: every reference inside is a ref this hook owns, so there is nothing to depend on.\n // It used to close over a ref object a host could substitute, which is the prop this file deletes.\n [],\n );\n\n /**\n * The renderer, built into an element this hook does not render.\n *\n * That is sound and not a gamble: React attaches refs during the commit phase, before any effect\n * runs, so by the time this effect fires `hostRef.current` is whatever the provider mounted —\n * including when the provider is a child component, since child refs are attached in the same\n * commit. A host that calls `useGraph` and renders no surface at all gets a hook that returns\n * early, forever, which is the honest outcome for a graph with nowhere to go.\n */\n useRenderer({\n clusters,\n events: wired,\n graphRef,\n hostRef,\n onFailure,\n report,\n reportProgress,\n sim,\n simulate,\n });\n\n useGraphLook({ channels, getGraph, hostRef, look, schedule, slice });\n\n return { getGraph, getResident, hostRef, pending, refresh, resident, slice, sliced, total };\n}\n"],"names":[],"mappings":";;;;;;;;;AAqNA;AAEO;AACL;AAAM;AACJ;AACA;AACA;AACA;AACA;AACM;AACN;AACA;AACA;AACA;AACA;AACA;AACK;AACM;AACX;AACA;AACA;AAoCwE;AACxE;AACM;AACN;AACA;AACA;AACS;AACT;AACA;AACA;AAUF;AAEA;AAWA;AAEA;AACA;AAEA;AAAc;AACL;;AACoB;AAAA;AAAA;;AAEvB;AACA;AAAoD;AACtD;;AAEE;AACA;AAAsE;AACxE;;AACoB;AAAA;AAAA;;AAElB;AACA;AAAgE;AAClE;;AACc;AAAA;AAAA;AAAA;AAAA;AAAA;;AAKZ;AACA;AACF;AAAA;AAAA;AAAA;AAIF;AAYF;AAAY;AACV;AACQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AAMJ;;;;"}
@@ -7,12 +7,18 @@ import { Resident, VertexId } from './resident';
7
7
  *
8
8
  * This is the half the engine rule requires of anything with an engine — the canvas stays presentational
9
9
  * and this owns the asking. It debounces, cancels what the camera has already superseded, and pushes
10
- * each answer's geometry into the renderer. A host that already holds its arrays wraps them in a
11
- * source and gets the same path; there is no second one.
10
+ * each answer's geometry into the renderer. There is one source and it is `openCorpus`; this loop is
11
+ * written against the contract rather than against it, which is what keeps a second one possible.
12
12
  *
13
13
  * **A graph that fits pays for nothing.** `total()` is asked first, and under the limit the source is
14
14
  * asked once for everything and never again — panning is then free exactly when it can be. Above it,
15
15
  * every camera move costs a query, which at 200,000 nodes is the trade that buys the ceiling away.
16
+ *
17
+ * **It is also where the question is finished.** A host's `limit` is optional and `minLinkPixels` is
18
+ * not a host's business at all, and both are **required** on the `SliceRequest` a source receives —
19
+ * resolved here, once, from `DEFAULT_LIMIT` and `DEFAULT_MIN_LINK_PIXELS`. They used to reach a
20
+ * source as an exported `BOUNDED_DEFAULTS` table it looked them up in, which made a request
21
+ * something a source had to *complete* rather than answer, in its own words, in two repositories.
16
22
  */
17
23
  export interface QueryLoopOptions {
18
24
  source: BoundedSource | null;
@@ -77,8 +83,6 @@ export interface QueryLoopState {
77
83
  sliced: boolean;
78
84
  /** Ask again. Wire it to the camera, and call it when the pinned set changes. */
79
85
  refresh: () => void;
80
- /** Ask a topological question instead of a spatial one, when the source supports one. */
81
- explore: (seeds: VertexId[], depth: number) => void;
82
86
  }
83
87
  export declare function useQueryLoop(options: QueryLoopOptions): QueryLoopState;
84
88
  //# sourceMappingURL=use-query-loop.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-query-loop.d.ts","sourceRoot":"","sources":["../src/use-query-loop.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAIL,KAAK,aAAa,EAElB,KAAK,KAAK,EAEX,MAAM,WAAW,CAAC;AACnB,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtE;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClC,8EAA8E;IAC9E,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACvC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB;;;;;;;;OAQG;IACH,QAAQ,EAAE,QAAQ,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,iFAAiF;IACjF,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,yFAAyF;IACzF,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAwCD,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,CA6StE"}
1
+ {"version":3,"file":"use-query-loop.d.ts","sourceRoot":"","sources":["../src/use-query-loop.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,KAAK,EAEX,MAAM,WAAW,CAAC;AACnB,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtE;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClC,8EAA8E;IAC9E,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACvC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB;;;;;;;;OAQG;IACH,QAAQ,EAAE,QAAQ,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,iFAAiF;IACjF,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAwCD,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,cAAc,CA4StE"}
@@ -1,150 +1,147 @@
1
1
  "use client";
2
- import { useState as I, useRef as m, useCallback as b, useEffect as g, useMemo as L } from "react";
3
- import { BOUNDED_DEFAULTS as U, isSuperseded as z, shouldSlice as H } from "./bounded.js";
4
- import { residentOf as Q } from "./resident.js";
5
- import { whenReady as j } from "./when-ready.js";
6
- const q = {
2
+ import { useState as y, useRef as d, useCallback as v, useEffect as N, useMemo as G } from "react";
3
+ import { isAbort as U, DEFAULT_MIN_LINK_PIXELS as C, DEFAULT_LIMIT as z, shouldSlice as H } from "./bounded.js";
4
+ import { residentOf as K } from "./resident.js";
5
+ import { whenReady as Q } from "./when-ready.js";
6
+ const X = {
7
7
  xMin: Number.NEGATIVE_INFINITY,
8
8
  yMin: Number.NEGATIVE_INFINITY,
9
9
  xMax: Number.POSITIVE_INFINITY,
10
10
  yMax: Number.POSITIVE_INFINITY
11
11
  };
12
- function J(w, N) {
13
- const { height: l, width: s } = N.getBoundingClientRect(), [M, c] = w.screenToSpacePosition([0, 0]), [h, y] = w.screenToSpacePosition([s, l]), u = {
14
- xMin: Math.min(M, h),
15
- yMin: Math.min(c, y),
16
- xMax: Math.max(M, h),
17
- yMax: Math.max(c, y)
18
- }, e = s > 0 ? (u.xMax - u.xMin) / s : 0;
19
- return { view: u, perPixel: e };
12
+ function j(T, p) {
13
+ const { height: f, width: i } = p.getBoundingClientRect(), [M, o] = T.screenToSpacePosition([0, 0]), [m, I] = T.screenToSpacePosition([i, f]), a = {
14
+ xMin: Math.min(M, m),
15
+ yMin: Math.min(o, I),
16
+ xMax: Math.max(M, m),
17
+ yMax: Math.max(o, I)
18
+ }, e = i > 0 ? (a.xMax - a.xMin) / i : 0;
19
+ return { view: a, perPixel: e };
20
20
  }
21
- function tt(w) {
21
+ function tt(T) {
22
22
  const {
23
- debounce: N = K,
24
- fill: l,
25
- graphRef: s,
23
+ debounce: p = q,
24
+ fill: f,
25
+ graphRef: i,
26
26
  hostRef: M,
27
- limit: c = U.limit,
28
- onError: h,
29
- pinned: y,
30
- r: u,
27
+ limit: o = z,
28
+ onError: m,
29
+ pinned: I,
30
+ r: a,
31
31
  source: e
32
- } = w, [x, E] = I(null), [Y, V] = I(!1), [k, C] = I(void 0), [A, B] = I(!1), [F, R] = I(null), p = m(null), f = m(null), S = m(!1), T = m(y);
33
- T.current = y;
34
- const a = m(h);
35
- a.current = h;
36
- const d = b(
32
+ } = T, [u, g] = y(null), [R, L] = y(!1), [Y, _] = y(void 0), [O, B] = y(!1), [F, V] = y(null), h = d(null), x = d(null), w = d(!1), E = d(I);
33
+ E.current = I;
34
+ const l = d(m);
35
+ l.current = m;
36
+ const b = v(
37
37
  async (n) => {
38
- var r, i;
38
+ var r, c;
39
39
  if (!e) return;
40
- (r = p.current) == null || r.abort();
40
+ (r = h.current) == null || r.abort();
41
41
  const t = new AbortController();
42
- p.current = t, V(!0);
42
+ h.current = t, L(!0);
43
43
  try {
44
- const o = await n(e, t.signal);
44
+ const s = await n(e, t.signal);
45
45
  if (t.signal.aborted) return;
46
- E(o);
47
- } catch (o) {
48
- if (t.signal.aborted || z(o)) return;
49
- (i = a.current) == null || i.call(a, String(o));
46
+ g(s);
47
+ } catch (s) {
48
+ if (t.signal.aborted || U(s)) return;
49
+ (c = l.current) == null || c.call(l, String(s));
50
50
  } finally {
51
- p.current === t && (p.current = null, V(!1));
51
+ h.current === t && (h.current = null, L(!1));
52
52
  }
53
53
  },
54
54
  [e]
55
- ), _ = m(null), O = b(
55
+ ), k = d(null), A = v(
56
56
  async (n) => {
57
- var o;
58
- if (!n.extent || _.current === n) return;
59
- _.current = n;
57
+ var s;
58
+ if (!n.extent || k.current === n) return;
59
+ k.current = n;
60
60
  let t;
61
61
  try {
62
62
  t = await n.extent();
63
- } catch (v) {
64
- (o = a.current) == null || o.call(a, String(v));
63
+ } catch (S) {
64
+ (s = l.current) == null || s.call(l, String(S));
65
65
  return;
66
66
  }
67
- const r = s.current;
67
+ const r = i.current;
68
68
  if (!r) return;
69
- const i = await r.ready.then(() => r);
70
- i.setConfigPartial({ spaceSize: Math.max(t.xMax - t.xMin, t.yMax - t.yMin) }), i.fitViewByPointPositions([t.xMin, t.yMin, t.xMax, t.yMax], 0);
69
+ const c = await r.ready.then(() => r);
70
+ c.setConfigPartial({ spaceSize: Math.max(t.xMax - t.xMin, t.yMax - t.yMin) }), c.fitViewByPointPositions([t.xMin, t.yMin, t.xMax, t.yMax], 0);
71
71
  },
72
- [s]
73
- ), P = b(() => {
74
- S.current && (f.current && clearTimeout(f.current), f.current = setTimeout(() => {
75
- f.current = null;
76
- const n = s.current, t = M.current;
72
+ [i]
73
+ ), P = v(() => {
74
+ w.current && (x.current && clearTimeout(x.current), x.current = setTimeout(() => {
75
+ x.current = null;
76
+ const n = i.current, t = M.current;
77
77
  if (!n || !t) return;
78
- const { perPixel: r, view: i } = J(n, t);
79
- d(
80
- (o, v) => o.slice({ view: i, perPixel: r, pinned: T.current, fill: l, r: u, limit: c, signal: v })
81
- );
82
- }, N));
83
- }, [d, N, l, s, M, c, u]), D = b(
84
- (n, t) => {
85
- var r;
86
- if (!e || !("explore" in e)) {
87
- (r = a.current) == null || r.call(a, "this source answers regions only");
88
- return;
89
- }
90
- f.current && clearTimeout(f.current), d(
91
- (i, o) => i.explore({
92
- seeds: n,
93
- depth: t,
94
- pinned: T.current,
95
- fill: l,
96
- r: u,
97
- limit: c,
98
- signal: o
78
+ const { perPixel: r, view: c } = j(n, t);
79
+ b(
80
+ (s, S) => s.slice({
81
+ view: c,
82
+ perPixel: r,
83
+ pinned: E.current,
84
+ fill: f,
85
+ r: a,
86
+ limit: o,
87
+ minLinkPixels: C,
88
+ signal: S
99
89
  })
100
90
  );
101
- },
102
- [d, l, c, u, e]
103
- );
104
- g(() => {
91
+ }, p));
92
+ }, [b, p, f, i, M, o, a]);
93
+ N(() => {
105
94
  if (!e) {
106
- E(null), C(void 0), R(null);
95
+ g(null), _(void 0), V(null);
107
96
  return;
108
97
  }
109
98
  let n = !0;
110
99
  return (async () => {
111
- var i;
100
+ var c;
112
101
  let t;
113
102
  try {
114
- t = await ((i = e.total) == null ? void 0 : i.call(e));
103
+ t = await ((c = e.total) == null ? void 0 : c.call(e));
115
104
  } catch {
116
105
  }
117
106
  if (!n) return;
118
- C(t);
119
- const r = H(t, c);
120
- S.current = r, B(r), R(e);
107
+ _(t);
108
+ const r = H(t, o);
109
+ w.current = r, B(r), V(e);
121
110
  })(), () => {
122
111
  n = !1;
123
112
  };
124
- }, [c, e]), g(() => {
125
- !e || F !== e || (async () => (await O(e), S.current ? P() : await d(
126
- (n, t) => n.slice({ view: q, pinned: T.current, fill: l, r: u, limit: c, signal: t })
113
+ }, [o, e]), N(() => {
114
+ !e || F !== e || (async () => (await A(e), w.current ? P() : await b(
115
+ (n, t) => n.slice({
116
+ view: X,
117
+ pinned: E.current,
118
+ fill: f,
119
+ r: a,
120
+ limit: o,
121
+ minLinkPixels: C,
122
+ signal: t
123
+ })
127
124
  )))();
128
- }, [d, F, l, O, c, u, P, e]), g(() => {
125
+ }, [b, F, f, A, o, a, P, e]), N(() => {
129
126
  var n;
130
- return (n = e == null ? void 0 : e.watch) == null ? void 0 : n.call(e, E);
131
- }, [e]), g(
127
+ return (n = e == null ? void 0 : e.watch) == null ? void 0 : n.call(e, g);
128
+ }, [e]), N(
132
129
  () => () => {
133
130
  var n;
134
- f.current && clearTimeout(f.current), (n = p.current) == null || n.abort();
131
+ x.current && clearTimeout(x.current), (n = h.current) == null || n.abort();
135
132
  },
136
133
  []
137
- ), g(() => {
138
- const n = s.current;
139
- if (!(!n || !x))
140
- return j(n, (t) => {
141
- t.setPointPositions(x.positions), t.setLinks(x.links), t.render();
134
+ ), N(() => {
135
+ const n = i.current;
136
+ if (!(!n || !u))
137
+ return Q(n, (t) => {
138
+ t.setPointPositions(u.positions), t.setLinks(u.links), t.render();
142
139
  });
143
- }, [s, x]);
144
- const G = L(() => Q(x), [x]);
145
- return { slice: x, resident: G, pending: Y, total: k, sliced: A, refresh: P, explore: D };
140
+ }, [i, u]);
141
+ const D = G(() => K(u), [u]);
142
+ return { slice: u, resident: D, pending: R, total: Y, sliced: O, refresh: P };
146
143
  }
147
- const K = 120;
144
+ const q = 120;
148
145
  export {
149
146
  tt as useQueryLoop
150
147
  };