@kanzo-tech/graph 0.26.0 → 0.27.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.
package/README.md CHANGED
@@ -77,8 +77,8 @@ graph, a second main view and a dock of panels.
77
77
 
78
78
  No part takes a callback: what a click means is `onSelect`, `onFocus` and `onFailure` on the root.
79
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`).
80
+ own `GraphError` (`graph/no-webgl`, `graph/context-lost`, `graph/no-positions`,
81
+ `graph/nothing-to-draw`, `graph/unfilterable`).
82
82
 
83
83
  - `lookFrom`, `simFrom`, `useGraphPrefs` — the form and the forces from a preferences panel's
84
84
  answers, and the look `preset` they are; `scaleOf` — what colour and shape a category wears.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgC,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEjH,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAYxE,qBAAa,WAAY,SAAQ,YAAY;;gBAMzC,QAAQ,EAAE,SAAS,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,SAAS,GAAG,IAAI,EACjC,IAAI,EAAE,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,EACxC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IAQhC;;;OAGG;IACH,IAAa,YAAY,IAAI,OAAO,CAEnC;IAED;;;;OAIG;IACM,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM;IAwBzC,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAMhC,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;CAIxC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAOrG"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIZ,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAYxE,qBAAa,WAAY,SAAQ,YAAY;;gBAMzC,QAAQ,EAAE,SAAS,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,SAAS,GAAG,IAAI,EACjC,IAAI,EAAE,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,EACxC,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IAQhC;;;OAGG;IACH,IAAa,YAAY,IAAI,OAAO,CAEnC;IAED;;;;OAIG;IACM,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM;IAwBzC,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAMhC,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;CAIxC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAOrG"}
@@ -1,11 +1,11 @@
1
- var f = (t) => {
2
- throw TypeError(t);
1
+ var f = (r) => {
2
+ throw TypeError(r);
3
3
  };
4
- var L = (t, r, e) => r.has(t) || f("Cannot " + e);
5
- var u = (t, r, e) => (L(t, r, "read from private field"), e ? e.call(t) : r.get(t)), h = (t, r, e) => r.has(t) ? f("Cannot add the same private member more than once") : r instanceof WeakSet ? r.add(t) : r.set(t, e), m = (t, r, e, n) => (L(t, r, "write to private field"), n ? n.call(t, e) : r.set(t, e), e);
6
- import { MosaicClient as g, collectColumns as A, clausePoints as v } from "@kanzo-tech/mosaic";
7
- import { GraphError as C } from "./error.js";
8
- import { relation as $, ident as y } from "./source.js";
4
+ var y = (r, t, e) => t.has(r) || f("Cannot " + e);
5
+ var u = (r, t, e) => (y(r, t, "read from private field"), e ? e.call(r) : t.get(r)), h = (r, t, e) => t.has(r) ? f("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(r) : t.set(r, e), m = (r, t, e, n) => (y(r, t, "write to private field"), n ? n.call(r, e) : t.set(r, e), e);
6
+ import { MosaicClient as g, collectColumns as A, queryFailure as v, clausePoints as C } from "@kanzo-tech/mosaic";
7
+ import { GraphError as $ } from "./error.js";
8
+ import { relation as O, ident as L } from "./source.js";
9
9
  const S = "SELECT NULL::DOUBLE AS id";
10
10
  var a, c, l;
11
11
  class N extends g {
@@ -32,9 +32,9 @@ class N extends g {
32
32
  const n = (Array.isArray(e) ? e : [e]).filter((s) => s != null), i = u(this, a).call(this);
33
33
  if (!i || n.length === 0) return S;
34
34
  const d = n.map((s) => `(${String(s)})`).join(" AND "), p = [...new Set(n.flatMap((s) => A(s).map((o) => o.column)))], E = i.vertices.filter((s) => p.every((o) => s.columns.has(o)));
35
- return E.length === 0 ? (u(this, l).call(this, new C("graph/unfilterable", `no vertex type has every column this clause names: ${p.join(", ")}`)), S) : i.vertices.map((s) => {
36
- const o = $(i.from, s.name);
37
- return E.includes(s) ? `SELECT ${y("dense_id")}::DOUBLE AS id FROM ${o} WHERE ${d}` : `SELECT ${y("dense_id")}::DOUBLE AS id FROM ${o}`;
35
+ return E.length === 0 ? (u(this, l).call(this, new $("graph/unfilterable", `no vertex type has every column this clause names: ${p.join(", ")}`)), S) : i.vertices.map((s) => {
36
+ const o = O(i.from, s.name);
37
+ return E.includes(s) ? `SELECT ${L("dense_id")}::DOUBLE AS id FROM ${o} WHERE ${d}` : `SELECT ${L("dense_id")}::DOUBLE AS id FROM ${o}`;
38
38
  }).join(" UNION ALL ");
39
39
  }
40
40
  queryResult(e) {
@@ -43,15 +43,15 @@ class N extends g {
43
43
  return u(this, c).call(this, n.length === 1 && n[0] === null ? null : Float64Array.from(n)), this;
44
44
  }
45
45
  queryError(e) {
46
- return u(this, l).call(this, e), this;
46
+ return u(this, l).call(this, v(e)), this;
47
47
  }
48
48
  }
49
49
  a = new WeakMap(), c = new WeakMap(), l = new WeakMap();
50
- function R(t, r, e) {
51
- t.update(
52
- v(["dense_id"], e && e.length > 0 ? e.map((n) => [n]) : void 0, {
53
- source: r,
54
- clients: /* @__PURE__ */ new Set([r])
50
+ function R(r, t, e) {
51
+ r.update(
52
+ C(["dense_id"], e && e.length > 0 ? e.map((n) => [n]) : void 0, {
53
+ source: t,
54
+ clients: /* @__PURE__ */ new Set([t])
55
55
  })
56
56
  );
57
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sources":["../../src/core/client.ts"],"sourcesContent":["import { MosaicClient, clausePoints, collectColumns, type FilterExpr, type Selection } from \"@kanzo-tech/mosaic\";\nimport { GraphError } from \"./error\";\nimport { ident, relation, type Answer, type Structure } from \"./source\";\n\n/**\n * **The graph as a client of the page's coordinator** — one Mosaic client, like every chart beside\n * it. The coordinator hands it the crossfilter's predicate, minus the clause the graph published\n * itself; it answers with the `dense_id`s that survive, and the canvas greys out the rest on the GPU.\n * No predicate is translated, no position is uploaded again, and the query runs on the coordinator's\n * one connection, through its cache — Cosmograph's crossfilter, on our stack.\n */\n/** The answer that clears the filter. */\nconst UNFILTERED = \"SELECT NULL::DOUBLE AS id\";\n\nexport class GraphClient extends MosaicClient {\n #structure: () => Structure | null;\n #kept: (ids: Float64Array | null) => void;\n #fail: (error: unknown) => void;\n\n constructor(\n filterBy: Selection | undefined,\n structure: () => Structure | null,\n kept: (ids: Float64Array | null) => void,\n fail: (error: unknown) => void,\n ) {\n super(filterBy);\n this.#structure = structure;\n this.#kept = kept;\n this.#fail = fail;\n }\n\n /**\n * Not stable: the answer is a set of ids, not a group-by a pre-aggregate could stand in for. And\n * Mosaic's pre-aggregator calls `query()` with no filter to analyse a client — false keeps it away.\n */\n override get filterStable(): boolean {\n return false;\n }\n\n /**\n * The surviving ids — or, with nothing to filter by, {@link UNFILTERED}: one row whose id is\n * `NULL`, which no `dense_id` is. The answer says which it is, so two statements in flight can\n * never be read as each other's.\n */\n override query(filter?: FilterExpr | null): string {\n const clauses = (Array.isArray(filter) ? filter : [filter]).filter((c) => c !== undefined && c !== null);\n const structure = this.#structure();\n if (!structure || clauses.length === 0) return UNFILTERED;\n const where = clauses.map((c) => `(${String(c)})`).join(\" AND \");\n const named = [...new Set(clauses.flatMap((c) => collectColumns(c as never).map((ref: { column: string }) => ref.column)))];\n // A table that lacks a column the predicate names is not filtered by it — what a `WHERE` over a\n // union of the tables would do. A predicate no table can answer is refused rather than ignored,\n // so the unfiltered picture is never drawn as the filtered one.\n const answering = structure.vertices.filter((t) => named.every((c) => t.columns.has(c)));\n if (answering.length === 0) {\n this.#fail(new GraphError(\"graph/unfilterable\", `no vertex type has every column this clause names: ${named.join(\", \")}`));\n return UNFILTERED;\n }\n return structure.vertices\n .map((t) => {\n const from = relation(structure.from, t.name);\n return answering.includes(t)\n ? `SELECT ${ident(\"dense_id\")}::DOUBLE AS id FROM ${from} WHERE ${where}`\n : `SELECT ${ident(\"dense_id\")}::DOUBLE AS id FROM ${from}`;\n })\n .join(\" UNION ALL \");\n }\n\n override queryResult(data: unknown): this {\n const ids = ((data as Answer).getChild(\"id\")?.toArray() ?? []) as ArrayLike<number | null>;\n this.#kept(ids.length === 1 && ids[0] === null ? null : Float64Array.from(ids as ArrayLike<number>));\n return this;\n }\n\n override queryError(error: Error): this {\n this.#fail(error);\n return this;\n }\n}\n\n/**\n * **The reader's pick, published** — from the graph, so the crossfilter applies it to every chart and\n * skips it for the graph. A canvas that greyed out everything but a lasso of thirteen would hide the\n * neighbourhood the reader was looking at.\n */\nexport function publish(selection: Selection, self: MosaicClient, ids: readonly number[] | null): void {\n selection.update(\n clausePoints([\"dense_id\"], ids && ids.length > 0 ? ids.map((d) => [d]) : undefined, {\n source: self,\n clients: new Set([self]),\n }),\n );\n}\n"],"names":["UNFILTERED","GraphClient","MosaicClient","filterBy","structure","kept","fail","__privateAdd","_structure","_kept","_fail","__privateSet","filter","clauses","c","__privateGet","where","named","collectColumns","ref","answering","t","GraphError","from","relation","ident","data","ids","_a","error","publish","selection","self","clausePoints","d"],"mappings":";;;;;;;;AAYA,MAAMA,IAAa;;AAEZ,MAAMC,UAAoBC,EAAa;AAAA,EAK5C,YACEC,GACAC,GACAC,GACAC,GACA;AACA,UAAMH,CAAQ;AAVhB,IAAAI,EAAA,MAAAC;AACA,IAAAD,EAAA,MAAAE;AACA,IAAAF,EAAA,MAAAG;AASE,IAAAC,EAAA,MAAKH,GAAaJ,IAClBO,EAAA,MAAKF,GAAQJ,IACbM,EAAA,MAAKD,GAAQJ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAa,eAAwB;AACnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,MAAMM,GAAoC;AACjD,UAAMC,KAAW,MAAM,QAAQD,CAAM,IAAIA,IAAS,CAACA,CAAM,GAAG,OAAO,CAACE,MAAyBA,KAAM,IAAI,GACjGV,IAAYW,EAAA,MAAKP,GAAL;AAClB,QAAI,CAACJ,KAAaS,EAAQ,WAAW,EAAG,QAAOb;AAC/C,UAAMgB,IAAQH,EAAQ,IAAI,CAACC,MAAM,IAAI,OAAOA,CAAC,CAAC,GAAG,EAAE,KAAK,OAAO,GACzDG,IAAQ,CAAC,GAAG,IAAI,IAAIJ,EAAQ,QAAQ,CAACC,MAAMI,EAAeJ,CAAU,EAAE,IAAI,CAACK,MAA4BA,EAAI,MAAM,CAAC,CAAC,CAAC,GAIpHC,IAAYhB,EAAU,SAAS,OAAO,CAACiB,MAAMJ,EAAM,MAAM,CAACH,MAAMO,EAAE,QAAQ,IAAIP,CAAC,CAAC,CAAC;AACvF,WAAIM,EAAU,WAAW,KACvBL,EAAA,MAAKL,GAAL,WAAW,IAAIY,EAAW,sBAAsB,sDAAsDL,EAAM,KAAK,IAAI,CAAC,EAAE,IACjHjB,KAEFI,EAAU,SACd,IAAI,CAACiB,MAAM;AACV,YAAME,IAAOC,EAASpB,EAAU,MAAMiB,EAAE,IAAI;AAC5C,aAAOD,EAAU,SAASC,CAAC,IACvB,UAAUI,EAAM,UAAU,CAAC,uBAAuBF,CAAI,UAAUP,CAAK,KACrE,UAAUS,EAAM,UAAU,CAAC,uBAAuBF,CAAI;AAAA,IAC5D,CAAC,EACA,KAAK,aAAa;AAAA,EACvB;AAAA,EAES,YAAYG,GAAqB;;AACxC,UAAMC,MAAQC,IAAAF,EAAgB,SAAS,IAAI,MAA7B,gBAAAE,EAAgC,cAAa,CAAA;AAC3D,WAAAb,EAAA,MAAKN,GAAL,WAAWkB,EAAI,WAAW,KAAKA,EAAI,CAAC,MAAM,OAAO,OAAO,aAAa,KAAKA,CAAwB,IAC3F;AAAA,EACT;AAAA,EAES,WAAWE,GAAoB;AACtC,WAAAd,EAAA,MAAKL,GAAL,WAAWmB,IACJ;AAAA,EACT;AACF;AA/DErB,IAAA,eACAC,IAAA,eACAC,IAAA;AAoEK,SAASoB,EAAQC,GAAsBC,GAAoBL,GAAqC;AACrG,EAAAI,EAAU;AAAA,IACRE,EAAa,CAAC,UAAU,GAAGN,KAAOA,EAAI,SAAS,IAAIA,EAAI,IAAI,CAACO,MAAM,CAACA,CAAC,CAAC,IAAI,QAAW;AAAA,MAClF,QAAQF;AAAA,MACR,SAAS,oBAAI,IAAI,CAACA,CAAI,CAAC;AAAA,IAAA,CACxB;AAAA,EAAA;AAEL;"}
1
+ {"version":3,"file":"client.js","sources":["../../src/core/client.ts"],"sourcesContent":["import {\n MosaicClient,\n clausePoints,\n collectColumns,\n queryFailure,\n type FilterExpr,\n type Selection,\n} from \"@kanzo-tech/mosaic\";\nimport { GraphError } from \"./error\";\nimport { ident, relation, type Answer, type Structure } from \"./source\";\n\n/**\n * **The graph as a client of the page's coordinator** — one Mosaic client, like every chart beside\n * it. The coordinator hands it the crossfilter's predicate, minus the clause the graph published\n * itself; it answers with the `dense_id`s that survive, and the canvas greys out the rest on the GPU.\n * No predicate is translated, no position is uploaded again, and the query runs on the coordinator's\n * one connection, through its cache — Cosmograph's crossfilter, on our stack.\n */\n/** The answer that clears the filter. */\nconst UNFILTERED = \"SELECT NULL::DOUBLE AS id\";\n\nexport class GraphClient extends MosaicClient {\n #structure: () => Structure | null;\n #kept: (ids: Float64Array | null) => void;\n #fail: (error: unknown) => void;\n\n constructor(\n filterBy: Selection | undefined,\n structure: () => Structure | null,\n kept: (ids: Float64Array | null) => void,\n fail: (error: unknown) => void,\n ) {\n super(filterBy);\n this.#structure = structure;\n this.#kept = kept;\n this.#fail = fail;\n }\n\n /**\n * Not stable: the answer is a set of ids, not a group-by a pre-aggregate could stand in for. And\n * Mosaic's pre-aggregator calls `query()` with no filter to analyse a client — false keeps it away.\n */\n override get filterStable(): boolean {\n return false;\n }\n\n /**\n * The surviving ids — or, with nothing to filter by, {@link UNFILTERED}: one row whose id is\n * `NULL`, which no `dense_id` is. The answer says which it is, so two statements in flight can\n * never be read as each other's.\n */\n override query(filter?: FilterExpr | null): string {\n const clauses = (Array.isArray(filter) ? filter : [filter]).filter((c) => c !== undefined && c !== null);\n const structure = this.#structure();\n if (!structure || clauses.length === 0) return UNFILTERED;\n const where = clauses.map((c) => `(${String(c)})`).join(\" AND \");\n const named = [...new Set(clauses.flatMap((c) => collectColumns(c as never).map((ref: { column: string }) => ref.column)))];\n // A table that lacks a column the predicate names is not filtered by it — what a `WHERE` over a\n // union of the tables would do. A predicate no table can answer is refused rather than ignored,\n // so the unfiltered picture is never drawn as the filtered one.\n const answering = structure.vertices.filter((t) => named.every((c) => t.columns.has(c)));\n if (answering.length === 0) {\n this.#fail(new GraphError(\"graph/unfilterable\", `no vertex type has every column this clause names: ${named.join(\", \")}`));\n return UNFILTERED;\n }\n return structure.vertices\n .map((t) => {\n const from = relation(structure.from, t.name);\n return answering.includes(t)\n ? `SELECT ${ident(\"dense_id\")}::DOUBLE AS id FROM ${from} WHERE ${where}`\n : `SELECT ${ident(\"dense_id\")}::DOUBLE AS id FROM ${from}`;\n })\n .join(\" UNION ALL \");\n }\n\n override queryResult(data: unknown): this {\n const ids = ((data as Answer).getChild(\"id\")?.toArray() ?? []) as ArrayLike<number | null>;\n this.#kept(ids.length === 1 && ids[0] === null ? null : Float64Array.from(ids as ArrayLike<number>));\n return this;\n }\n\n override queryError(error: Error): this {\n this.#fail(queryFailure(error));\n return this;\n }\n}\n\n/**\n * **The reader's pick, published** — from the graph, so the crossfilter applies it to every chart and\n * skips it for the graph. A canvas that greyed out everything but a lasso of thirteen would hide the\n * neighbourhood the reader was looking at.\n */\nexport function publish(selection: Selection, self: MosaicClient, ids: readonly number[] | null): void {\n selection.update(\n clausePoints([\"dense_id\"], ids && ids.length > 0 ? ids.map((d) => [d]) : undefined, {\n source: self,\n clients: new Set([self]),\n }),\n );\n}\n"],"names":["UNFILTERED","GraphClient","MosaicClient","filterBy","structure","kept","fail","__privateAdd","_structure","_kept","_fail","__privateSet","filter","clauses","c","__privateGet","where","named","collectColumns","ref","answering","t","GraphError","from","relation","ident","data","ids","_a","error","queryFailure","publish","selection","self","clausePoints","d"],"mappings":";;;;;;;;AAmBA,MAAMA,IAAa;;AAEZ,MAAMC,UAAoBC,EAAa;AAAA,EAK5C,YACEC,GACAC,GACAC,GACAC,GACA;AACA,UAAMH,CAAQ;AAVhB,IAAAI,EAAA,MAAAC;AACA,IAAAD,EAAA,MAAAE;AACA,IAAAF,EAAA,MAAAG;AASE,IAAAC,EAAA,MAAKH,GAAaJ,IAClBO,EAAA,MAAKF,GAAQJ,IACbM,EAAA,MAAKD,GAAQJ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAa,eAAwB;AACnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,MAAMM,GAAoC;AACjD,UAAMC,KAAW,MAAM,QAAQD,CAAM,IAAIA,IAAS,CAACA,CAAM,GAAG,OAAO,CAACE,MAAyBA,KAAM,IAAI,GACjGV,IAAYW,EAAA,MAAKP,GAAL;AAClB,QAAI,CAACJ,KAAaS,EAAQ,WAAW,EAAG,QAAOb;AAC/C,UAAMgB,IAAQH,EAAQ,IAAI,CAACC,MAAM,IAAI,OAAOA,CAAC,CAAC,GAAG,EAAE,KAAK,OAAO,GACzDG,IAAQ,CAAC,GAAG,IAAI,IAAIJ,EAAQ,QAAQ,CAACC,MAAMI,EAAeJ,CAAU,EAAE,IAAI,CAACK,MAA4BA,EAAI,MAAM,CAAC,CAAC,CAAC,GAIpHC,IAAYhB,EAAU,SAAS,OAAO,CAACiB,MAAMJ,EAAM,MAAM,CAACH,MAAMO,EAAE,QAAQ,IAAIP,CAAC,CAAC,CAAC;AACvF,WAAIM,EAAU,WAAW,KACvBL,EAAA,MAAKL,GAAL,WAAW,IAAIY,EAAW,sBAAsB,sDAAsDL,EAAM,KAAK,IAAI,CAAC,EAAE,IACjHjB,KAEFI,EAAU,SACd,IAAI,CAACiB,MAAM;AACV,YAAME,IAAOC,EAASpB,EAAU,MAAMiB,EAAE,IAAI;AAC5C,aAAOD,EAAU,SAASC,CAAC,IACvB,UAAUI,EAAM,UAAU,CAAC,uBAAuBF,CAAI,UAAUP,CAAK,KACrE,UAAUS,EAAM,UAAU,CAAC,uBAAuBF,CAAI;AAAA,IAC5D,CAAC,EACA,KAAK,aAAa;AAAA,EACvB;AAAA,EAES,YAAYG,GAAqB;;AACxC,UAAMC,MAAQC,IAAAF,EAAgB,SAAS,IAAI,MAA7B,gBAAAE,EAAgC,cAAa,CAAA;AAC3D,WAAAb,EAAA,MAAKN,GAAL,WAAWkB,EAAI,WAAW,KAAKA,EAAI,CAAC,MAAM,OAAO,OAAO,aAAa,KAAKA,CAAwB,IAC3F;AAAA,EACT;AAAA,EAES,WAAWE,GAAoB;AACtC,WAAAd,EAAA,MAAKL,GAAL,WAAWoB,EAAaD,CAAK,IACtB;AAAA,EACT;AACF;AA/DErB,IAAA,eACAC,IAAA,eACAC,IAAA;AAoEK,SAASqB,EAAQC,GAAsBC,GAAoBN,GAAqC;AACrG,EAAAK,EAAU;AAAA,IACRE,EAAa,CAAC,UAAU,GAAGP,KAAOA,EAAI,SAAS,IAAIA,EAAI,IAAI,CAACQ,MAAM,CAACA,CAAC,CAAC,IAAI,QAAW;AAAA,MAClF,QAAQF;AAAA,MACR,SAAS,oBAAI,IAAI,CAACA,CAAI,CAAC;AAAA,IAAA,CACxB;AAAA,EAAA;AAEL;"}
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * The failures the graph names itself: no GPU device to draw with, or the one it had was taken back;
3
- * a corpus with no vertex to draw; a crossfilter clause no vertex type can answer. A
3
+ * a device that holds no positions for the points it was given; a corpus with no vertex to draw; a crossfilter clause no vertex type can answer. A
4
4
  * corpus fossil will not read never gets here: `open` refuses it, coded. `data.after` is set when a deadline fired. Everything else reaches `onFailure` as thrown.
5
5
  */
6
6
  export declare class GraphError extends Error {
7
- readonly code: "graph/no-webgl" | "graph/context-lost" | "graph/nothing-to-draw" | "graph/unfilterable";
7
+ readonly code: "graph/no-webgl" | "graph/context-lost" | "graph/no-positions" | "graph/nothing-to-draw" | "graph/unfilterable";
8
8
  readonly data: {
9
9
  readonly after?: number;
10
10
  };
11
11
  readonly name = "GraphError";
12
- constructor(code: "graph/no-webgl" | "graph/context-lost" | "graph/nothing-to-draw" | "graph/unfilterable", message: string, data?: {
12
+ constructor(code: "graph/no-webgl" | "graph/context-lost" | "graph/no-positions" | "graph/nothing-to-draw" | "graph/unfilterable", message: string, data?: {
13
13
  readonly after?: number;
14
14
  }, options?: ErrorOptions);
15
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/core/error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAGjC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,oBAAoB;IAEvG,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAJ5C,SAAkB,IAAI,gBAAgB;gBAE3B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,oBAAoB,EACvG,OAAO,EAAE,MAAM,EACN,IAAI,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,EAC/C,OAAO,CAAC,EAAE,YAAY;CAIzB"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/core/error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAGjC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,oBAAoB;IAE9H,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAJ5C,SAAkB,IAAI,gBAAgB;gBAE3B,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,oBAAoB,EAC9H,OAAO,EAAE,MAAM,EACN,IAAI,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,EAC/C,OAAO,CAAC,EAAE,YAAY;CAIzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"error.js","sources":["../../src/core/error.ts"],"sourcesContent":["/**\n * The failures the graph names itself: no GPU device to draw with, or the one it had was taken back;\n * a corpus with no vertex to draw; a crossfilter clause no vertex type can answer. A\n * corpus fossil will not read never gets here: `open` refuses it, coded. `data.after` is set when a deadline fired. Everything else reaches `onFailure` as thrown.\n */\nexport class GraphError extends Error {\n override readonly name = \"GraphError\";\n constructor(\n readonly code: \"graph/no-webgl\" | \"graph/context-lost\" | \"graph/nothing-to-draw\" | \"graph/unfilterable\",\n message: string,\n readonly data: { readonly after?: number } = {},\n options?: ErrorOptions,\n ) {\n super(message, options);\n }\n}\n"],"names":["GraphError","code","message","data","options","__publicField"],"mappings":";;;AAKO,MAAMA,UAAmB,MAAM;AAAA,EAEpC,YACWC,GACTC,GACSC,IAAoC,CAAA,GAC7CC,GACA;AACA,UAAMF,GAASE,CAAO;AAPN,IAAAC,EAAA,cAAO;AAEd,SAAA,OAAAJ,GAEA,KAAA,OAAAE;AAAA,EAIX;AACF;"}
1
+ {"version":3,"file":"error.js","sources":["../../src/core/error.ts"],"sourcesContent":["/**\n * The failures the graph names itself: no GPU device to draw with, or the one it had was taken back;\n * a device that holds no positions for the points it was given; a corpus with no vertex to draw; a crossfilter clause no vertex type can answer. A\n * corpus fossil will not read never gets here: `open` refuses it, coded. `data.after` is set when a deadline fired. Everything else reaches `onFailure` as thrown.\n */\nexport class GraphError extends Error {\n override readonly name = \"GraphError\";\n constructor(\n readonly code: \"graph/no-webgl\" | \"graph/context-lost\" | \"graph/no-positions\" | \"graph/nothing-to-draw\" | \"graph/unfilterable\",\n message: string,\n readonly data: { readonly after?: number } = {},\n options?: ErrorOptions,\n ) {\n super(message, options);\n }\n}\n"],"names":["GraphError","code","message","data","options","__publicField"],"mappings":";;;AAKO,MAAMA,UAAmB,MAAM;AAAA,EAEpC,YACWC,GACTC,GACSC,IAAoC,CAAA,GAC7CC,GACA;AACA,UAAMF,GAASE,CAAO;AAPN,IAAAC,EAAA,cAAO;AAEd,SAAA,OAAAJ,GAEA,KAAA,OAAAE;AAAA,EAIX;AACF;"}
@@ -4,30 +4,30 @@ import { GraphClient as M, publish as R } from "./client.js";
4
4
  import { GraphError as V } from "./error.js";
5
5
  import { loadGeometry as W, loadEncoding as X } from "./load.js";
6
6
  import { readStructure as Y } from "./source.js";
7
- function Z(d, o, f) {
8
- const a = o.domain.map(() => 0), y = new Uint8Array(d.size);
7
+ function Z(d, o, a) {
8
+ const f = o.domain.map(() => 0), y = new Uint8Array(d.size);
9
9
  let l = 0;
10
10
  for (let r = 0; r < d.size; r++) {
11
- if (f && !f[r] || Number.isNaN(d.positions[r * 2])) continue;
11
+ if (a && !a[r] || Number.isNaN(d.positions[r * 2])) continue;
12
12
  y[r] = 1, l++;
13
13
  const p = o.ranks[r];
14
- a[p] = (a[p] ?? 0) + 1;
14
+ f[p] = (f[p] ?? 0) + 1;
15
15
  }
16
16
  let u = 0;
17
17
  const { links: i } = d;
18
18
  for (let r = 0; r < i.length; r += 2)
19
19
  y[i[r]] && y[i[r + 1]] && u++;
20
- return { vertices: l, edges: u, domain: o.domain, tally: a };
20
+ return { vertices: l, edges: u, domain: o.domain, tally: f };
21
21
  }
22
22
  function H(d, o) {
23
- const f = new Uint8Array(d);
24
- for (const a of o) a < d && (f[a] = 1);
25
- return f;
23
+ const a = new Uint8Array(d);
24
+ for (const f of o) f < d && (a[f] = 1);
25
+ return a;
26
26
  }
27
27
  function re(d) {
28
28
  let o = d;
29
- const f = /* @__PURE__ */ new Set();
30
- let a = !1, y = !1, l = $(d), u = null, i = null, r = null, p = null, h = null, O = [];
29
+ const a = /* @__PURE__ */ new Set();
30
+ let f = !1, y = !1, l = $(d), u = null, i = null, r = null, p = null, h = null, O = [];
31
31
  const k = { structure: 0, geometry: 0, encoding: 0 }, b = { structure: !1, geometry: !1, encoding: !1 };
32
32
  let m = null, B = null, z = !1, C = !0, N = null;
33
33
  const G = (e) => {
@@ -62,7 +62,7 @@ function re(d) {
62
62
  return e.some((n, t) => n !== F.key[t]) && (F = { key: e, value: i && r ? Z(i, r, h) : null }), F.value;
63
63
  }
64
64
  function J() {
65
- if (a || y) return "failed";
65
+ if (f || y) return "failed";
66
66
  if (o.from === null || o.coordinator === null) return "none";
67
67
  const n = i !== null && [i, r, h].every((t, s) => t === O[s]);
68
68
  return b.structure || b.geometry || b.encoding || !n ? "loading" : "idle";
@@ -86,7 +86,7 @@ function re(d) {
86
86
  c = { ...c, ...e }, P();
87
87
  }
88
88
  function P() {
89
- for (const e of f) e();
89
+ for (const e of a) e();
90
90
  }
91
91
  function S(e, n, t) {
92
92
  const s = ++k[e];
@@ -95,7 +95,7 @@ function re(d) {
95
95
  k[e] === s && (b[e] = !1, t(w), g());
96
96
  },
97
97
  (w) => {
98
- k[e] === s && (b[e] = !1, a = !0, G(w), g());
98
+ k[e] === s && (b[e] = !1, f = !0, G(w), g());
99
99
  }
100
100
  );
101
101
  }
@@ -103,7 +103,7 @@ function re(d) {
103
103
  const { coordinator: e, from: n } = o;
104
104
  e === null || n === null || S("structure", () => Y(e, n), (t) => {
105
105
  if (t.size === 0) {
106
- a = !0, G(new V("graph/nothing-to-draw", `the corpus attached as ${n} has no vertex`));
106
+ f = !0, G(new V("graph/nothing-to-draw", `the corpus attached as ${n} has no vertex`));
107
107
  return;
108
108
  }
109
109
  u = t, j(), m == null || m.requestQuery();
@@ -126,7 +126,7 @@ function re(d) {
126
126
  function K() {
127
127
  for (const e of Object.keys(k))
128
128
  k[e]++, b[e] = !1;
129
- a = !1, u = i = r = null, p = h = null, O = [], g({ selection: null, focus: null, hovered: null, pinned: [] }), U();
129
+ f = !1, u = i = r = null, p = h = null, O = [], g({ selection: null, focus: null, hovered: null, pinned: [] }), U();
130
130
  }
131
131
  function A() {
132
132
  D();
@@ -150,12 +150,12 @@ function re(d) {
150
150
  * survives StrictMode mounting everything twice.
151
151
  */
152
152
  subscribe(e) {
153
- if (f.add(e), !z && (z = !0, A(), N !== null)) {
153
+ if (a.add(e), !z && (z = !0, A(), N !== null)) {
154
154
  const { error: n } = N;
155
155
  N = null, o.onFailure(n);
156
156
  }
157
157
  return () => {
158
- f.delete(e), f.size === 0 && Q.destroy();
158
+ a.delete(e), a.size === 0 && Q.destroy();
159
159
  };
160
160
  },
161
161
  getSnapshot: () => c,
@@ -169,9 +169,9 @@ function re(d) {
169
169
  z && e.filterBy !== n.filterBy && A(), l.x !== t.x || l.y !== t.y ? j() : (l.category !== t.category || l.byTable !== t.byTable || l.size !== t.size || l.cluster !== t.cluster || e.categories !== n.categories) && q(), g();
170
170
  },
171
171
  destroy() {
172
- z = !1, D(), f.clear();
172
+ z = !1, D(), a.clear();
173
173
  },
174
- select(e, n = "node", t = "") {
174
+ select(e, n = "external", t = "") {
175
175
  var w;
176
176
  const s = e && e.length > 0 ? { vertices: [...e], source: n, label: t } : null;
177
177
  v({ selection: s }), (w = o.onSelect) == null || w.call(o, s), o.filterBy && m && R(o.filterBy, m, s ? s.vertices : null);
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sources":["../../src/core/store.ts"],"sourcesContent":["import type { Coordinator } from \"@kanzo-tech/mosaic\";\nimport { domainOf } from \"./categories\";\nimport { bindingOf } from \"./channels\";\nimport { GraphClient, publish } from \"./client\";\nimport { GraphError } from \"./error\";\nimport { loadEncoding, loadGeometry, type Encoding, type Geometry } from \"./load\";\nimport { readStructure, type Structure } from \"./source\";\nimport type { Drawn, GraphOptions, GraphSnapshot, GraphStatus, GraphStore } from \"./state\";\n\nexport type { Drawn, GraphOptions, GraphSnapshot, GraphState, GraphStatus, GraphStore } from \"./state\";\n\nfunction drawnOf(geometry: Geometry, encoding: Encoding, mask: Uint8Array | null): Drawn {\n const tally = encoding.domain.map(() => 0);\n const shown = new Uint8Array(geometry.size);\n let vertices = 0;\n for (let id = 0; id < geometry.size; id++) {\n if ((mask && !mask[id]) || Number.isNaN(geometry.positions[id * 2])) continue;\n shown[id] = 1;\n vertices++;\n const rank = encoding.ranks[id] as number;\n tally[rank] = (tally[rank] ?? 0) + 1;\n }\n let edges = 0;\n const { links } = geometry;\n for (let i = 0; i < links.length; i += 2) {\n if (shown[links[i] as number] && shown[links[i + 1] as number]) edges++;\n }\n return { vertices, edges, domain: encoding.domain, tally };\n}\n\n/** `1` where a vertex is among `ids`. */\nfunction maskOf(size: number, ids: Float64Array): Uint8Array {\n const mask = new Uint8Array(size);\n for (const id of ids) if (id < size) mask[id] = 1;\n return mask;\n}\n\nexport function createGraph(initial: GraphOptions): GraphStore {\n let options = initial;\n const listeners = new Set<() => void>();\n\n let failed = false;\n let unrenderable = false;\n let binding = bindingOf(initial);\n let structure: Structure | null = null;\n let geometry: Geometry | null = null;\n let encoding: Encoding | null = null;\n let kept: Float64Array | null = null;\n let mask: Uint8Array | null = null;\n let uploaded: readonly unknown[] = [];\n /** One read per kind in flight; a newer one makes an older one's answer stale. */\n const reading = { structure: 0, geometry: 0, encoding: 0 };\n const pending = { structure: false, geometry: false, encoding: false };\n let client: GraphClient | null = null;\n let connected: Coordinator | null = null;\n let active = false;\n\n // A failure found while the store is being built is found while React is still rendering —\n // `useGraph` builds it in `useState` — and a host's `onFailure` is usually a `setState`. It is held\n // and reported to the first subscriber, which arrives in the commit phase.\n let building = true;\n let held: { error: unknown } | null = null;\n const fail = (error: unknown) => {\n if (building) held = { error };\n else options.onFailure(error);\n };\n\n let snapshot: GraphSnapshot = {\n status: \"none\",\n total: undefined,\n drawn: null,\n domain: [],\n selection: null,\n focus: null,\n hovered: null,\n pinned: [],\n tool: null,\n motion: \"settled\",\n progress: 1,\n options,\n structure: null,\n binding,\n geometry: null,\n encoding: null,\n mask: null,\n };\n\n let domain: { key: unknown[]; value: readonly unknown[] } = { key: [], value: [] };\n function domainNow(): readonly unknown[] {\n const tables = structure?.vertices ?? [];\n const key = [tables, binding.byTable, binding.category, options.categories];\n if (key.some((part, i) => part !== domain.key[i])) domain = { key, value: domainOf(tables, binding, options.categories) };\n return domain.value;\n }\n\n let drawn: { key: unknown[]; value: Drawn | null } = { key: [], value: null };\n function drawnNow(): Drawn | null {\n const key = [geometry, encoding, mask];\n if (key.some((part, i) => part !== drawn.key[i])) {\n drawn = { key, value: geometry && encoding ? drawnOf(geometry, encoding, mask) : null };\n }\n return drawn.value;\n }\n\n function statusOf(): GraphStatus {\n if (failed || unrenderable) return \"failed\";\n if (options.from === null || options.coordinator === null) return \"none\";\n const current = [geometry, encoding, mask];\n const shown = geometry !== null && current.every((part, i) => part === uploaded[i]);\n return pending.structure || pending.geometry || pending.encoding || !shown ? \"loading\" : \"idle\";\n }\n\n function notify(fields: Partial<GraphSnapshot> = {}): void {\n snapshot = {\n ...snapshot,\n total: structure?.size,\n drawn: drawnNow(),\n domain: domainNow(),\n options,\n structure,\n binding,\n geometry,\n encoding,\n mask,\n ...fields,\n };\n snapshot = { ...snapshot, status: statusOf() };\n emit();\n }\n\n /** A change of state only — hover, focus, selection, tool, motion — derives nothing. */\n function patch(fields: Partial<GraphSnapshot>): void {\n snapshot = { ...snapshot, ...fields };\n emit();\n }\n\n function emit(): void {\n for (const listener of listeners) listener();\n }\n\n /**\n * Read `kind`, and keep the answer only if no newer read of the same kind started meanwhile. The\n * coordinator cannot be told to stop a statement, so a stale answer is dropped rather than aborted.\n */\n function read<T>(kind: keyof typeof reading, ask: () => Promise<T>, done: (value: T) => void): void {\n const ticket = ++reading[kind];\n pending[kind] = true;\n ask().then(\n (value) => {\n if (reading[kind] !== ticket) return;\n pending[kind] = false;\n done(value);\n notify();\n },\n (error: unknown) => {\n if (reading[kind] !== ticket) return;\n pending[kind] = false;\n failed = true;\n fail(error);\n notify();\n },\n );\n }\n\n function loadStructure(): void {\n const { coordinator, from } = options;\n if (coordinator === null || from === null) return;\n read(\"structure\", () => readStructure(coordinator, from), (next) => {\n if (next.size === 0) {\n failed = true;\n fail(new GraphError(\"graph/nothing-to-draw\", `the corpus attached as ${from} has no vertex`));\n return;\n }\n structure = next;\n loadPositions();\n client?.requestQuery();\n });\n }\n\n function loadPositions(): void {\n const { coordinator } = options;\n const given = structure;\n if (coordinator === null || given === null) return;\n read(\"geometry\", () => loadGeometry(coordinator, given, binding), (next) => {\n geometry = next;\n mask = kept ? maskOf(next.size, kept) : null;\n loadChannels();\n });\n }\n\n function loadChannels(): void {\n const { coordinator } = options;\n const given = geometry;\n if (coordinator === null || given === null) return;\n const seed = domainNow();\n read(\"encoding\", () => loadEncoding(coordinator, given, binding, seed), (next) => {\n encoding = next;\n });\n }\n\n function reset(): void {\n for (const kind of Object.keys(reading) as (keyof typeof reading)[]) {\n reading[kind]++;\n pending[kind] = false;\n }\n failed = false;\n structure = geometry = encoding = null;\n kept = mask = null;\n uploaded = [];\n notify({ selection: null, focus: null, hovered: null, pinned: [] });\n loadStructure();\n }\n\n /** The graph's Mosaic client, connected to the coordinator while anything is subscribed. */\n function connect(): void {\n disconnect();\n const { coordinator, filterBy } = options;\n if (coordinator === null) return;\n client = new GraphClient(\n filterBy,\n () => structure,\n (ids) => {\n kept = ids;\n mask = ids && geometry ? maskOf(geometry.size, ids) : null;\n notify();\n },\n (error) => fail(error),\n );\n coordinator.connect(client);\n connected = coordinator;\n }\n\n function disconnect(): void {\n if (client && connected) connected.disconnect(client);\n client = null;\n connected = null;\n }\n\n const store: GraphStore = {\n /**\n * The first subscriber connects the client to the coordinator, and the last one disconnects it\n * and lets the graph go — `QueryObserver`'s `onSubscribe`/`onUnsubscribe`, which is what\n * survives StrictMode mounting everything twice.\n */\n subscribe(listener) {\n listeners.add(listener);\n if (!active) {\n active = true;\n connect();\n if (held !== null) {\n const { error } = held;\n held = null;\n options.onFailure(error);\n }\n }\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) store.destroy();\n };\n },\n getSnapshot: () => snapshot,\n getOptions: () => options,\n setOptions(next) {\n const previous = options;\n options = next;\n const before = binding;\n binding = bindingOf(next);\n if (next.from !== previous.from || next.coordinator !== previous.coordinator) {\n if (active && next.coordinator !== previous.coordinator) connect();\n return reset();\n }\n if (active && next.filterBy !== previous.filterBy) connect();\n if (binding.x !== before.x || binding.y !== before.y) loadPositions();\n else if (\n binding.category !== before.category ||\n binding.byTable !== before.byTable ||\n binding.size !== before.size ||\n binding.cluster !== before.cluster ||\n next.categories !== previous.categories\n ) {\n loadChannels();\n }\n notify();\n },\n destroy() {\n active = false;\n disconnect();\n listeners.clear();\n },\n select(vertices, source = \"node\", label = \"\") {\n const selection = vertices && vertices.length > 0 ? { vertices: [...vertices], source, label } : null;\n patch({ selection });\n options.onSelect?.(selection);\n if (options.filterBy && client) publish(options.filterBy, client, selection ? selection.vertices : null);\n },\n focus(vertex) {\n if (vertex === snapshot.focus) return;\n patch({ focus: vertex });\n options.onFocus?.(vertex);\n },\n hover(vertex) {\n if (vertex !== snapshot.hovered) patch({ hovered: vertex });\n },\n pin(vertices) {\n patch({ pinned: [...vertices] });\n },\n setTool(tool) {\n if (tool !== snapshot.tool) patch({ tool });\n },\n report(motion) {\n if (motion !== snapshot.motion) patch({ motion });\n },\n reportProgress(value) {\n if (value !== snapshot.progress) patch({ progress: value });\n },\n renderable() {\n if (!unrenderable) return;\n unrenderable = false;\n notify();\n },\n unrenderable(error) {\n unrenderable = true;\n fail(error);\n notify();\n },\n reportDrawn(drawnSnapshot) {\n const next = [drawnSnapshot.geometry, drawnSnapshot.encoding, drawnSnapshot.mask];\n if (next.every((part, i) => part === uploaded[i])) return;\n uploaded = next;\n notify();\n },\n };\n\n loadStructure();\n notify();\n building = false;\n return store;\n}\n"],"names":["drawnOf","geometry","encoding","mask","tally","shown","vertices","id","rank","edges","links","i","maskOf","size","ids","createGraph","initial","options","listeners","failed","unrenderable","binding","bindingOf","structure","kept","uploaded","reading","pending","client","connected","active","building","held","fail","error","snapshot","domain","domainNow","tables","key","part","domainOf","drawn","drawnNow","statusOf","notify","fields","emit","patch","listener","read","kind","ask","done","ticket","value","loadStructure","coordinator","from","readStructure","next","GraphError","loadPositions","given","loadGeometry","loadChannels","seed","loadEncoding","reset","connect","disconnect","filterBy","GraphClient","store","previous","before","source","label","selection","_a","publish","vertex","tool","motion","drawnSnapshot"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sources":["../../src/core/store.ts"],"sourcesContent":["import type { Coordinator } from \"@kanzo-tech/mosaic\";\nimport { domainOf } from \"./categories\";\nimport { bindingOf } from \"./channels\";\nimport { GraphClient, publish } from \"./client\";\nimport { GraphError } from \"./error\";\nimport { loadEncoding, loadGeometry, type Encoding, type Geometry } from \"./load\";\nimport { readStructure, type Structure } from \"./source\";\nimport type { Drawn, GraphOptions, GraphSnapshot, GraphStatus, GraphStore } from \"./state\";\n\nexport type { Drawn, GraphOptions, GraphSnapshot, GraphState, GraphStatus, GraphStore } from \"./state\";\n\nfunction drawnOf(geometry: Geometry, encoding: Encoding, mask: Uint8Array | null): Drawn {\n const tally = encoding.domain.map(() => 0);\n const shown = new Uint8Array(geometry.size);\n let vertices = 0;\n for (let id = 0; id < geometry.size; id++) {\n if ((mask && !mask[id]) || Number.isNaN(geometry.positions[id * 2])) continue;\n shown[id] = 1;\n vertices++;\n const rank = encoding.ranks[id] as number;\n tally[rank] = (tally[rank] ?? 0) + 1;\n }\n let edges = 0;\n const { links } = geometry;\n for (let i = 0; i < links.length; i += 2) {\n if (shown[links[i] as number] && shown[links[i + 1] as number]) edges++;\n }\n return { vertices, edges, domain: encoding.domain, tally };\n}\n\n/** `1` where a vertex is among `ids`. */\nfunction maskOf(size: number, ids: Float64Array): Uint8Array {\n const mask = new Uint8Array(size);\n for (const id of ids) if (id < size) mask[id] = 1;\n return mask;\n}\n\nexport function createGraph(initial: GraphOptions): GraphStore {\n let options = initial;\n const listeners = new Set<() => void>();\n\n let failed = false;\n let unrenderable = false;\n let binding = bindingOf(initial);\n let structure: Structure | null = null;\n let geometry: Geometry | null = null;\n let encoding: Encoding | null = null;\n let kept: Float64Array | null = null;\n let mask: Uint8Array | null = null;\n let uploaded: readonly unknown[] = [];\n /** One read per kind in flight; a newer one makes an older one's answer stale. */\n const reading = { structure: 0, geometry: 0, encoding: 0 };\n const pending = { structure: false, geometry: false, encoding: false };\n let client: GraphClient | null = null;\n let connected: Coordinator | null = null;\n let active = false;\n\n // A failure found while the store is being built is found while React is still rendering —\n // `useGraph` builds it in `useState` — and a host's `onFailure` is usually a `setState`. It is held\n // and reported to the first subscriber, which arrives in the commit phase.\n let building = true;\n let held: { error: unknown } | null = null;\n const fail = (error: unknown) => {\n if (building) held = { error };\n else options.onFailure(error);\n };\n\n let snapshot: GraphSnapshot = {\n status: \"none\",\n total: undefined,\n drawn: null,\n domain: [],\n selection: null,\n focus: null,\n hovered: null,\n pinned: [],\n tool: null,\n motion: \"settled\",\n progress: 1,\n options,\n structure: null,\n binding,\n geometry: null,\n encoding: null,\n mask: null,\n };\n\n let domain: { key: unknown[]; value: readonly unknown[] } = { key: [], value: [] };\n function domainNow(): readonly unknown[] {\n const tables = structure?.vertices ?? [];\n const key = [tables, binding.byTable, binding.category, options.categories];\n if (key.some((part, i) => part !== domain.key[i])) domain = { key, value: domainOf(tables, binding, options.categories) };\n return domain.value;\n }\n\n let drawn: { key: unknown[]; value: Drawn | null } = { key: [], value: null };\n function drawnNow(): Drawn | null {\n const key = [geometry, encoding, mask];\n if (key.some((part, i) => part !== drawn.key[i])) {\n drawn = { key, value: geometry && encoding ? drawnOf(geometry, encoding, mask) : null };\n }\n return drawn.value;\n }\n\n function statusOf(): GraphStatus {\n if (failed || unrenderable) return \"failed\";\n if (options.from === null || options.coordinator === null) return \"none\";\n const current = [geometry, encoding, mask];\n const shown = geometry !== null && current.every((part, i) => part === uploaded[i]);\n return pending.structure || pending.geometry || pending.encoding || !shown ? \"loading\" : \"idle\";\n }\n\n function notify(fields: Partial<GraphSnapshot> = {}): void {\n snapshot = {\n ...snapshot,\n total: structure?.size,\n drawn: drawnNow(),\n domain: domainNow(),\n options,\n structure,\n binding,\n geometry,\n encoding,\n mask,\n ...fields,\n };\n snapshot = { ...snapshot, status: statusOf() };\n emit();\n }\n\n /** A change of state only — hover, focus, selection, tool, motion — derives nothing. */\n function patch(fields: Partial<GraphSnapshot>): void {\n snapshot = { ...snapshot, ...fields };\n emit();\n }\n\n function emit(): void {\n for (const listener of listeners) listener();\n }\n\n /**\n * Read `kind`, and keep the answer only if no newer read of the same kind started meanwhile. The\n * coordinator cannot be told to stop a statement, so a stale answer is dropped rather than aborted.\n */\n function read<T>(kind: keyof typeof reading, ask: () => Promise<T>, done: (value: T) => void): void {\n const ticket = ++reading[kind];\n pending[kind] = true;\n ask().then(\n (value) => {\n if (reading[kind] !== ticket) return;\n pending[kind] = false;\n done(value);\n notify();\n },\n (error: unknown) => {\n if (reading[kind] !== ticket) return;\n pending[kind] = false;\n failed = true;\n fail(error);\n notify();\n },\n );\n }\n\n function loadStructure(): void {\n const { coordinator, from } = options;\n if (coordinator === null || from === null) return;\n read(\"structure\", () => readStructure(coordinator, from), (next) => {\n if (next.size === 0) {\n failed = true;\n fail(new GraphError(\"graph/nothing-to-draw\", `the corpus attached as ${from} has no vertex`));\n return;\n }\n structure = next;\n loadPositions();\n client?.requestQuery();\n });\n }\n\n function loadPositions(): void {\n const { coordinator } = options;\n const given = structure;\n if (coordinator === null || given === null) return;\n read(\"geometry\", () => loadGeometry(coordinator, given, binding), (next) => {\n geometry = next;\n mask = kept ? maskOf(next.size, kept) : null;\n loadChannels();\n });\n }\n\n function loadChannels(): void {\n const { coordinator } = options;\n const given = geometry;\n if (coordinator === null || given === null) return;\n const seed = domainNow();\n read(\"encoding\", () => loadEncoding(coordinator, given, binding, seed), (next) => {\n encoding = next;\n });\n }\n\n function reset(): void {\n for (const kind of Object.keys(reading) as (keyof typeof reading)[]) {\n reading[kind]++;\n pending[kind] = false;\n }\n failed = false;\n structure = geometry = encoding = null;\n kept = mask = null;\n uploaded = [];\n notify({ selection: null, focus: null, hovered: null, pinned: [] });\n loadStructure();\n }\n\n /** The graph's Mosaic client, connected to the coordinator while anything is subscribed. */\n function connect(): void {\n disconnect();\n const { coordinator, filterBy } = options;\n if (coordinator === null) return;\n client = new GraphClient(\n filterBy,\n () => structure,\n (ids) => {\n kept = ids;\n mask = ids && geometry ? maskOf(geometry.size, ids) : null;\n notify();\n },\n (error) => fail(error),\n );\n coordinator.connect(client);\n connected = coordinator;\n }\n\n function disconnect(): void {\n if (client && connected) connected.disconnect(client);\n client = null;\n connected = null;\n }\n\n const store: GraphStore = {\n /**\n * The first subscriber connects the client to the coordinator, and the last one disconnects it\n * and lets the graph go — `QueryObserver`'s `onSubscribe`/`onUnsubscribe`, which is what\n * survives StrictMode mounting everything twice.\n */\n subscribe(listener) {\n listeners.add(listener);\n if (!active) {\n active = true;\n connect();\n if (held !== null) {\n const { error } = held;\n held = null;\n options.onFailure(error);\n }\n }\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) store.destroy();\n };\n },\n getSnapshot: () => snapshot,\n getOptions: () => options,\n setOptions(next) {\n const previous = options;\n options = next;\n const before = binding;\n binding = bindingOf(next);\n if (next.from !== previous.from || next.coordinator !== previous.coordinator) {\n if (active && next.coordinator !== previous.coordinator) connect();\n return reset();\n }\n if (active && next.filterBy !== previous.filterBy) connect();\n if (binding.x !== before.x || binding.y !== before.y) loadPositions();\n else if (\n binding.category !== before.category ||\n binding.byTable !== before.byTable ||\n binding.size !== before.size ||\n binding.cluster !== before.cluster ||\n next.categories !== previous.categories\n ) {\n loadChannels();\n }\n notify();\n },\n destroy() {\n active = false;\n disconnect();\n listeners.clear();\n },\n select(vertices, source = \"external\", label = \"\") {\n const selection = vertices && vertices.length > 0 ? { vertices: [...vertices], source, label } : null;\n patch({ selection });\n options.onSelect?.(selection);\n if (options.filterBy && client) publish(options.filterBy, client, selection ? selection.vertices : null);\n },\n focus(vertex) {\n if (vertex === snapshot.focus) return;\n patch({ focus: vertex });\n options.onFocus?.(vertex);\n },\n hover(vertex) {\n if (vertex !== snapshot.hovered) patch({ hovered: vertex });\n },\n pin(vertices) {\n patch({ pinned: [...vertices] });\n },\n setTool(tool) {\n if (tool !== snapshot.tool) patch({ tool });\n },\n report(motion) {\n if (motion !== snapshot.motion) patch({ motion });\n },\n reportProgress(value) {\n if (value !== snapshot.progress) patch({ progress: value });\n },\n renderable() {\n if (!unrenderable) return;\n unrenderable = false;\n notify();\n },\n unrenderable(error) {\n unrenderable = true;\n fail(error);\n notify();\n },\n reportDrawn(drawnSnapshot) {\n const next = [drawnSnapshot.geometry, drawnSnapshot.encoding, drawnSnapshot.mask];\n if (next.every((part, i) => part === uploaded[i])) return;\n uploaded = next;\n notify();\n },\n };\n\n loadStructure();\n notify();\n building = false;\n return store;\n}\n"],"names":["drawnOf","geometry","encoding","mask","tally","shown","vertices","id","rank","edges","links","i","maskOf","size","ids","createGraph","initial","options","listeners","failed","unrenderable","binding","bindingOf","structure","kept","uploaded","reading","pending","client","connected","active","building","held","fail","error","snapshot","domain","domainNow","tables","key","part","domainOf","drawn","drawnNow","statusOf","notify","fields","emit","patch","listener","read","kind","ask","done","ticket","value","loadStructure","coordinator","from","readStructure","next","GraphError","loadPositions","given","loadGeometry","loadChannels","seed","loadEncoding","reset","connect","disconnect","filterBy","GraphClient","store","previous","before","source","label","selection","_a","publish","vertex","tool","motion","drawnSnapshot"],"mappings":";;;;;;AAWA,SAASA,EAAQC,GAAoBC,GAAoBC,GAAgC;AACvF,QAAMC,IAAQF,EAAS,OAAO,IAAI,MAAM,CAAC,GACnCG,IAAQ,IAAI,WAAWJ,EAAS,IAAI;AAC1C,MAAIK,IAAW;AACf,WAASC,IAAK,GAAGA,IAAKN,EAAS,MAAMM,KAAM;AACzC,QAAKJ,KAAQ,CAACA,EAAKI,CAAE,KAAM,OAAO,MAAMN,EAAS,UAAUM,IAAK,CAAC,CAAC,EAAG;AACrE,IAAAF,EAAME,CAAE,IAAI,GACZD;AACA,UAAME,IAAON,EAAS,MAAMK,CAAE;AAC9B,IAAAH,EAAMI,CAAI,KAAKJ,EAAMI,CAAI,KAAK,KAAK;AAAA,EACrC;AACA,MAAIC,IAAQ;AACZ,QAAM,EAAE,OAAAC,MAAUT;AAClB,WAASU,IAAI,GAAGA,IAAID,EAAM,QAAQC,KAAK;AACrC,IAAIN,EAAMK,EAAMC,CAAC,CAAW,KAAKN,EAAMK,EAAMC,IAAI,CAAC,CAAW,KAAGF;AAElE,SAAO,EAAE,UAAAH,GAAU,OAAAG,GAAO,QAAQP,EAAS,QAAQ,OAAAE,EAAA;AACrD;AAGA,SAASQ,EAAOC,GAAcC,GAA+B;AAC3D,QAAMX,IAAO,IAAI,WAAWU,CAAI;AAChC,aAAWN,KAAMO,EAAK,CAAIP,IAAKM,MAAMV,EAAKI,CAAE,IAAI;AAChD,SAAOJ;AACT;AAEO,SAASY,GAAYC,GAAmC;AAC7D,MAAIC,IAAUD;AACd,QAAME,wBAAgB,IAAA;AAEtB,MAAIC,IAAS,IACTC,IAAe,IACfC,IAAUC,EAAUN,CAAO,GAC3BO,IAA8B,MAC9BtB,IAA4B,MAC5BC,IAA4B,MAC5BsB,IAA4B,MAC5BrB,IAA0B,MAC1BsB,IAA+B,CAAA;AAEnC,QAAMC,IAAU,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,EAAA,GACjDC,IAAU,EAAE,WAAW,IAAO,UAAU,IAAO,UAAU,GAAA;AAC/D,MAAIC,IAA6B,MAC7BC,IAAgC,MAChCC,IAAS,IAKTC,IAAW,IACXC,IAAkC;AACtC,QAAMC,IAAO,CAACC,MAAmB;AAC/B,IAAIH,IAAUC,IAAO,EAAE,OAAAE,EAAA,IAClBjB,EAAQ,UAAUiB,CAAK;AAAA,EAC9B;AAEA,MAAIC,IAA0B;AAAA,IAC5B,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,CAAA;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAA;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAAlB;AAAA,IACA,WAAW;AAAA,IACX,SAAAI;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,EAAA,GAGJe,IAAwD,EAAE,KAAK,CAAA,GAAI,OAAO,CAAA,EAAC;AAC/E,WAASC,IAAgC;AACvC,UAAMC,KAASf,KAAA,gBAAAA,EAAW,aAAY,CAAA,GAChCgB,IAAM,CAACD,GAAQjB,EAAQ,SAASA,EAAQ,UAAUJ,EAAQ,UAAU;AAC1E,WAAIsB,EAAI,KAAK,CAACC,GAAM7B,MAAM6B,MAASJ,EAAO,IAAIzB,CAAC,CAAC,MAAGyB,IAAS,EAAE,KAAAG,GAAK,OAAOE,EAASH,GAAQjB,GAASJ,EAAQ,UAAU,EAAA,IAC/GmB,EAAO;AAAA,EAChB;AAEA,MAAIM,IAAiD,EAAE,KAAK,CAAA,GAAI,OAAO,KAAA;AACvE,WAASC,IAAyB;AAChC,UAAMJ,IAAM,CAACtC,GAAUC,GAAUC,CAAI;AACrC,WAAIoC,EAAI,KAAK,CAACC,GAAM7B,MAAM6B,MAASE,EAAM,IAAI/B,CAAC,CAAC,MAC7C+B,IAAQ,EAAE,KAAAH,GAAK,OAAOtC,KAAYC,IAAWF,EAAQC,GAAUC,GAAUC,CAAI,IAAI,KAAA,IAE5EuC,EAAM;AAAA,EACf;AAEA,WAASE,IAAwB;AAC/B,QAAIzB,KAAUC,EAAc,QAAO;AACnC,QAAIH,EAAQ,SAAS,QAAQA,EAAQ,gBAAgB,KAAM,QAAO;AAElE,UAAMZ,IAAQJ,MAAa,QADX,CAACA,GAAUC,GAAUC,CAAI,EACE,MAAM,CAACqC,GAAM7B,MAAM6B,MAASf,EAASd,CAAC,CAAC;AAClF,WAAOgB,EAAQ,aAAaA,EAAQ,YAAYA,EAAQ,YAAY,CAACtB,IAAQ,YAAY;AAAA,EAC3F;AAEA,WAASwC,EAAOC,IAAiC,IAAU;AACzD,IAAAX,IAAW;AAAA,MACT,GAAGA;AAAA,MACH,OAAOZ,KAAA,gBAAAA,EAAW;AAAA,MAClB,OAAOoB,EAAA;AAAA,MACP,QAAQN,EAAA;AAAA,MACR,SAAApB;AAAA,MACA,WAAAM;AAAA,MACA,SAAAF;AAAA,MACA,UAAApB;AAAA,MACA,UAAAC;AAAA,MACA,MAAAC;AAAA,MACA,GAAG2C;AAAA,IAAA,GAELX,IAAW,EAAE,GAAGA,GAAU,QAAQS,IAAS,GAC3CG,EAAA;AAAA,EACF;AAGA,WAASC,EAAMF,GAAsC;AACnD,IAAAX,IAAW,EAAE,GAAGA,GAAU,GAAGW,EAAA,GAC7BC,EAAA;AAAA,EACF;AAEA,WAASA,IAAa;AACpB,eAAWE,KAAY/B,EAAW,CAAA+B,EAAA;AAAA,EACpC;AAMA,WAASC,EAAQC,GAA4BC,GAAuBC,GAAgC;AAClG,UAAMC,IAAS,EAAE5B,EAAQyB,CAAI;AAC7B,IAAAxB,EAAQwB,CAAI,IAAI,IAChBC,EAAA,EAAM;AAAA,MACJ,CAACG,MAAU;AACT,QAAI7B,EAAQyB,CAAI,MAAMG,MACtB3B,EAAQwB,CAAI,IAAI,IAChBE,EAAKE,CAAK,GACVV,EAAA;AAAA,MACF;AAAA,MACA,CAACX,MAAmB;AAClB,QAAIR,EAAQyB,CAAI,MAAMG,MACtB3B,EAAQwB,CAAI,IAAI,IAChBhC,IAAS,IACTc,EAAKC,CAAK,GACVW,EAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AAEA,WAASW,IAAsB;AAC7B,UAAM,EAAE,aAAAC,GAAa,MAAAC,EAAA,IAASzC;AAC9B,IAAIwC,MAAgB,QAAQC,MAAS,QACrCR,EAAK,aAAa,MAAMS,EAAcF,GAAaC,CAAI,GAAG,CAACE,MAAS;AAClE,UAAIA,EAAK,SAAS,GAAG;AACnB,QAAAzC,IAAS,IACTc,EAAK,IAAI4B,EAAW,yBAAyB,0BAA0BH,CAAI,gBAAgB,CAAC;AAC5F;AAAA,MACF;AACA,MAAAnC,IAAYqC,GACZE,EAAA,GACAlC,KAAA,QAAAA,EAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,WAASkC,IAAsB;AAC7B,UAAM,EAAE,aAAAL,MAAgBxC,GAClB8C,IAAQxC;AACd,IAAIkC,MAAgB,QAAQM,MAAU,QACtCb,EAAK,YAAY,MAAMc,EAAaP,GAAaM,GAAO1C,CAAO,GAAG,CAACuC,MAAS;AAC1E,MAAA3D,IAAW2D,GACXzD,IAAOqB,IAAOZ,EAAOgD,EAAK,MAAMpC,CAAI,IAAI,MACxCyC,EAAA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAASA,IAAqB;AAC5B,UAAM,EAAE,aAAAR,MAAgBxC,GAClB8C,IAAQ9D;AACd,QAAIwD,MAAgB,QAAQM,MAAU,KAAM;AAC5C,UAAMG,IAAO7B,EAAA;AACb,IAAAa,EAAK,YAAY,MAAMiB,EAAaV,GAAaM,GAAO1C,GAAS6C,CAAI,GAAG,CAACN,MAAS;AAChF,MAAA1D,IAAW0D;AAAA,IACb,CAAC;AAAA,EACH;AAEA,WAASQ,IAAc;AACrB,eAAWjB,KAAQ,OAAO,KAAKzB,CAAO;AACpC,MAAAA,EAAQyB,CAAI,KACZxB,EAAQwB,CAAI,IAAI;AAElB,IAAAhC,IAAS,IACTI,IAAYtB,IAAWC,IAAW,MAClCsB,IAAOrB,IAAO,MACdsB,IAAW,CAAA,GACXoB,EAAO,EAAE,WAAW,MAAM,OAAO,MAAM,SAAS,MAAM,QAAQ,CAAA,GAAI,GAClEW,EAAA;AAAA,EACF;AAGA,WAASa,IAAgB;AACvB,IAAAC,EAAA;AACA,UAAM,EAAE,aAAAb,GAAa,UAAAc,EAAA,IAAatD;AAClC,IAAIwC,MAAgB,SACpB7B,IAAS,IAAI4C;AAAA,MACXD;AAAA,MACA,MAAMhD;AAAA,MACN,CAACT,MAAQ;AACP,QAAAU,IAAOV,GACPX,IAAOW,KAAOb,IAAWW,EAAOX,EAAS,MAAMa,CAAG,IAAI,MACtD+B,EAAA;AAAA,MACF;AAAA,MACA,CAACX,MAAUD,EAAKC,CAAK;AAAA,IAAA,GAEvBuB,EAAY,QAAQ7B,CAAM,GAC1BC,IAAY4B;AAAA,EACd;AAEA,WAASa,IAAmB;AAC1B,IAAI1C,KAAUC,KAAWA,EAAU,WAAWD,CAAM,GACpDA,IAAS,MACTC,IAAY;AAAA,EACd;AAEA,QAAM4C,IAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMxB,UAAUxB,GAAU;AAElB,UADA/B,EAAU,IAAI+B,CAAQ,GAClB,CAACnB,MACHA,IAAS,IACTuC,EAAA,GACIrC,MAAS,OAAM;AACjB,cAAM,EAAE,OAAAE,MAAUF;AAClB,QAAAA,IAAO,MACPf,EAAQ,UAAUiB,CAAK;AAAA,MACzB;AAEF,aAAO,MAAM;AACX,QAAAhB,EAAU,OAAO+B,CAAQ,GACrB/B,EAAU,SAAS,KAAGuD,EAAM,QAAA;AAAA,MAClC;AAAA,IACF;AAAA,IACA,aAAa,MAAMtC;AAAA,IACnB,YAAY,MAAMlB;AAAA,IAClB,WAAW2C,GAAM;AACf,YAAMc,IAAWzD;AACjB,MAAAA,IAAU2C;AACV,YAAMe,IAAStD;AAEf,UADAA,IAAUC,EAAUsC,CAAI,GACpBA,EAAK,SAASc,EAAS,QAAQd,EAAK,gBAAgBc,EAAS;AAC/D,eAAI5C,KAAU8B,EAAK,gBAAgBc,EAAS,eAAaL,EAAA,GAClDD,EAAA;AAET,MAAItC,KAAU8B,EAAK,aAAac,EAAS,YAAUL,EAAA,GAC/ChD,EAAQ,MAAMsD,EAAO,KAAKtD,EAAQ,MAAMsD,EAAO,IAAGb,EAAA,KAEpDzC,EAAQ,aAAasD,EAAO,YAC5BtD,EAAQ,YAAYsD,EAAO,WAC3BtD,EAAQ,SAASsD,EAAO,QACxBtD,EAAQ,YAAYsD,EAAO,WAC3Bf,EAAK,eAAec,EAAS,eAE7BT,EAAA,GAEFpB,EAAA;AAAA,IACF;AAAA,IACA,UAAU;AACR,MAAAf,IAAS,IACTwC,EAAA,GACApD,EAAU,MAAA;AAAA,IACZ;AAAA,IACA,OAAOZ,GAAUsE,IAAS,YAAYC,IAAQ,IAAI;;AAChD,YAAMC,IAAYxE,KAAYA,EAAS,SAAS,IAAI,EAAE,UAAU,CAAC,GAAGA,CAAQ,GAAG,QAAAsE,GAAQ,OAAAC,MAAU;AACjG,MAAA7B,EAAM,EAAE,WAAA8B,GAAW,IACnBC,IAAA9D,EAAQ,aAAR,QAAA8D,EAAA,KAAA9D,GAAmB6D,IACf7D,EAAQ,YAAYW,KAAQoD,EAAQ/D,EAAQ,UAAUW,GAAQkD,IAAYA,EAAU,WAAW,IAAI;AAAA,IACzG;AAAA,IACA,MAAMG,GAAQ;;AACZ,MAAIA,MAAW9C,EAAS,UACxBa,EAAM,EAAE,OAAOiC,GAAQ,IACvBF,IAAA9D,EAAQ,YAAR,QAAA8D,EAAA,KAAA9D,GAAkBgE;AAAA,IACpB;AAAA,IACA,MAAMA,GAAQ;AACZ,MAAIA,MAAW9C,EAAS,aAAe,EAAE,SAAS8C,GAAQ;AAAA,IAC5D;AAAA,IACA,IAAI3E,GAAU;AACZ,MAAA0C,EAAM,EAAE,QAAQ,CAAC,GAAG1C,CAAQ,GAAG;AAAA,IACjC;AAAA,IACA,QAAQ4E,GAAM;AACZ,MAAIA,MAAS/C,EAAS,QAAMa,EAAM,EAAE,MAAAkC,GAAM;AAAA,IAC5C;AAAA,IACA,OAAOC,GAAQ;AACb,MAAIA,MAAWhD,EAAS,UAAQa,EAAM,EAAE,QAAAmC,GAAQ;AAAA,IAClD;AAAA,IACA,eAAe5B,GAAO;AACpB,MAAIA,MAAUpB,EAAS,cAAgB,EAAE,UAAUoB,GAAO;AAAA,IAC5D;AAAA,IACA,aAAa;AACX,MAAKnC,MACLA,IAAe,IACfyB,EAAA;AAAA,IACF;AAAA,IACA,aAAaX,GAAO;AAClB,MAAAd,IAAe,IACfa,EAAKC,CAAK,GACVW,EAAA;AAAA,IACF;AAAA,IACA,YAAYuC,GAAe;AACzB,YAAMxB,IAAO,CAACwB,EAAc,UAAUA,EAAc,UAAUA,EAAc,IAAI;AAChF,MAAIxB,EAAK,MAAM,CAACpB,GAAM7B,MAAM6B,MAASf,EAASd,CAAC,CAAC,MAChDc,IAAWmC,GACXf,EAAA;AAAA,IACF;AAAA,EAAA;AAGF,SAAAW,EAAA,GACAX,EAAA,GACAd,IAAW,IACJ0C;AACT;"}
@@ -30,10 +30,12 @@ export type Tool = "rect" | "lasso" | null;
30
30
  /** `settled` converged on its own; `paused` is waiting for you. */
31
31
  export type Motion = "running" | "settled" | "paused";
32
32
  /**
33
- * Where a selection came from. Open-ended on purpose: a host with its own panels adds its own
34
- * sources, and the canvas only ever uses this to label the chip in the corner.
33
+ * Where a selection came from: one of the canvas's own gestures, or `"external"` — a selection the
34
+ * host set through `select`, from a panel of its own. An external selection's identity is its
35
+ * `label`, which is how `GraphSelect` knows the live selection is the one it made. The graph names
36
+ * no host's panels here: a source per panel would be a host's vocabulary in the renderer's type.
35
37
  */
36
- export type SelectionSource = "marquee" | "lasso" | "node" | "order" | "ask";
38
+ export type SelectionSource = "marquee" | "lasso" | "node" | "external";
37
39
  /**
38
40
  * The selection — one value, published once.
39
41
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAE3C,mEAAmE;AACnE,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtD;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,GAAG,IAAI,IAAI,CAAC;IACZ,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,qGAAqG;IACrG,KAAK,IAAI,IAAI,CAAC;IACd,oCAAoC;IACpC,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/B,wDAAwD;IACxD,cAAc,IAAI,IAAI,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;CACf"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAE3C,mEAAmE;AACnE,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAExE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,GAAG,IAAI,IAAI,CAAC;IACZ,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,qGAAqG;IACrG,KAAK,IAAI,IAAI,CAAC;IACd,oCAAoC;IACpC,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/B,wDAAwD;IACxD,cAAc,IAAI,IAAI,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;CACf"}
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * `useGraphState`, TanStack Store's selector, because the api is commands and the state moves at
14
14
  * frame rate.
15
15
  * - `parts/` — `GraphCanvas`, `GraphLegend`, `GraphCounts`, `GraphToolbar`, `GraphInspector`,
16
- * `GraphSearch`: flat parts over the one context; and `GraphLooks`, the picture's preferences,
16
+ * `GraphSearch`, `GraphSelect`: flat parts over the one context; and `GraphLooks`, the picture's preferences,
17
17
  * which needs only the theme provider. They own the vocabulary; the root's callbacks are the host's policy.
18
18
  *
19
19
  * **Why a package and not `@kanzo-tech/ui`.** The first admission rule is *domain-free — nothing
@@ -30,6 +30,7 @@ export { GraphCounts, type GraphCountsProps } from './parts/graph-counts';
30
30
  export { GraphToolbar, type GraphToolbarProps } from './parts/graph-toolbar';
31
31
  export { GraphInspector, type GraphInspectorProps } from './parts/graph-inspector';
32
32
  export { GraphSearch, type GraphSearchProps } from './parts/graph-search';
33
+ export { GraphSelect, type GraphSelectProps } from './parts/graph-select';
33
34
  export { GraphLooks, type GraphLooksProps } from './parts/graph-looks';
34
35
  export { ShapeGlyph, type ShapeGlyphProps } from './parts/shape-glyph';
35
36
  export { lookFrom, type Look, type LookPatch, type LookPreset, type Shape } from './render/graph-looks';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAIvE,OAAO,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACxG,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACtG,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,sBAAsB,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAIvE,OAAO,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACxG,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACtG,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -2,33 +2,35 @@ import { GraphRoot as p, GraphRootProvider as e, useGraphContext as t } from "./
2
2
  import { useGraph as m } from "./react/use-graph.js";
3
3
  import { useGraphState as h } from "./react/use-graph-state.js";
4
4
  import { useGraphPrefs as G } from "./react/use-graph-prefs.js";
5
- import { GraphCanvas as n } from "./parts/graph-canvas.js";
6
- import { GraphLegend as l } from "./parts/graph-legend.js";
7
- import { GraphCounts as C } from "./parts/graph-counts.js";
5
+ import { GraphCanvas as l } from "./parts/graph-canvas.js";
6
+ import { GraphLegend as u } from "./parts/graph-legend.js";
7
+ import { GraphCounts as S } from "./parts/graph-counts.js";
8
8
  import { GraphToolbar as d } from "./parts/graph-toolbar.js";
9
9
  import { GraphInspector as k } from "./parts/graph-inspector.js";
10
10
  import { GraphSearch as F } from "./parts/graph-search.js";
11
- import { GraphLooks as P } from "./parts/graph-looks.js";
12
- import { ShapeGlyph as b } from "./parts/shape-glyph.js";
13
- import { lookFrom as y } from "./render/graph-looks.js";
14
- import { simFrom as I } from "./render/graph-sim.js";
15
- import { scaleOf as T } from "./render/graph-model.js";
16
- import { GraphError as q } from "./core/error.js";
11
+ import { GraphSelect as P } from "./parts/graph-select.js";
12
+ import { GraphLooks as b } from "./parts/graph-looks.js";
13
+ import { ShapeGlyph as y } from "./parts/shape-glyph.js";
14
+ import { lookFrom as I } from "./render/graph-looks.js";
15
+ import { simFrom as T } from "./render/graph-sim.js";
16
+ import { scaleOf as q } from "./render/graph-model.js";
17
+ import { GraphError as z } from "./core/error.js";
17
18
  export {
18
- n as GraphCanvas,
19
- C as GraphCounts,
20
- q as GraphError,
19
+ l as GraphCanvas,
20
+ S as GraphCounts,
21
+ z as GraphError,
21
22
  k as GraphInspector,
22
- l as GraphLegend,
23
- P as GraphLooks,
23
+ u as GraphLegend,
24
+ b as GraphLooks,
24
25
  p as GraphRoot,
25
26
  e as GraphRootProvider,
26
27
  F as GraphSearch,
28
+ P as GraphSelect,
27
29
  d as GraphToolbar,
28
- b as ShapeGlyph,
29
- y as lookFrom,
30
- T as scaleOf,
31
- I as simFrom,
30
+ y as ShapeGlyph,
31
+ I as lookFrom,
32
+ q as scaleOf,
33
+ T as simFrom,
32
34
  m as useGraph,
33
35
  t as useGraphContext,
34
36
  G as useGraphPrefs,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,27 @@
1
+ import { VertexId } from '../core/types';
2
+ export interface GraphSelectProps extends Omit<React.ComponentProps<"button">, "onClick" | "type"> {
3
+ /**
4
+ * The vertices this control selects — `dense_id`s, as SQL returns them. Called when the reader
5
+ * presses it and not before, so a panel of fifty findings reads nothing until one is asked for.
6
+ */
7
+ load: () => Promise<readonly VertexId[]>;
8
+ /** What the corner calls the selection, and its identity: keep it distinct among a panel's controls. */
9
+ label: string;
10
+ }
11
+ /**
12
+ * **A selection the host offers, as a toggle** — *these vertices, on the canvas*. Pressing it calls
13
+ * `load` and selects what it answers as an `"external"` selection named `label`; pressing it again
14
+ * clears it. Whatever the host renders inside is the clickable body: a rule and its count, an answer.
15
+ *
16
+ * The pressed state is **derived** from the live selection — `source === "external"` and the same
17
+ * `label` — rather than stored beside it. A stored flag goes stale the moment the reader lassoes
18
+ * something else; a selection cannot disagree with itself. It is disabled while `load` runs.
19
+ *
20
+ * **A rejected `load` reaches the root's `onFailure`, as thrown** — as `GraphSearch`'s failed read
21
+ * does — and the control stays unpressed, ready to be pressed again. It takes no handler of its own:
22
+ * what a failure shows is the host's policy, and the root is where that lives.
23
+ *
24
+ * ARIA: a `button` with `aria-pressed`, the toggle-button pattern; its name is its content.
25
+ */
26
+ export declare function GraphSelect({ children, className, disabled, label, load, slot, ...rest }: GraphSelectProps): import("react").JSX.Element;
27
+ //# sourceMappingURL=graph-select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-select.d.ts","sourceRoot":"","sources":["../../src/parts/graph-select.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAChG;;;OAGG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;IACzC,wGAAwG;IACxG,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,+BA4C1G"}
@@ -0,0 +1,53 @@
1
+ "use client";
2
+ import { jsx as b } from "react/jsx-runtime";
3
+ import { useState as f } from "react";
4
+ import { cn as y } from "@kanzo-tech/ui";
5
+ import { useGraphContext as g } from "../react/graph-root.js";
6
+ import { useGraphState as h } from "../react/use-graph-state.js";
7
+ function C({ children: n, className: l, disabled: i, label: o, load: c, slot: d, ...p }) {
8
+ const t = g(), r = h((e) => {
9
+ var s;
10
+ return ((s = e.selection) == null ? void 0 : s.source) === "external" && e.selection.label === o;
11
+ }), [u, a] = f(!1), m = async () => {
12
+ if (r) {
13
+ t.select(null);
14
+ return;
15
+ }
16
+ a(!0);
17
+ try {
18
+ t.select(await c(), "external", o);
19
+ } catch (e) {
20
+ t.getState().options.onFailure(e);
21
+ } finally {
22
+ a(!1);
23
+ }
24
+ };
25
+ return /* @__PURE__ */ b(
26
+ "button",
27
+ {
28
+ "aria-pressed": r,
29
+ ...p,
30
+ className: y(
31
+ "w-full rounded-md border p-2 text-start transition-colors",
32
+ "disabled:cursor-default disabled:opacity-60",
33
+ // Normal / hover / pressed is the ramp's own 3→4→5 progression, written with its names.
34
+ // Over the card, `bg-accent/50` and `bg-accent/60` composite to ΔE 1.21 (light) / 0.86
35
+ // (dark) of each other — under the ΔE 2 the ramp makes a hover owe, so the fill was not
36
+ // carrying the pressed state at all; `--secondary` → `--accent` measures 3.96 / 4.61.
37
+ // `border-primary/50` measures 1.73–2.32:1 for a hued seed, the failure `stroke-primary/50`
38
+ // had on the canvas; solid measures 3.07–6.07 (measured 2026-09, in the workspace showcase).
39
+ r ? "border-primary bg-accent" : "hover:bg-secondary disabled:hover:bg-transparent",
40
+ l
41
+ ),
42
+ "data-slot": d ?? "graph-select",
43
+ disabled: i || u,
44
+ onClick: () => void m(),
45
+ type: "button",
46
+ children: n
47
+ }
48
+ );
49
+ }
50
+ export {
51
+ C as GraphSelect
52
+ };
53
+ //# sourceMappingURL=graph-select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-select.js","sources":["../../src/parts/graph-select.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport { cn } from \"@kanzo-tech/ui\";\nimport type { VertexId } from \"../core/types\";\nimport { useGraphContext } from \"../react/graph-root\";\nimport { useGraphState } from \"../react/use-graph-state\";\n\nexport interface GraphSelectProps extends Omit<React.ComponentProps<\"button\">, \"onClick\" | \"type\"> {\n /**\n * The vertices this control selects — `dense_id`s, as SQL returns them. Called when the reader\n * presses it and not before, so a panel of fifty findings reads nothing until one is asked for.\n */\n load: () => Promise<readonly VertexId[]>;\n /** What the corner calls the selection, and its identity: keep it distinct among a panel's controls. */\n label: string;\n}\n\n/**\n * **A selection the host offers, as a toggle** — *these vertices, on the canvas*. Pressing it calls\n * `load` and selects what it answers as an `\"external\"` selection named `label`; pressing it again\n * clears it. Whatever the host renders inside is the clickable body: a rule and its count, an answer.\n *\n * The pressed state is **derived** from the live selection — `source === \"external\"` and the same\n * `label` — rather than stored beside it. A stored flag goes stale the moment the reader lassoes\n * something else; a selection cannot disagree with itself. It is disabled while `load` runs.\n *\n * **A rejected `load` reaches the root's `onFailure`, as thrown** — as `GraphSearch`'s failed read\n * does — and the control stays unpressed, ready to be pressed again. It takes no handler of its own:\n * what a failure shows is the host's policy, and the root is where that lives.\n *\n * ARIA: a `button` with `aria-pressed`, the toggle-button pattern; its name is its content.\n */\nexport function GraphSelect({ children, className, disabled, label, load, slot, ...rest }: GraphSelectProps) {\n const api = useGraphContext();\n const pressed = useGraphState((s) => s.selection?.source === \"external\" && s.selection.label === label);\n const [busy, setBusy] = useState(false);\n\n const toggle = async () => {\n if (pressed) {\n api.select(null);\n return;\n }\n setBusy(true);\n try {\n api.select(await load(), \"external\", label);\n } catch (error) {\n api.getState().options.onFailure(error);\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <button\n aria-pressed={pressed}\n {...rest}\n className={cn(\n \"w-full rounded-md border p-2 text-start transition-colors\",\n \"disabled:cursor-default disabled:opacity-60\",\n // Normal / hover / pressed is the ramp's own 3→4→5 progression, written with its names.\n // Over the card, `bg-accent/50` and `bg-accent/60` composite to ΔE 1.21 (light) / 0.86\n // (dark) of each other — under the ΔE 2 the ramp makes a hover owe, so the fill was not\n // carrying the pressed state at all; `--secondary` → `--accent` measures 3.96 / 4.61.\n // `border-primary/50` measures 1.73–2.32:1 for a hued seed, the failure `stroke-primary/50`\n // had on the canvas; solid measures 3.07–6.07 (measured 2026-09, in the workspace showcase).\n pressed ? \"border-primary bg-accent\" : \"hover:bg-secondary disabled:hover:bg-transparent\",\n className,\n )}\n data-slot={slot ?? \"graph-select\"}\n disabled={disabled || busy}\n onClick={() => void toggle()}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n"],"names":[],"mappings":";;;;;;AAiCO;AACL;;AACqC;AAA4D;AAI/F;AACE;AACA;AAAA;AAEF;AACA;AACE;AAA0C;AAE1C;AAAsC;AAEtC;AAAa;AACf;AAGF;AACE;AAAC;AAAA;AACe;AACV;AACO;AACT;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOuC;AACvC;AAAA;AAEiB;AACG;AACF;AACf;AAEJ;AAAA;AAGP;;;;"}
@@ -1,4 +1,5 @@
1
1
  import { Graph } from '@cosmos.gl/graph';
2
+ import { GraphError } from '../core/error';
2
3
  export declare const FIT_DURATION = 420;
3
4
  export interface Camera {
4
5
  /** A corpus was placed: frame these corners now, and again whenever the canvas changes size. */
@@ -7,6 +8,10 @@ export interface Camera {
7
8
  fit(): void;
8
9
  /** A layout started moving the points: the corners are history, and the camera follows. */
9
10
  run(): void;
11
+ /** cosmos.gl's device came up: the frame is taken again, at the canvas's size. */
12
+ ready(): void;
13
+ /** cosmos.gl was handed positions and rendered them: from now on a fit may read them back. */
14
+ drawn(): void;
10
15
  tick(): void;
11
16
  settle(): void;
12
17
  destroy(): void;
@@ -18,8 +23,14 @@ export interface Camera {
18
23
  * zoomed into a corner — and while a layout runs it follows the moving points and frames them when
19
24
  * they settle: Cosmograph's fit-on-settle, kept up. All of it stops the moment the reader zooms or
20
25
  * pans, and comes back with Fit or the next run.
26
+ *
27
+ * **A fit without corners reads the points back from the GPU** (`fitView` → `getPointPositions` →
28
+ * `readPixels` of cosmos.gl's position framebuffer), so it waits until there is one to read: the
29
+ * device is up and a render has uploaded positions. If the read throws anyway, cosmos.gl holds no
30
+ * positions: that is `graph/no-positions`, reported once through `fail`, and the camera reads no more
31
+ * — rather than the same exception on every tick of a layout over a blank canvas.
21
32
  */
22
- export declare function createCamera(graph: Graph, host: HTMLElement): {
33
+ export declare function createCamera(graph: Graph, host: HTMLElement, fail: (error: GraphError) => void): {
23
34
  camera: Camera;
24
35
  taken: (userDriven: boolean) => void;
25
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../src/render/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,eAAO,MAAM,YAAY,MAAM,CAAC;AAMhC,MAAM,WAAW,MAAM;IACrB,gGAAgG;IAChG,KAAK,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,2FAA2F;IAC3F,GAAG,IAAI,IAAI,CAAC;IACZ,2FAA2F;IAC3F,GAAG,IAAI,IAAI,CAAC;IACZ,IAAI,IAAI,IAAI,CAAC;IACb,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,CA6DtH"}
1
+ {"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../src/render/camera.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,eAAO,MAAM,YAAY,MAAM,CAAC;AAMhC,MAAM,WAAW,MAAM;IACrB,gGAAgG;IAChG,KAAK,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,2FAA2F;IAC3F,GAAG,IAAI,IAAI,CAAC;IACZ,2FAA2F;IAC3F,GAAG,IAAI,IAAI,CAAC;IACZ,kFAAkF;IAClF,KAAK,IAAI,IAAI,CAAC;IACd,8FAA8F;IAC9F,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAChC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,CA6E1D"}