@kanzo-tech/graph 0.19.0 → 0.20.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 +25 -7
  2. package/dist/core/detail.d.ts +2 -0
  3. package/dist/core/detail.d.ts.map +1 -1
  4. package/dist/core/detail.js +51 -30
  5. package/dist/core/detail.js.map +1 -1
  6. package/dist/core/state.d.ts +7 -0
  7. package/dist/core/state.d.ts.map +1 -1
  8. package/dist/core/store.d.ts.map +1 -1
  9. package/dist/core/store.js +112 -108
  10. package/dist/core/store.js.map +1 -1
  11. package/dist/core/types.d.ts +4 -4
  12. package/dist/core/types.d.ts.map +1 -1
  13. package/dist/index.d.ts +7 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +24 -18
  16. package/dist/index.js.map +1 -1
  17. package/dist/parts/graph-counts.d.ts +15 -0
  18. package/dist/parts/graph-counts.d.ts.map +1 -0
  19. package/dist/parts/graph-counts.js +41 -0
  20. package/dist/parts/graph-counts.js.map +1 -0
  21. package/dist/parts/graph-legend.d.ts +3 -2
  22. package/dist/parts/graph-legend.d.ts.map +1 -1
  23. package/dist/parts/graph-legend.js +30 -48
  24. package/dist/parts/graph-legend.js.map +1 -1
  25. package/dist/parts/graph-looks.d.ts +16 -0
  26. package/dist/parts/graph-looks.d.ts.map +1 -0
  27. package/dist/parts/graph-looks.js +138 -0
  28. package/dist/parts/graph-looks.js.map +1 -0
  29. package/dist/parts/graph-search.d.ts +17 -0
  30. package/dist/parts/graph-search.d.ts.map +1 -0
  31. package/dist/parts/graph-search.js +89 -0
  32. package/dist/parts/graph-search.js.map +1 -0
  33. package/dist/react/use-graph-prefs.d.ts +6 -2
  34. package/dist/react/use-graph-prefs.d.ts.map +1 -1
  35. package/dist/react/use-graph-prefs.js +7 -7
  36. package/dist/react/use-graph-prefs.js.map +1 -1
  37. package/dist/render/camera.d.ts +26 -0
  38. package/dist/render/camera.d.ts.map +1 -0
  39. package/dist/render/camera.js +40 -0
  40. package/dist/render/camera.js.map +1 -0
  41. package/dist/render/graph-looks.d.ts +10 -0
  42. package/dist/render/graph-looks.d.ts.map +1 -1
  43. package/dist/render/graph-looks.js +37 -20
  44. package/dist/render/graph-looks.js.map +1 -1
  45. package/dist/render/placement.d.ts +23 -0
  46. package/dist/render/placement.d.ts.map +1 -0
  47. package/dist/render/placement.js +23 -0
  48. package/dist/render/placement.js.map +1 -0
  49. package/dist/render/renderer.d.ts.map +1 -1
  50. package/dist/render/renderer.js +109 -100
  51. package/dist/render/renderer.js.map +1 -1
  52. package/dist/render/webgl.d.ts +5 -4
  53. package/dist/render/webgl.d.ts.map +1 -1
  54. package/dist/render/webgl.js +14 -11
  55. package/dist/render/webgl.js.map +1 -1
  56. package/package.json +7 -6
package/README.md CHANGED
@@ -19,7 +19,7 @@ DuckDB-WASM engine and its crossfilter, cosmos.gl draws, and the parts are built
19
19
  ```tsx
20
20
  import { open } from "@fossil-lang/corpus";
21
21
  import { engine } from "@kanzo-tech/mosaic";
22
- import { GraphCanvas, GraphInspector, GraphLegend, GraphRoot, GraphToolbar } from "@kanzo-tech/graph";
22
+ import { GraphCanvas, GraphCounts, GraphInspector, GraphLegend, GraphRoot, GraphToolbar } from "@kanzo-tech/graph";
23
23
 
24
24
  const corpus = engine().then((e) => open(url, { engine: e }));
25
25
 
@@ -28,6 +28,7 @@ const corpus = engine().then((e) => open(url, { engine: e }));
28
28
  <GraphToolbar />
29
29
  <GraphLegend />
30
30
  </GraphCanvas>
31
+ <GraphCounts />
31
32
  <GraphInspector />
32
33
  </GraphRoot>;
33
34
  ```
@@ -49,8 +50,11 @@ does not survive is hidden; a clause that cannot be translated reaches `onFailur
49
50
  dropped. A lasso or a click publishes the reader's pick back into it, exempting the graph itself.
50
51
 
51
52
  **The layout is off at load.** The corpus's positions are drawn as they are; `GraphToolbar` runs and
52
- stops a force layout from where the points are, and dragging a node pins it either way. At 200,000
53
- vertices the viewer's p95 frame interval is 9.0 ms against 9.3 ms for cosmos.gl alone.
53
+ stops a force layout from where the points are. Dragging a node pins it only while a layout runs,
54
+ and releasing the pins reheats it. While a layout runs the camera follows the points until the reader
55
+ zooms or pans. `status` is the data's life and `motion` the layout's — two axes, because each moves
56
+ without the other. At 200,000 vertices the viewer's p95 frame interval is 9.0 ms against 9.3 ms for
57
+ cosmos.gl alone.
54
58
 
55
59
  The reference composition is the docs' workspace showcase: one `GraphRoot` around a shell with the
56
60
  graph, a second main view and a dock of panels.
@@ -62,13 +66,27 @@ graph, a second main view and a dock of panels.
62
66
  never changes; `useGraphState(selector)` reads a slice of the state.
63
67
  - `GraphCanvas` — the element, the grid, the vignette, the labels, the hover card and the marquee
64
68
  and lasso gesture. Children are chrome positioned over it.
65
- - `GraphLegend`, `GraphToolbar`, `GraphInspector` — the categorical scale with its tally, the
66
- commands drawn, and the focused vertex's row with a render prop for a product's own fields. No
67
- part takes a callback: what a click means is `onSelect`, `onFocus` and `onFailure` on the root.
69
+ - `GraphToolbar` — the selection tools, the selection, zoom and fit, and the layout's transport.
70
+ - `GraphLegend` — one row per category: glyph, name and how many are drawn.
71
+ - `GraphCounts` — what is drawn of the whole: "1.2K of 5K nodes drawn · 8K edges".
72
+ - `GraphSearch` — every drawn vertex's text, read once and filtered in the browser; picking reveals.
73
+ - `GraphInspector` — the focused vertex's row, with a render prop for a product's own fields.
74
+ - `GraphLooks` — Nebula, Atlas and Ink as presets over `GRAPH_SECTION`'s axes, with the axes under
75
+ Customize. Needs `GRAPH_SECTION` on the theme provider, and no root.
76
+ - `ShapeGlyph` — the glyph the canvas draws for a category, in the DOM.
77
+
78
+ No part takes a callback: what a click means is `onSelect`, `onFocus` and `onFailure` on the root.
79
+ `onFailure` is required and receives every failure as thrown — fossil's coded errors, and the graph's
80
+ own `GraphError` (`graph/no-webgl`, `graph/context-lost`, `graph/nothing-to-draw`,
81
+ `graph/untranslatable-filter`).
82
+
68
83
  - `lookFrom`, `simFrom`, `useGraphPrefs` — the form and the forces from a preferences panel's
69
- answers; `scaleOf` — what colour and shape a category wears.
84
+ answers, and the look `preset` they are; `scaleOf` — what colour and shape a category wears.
70
85
  - `VertexId` — a vertex is its `dense_id`, a `number`.
71
86
 
87
+ The full guide — the parts, layout and camera, and the workspace recipe — is at
88
+ [kanzo-tech.github.io/ui/docs/graph](https://kanzo-tech.github.io/ui/docs/graph).
89
+
72
90
  ## Why a package, and not part of `@kanzo-tech/ui`
73
91
 
74
92
  The [first admission rule](https://kanzo-tech.github.io/ui/docs/philosophy#admission) is
@@ -21,4 +21,6 @@ export declare function readVertex(corpus: Corpus, geometry: Geometry, vertex: V
21
21
  * column, and the table's declared `identity` where it does not. One scan per table they fall in.
22
22
  */
23
23
  export declare function readTitles(corpus: Corpus, geometry: Geometry, vertices: readonly VertexId[], title: string | undefined, signal: AbortSignal): Promise<Map<VertexId, string>>;
24
+ /** **Every drawn vertex's text, by id** — what a search filters. One scan per drawn table, two columns. */
25
+ export declare function readEveryTitle(corpus: Corpus, geometry: Geometry, title: string | undefined, signal: AbortSignal): Promise<string[]>;
24
26
  //# sourceMappingURL=detail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detail.d.ts","sourceRoot":"","sources":["../../src/core/detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAChF;AAKD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,QAAQ,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa9B;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAqBhC"}
1
+ {"version":3,"file":"detail.d.ts","sourceRoot":"","sources":["../../src/core/detail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAChF;AAKD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,QAAQ,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAa9B;AAMD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAqBhC;AAED,2GAA2G;AAC3G,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmBnB"}
@@ -1,42 +1,63 @@
1
- import { together as k } from "./load.js";
2
- const w = (o, a) => a < o.size ? o.tables[o.table[a]] : void 0;
3
- async function v(o, a, i, r) {
4
- var p, u;
5
- const e = w(a, i);
6
- if (!e) return null;
7
- const l = /* @__PURE__ */ new Set([e.key, (p = e.position) == null ? void 0 : p.x, (u = e.position) == null ? void 0 : u.y]), c = e.properties.map((t) => t.name).filter((t) => !l.has(t)), s = o.scan({ table: e.name, filter: { column: e.key, op: "=", value: i }, select: [e.key, ...c], limit: 1 }), n = (await s.read(s.plan(), { signal: r })).find((t) => t.numRows > 0);
1
+ import { together as h } from "./load.js";
2
+ const k = (o, e) => e < o.size ? o.tables[o.table[e]] : void 0;
3
+ async function C(o, e, r, y) {
4
+ var f, c;
5
+ const s = k(e, r);
6
+ if (!s) return null;
7
+ const u = /* @__PURE__ */ new Set([s.key, (f = s.position) == null ? void 0 : f.x, (c = s.position) == null ? void 0 : c.y]), a = s.properties.map((t) => t.name).filter((t) => !u.has(t)), i = o.scan({ table: s.name, filter: { column: s.key, op: "=", value: r }, select: [s.key, ...a], limit: 1 }), n = (await i.read(i.plan(), { signal: y })).find((t) => t.numRows > 0);
8
8
  return n ? {
9
- vertex: i,
10
- table: e.name,
11
- fields: c.map((t) => {
12
- var f;
13
- return { name: t, value: ((f = n.getChild(t)) == null ? void 0 : f.toArray()[0]) ?? null };
9
+ vertex: r,
10
+ table: s.name,
11
+ fields: a.map((t) => {
12
+ var m;
13
+ return { name: t, value: ((m = n.getChild(t)) == null ? void 0 : m.toArray()[0]) ?? null };
14
14
  })
15
15
  } : null;
16
16
  }
17
- async function A(o, a, i, r, e) {
18
- const l = /* @__PURE__ */ new Map();
19
- for (const s of i) {
20
- const n = w(a, s);
21
- n && l.set(n, [...l.get(n) ?? [], s]);
17
+ const b = (o, e) => e !== void 0 && o.properties.some((r) => r.name === e) ? e : o.identity;
18
+ async function v(o, e, r, y, s) {
19
+ const u = /* @__PURE__ */ new Map();
20
+ for (const i of r) {
21
+ const n = k(e, i);
22
+ n && u.set(n, [...u.get(n) ?? [], i]);
22
23
  }
23
- const c = /* @__PURE__ */ new Map();
24
- return await k(
25
- e,
26
- (s) => Promise.all(
27
- [...l].map(async ([n, p]) => {
28
- var f, y;
29
- const u = r !== void 0 && n.properties.some((m) => m.name === r) ? r : n.identity, t = o.scan({ table: n.name, filter: { column: n.key, op: "in", values: p }, select: [n.key, u] });
30
- for (const m of await t.read(t.plan(), { signal: s })) {
31
- const b = ((f = m.getChild(n.key)) == null ? void 0 : f.toArray()) ?? [], h = ((y = m.getChild(u)) == null ? void 0 : y.toArray()) ?? [];
32
- for (let d = 0; d < m.numRows; d++) c.set(Number(b[d]), String(h[d] ?? ""));
24
+ const a = /* @__PURE__ */ new Map();
25
+ return await h(
26
+ s,
27
+ (i) => Promise.all(
28
+ [...u].map(async ([n, f]) => {
29
+ var m, d;
30
+ const c = b(n, y), t = o.scan({ table: n.name, filter: { column: n.key, op: "in", values: f }, select: [n.key, c] });
31
+ for (const l of await t.read(t.plan(), { signal: i })) {
32
+ const p = ((m = l.getChild(n.key)) == null ? void 0 : m.toArray()) ?? [], g = ((d = l.getChild(c)) == null ? void 0 : d.toArray()) ?? [];
33
+ for (let w = 0; w < l.numRows; w++) a.set(Number(p[w]), String(g[w] ?? ""));
33
34
  }
34
35
  })
35
36
  )
36
- ), c;
37
+ ), a;
38
+ }
39
+ async function x(o, e, r, y) {
40
+ const s = new Array(e.size).fill("");
41
+ return await h(
42
+ y,
43
+ (u) => Promise.all(
44
+ e.tables.map(async (a) => {
45
+ var f, c;
46
+ const i = b(a, r), n = o.scan({ table: a.name, select: [a.key, i] });
47
+ for (const t of await n.read(n.plan(), { signal: u })) {
48
+ const m = ((f = t.getChild(a.key)) == null ? void 0 : f.toArray()) ?? [], d = ((c = t.getChild(i)) == null ? void 0 : c.toArray()) ?? [];
49
+ for (let l = 0; l < t.numRows; l++) {
50
+ const p = Number(m[l]);
51
+ p < e.size && (s[p] = String(d[l] ?? ""));
52
+ }
53
+ }
54
+ })
55
+ )
56
+ ), s;
37
57
  }
38
58
  export {
39
- A as readTitles,
40
- v as readVertex
59
+ x as readEveryTitle,
60
+ v as readTitles,
61
+ C as readVertex
41
62
  };
42
63
  //# sourceMappingURL=detail.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detail.js","sources":["../../src/core/detail.ts"],"sourcesContent":["import type { Corpus, VertexTable } from \"@fossil-lang/corpus\";\nimport { together, type Geometry } from \"./load\";\nimport type { VertexId } from \"./types\";\n\n/** One vertex's row, as its table's properties order it — every column but the key and the position. */\nexport interface VertexDetail {\n readonly vertex: VertexId;\n /** The vertex table it belongs to. */\n readonly table: string;\n readonly fields: readonly { readonly name: string; readonly value: unknown }[];\n}\n\nconst tableOf = (geometry: Geometry, vertex: VertexId): VertexTable | undefined =>\n vertex < geometry.size ? geometry.tables[geometry.table[vertex] as number] : undefined;\n\n/**\n * **Detail is fetched, not carried.** The loaded graph holds positions and channels; the rest of a\n * row is asked for when a reader focuses the vertex — a scan of its table, filtered to its key.\n */\nexport async function readVertex(\n corpus: Corpus,\n geometry: Geometry,\n vertex: VertexId,\n signal?: AbortSignal,\n): Promise<VertexDetail | null> {\n const table = tableOf(geometry, vertex);\n if (!table) return null;\n const hidden = new Set([table.key, table.position?.x, table.position?.y]);\n const names = table.properties.map((p) => p.name).filter((name) => !hidden.has(name));\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"=\", value: vertex }, select: [table.key, ...names], limit: 1 });\n const rows = (await scan.read(scan.plan(), { signal })).find((batch) => batch.numRows > 0);\n if (!rows) return null;\n return {\n vertex,\n table: table.name,\n fields: names.map((name) => ({ name, value: rows.getChild(name)?.toArray()[0] ?? null })),\n };\n}\n\n/**\n * **A label's text, for the few vertices that carry one** — `title` where the vertex's table has the\n * column, and the table's declared `identity` where it does not. One scan per table they fall in.\n */\nexport async function readTitles(\n corpus: Corpus,\n geometry: Geometry,\n vertices: readonly VertexId[],\n title: string | undefined,\n signal: AbortSignal,\n): Promise<Map<VertexId, string>> {\n const byTable = new Map<VertexTable, VertexId[]>();\n for (const vertex of vertices) {\n const table = tableOf(geometry, vertex);\n if (table) byTable.set(table, [...(byTable.get(table) ?? []), vertex]);\n }\n const found = new Map<VertexId, string>();\n await together(signal, (signal) =>\n Promise.all(\n [...byTable].map(async ([table, ids]) => {\n const column = title !== undefined && table.properties.some((p) => p.name === title) ? title : table.identity;\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"in\", values: ids }, select: [table.key, column] });\n for (const batch of await scan.read(scan.plan(), { signal })) {\n const keys = batch.getChild(table.key)?.toArray() ?? [];\n const texts = batch.getChild(column)?.toArray() ?? [];\n for (let i = 0; i < batch.numRows; i++) found.set(Number(keys[i]), String(texts[i] ?? \"\"));\n }\n }),\n ),\n );\n return found;\n}\n"],"names":["tableOf","geometry","vertex","readVertex","corpus","signal","table","hidden","_a","_b","names","p","name","scan","rows","batch","readTitles","vertices","title","byTable","found","together","ids","column","keys","texts","i"],"mappings":";AAYA,MAAMA,IAAU,CAACC,GAAoBC,MACnCA,IAASD,EAAS,OAAOA,EAAS,OAAOA,EAAS,MAAMC,CAAM,CAAW,IAAI;AAM/E,eAAsBC,EACpBC,GACAH,GACAC,GACAG,GAC8B;;AAC9B,QAAMC,IAAQN,EAAQC,GAAUC,CAAM;AACtC,MAAI,CAACI,EAAO,QAAO;AACnB,QAAMC,IAAS,oBAAI,IAAI,CAACD,EAAM,MAAKE,IAAAF,EAAM,aAAN,gBAAAE,EAAgB,IAAGC,IAAAH,EAAM,aAAN,gBAAAG,EAAgB,CAAC,CAAC,GAClEC,IAAQJ,EAAM,WAAW,IAAI,CAACK,MAAMA,EAAE,IAAI,EAAE,OAAO,CAACC,MAAS,CAACL,EAAO,IAAIK,CAAI,CAAC,GAC9EC,IAAOT,EAAO,KAAK,EAAE,OAAOE,EAAM,MAAM,QAAQ,EAAE,QAAQA,EAAM,KAAK,IAAI,KAAK,OAAOJ,EAAA,GAAU,QAAQ,CAACI,EAAM,KAAK,GAAGI,CAAK,GAAG,OAAO,EAAA,CAAG,GACxII,KAAQ,MAAMD,EAAK,KAAKA,EAAK,QAAQ,EAAE,QAAAR,EAAA,CAAQ,GAAG,KAAK,CAACU,MAAUA,EAAM,UAAU,CAAC;AACzF,SAAKD,IACE;AAAA,IACL,QAAAZ;AAAA,IACA,OAAOI,EAAM;AAAA,IACb,QAAQI,EAAM,IAAI,CAACE,MAAA;;AAAU,eAAE,MAAAA,GAAM,SAAOJ,IAAAM,EAAK,SAASF,CAAI,MAAlB,gBAAAJ,EAAqB,UAAU,OAAM;KAAO;AAAA,EAAA,IAJxE;AAMpB;AAMA,eAAsBQ,EACpBZ,GACAH,GACAgB,GACAC,GACAb,GACgC;AAChC,QAAMc,wBAAc,IAAA;AACpB,aAAWjB,KAAUe,GAAU;AAC7B,UAAMX,IAAQN,EAAQC,GAAUC,CAAM;AACtC,IAAII,KAAOa,EAAQ,IAAIb,GAAO,CAAC,GAAIa,EAAQ,IAAIb,CAAK,KAAK,CAAA,GAAKJ,CAAM,CAAC;AAAA,EACvE;AACA,QAAMkB,wBAAY,IAAA;AAClB,eAAMC;AAAA,IAAShB;AAAA,IAAQ,CAACA,MACtB,QAAQ;AAAA,MACN,CAAC,GAAGc,CAAO,EAAE,IAAI,OAAO,CAACb,GAAOgB,CAAG,MAAM;;AACvC,cAAMC,IAASL,MAAU,UAAaZ,EAAM,WAAW,KAAK,CAACK,MAAMA,EAAE,SAASO,CAAK,IAAIA,IAAQZ,EAAM,UAC/FO,IAAOT,EAAO,KAAK,EAAE,OAAOE,EAAM,MAAM,QAAQ,EAAE,QAAQA,EAAM,KAAK,IAAI,MAAM,QAAQgB,EAAA,GAAO,QAAQ,CAAChB,EAAM,KAAKiB,CAAM,GAAG;AACjI,mBAAWR,KAAS,MAAMF,EAAK,KAAKA,EAAK,KAAA,GAAQ,EAAE,QAAAR,EAAAA,CAAQ,GAAG;AAC5D,gBAAMmB,MAAOhB,IAAAO,EAAM,SAAST,EAAM,GAAG,MAAxB,gBAAAE,EAA2B,cAAa,CAAA,GAC/CiB,MAAQhB,IAAAM,EAAM,SAASQ,CAAM,MAArB,gBAAAd,EAAwB,cAAa,CAAA;AACnD,mBAASiB,IAAI,GAAGA,IAAIX,EAAM,SAASW,OAAW,IAAI,OAAOF,EAAKE,CAAC,CAAC,GAAG,OAAOD,EAAMC,CAAC,KAAK,EAAE,CAAC;AAAA,QAC3F;AAAA,MACF,CAAC;AAAA,IAAA;AAAA,EACH,GAEKN;AACT;"}
1
+ {"version":3,"file":"detail.js","sources":["../../src/core/detail.ts"],"sourcesContent":["import type { Corpus, VertexTable } from \"@fossil-lang/corpus\";\nimport { together, type Geometry } from \"./load\";\nimport type { VertexId } from \"./types\";\n\n/** One vertex's row, as its table's properties order it — every column but the key and the position. */\nexport interface VertexDetail {\n readonly vertex: VertexId;\n /** The vertex table it belongs to. */\n readonly table: string;\n readonly fields: readonly { readonly name: string; readonly value: unknown }[];\n}\n\nconst tableOf = (geometry: Geometry, vertex: VertexId): VertexTable | undefined =>\n vertex < geometry.size ? geometry.tables[geometry.table[vertex] as number] : undefined;\n\n/**\n * **Detail is fetched, not carried.** The loaded graph holds positions and channels; the rest of a\n * row is asked for when a reader focuses the vertex — a scan of its table, filtered to its key.\n */\nexport async function readVertex(\n corpus: Corpus,\n geometry: Geometry,\n vertex: VertexId,\n signal?: AbortSignal,\n): Promise<VertexDetail | null> {\n const table = tableOf(geometry, vertex);\n if (!table) return null;\n const hidden = new Set([table.key, table.position?.x, table.position?.y]);\n const names = table.properties.map((p) => p.name).filter((name) => !hidden.has(name));\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"=\", value: vertex }, select: [table.key, ...names], limit: 1 });\n const rows = (await scan.read(scan.plan(), { signal })).find((batch) => batch.numRows > 0);\n if (!rows) return null;\n return {\n vertex,\n table: table.name,\n fields: names.map((name) => ({ name, value: rows.getChild(name)?.toArray()[0] ?? null })),\n };\n}\n\n/** The column a vertex's text is read from: `title` where its table has it, else the table's `identity`. */\nconst titleColumn = (table: VertexTable, title: string | undefined): string =>\n title !== undefined && table.properties.some((p) => p.name === title) ? title : table.identity;\n\n/**\n * **A label's text, for the few vertices that carry one** — `title` where the vertex's table has the\n * column, and the table's declared `identity` where it does not. One scan per table they fall in.\n */\nexport async function readTitles(\n corpus: Corpus,\n geometry: Geometry,\n vertices: readonly VertexId[],\n title: string | undefined,\n signal: AbortSignal,\n): Promise<Map<VertexId, string>> {\n const byTable = new Map<VertexTable, VertexId[]>();\n for (const vertex of vertices) {\n const table = tableOf(geometry, vertex);\n if (table) byTable.set(table, [...(byTable.get(table) ?? []), vertex]);\n }\n const found = new Map<VertexId, string>();\n await together(signal, (signal) =>\n Promise.all(\n [...byTable].map(async ([table, ids]) => {\n const column = titleColumn(table, title);\n const scan = corpus.scan({ table: table.name, filter: { column: table.key, op: \"in\", values: ids }, select: [table.key, column] });\n for (const batch of await scan.read(scan.plan(), { signal })) {\n const keys = batch.getChild(table.key)?.toArray() ?? [];\n const texts = batch.getChild(column)?.toArray() ?? [];\n for (let i = 0; i < batch.numRows; i++) found.set(Number(keys[i]), String(texts[i] ?? \"\"));\n }\n }),\n ),\n );\n return found;\n}\n\n/** **Every drawn vertex's text, by id** — what a search filters. One scan per drawn table, two columns. */\nexport async function readEveryTitle(\n corpus: Corpus,\n geometry: Geometry,\n title: string | undefined,\n signal: AbortSignal,\n): Promise<string[]> {\n const titles = new Array<string>(geometry.size).fill(\"\");\n await together(signal, (signal) =>\n Promise.all(\n geometry.tables.map(async (table) => {\n const column = titleColumn(table, title);\n const scan = corpus.scan({ table: table.name, select: [table.key, column] });\n for (const batch of await scan.read(scan.plan(), { signal })) {\n const keys = batch.getChild(table.key)?.toArray() ?? [];\n const texts = batch.getChild(column)?.toArray() ?? [];\n for (let i = 0; i < batch.numRows; i++) {\n const id = Number(keys[i]);\n if (id < geometry.size) titles[id] = String(texts[i] ?? \"\");\n }\n }\n }),\n ),\n );\n return titles;\n}\n"],"names":["tableOf","geometry","vertex","readVertex","corpus","signal","table","hidden","_a","_b","names","p","name","scan","rows","batch","titleColumn","title","readTitles","vertices","byTable","found","together","ids","column","keys","texts","i","readEveryTitle","titles","id"],"mappings":"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"}
@@ -47,12 +47,19 @@ export interface GraphOptions extends Channels {
47
47
  * open; `loading` is the graph, a binding or a filter not yet read or not yet drawn; `idle` is all of
48
48
  * it drawn; `failed` is a corpus that would not open or read, or a canvas with no GPU device to draw
49
49
  * with — none came up, or the one it had was lost.
50
+ *
51
+ * **It is the data's life, not the layout's.** A layout running over a drawn graph is `idle` here and
52
+ * `running` in `motion`: two axes, because each can move without the other — a filter loads under a
53
+ * paused layout, and a layout runs over a graph with nothing left to read. A host that shows one word
54
+ * reads both, `motion === "running"` as "laying out" and `progress` for how far.
50
55
  */
51
56
  export type GraphStatus = "none" | "opening" | "loading" | "idle" | "failed";
52
57
  /** What the loaded graph holds, under the page's filter. */
53
58
  export interface Drawn {
54
59
  /** Vertices drawn: every one with a position that survives the filter. */
55
60
  readonly vertices: number;
61
+ /** Links drawn: every loaded relation whose two ends are drawn. */
62
+ readonly edges: number;
56
63
  /** What each category rank is: the seed first, then any other value seen, in rank order. */
57
64
  readonly domain: readonly unknown[];
58
65
  /** Per rank, the vertices drawn. */
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAElF,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC5C,2DAA2D;IAC3D,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,sGAAsG;IACtG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,kEAAkE;IAClE,GAAG,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;CAC7C;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7E,4DAA4D;AAC5D,MAAM,WAAW,KAAK;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,oCAAoC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACnC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,WAAW,IAAI,aAAa,CAAC;IAC7B,UAAU,IAAI,YAAY,CAAC;IAC3B,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC,OAAO,IAAI,IAAI,CAAC;IAChB,MAAM,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7F,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;IACrC,GAAG,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,uCAAuC;IACvC,UAAU,IAAI,IAAI,CAAC;IACnB,sFAAsF;IACtF,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,yEAAyE;IACzE,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CAC5C"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/core/store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAS,YAAY,EAA8B,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3F,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAqBvG,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CAiW7D"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/core/store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAS,YAAY,EAA8B,UAAU,EAAE,MAAM,SAAS,CAAC;AAE3F,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA4BvG,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CAiW7D"}
@@ -1,28 +1,32 @@
1
1
  import { GraphError as Y } from "./error.js";
2
2
  import { domainOf as Z } from "./categories.js";
3
- import { bindingOf as R } from "./channels.js";
3
+ import { bindingOf as Q } from "./channels.js";
4
4
  import { graphClient as x, publish as ee, filterFor as ne } from "./filter.js";
5
- import { drawnTables as te, loadEncoding as re, loadGraph as oe, maskOf as U, readKept as le } from "./load.js";
6
- const ie = (a, r) => JSON.stringify(a, (m, i) => typeof i == "bigint" ? `${i}n` : i) === JSON.stringify(r, (m, i) => typeof i == "bigint" ? `${i}n` : i), ue = (a) => typeof (a == null ? void 0 : a.then) == "function";
7
- function se(a, r, m) {
8
- const i = r.domain.map(() => 0);
9
- let u = 0;
10
- for (let s = 0; s < a.size; s++) {
11
- if (m && !m[s] || Number.isNaN(a.positions[s * 2])) continue;
12
- u++;
13
- const b = r.ranks[s];
14
- i[b] = (i[b] ?? 0) + 1;
5
+ import { drawnTables as te, loadEncoding as re, loadGraph as oe, maskOf as R, readKept as le } from "./load.js";
6
+ const ie = (u, r) => JSON.stringify(u, (m, f) => typeof f == "bigint" ? `${f}n` : f) === JSON.stringify(r, (m, f) => typeof f == "bigint" ? `${f}n` : f), se = (u) => typeof (u == null ? void 0 : u.then) == "function";
7
+ function ue(u, r, m) {
8
+ const f = r.domain.map(() => 0), s = new Uint8Array(u.size);
9
+ let y = 0;
10
+ for (let l = 0; l < u.size; l++) {
11
+ if (m && !m[l] || Number.isNaN(u.positions[l * 2])) continue;
12
+ s[l] = 1, y++;
13
+ const c = r.ranks[l];
14
+ f[c] = (f[c] ?? 0) + 1;
15
15
  }
16
- return { vertices: u, domain: r.domain, tally: i };
16
+ let h = 0;
17
+ const { links: k } = u;
18
+ for (let l = 0; l < k.length; l += 2)
19
+ s[k[l]] && s[k[l + 1]] && h++;
20
+ return { vertices: y, edges: h, domain: r.domain, tally: f };
17
21
  }
18
- function ye(a) {
19
- let r = a;
20
- const m = /* @__PURE__ */ new Set(), i = x();
21
- let u = null, s = null, b = !1, E = !1, g = [], p = R(a), N, c = null, l = null, f = null, h = null, B = null, w = null, G = [], v = null, F = !1, j = !0, L = null;
22
+ function ye(u) {
23
+ let r = u;
24
+ const m = /* @__PURE__ */ new Set(), f = x();
25
+ let s = null, y = null, h = !1, k = !1, l = [], c = Q(u), F, d = null, i = null, a = null, b = null, N = null, v = null, G = [], w = null, z = !1, U = !0, L = null;
22
26
  const T = (e) => {
23
- j ? L = { error: e } : r.onFailure(e);
27
+ U ? L = { error: e } : r.onFailure(e);
24
28
  };
25
- let d = {
29
+ let p = {
26
30
  status: "none",
27
31
  total: void 0,
28
32
  drawn: null,
@@ -36,193 +40,193 @@ function ye(a) {
36
40
  progress: 1,
37
41
  options: r,
38
42
  corpus: null,
39
- binding: p,
43
+ binding: c,
40
44
  geometry: null,
41
45
  encoding: null,
42
46
  mask: null
43
47
  }, _ = { key: [], value: [] };
44
- function q() {
45
- const e = [g, p.byTable, p.category, r.categories];
46
- return e.some((n, t) => n !== _.key[t]) && (_ = { key: e, value: Z(g, p, r.categories) }), _.value;
48
+ function j() {
49
+ const e = [l, c.byTable, c.category, r.categories];
50
+ return e.some((n, t) => n !== _.key[t]) && (_ = { key: e, value: Z(l, c, r.categories) }), _.value;
47
51
  }
48
- let C = { key: [], value: null };
52
+ let A = { key: [], value: null };
49
53
  function V() {
50
- const e = [f, h, w];
51
- return e.some((n, t) => n !== C.key[t]) && (C = { key: e, value: f && h ? se(f, h, w) : null }), C.value;
54
+ const e = [a, b, v];
55
+ return e.some((n, t) => n !== A.key[t]) && (A = { key: e, value: a && b ? ue(a, b, v) : null }), A.value;
52
56
  }
53
57
  function W() {
54
- if (b || E) return "failed";
55
- if (s) return "opening";
56
- if (!u) return "none";
57
- const n = f !== null && [f, h, w].every((t, o) => t === G[o]);
58
- return c || l || !n ? "loading" : "idle";
58
+ if (h || k) return "failed";
59
+ if (y) return "opening";
60
+ if (!s) return "none";
61
+ const n = a !== null && [a, b, v].every((t, o) => t === G[o]);
62
+ return d || i || !n ? "loading" : "idle";
59
63
  }
60
- function y(e = {}) {
61
- d = {
62
- ...d,
63
- total: u ? g.reduce((n, t) => n + t.record_count, 0) : void 0,
64
+ function g(e = {}) {
65
+ p = {
66
+ ...p,
67
+ total: s ? l.reduce((n, t) => n + t.record_count, 0) : void 0,
64
68
  drawn: V(),
65
- domain: q(),
69
+ domain: j(),
66
70
  options: r,
67
- corpus: u,
68
- binding: p,
69
- geometry: f,
70
- encoding: h,
71
- mask: w,
71
+ corpus: s,
72
+ binding: c,
73
+ geometry: a,
74
+ encoding: b,
75
+ mask: v,
72
76
  ...e
73
- }, d = { ...d, status: W() }, H();
77
+ }, p = { ...p, status: W() }, q();
74
78
  }
75
- function O(e) {
76
- d = { ...d, ...e }, H();
79
+ function B(e) {
80
+ p = { ...p, ...e }, q();
77
81
  }
78
- function H() {
82
+ function q() {
79
83
  for (const e of m) e();
80
84
  }
81
- function J(e, n, t, o, S) {
82
- var z;
83
- (z = e()) == null || z.abort();
84
- const k = new AbortController();
85
- n(k), o(k.signal).then(
85
+ function C(e, n, t, o, S) {
86
+ var E;
87
+ (E = e()) == null || E.abort();
88
+ const O = new AbortController();
89
+ n(O), o(O.signal).then(
86
90
  (K) => {
87
- e() === k && (n(null), S(K), y());
91
+ e() === O && (n(null), S(K), g());
88
92
  },
89
93
  (K) => {
90
- e() !== k || k.signal.aborted || (n(null), b || (b = t), T(K), y());
94
+ e() !== O || O.signal.aborted || (n(null), h || (h = t), T(K), g());
91
95
  }
92
96
  );
93
97
  }
94
- const I = [() => c, (e) => c = e, !0], X = [() => l, (e) => l = e, !1];
95
- function P() {
96
- const e = u;
98
+ const H = [() => d, (e) => d = e, !0], X = [() => i, (e) => i = e, !1];
99
+ function J() {
100
+ const e = s;
97
101
  if (!e) return;
98
- const n = q(), t = f;
102
+ const n = j(), t = a;
99
103
  if (t)
100
- return J(...I, (o) => re(e, t, p, n, o), (o) => {
101
- h = o;
104
+ return C(...H, (o) => re(e, t, c, n, o), (o) => {
105
+ b = o;
102
106
  });
103
- J(...I, (o) => oe(e, p, n, o), (o) => {
104
- f = o.geometry, h = o.encoding, w = f && B ? U(f, B) : null;
107
+ C(...H, (o) => oe(e, c, n, o), (o) => {
108
+ a = o.geometry, b = o.encoding, v = a && N ? R(a, N) : null;
105
109
  });
106
110
  }
107
- function $() {
108
- l == null || l.abort(), l = null;
109
- const e = u;
110
- if (!e || g.length === 0) return;
111
- if (N === void 0) {
112
- B = null, w = null;
111
+ function P() {
112
+ i == null || i.abort(), i = null;
113
+ const e = s;
114
+ if (!e || l.length === 0) return;
115
+ if (F === void 0) {
116
+ N = null, v = null;
113
117
  return;
114
118
  }
115
- const n = N;
116
- J(...X, (t) => le(e, g, n, t), (t) => {
117
- B = t, w = f ? U(f, t) : null;
119
+ const n = F;
120
+ C(...X, (t) => le(e, l, n, t), (t) => {
121
+ N = t, v = a ? R(a, t) : null;
118
122
  });
119
123
  }
120
- function A() {
121
- c == null || c.abort(), l == null || l.abort(), c = l = null, b = !1, f = h = w = B = null, G = [], g = [];
124
+ function $() {
125
+ d == null || d.abort(), i == null || i.abort(), d = i = null, h = !1, a = b = v = N = null, G = [], l = [];
122
126
  const e = { selection: null, focus: null, hovered: null, pinned: [] };
123
- if (!u) return y(e);
127
+ if (!s) return g(e);
124
128
  try {
125
- if (g = te(u), g.length === 0) throw new Y("graph/nothing-to-draw", "the corpus has no vertex type with a position to draw");
129
+ if (l = te(s), l.length === 0) throw new Y("graph/nothing-to-draw", "the corpus has no vertex type with a position to draw");
126
130
  } catch (n) {
127
- return b = !0, T(n), y(e);
131
+ return h = !0, T(n), g(e);
128
132
  }
129
- P(), $(), y(e);
133
+ J(), P(), g(e);
130
134
  }
131
- function M(e) {
132
- if (u = null, s = ue(e) ? e : null, !s)
133
- return u = e, A();
134
- const n = s;
135
- A(), n.then(
135
+ function I(e) {
136
+ if (s = null, y = se(e) ? e : null, !y)
137
+ return s = e, $();
138
+ const n = y;
139
+ $(), n.then(
136
140
  (t) => {
137
- s === n && (s = null, u = t, A());
141
+ y === n && (y = null, s = t, $());
138
142
  },
139
143
  (t) => {
140
- s === n && (s = null, b = !0, T(t), y());
144
+ y === n && (y = null, h = !0, T(t), g());
141
145
  }
142
146
  );
143
147
  }
144
148
  function D() {
145
- v == null || v(), v = null;
149
+ w == null || w(), w = null;
146
150
  const e = r.filterBy, n = () => {
147
151
  let t;
148
152
  try {
149
- t = e ? ne(e, i) : void 0;
153
+ t = e ? ne(e, f) : void 0;
150
154
  } catch (o) {
151
155
  T(o);
152
156
  return;
153
157
  }
154
- ie(t, N) || (N = t, $(), y());
158
+ ie(t, F) || (F = t, P(), g());
155
159
  };
156
- e && (e.addEventListener("value", n), v = () => e.removeEventListener("value", n)), n();
160
+ e && (e.addEventListener("value", n), w = () => e.removeEventListener("value", n)), n();
157
161
  }
158
- const Q = {
162
+ const M = {
159
163
  /**
160
164
  * The first subscriber starts listening to the crossfilter and the last one stops it and lets the
161
165
  * graph go — `QueryObserver`'s `onSubscribe`/`onUnsubscribe`, which is what survives StrictMode
162
166
  * mounting everything twice.
163
167
  */
164
168
  subscribe(e) {
165
- if (m.add(e), !F && (F = !0, D(), u && !b && g.length > 0 && (!c && (!f || !h) && P(), !l && N !== void 0 && !B && $()), L !== null)) {
169
+ if (m.add(e), !z && (z = !0, D(), s && !h && l.length > 0 && (!d && (!a || !b) && J(), !i && F !== void 0 && !N && P()), L !== null)) {
166
170
  const { error: n } = L;
167
171
  L = null, r.onFailure(n);
168
172
  }
169
173
  return () => {
170
- m.delete(e), m.size === 0 && Q.destroy();
174
+ m.delete(e), m.size === 0 && M.destroy();
171
175
  };
172
176
  },
173
- getSnapshot: () => d,
177
+ getSnapshot: () => p,
174
178
  getOptions: () => r,
175
179
  setOptions(e) {
176
180
  const n = r;
177
181
  r = e;
178
- const t = R(e), o = t.category !== p.category || t.byTable !== p.byTable || t.size !== p.size || e.categories !== n.categories;
179
- if (p = t, e.corpus !== n.corpus) {
180
- M(e.corpus), F && e.filterBy !== n.filterBy && D();
182
+ const t = Q(e), o = t.category !== c.category || t.byTable !== c.byTable || t.size !== c.size || e.categories !== n.categories;
183
+ if (c = t, e.corpus !== n.corpus) {
184
+ I(e.corpus), z && e.filterBy !== n.filterBy && D();
181
185
  return;
182
186
  }
183
- F && e.filterBy !== n.filterBy && D(), o && P(), y();
187
+ z && e.filterBy !== n.filterBy && D(), o && J(), g();
184
188
  },
185
189
  destroy() {
186
- F = !1, v == null || v(), v = null, c == null || c.abort(), l == null || l.abort(), c = l = null, m.clear();
190
+ z = !1, w == null || w(), w = null, d == null || d.abort(), i == null || i.abort(), d = i = null, m.clear();
187
191
  },
188
192
  select(e, n = "node", t = "") {
189
- var k, z;
193
+ var O, E;
190
194
  const o = e && e.length > 0 ? { vertices: [...e], source: n, label: t } : null;
191
- O({ selection: o }), (k = r.onSelect) == null || k.call(r, o);
192
- const S = (z = g[0]) == null ? void 0 : z.key;
193
- r.filterBy && S && ee(r.filterBy, i, S, o ? o.vertices : null);
195
+ B({ selection: o }), (O = r.onSelect) == null || O.call(r, o);
196
+ const S = (E = l[0]) == null ? void 0 : E.key;
197
+ r.filterBy && S && ee(r.filterBy, f, S, o ? o.vertices : null);
194
198
  },
195
199
  focus(e) {
196
200
  var n;
197
- e !== d.focus && (O({ focus: e }), (n = r.onFocus) == null || n.call(r, e));
201
+ e !== p.focus && (B({ focus: e }), (n = r.onFocus) == null || n.call(r, e));
198
202
  },
199
203
  hover(e) {
200
- e !== d.hovered && O({ hovered: e });
204
+ e !== p.hovered && B({ hovered: e });
201
205
  },
202
206
  pin(e) {
203
- O({ pinned: [...e] });
207
+ B({ pinned: [...e] });
204
208
  },
205
209
  setTool(e) {
206
- e !== d.tool && O({ tool: e });
210
+ e !== p.tool && B({ tool: e });
207
211
  },
208
212
  report(e) {
209
- e !== d.motion && O({ motion: e });
213
+ e !== p.motion && B({ motion: e });
210
214
  },
211
215
  reportProgress(e) {
212
- e !== d.progress && O({ progress: e });
216
+ e !== p.progress && B({ progress: e });
213
217
  },
214
218
  renderable() {
215
- E && (E = !1, y());
219
+ k && (k = !1, g());
216
220
  },
217
221
  unrenderable(e) {
218
- E = !0, T(e), y();
222
+ k = !0, T(e), g();
219
223
  },
220
224
  reportDrawn(e) {
221
225
  const n = [e.geometry, e.encoding, e.mask];
222
- n.every((t, o) => t === G[o]) || (G = n, y());
226
+ n.every((t, o) => t === G[o]) || (G = n, g());
223
227
  }
224
228
  };
225
- return M(a.corpus), j = !1, Q;
229
+ return I(u.corpus), U = !1, M;
226
230
  }
227
231
  export {
228
232
  ye as createGraph