@nodegx/mcp 0.2.2 → 0.2.4

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.
@@ -14407,7 +14407,7 @@ var require_common2 = __commonJS({
14407
14407
  exports2.setopts = setopts;
14408
14408
  exports2.ownProp = ownProp;
14409
14409
  exports2.makeAbs = makeAbs;
14410
- exports2.finish = finish2;
14410
+ exports2.finish = finish3;
14411
14411
  exports2.mark = mark;
14412
14412
  exports2.isIgnored = isIgnored;
14413
14413
  exports2.childrenIgnored = childrenIgnored;
@@ -14497,7 +14497,7 @@ var require_common2 = __commonJS({
14497
14497
  self2.minimatch = new Minimatch(pattern, options);
14498
14498
  self2.options = self2.minimatch.options;
14499
14499
  }
14500
- function finish2(self2) {
14500
+ function finish3(self2) {
14501
14501
  var nou = self2.nounique;
14502
14502
  var all = nou ? [] : /* @__PURE__ */ Object.create(null);
14503
14503
  for (var i = 0, l = self2.matches.length; i < l; i++) {
@@ -55335,6 +55335,18 @@ var init_node_catalog = __esm({
55335
55335
  isSignal: false,
55336
55336
  description: "Object path inside the bucket, such as avatars/me.png. Supabase only. Leave blank to use the file's own name"
55337
55337
  },
55338
+ {
55339
+ name: "photoQuality",
55340
+ displayName: "Photo Quality",
55341
+ group: "Shrink Photos",
55342
+ plug: "input",
55343
+ type: {
55344
+ name: "number"
55345
+ },
55346
+ isSignal: false,
55347
+ default: 80,
55348
+ description: "How much detail a shrunk JPEG or WebP keeps, from 1 to 100; PNG keeps every pixel and its transparency, GIF and SVG are never shrunk"
55349
+ },
55338
55350
  {
55339
55351
  name: "private",
55340
55352
  displayName: "Private",
@@ -55347,6 +55359,17 @@ var init_node_catalog = __esm({
55347
55359
  default: false,
55348
55360
  description: "Restricts the stored file to whoever uploaded it, so reading it later needs a Sign File URL node"
55349
55361
  },
55362
+ {
55363
+ name: "readers",
55364
+ displayName: "Readers",
55365
+ group: "General",
55366
+ plug: "input",
55367
+ type: {
55368
+ name: "string"
55369
+ },
55370
+ isSignal: false,
55371
+ description: "Roles that may read the file as well as whoever uploaded it, such as role:member, separated by commas; setting it makes the file private. NodeGX backends only"
55372
+ },
55350
55373
  {
55351
55374
  name: "recordId",
55352
55375
  displayName: "Record ID (PocketBase)",
@@ -55358,6 +55381,18 @@ var init_node_catalog = __esm({
55358
55381
  isSignal: false,
55359
55382
  description: "Existing record to attach the file to. PocketBase only. Leave blank to create a new record as part of the upload"
55360
55383
  },
55384
+ {
55385
+ name: "shrinkTo",
55386
+ displayName: "Shrink To",
55387
+ group: "Shrink Photos",
55388
+ plug: "input",
55389
+ type: {
55390
+ name: "number"
55391
+ },
55392
+ isSignal: false,
55393
+ default: 0,
55394
+ description: "Makes a photo smaller in the browser before sending it, so its longer side is at most this many pixels; 0 sends every file as picked"
55395
+ },
55361
55396
  {
55362
55397
  name: "upload",
55363
55398
  displayName: "Upload",
@@ -55500,6 +55535,15 @@ var init_node_catalog = __esm({
55500
55535
  private: [
55501
55536
  "always: held as handed; read when the next Upload goes"
55502
55537
  ],
55538
+ readers: [
55539
+ "always: held as handed; read when the next Upload goes \u2014 set, those roles may read the file as well as whoever uploaded it, and the file is private"
55540
+ ],
55541
+ shrinkTo: [
55542
+ "always: held as handed; read when the next Upload goes \u2014 above 0, a photo (not a GIF or an SVG) is drawn smaller in the browser first, its longer side at most this many pixels, and the smaller file is what goes; it goes as picked when it is already that small, when the smaller one would weigh more, or when the browser cannot read it"
55543
+ ],
55544
+ photoQuality: [
55545
+ "always: held as handed; read when the next Upload goes, and only when a photo is shrunk \u2014 1 to 100 for a JPEG or WebP, anything else is 80; a PNG keeps every pixel"
55546
+ ],
55503
55547
  bucket: [
55504
55548
  "always: held as handed; read when the next Upload goes \u2014 set, the file goes to this bucket at Path (or under the file's own name)"
55505
55549
  ],
@@ -55516,7 +55560,7 @@ var init_node_catalog = __esm({
55516
55560
  "always: held as handed; read when the next Upload goes, and only with Collection \u2014 a half-filled group is no location at all"
55517
55561
  ],
55518
55562
  upload: [
55519
- "always: its own upload at the frame's end \u2014 two presses are two uploads \u2014 with File, Private and the File Location held then: no File, or a Backend the project does not have, is Error, Error Status Code 0 and Failure at once; otherwise the backend answers with Cloud File then Done, or Error, Error Status Code and Failure"
55563
+ "always: its own upload at the frame's end \u2014 two presses are two uploads \u2014 with File, Private, Readers and the File Location held then: no File, or a Backend the project does not have, is Error, Error Status Code 0 and Failure at once; otherwise the backend answers with Cloud File then Done, or Error, Error Status Code and Failure"
55520
55564
  ]
55521
55565
  }
55522
55566
  },
@@ -89210,8 +89254,8 @@ var init_nodegx = __esm({
89210
89254
  // The header works. What it cannot do is make a file private on behalf of
89211
89255
  // nobody, and an author reading "supported" would not expect that.
89212
89256
  "files.private": degraded(
89213
- "A private upload is locked to the person who made it, so a file uploaded by anything other than a signed-in user \u2014 an admin tool, a cloud function \u2014 ends up private with no owner, and stays readable by everyone.",
89214
- "BCN-007 live, security enforced: uploaded as a signed-in user, the plain URL is 403 for anonymous AND for a second signed-in user. Uploaded with the master key, the identical request with X-NodeGX-File-Private: true serves 200 to anonymous \u2014 files.ts::upload sets owner=null for a non-user principal"
89257
+ "A private upload is locked to the person who made it, so a file uploaded by anything other than a signed-in user \u2014 an admin tool, a cloud function \u2014 ends up private with no owner, and stays readable by everyone. Upload File's Readers names roles that may read it too, such as role:member; a file with Readers is never readable by everyone.",
89258
+ "BCN-007 live, security enforced: uploaded as a signed-in user, the plain URL is 403 for anonymous AND for a second signed-in user. Uploaded with the master key, the identical request with X-NodeGX-File-Private: true serves 200 to anonymous \u2014 files.ts::upload sets owner=null for a non-user principal. P121-TWO001-MEMBERSPHOTO: X-NodeGX-File-Readers adds read-only role entries to the file's ACL; uploaded with the master key and role:crew, anonymous and a user without the role are 403 (files-http.test.ts)"
89215
89259
  ),
89216
89260
  "files.progress": supported("XHR upload path reports progress"),
89217
89261
  "auth.password": supported("nodegx-backend/src/server/users.ts"),
@@ -102712,6 +102756,13 @@ var init_store = __esm({
102712
102756
  }
102713
102757
  });
102714
102758
 
102759
+ // ../nodegx-project-contract/format/run-on-change-pins.ts
102760
+ var init_run_on_change_pins = __esm({
102761
+ "../nodegx-project-contract/format/run-on-change-pins.ts"() {
102762
+ "use strict";
102763
+ }
102764
+ });
102765
+
102715
102766
  // ../nodegx-project-contract/format.ts
102716
102767
  var init_format = __esm({
102717
102768
  "../nodegx-project-contract/format.ts"() {
@@ -102720,6 +102771,7 @@ var init_format = __esm({
102720
102771
  init_validator();
102721
102772
  init_files();
102722
102773
  init_store();
102774
+ init_run_on_change_pins();
102723
102775
  }
102724
102776
  });
102725
102777
 
@@ -111739,13 +111791,13 @@ function readMessagesAnswer(payload) {
111739
111791
  function readChatAnswer(payload) {
111740
111792
  const choice = payload && Array.isArray(payload.choices) ? payload.choices[0] : void 0;
111741
111793
  if (!choice) return void 0;
111742
- const finish2 = choice.finish_reason ? String(choice.finish_reason) : "";
111743
- const declined = choice.message && typeof choice.message.refusal === "string" && choice.message.refusal.length > 0 || finish2 === "content_filter";
111794
+ const finish3 = choice.finish_reason ? String(choice.finish_reason) : "";
111795
+ const declined = choice.message && typeof choice.message.refusal === "string" && choice.message.refusal.length > 0 || finish3 === "content_filter";
111744
111796
  return {
111745
111797
  usage: chatUsageOf(payload.usage),
111746
111798
  text: chatTextOf(choice.message),
111747
- stopReason: declined ? "refusal" : CHAT_STOP_REASONS[finish2] || finish2,
111748
- refusal: declined ? `Model Request: the model declined this request (${finish2 === "content_filter" ? "content_filter" : "refusal"})` : void 0
111799
+ stopReason: declined ? "refusal" : CHAT_STOP_REASONS[finish3] || finish3,
111800
+ refusal: declined ? `Model Request: the model declined this request (${finish3 === "content_filter" ? "content_filter" : "refusal"})` : void 0
111749
111801
  };
111750
111802
  }
111751
111803
  function errorIdentifiers(raw) {
@@ -133511,13 +133563,25 @@ var init_upload_file = __esm({
133511
133563
  // Error always a sentence; v1 read `code || status || 0` and put a message-less envelope on Error.
133512
133564
  // v3 (P109 ISL-030, row C52 ruled "Keep the real name"): the Cloud File keeps the name it was uploaded as (the backend's
133513
133565
  // `filename`, else the sent file's name); v2's dropped `filename` and knew only the stored name.
133514
- version: 3,
133566
+ // v4 (P121 TWO-001 / TWO-008): three inputs, Readers (roles that may read a private file), Shrink To and Photo Quality.
133567
+ version: 4,
133568
+ changes: [
133569
+ {
133570
+ version: 4,
133571
+ says: "Three new inputs: Readers names the roles that may read a private file as well as whoever uploaded it, and Shrink To and Photo Quality make a photo smaller in the browser before it is sent. Left empty, each sends the file as v3 did",
133572
+ row: "P121-TWO001-MEMBERSPHOTO, P121-TWO008-UPLOADSHRINK",
133573
+ migration: { none: "Each new input is empty in an app built on v3, and an empty one changes nothing: the upload is the same request" }
133574
+ }
133575
+ ],
133515
133576
  source: "packages/noodl-viewer-react/src/nodes/std-library/uploadfile.ts; noodl-runtime api/cloudstore.js, api/cloudfile.ts",
133516
133577
  needs: ["backend"],
133517
133578
  worldPool: { backends: [...pool2("uploadFile", FILE_REFS[0]), ...pool2("uploadFile", FILE_REFS[1]).slice(0, 1), ...pool2("uploadFile", FILE_REFS[2]).slice(2, 3)] },
133518
133579
  state: {
133519
133580
  file: void 0,
133520
133581
  private: void 0,
133582
+ readers: void 0,
133583
+ shrinkTo: void 0,
133584
+ photoQuality: void 0,
133521
133585
  bucket: void 0,
133522
133586
  path: void 0,
133523
133587
  collection: void 0,
@@ -133549,6 +133613,35 @@ var init_upload_file = __esm({
133549
133613
  description: "Restricts the stored file to whoever uploaded it, so reading it later needs a Sign File URL node",
133550
133614
  examples: [true, false]
133551
133615
  },
133616
+ // P121-TWO001-MEMBERSPHOTO — handed to the store with Private; the backend reads it (files.ts)
133617
+ readers: {
133618
+ type: "string",
133619
+ coerce: "none",
133620
+ displayName: "Readers",
133621
+ group: "General",
133622
+ description: "Roles that may read the file as well as whoever uploaded it, such as role:member, separated by commas; setting it makes the file private. NodeGX backends only",
133623
+ examples: ["role:member, role:admin", ""]
133624
+ },
133625
+ // P121-TWO008-UPLOADSHRINK — the Shrink Photos group. The canvas step (shrinkphoto.ts) is the browser's: this world
133626
+ // has no decoder, so a photo goes as it was picked, which is what the runtime does when the browser cannot read it.
133627
+ shrinkTo: {
133628
+ type: "number",
133629
+ coerce: "none",
133630
+ default: 0,
133631
+ displayName: "Shrink To",
133632
+ group: "Shrink Photos",
133633
+ description: "Makes a photo smaller in the browser before sending it, so its longer side is at most this many pixels; 0 sends every file as picked",
133634
+ examples: [0, 1600]
133635
+ },
133636
+ photoQuality: {
133637
+ type: "number",
133638
+ coerce: "none",
133639
+ default: 80,
133640
+ displayName: "Photo Quality",
133641
+ group: "Shrink Photos",
133642
+ description: "How much detail a shrunk JPEG or WebP keeps, from 1 to 100; PNG keeps every pixel and its transparency, GIF and SVG are never shrunk",
133643
+ examples: [80, 50]
133644
+ },
133552
133645
  // :111-158
133553
133646
  bucket: {
133554
133647
  ...stringInput("Bucket (Supabase)", "Supabase Storage bucket to upload into. Supabase only; there is no default, and a bucket that does not exist fails with an error that does not say so", ["avatars", ""]),
@@ -133588,6 +133681,21 @@ var init_upload_file = __esm({
133588
133681
  {
133589
133682
  file: (_s, v) => ({ says: "always: held as handed; read when the next Upload goes \u2014 an upload already running keeps the file it was sent with", set: { file: v }, send: [] }),
133590
133683
  private: (_s, v) => ({ says: "always: held as handed; read when the next Upload goes", set: { private: v }, send: [] }),
133684
+ readers: (_s, v) => ({
133685
+ says: "always: held as handed; read when the next Upload goes \u2014 set, those roles may read the file as well as whoever uploaded it, and the file is private",
133686
+ set: { readers: v },
133687
+ send: []
133688
+ }),
133689
+ shrinkTo: (_s, v) => ({
133690
+ says: "always: held as handed; read when the next Upload goes \u2014 above 0, a photo (not a GIF or an SVG) is drawn smaller in the browser first, its longer side at most this many pixels, and the smaller file is what goes; it goes as picked when it is already that small, when the smaller one would weigh more, or when the browser cannot read it",
133691
+ set: { shrinkTo: v },
133692
+ send: []
133693
+ }),
133694
+ photoQuality: (_s, v) => ({
133695
+ says: "always: held as handed; read when the next Upload goes, and only when a photo is shrunk \u2014 1 to 100 for a JPEG or WebP, anything else is 80; a PNG keeps every pixel",
133696
+ set: { photoQuality: v },
133697
+ send: []
133698
+ }),
133591
133699
  bucket: (_s, v) => ({ says: "always: held as handed; read when the next Upload goes \u2014 set, the file goes to this bucket at Path (or under the file's own name)", set: { bucket: v }, send: [] }),
133592
133700
  path: (_s, v) => ({ says: "always: held as handed; read when the next Upload goes, and only with a Bucket", set: { path: v }, send: [] }),
133593
133701
  collection: (_s, v) => ({ says: "always: held as handed; read when the next Upload goes \u2014 with Field set and no Bucket, the file attaches to a record of this collection", set: { collection: v }, send: [] }),
@@ -133595,7 +133703,7 @@ var init_upload_file = __esm({
133595
133703
  field: (_s, v) => ({ says: "always: held as handed; read when the next Upload goes, and only with Collection \u2014 a half-filled group is no location at all", set: { field: v }, send: [] }),
133596
133704
  // :170-172 — a token now, the work at the frame end
133597
133705
  upload: (s) => ({
133598
- says: "always: its own upload at the frame's end \u2014 two presses are two uploads \u2014 with File, Private and the File Location held then: no File, or a Backend the project does not have, is Error, Error Status Code 0 and Failure at once; otherwise the backend answers with Cloud File then Done, or Error, Error Status Code and Failure",
133706
+ says: "always: its own upload at the frame's end \u2014 two presses are two uploads \u2014 with File, Private, Readers and the File Location held then: no File, or a Backend the project does not have, is Error, Error Status Code 0 and Failure at once; otherwise the backend answers with Cloud File then Done, or Error, Error Status Code and Failure",
133599
133707
  set: { presses: s.presses + 1 },
133600
133708
  send: [],
133601
133709
  outcome: "pending"
@@ -133612,7 +133720,7 @@ var init_upload_file = __esm({
133612
133720
  },
133613
133721
  // :172-216
133614
133722
  afterInputs: (s, _i, w) => {
133615
- const r = frame(s, w, !!s.file, "uploadFile", () => ({ file: s.file, private: s.private, target: fileTarget(s) }), UPLOAD_ERROR_CODE, "upload");
133723
+ const r = frame(s, w, !!s.file, "uploadFile", () => ({ file: s.file, private: s.private, readers: s.readers, target: fileTarget(s) }), UPLOAD_ERROR_CODE, "upload");
133616
133724
  if (!("backend" in r) || !r.backend) return r;
133617
133725
  const f = s.file;
133618
133726
  const sent2 = f && typeof f.name === "string" ? f.name : void 0;
@@ -138626,16 +138734,743 @@ var init_runner = __esm({
138626
138734
  });
138627
138735
 
138628
138736
  // ../nodegx-node-spec/src/layout.ts
138737
+ function edges(e) {
138738
+ return { ...ZERO, ...e ?? {} };
138739
+ }
138740
+ function resolveLength(length, reference) {
138741
+ if (length.percent !== void 0 && length.percent !== 0) {
138742
+ if (reference === void 0) return void 0;
138743
+ return length.percent / 100 * reference + (length.px ?? 0);
138744
+ }
138745
+ return length.px ?? 0;
138746
+ }
138747
+ function clamp3(value, min2, max2) {
138748
+ let v = value;
138749
+ if (max2 !== void 0 && v > max2) v = max2;
138750
+ if (min2 !== void 0 && v < min2) v = min2;
138751
+ return v;
138752
+ }
138753
+ function minOf(child, dim, reference) {
138754
+ const l = dim === "width" ? child.minWidth : child.minHeight;
138755
+ return l ? resolveLength(l, reference) : void 0;
138756
+ }
138757
+ function maxOf(child, dim, reference) {
138758
+ const l = dim === "width" ? child.maxWidth : child.maxHeight;
138759
+ return l ? resolveLength(l, reference) : void 0;
138760
+ }
138761
+ function crossSize(child, dim, reference, marginSum, stretch) {
138762
+ const rule = child[dim];
138763
+ let size2;
138764
+ if (rule.kind === "length") {
138765
+ size2 = resolveLength(rule.length, reference) ?? child.content[dim];
138766
+ } else if (rule.kind === "fill" || stretch) {
138767
+ size2 = reference === void 0 ? child.content[dim] : Math.max(0, reference - marginSum);
138768
+ } else {
138769
+ size2 = child.content[dim];
138770
+ }
138771
+ return clamp3(size2, minOf(child, dim, reference), maxOf(child, dim, reference));
138772
+ }
138773
+ function basisOf(child, dim, reference) {
138774
+ const rule = child[dim];
138775
+ if (rule.kind === "hug") return child.content[dim];
138776
+ if (rule.kind === "length") return resolveLength(rule.length, reference) ?? child.content[dim];
138777
+ const basis = rule.basis ?? { px: 0 };
138778
+ if (basis === "hug") return child.content[dim];
138779
+ if (reference === void 0) {
138780
+ const resolved = resolveLength(basis, void 0);
138781
+ return resolved === void 0 || rule.basis === void 0 ? child.content[dim] : resolved;
138782
+ }
138783
+ return resolveLength(basis, reference) ?? child.content[dim];
138784
+ }
138785
+ function resolveMain(children, dim, available, reference, outerExtra, scroll = false) {
138786
+ const n = children.length;
138787
+ const base = children.map((c) => basisOf(c, dim, reference));
138788
+ const floorOf = (c) => scroll ? Math.max(c.floor ?? 0, c.content[dim]) : c.floor ?? 0;
138789
+ const min2 = children.map((c) => minOf(c, dim, reference) ?? (c[dim].kind === "fill" ? floorOf(c) : void 0));
138790
+ const max2 = children.map((c) => maxOf(c, dim, reference));
138791
+ const hyp = base.map((b, i) => clamp3(b, min2[i], max2[i]));
138792
+ const weight = children.map((c) => {
138793
+ const r = c[dim];
138794
+ return r.kind === "fill" ? Math.max(0, r.weight) : 0;
138795
+ });
138796
+ if (available === void 0) return hyp;
138797
+ const sumHyp = hyp.reduce((a, b) => a + b, 0) + outerExtra;
138798
+ const growing = sumHyp < available;
138799
+ const target = hyp.slice();
138800
+ const frozen = children.map((_3, i) => {
138801
+ if (growing) return weight[i] === 0 || base[i] > hyp[i];
138802
+ return weight[i] === 0 || base[i] < hyp[i];
138803
+ });
138804
+ const initialFree = available - outerExtra - children.reduce((a, _3, i) => a + (frozen[i] ? hyp[i] : base[i]), 0);
138805
+ for (let guard = 0; guard <= n; guard++) {
138806
+ if (frozen.every(Boolean)) break;
138807
+ let free = available - outerExtra - children.reduce((a, _3, i) => a + (frozen[i] ? target[i] : base[i]), 0);
138808
+ const open2 = [...Array(n).keys()].filter((i) => !frozen[i]);
138809
+ if (growing) {
138810
+ const sumW = open2.reduce((a, i) => a + weight[i], 0);
138811
+ if (sumW < 1 && Math.abs(initialFree * sumW) < Math.abs(free)) free = initialFree * sumW;
138812
+ for (const i of open2) target[i] = sumW > 0 ? base[i] + free * weight[i] / sumW : base[i];
138813
+ } else {
138814
+ const scaled = open2.map((i) => base[i]);
138815
+ const sumS = scaled.reduce((a, b) => a + b, 0);
138816
+ open2.forEach((i, k) => {
138817
+ target[i] = sumS > 0 ? base[i] + free * scaled[k] / sumS : base[i];
138818
+ });
138819
+ }
138820
+ let total = 0;
138821
+ const adjust = /* @__PURE__ */ new Map();
138822
+ for (const i of open2) {
138823
+ const clamped = clamp3(target[i], min2[i] ?? 0, max2[i]);
138824
+ adjust.set(i, clamped - target[i]);
138825
+ total += clamped - target[i];
138826
+ target[i] = clamped;
138827
+ }
138828
+ if (total === 0) break;
138829
+ for (const i of open2) {
138830
+ const a = adjust.get(i) ?? 0;
138831
+ if (total > 0 && a > 0 || total < 0 && a < 0) frozen[i] = true;
138832
+ }
138833
+ for (const i of open2) if (!frozen[i]) target[i] = base[i];
138834
+ }
138835
+ return target;
138836
+ }
138837
+ function transformBox(box, t) {
138838
+ if (!t) return box;
138839
+ const tx = resolveLength(t.x ?? {}, box.width) ?? 0;
138840
+ const ty = resolveLength(t.y ?? {}, box.height) ?? 0;
138841
+ const ox = box.x + (resolveLength(t.originX ?? { percent: 50 }, box.width) ?? 0);
138842
+ const oy = box.y + (resolveLength(t.originY ?? { percent: 50 }, box.height) ?? 0);
138843
+ const s = t.scale ?? 1;
138844
+ const rad = (t.rotation ?? 0) * Math.PI / 180;
138845
+ const cos = Math.cos(rad);
138846
+ const sin = Math.sin(rad);
138847
+ const corners = [
138848
+ [box.x, box.y],
138849
+ [box.x + box.width, box.y],
138850
+ [box.x, box.y + box.height],
138851
+ [box.x + box.width, box.y + box.height]
138852
+ ].map(([px2, py]) => {
138853
+ const dx = (px2 - ox) * s;
138854
+ const dy = (py - oy) * s;
138855
+ return [ox + tx + dx * cos - dy * sin, oy + ty + dx * sin + dy * cos];
138856
+ });
138857
+ const xs = corners.map((c) => c[0]);
138858
+ const ys = corners.map((c) => c[1]);
138859
+ const x = Math.min(...xs);
138860
+ const y = Math.min(...ys);
138861
+ return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
138862
+ }
138863
+ function intersect(a, b) {
138864
+ const x = Math.max(a.x, b.x);
138865
+ const y = Math.max(a.y, b.y);
138866
+ const r = Math.min(a.x + a.width, b.x + b.width);
138867
+ const bt = Math.min(a.y + a.height, b.y + b.height);
138868
+ if (r <= x || bt <= y) return null;
138869
+ return { x, y, width: r - x, height: bt - y };
138870
+ }
138871
+ function placeOverlay(child, pad) {
138872
+ const m = edges(child.margin);
138873
+ const width = crossSize(child, "width", pad.width, m.left + m.right, false);
138874
+ const height = crossSize(child, "height", pad.height, m.top + m.bottom, false);
138875
+ const ax = child.overlayAlign?.x ?? "start";
138876
+ const ay = child.overlayAlign?.y ?? "start";
138877
+ const x = ax === "start" ? pad.x + m.left : ax === "end" ? pad.x + pad.width - width - m.right : pad.x + pad.width / 2 + m.left - width / 2;
138878
+ const y = ay === "start" ? pad.y + m.top : ay === "end" ? pad.y + pad.height - height - m.bottom : pad.y + pad.height / 2 + m.top - height / 2;
138879
+ return { x, y, width, height };
138880
+ }
138881
+ function layoutChildren(parent, children) {
138882
+ const p = edges(parent.padding);
138883
+ const b = edges(parent.border);
138884
+ const padBox = {
138885
+ x: b.left,
138886
+ y: b.top,
138887
+ width: Math.max(0, parent.width - b.left - b.right),
138888
+ height: Math.max(0, parent.height - b.top - b.bottom)
138889
+ };
138890
+ const contentBox = {
138891
+ x: padBox.x + p.left,
138892
+ y: padBox.y + p.top,
138893
+ width: Math.max(0, padBox.width - p.left - p.right),
138894
+ height: Math.max(0, padBox.height - p.top - p.bottom)
138895
+ };
138896
+ const boxes = new Array(children.length);
138897
+ const flow = children.map((c, i) => ({ c, i })).filter(({ c }) => parent.axis !== "overlay" && (c.place ?? "flow") === "flow");
138898
+ for (const { c, i } of children.map((c2, i2) => ({ c: c2, i: i2 }))) {
138899
+ if (parent.axis === "overlay" || c.place === "overlay") boxes[i] = placeOverlay(c, padBox);
138900
+ }
138901
+ let extentMain = 0;
138902
+ let extentCross = 0;
138903
+ let overflows = false;
138904
+ if (parent.axis !== "overlay") {
138905
+ const v = parent.axis === "column" ? COLUMN : ROW;
138906
+ const mainRef = contentBox[v.main];
138907
+ const crossRef = contentBox[v.cross];
138908
+ const kids = flow.map(({ c }) => c);
138909
+ const margins = kids.map((c) => edges(c.margin));
138910
+ const gap = parent.gap ?? 0;
138911
+ const marginMain = margins.reduce((a, m) => a + m[v.mainEdges[0]] + m[v.mainEdges[1]], 0);
138912
+ const gaps = kids.length > 1 ? gap * (kids.length - 1) : 0;
138913
+ const main2 = resolveMain(kids, v.main, mainRef, mainRef, marginMain + gaps, parent.scroll);
138914
+ const used = main2.reduce((a, s) => a + s, 0) + marginMain + gaps;
138915
+ const free = parent.scroll ? Math.max(0, mainRef - used) : mainRef - used;
138916
+ const spacers = kids.reduce((a, c) => a + (c.spacer?.before ? 1 : 0) + (c.spacer?.after ? 1 : 0), 0);
138917
+ let lead = 0;
138918
+ let between = 0;
138919
+ let spacer = 0;
138920
+ const align = parent.mainAlign ?? "start";
138921
+ if (spacers > 0 && free > 0) {
138922
+ spacer = free / spacers;
138923
+ } else if (kids.length > 0) {
138924
+ const n = kids.length;
138925
+ if (align === "center") lead = free / 2;
138926
+ else if (align === "end") lead = free;
138927
+ else if (align === "space-between") {
138928
+ if (free > 0 && n > 1) between = free / (n - 1);
138929
+ } else if (align === "space-around") {
138930
+ if (free > 0 && n > 1) {
138931
+ between = free / n;
138932
+ lead = between / 2;
138933
+ } else lead = free / 2;
138934
+ } else if (align === "space-evenly") {
138935
+ if (free > 0) {
138936
+ between = free / (n + 1);
138937
+ lead = between;
138938
+ } else lead = free / 2;
138939
+ }
138940
+ }
138941
+ let cursor = contentBox[v.main === "height" ? "y" : "x"] + lead;
138942
+ const crossStart = contentBox[v.cross === "width" ? "x" : "y"];
138943
+ kids.forEach((c, k) => {
138944
+ const m = margins[k];
138945
+ const marginCross = m[v.crossEdges[0]] + m[v.crossEdges[1]];
138946
+ const selfAlign = c.alignSelf ?? parent.crossAlign ?? "start";
138947
+ const cross = crossSize(c, v.cross, crossRef, marginCross, selfAlign === "stretch" && c[v.cross].kind === "hug");
138948
+ if (c.spacer?.before && spacer > 0) cursor += spacer;
138949
+ cursor += m[v.mainEdges[0]];
138950
+ const at = cursor;
138951
+ cursor += main2[k] + m[v.mainEdges[1]];
138952
+ if (c.spacer?.after && spacer > 0) cursor += spacer;
138953
+ cursor += k < kids.length - 1 ? gap + between : 0;
138954
+ const room = crossRef - cross - marginCross;
138955
+ const crossAt = crossStart + m[v.crossEdges[0]] + (selfAlign === "center" ? room / 2 : selfAlign === "end" ? room : 0);
138956
+ extentCross = Math.max(extentCross, crossAt + cross + m[v.crossEdges[1]]);
138957
+ boxes[flow[k].i] = v.main === "height" ? { x: crossAt, y: at, width: cross, height: main2[k] } : { x: at, y: crossAt, width: main2[k], height: cross };
138958
+ });
138959
+ extentMain = cursor;
138960
+ overflows = used > mainRef + 1e-9;
138961
+ const endMain = v.main === "height" ? p.bottom + b.bottom : p.right + b.right;
138962
+ const endCross = v.cross === "width" ? p.right + b.right : p.bottom + b.bottom;
138963
+ extentMain += endMain;
138964
+ extentCross += endCross;
138965
+ const extent = v.main === "height" ? { width: extentCross, height: extentMain } : { width: extentMain, height: extentCross };
138966
+ return finish2(parent, children, boxes, padBox, extent, overflows);
138967
+ }
138968
+ return finish2(parent, children, boxes, padBox, { width: parent.width, height: parent.height }, false);
138969
+ }
138970
+ function finish2(parent, children, boxes, padBox, extent, overflows) {
138971
+ const clips = Boolean(parent.clip || parent.scroll);
138972
+ const out = boxes.map((box, i) => {
138973
+ const drawn3 = transformBox(box, children[i].transform);
138974
+ return { ...box, drawn: drawn3, visible: clips ? intersect(drawn3, padBox) : drawn3 };
138975
+ });
138976
+ const paintOrder = children.map((c, i) => ({ z: c.z ?? 0, i })).sort((a, b) => a.z - b.z || a.i - b.i).map((e) => e.i);
138977
+ return { children: out, paintOrder, extent, overflows };
138978
+ }
138979
+ function hugSize(parent, children, given = {}) {
138980
+ const p = edges(parent.padding);
138981
+ const b = edges(parent.border);
138982
+ const insetW = p.left + p.right + b.left + b.right;
138983
+ const insetH = p.top + p.bottom + b.top + b.bottom;
138984
+ const refW = given.width === void 0 ? void 0 : Math.max(0, given.width - insetW);
138985
+ const refH = given.height === void 0 ? void 0 : Math.max(0, given.height - insetH);
138986
+ const outer = (c, dim, ref) => {
138987
+ const m = edges(c.margin);
138988
+ const ms = dim === "width" ? m.left + m.right : m.top + m.bottom;
138989
+ const rule = c[dim];
138990
+ const size2 = rule.kind === "length" ? resolveLength(rule.length, ref) ?? c.content[dim] : rule.kind === "fill" && ref !== void 0 ? Math.max(0, ref - ms) : c.content[dim];
138991
+ return clamp3(size2, minOf(c, dim, ref), maxOf(c, dim, ref)) + ms;
138992
+ };
138993
+ let w = 0;
138994
+ let h = 0;
138995
+ const gap = parent.gap ?? 0;
138996
+ const flow = children.filter((c) => parent.axis !== "overlay" && (c.place ?? "flow") === "flow");
138997
+ if (parent.axis === "overlay") {
138998
+ for (const c of children) {
138999
+ w = Math.max(w, outer(c, "width", refW));
139000
+ h = Math.max(h, outer(c, "height", refH));
139001
+ }
139002
+ } else {
139003
+ const v = parent.axis === "column" ? COLUMN : ROW;
139004
+ const refMain = v.main === "width" ? refW : refH;
139005
+ const refCross = v.cross === "width" ? refW : refH;
139006
+ const m = flow.map((c) => {
139007
+ const marg = edges(c.margin);
139008
+ return basisOf(c, v.main, refMain) + marg[v.mainEdges[0]] + marg[v.mainEdges[1]];
139009
+ });
139010
+ const mainSum = m.reduce((a, x) => a + x, 0) + (flow.length > 1 ? gap * (flow.length - 1) : 0);
139011
+ const crossMax = flow.reduce((a, c) => Math.max(a, outer(c, v.cross, refCross)), 0);
139012
+ if (v.main === "height") [w, h] = [crossMax, mainSum];
139013
+ else [w, h] = [mainSum, crossMax];
139014
+ }
139015
+ return {
139016
+ width: given.width ?? w + insetW,
139017
+ height: given.height ?? h + insetH
139018
+ };
139019
+ }
139020
+ var ZERO, COLUMN, ROW;
138629
139021
  var init_layout = __esm({
138630
139022
  "../nodegx-node-spec/src/layout.ts"() {
138631
139023
  "use strict";
139024
+ ZERO = { top: 0, right: 0, bottom: 0, left: 0 };
139025
+ COLUMN = { main: "height", cross: "width", mainEdges: ["top", "bottom"], crossEdges: ["left", "right"] };
139026
+ ROW = { main: "width", cross: "height", mainEdges: ["left", "right"], crossEdges: ["top", "bottom"] };
138632
139027
  }
138633
139028
  });
138634
139029
 
138635
139030
  // ../nodegx-node-spec/src/layout-map.ts
139031
+ function readLength(raw, defaultUnit, options, why) {
139032
+ let value;
139033
+ let unit;
139034
+ if (raw === void 0 || raw === null || raw === "") return void 0;
139035
+ if (typeof raw === "number") {
139036
+ value = raw;
139037
+ unit = defaultUnit;
139038
+ } else if (typeof raw === "object" && raw !== null && "value" in raw) {
139039
+ const o = raw;
139040
+ value = Number(o.value);
139041
+ unit = typeof o.unit === "string" && o.unit !== "" ? o.unit : defaultUnit;
139042
+ } else if (typeof raw === "string") {
139043
+ const token = /^var\(\s*(--[\w-]+)\s*(?:,\s*([^)]*))?\)$/.exec(raw.trim());
139044
+ if (token) {
139045
+ const px2 = options.token?.(token[1]);
139046
+ if (px2 !== void 0) return { px: px2 };
139047
+ if (token[2] !== void 0) return readLength(token[2].trim(), defaultUnit, options, why);
139048
+ why(`the token ${token[1]} has no value here`);
139049
+ return void 0;
139050
+ }
139051
+ const m = /^(-?\d*\.?\d+)\s*([a-z%]*)$/i.exec(raw.trim());
139052
+ if (!m) {
139053
+ why(`"${raw}" is not a length`);
139054
+ return void 0;
139055
+ }
139056
+ value = Number(m[1]);
139057
+ unit = m[2] === "" ? defaultUnit : m[2].toLowerCase();
139058
+ } else {
139059
+ why(`${JSON.stringify(raw)} is not a length`);
139060
+ return void 0;
139061
+ }
139062
+ if (!Number.isFinite(value)) {
139063
+ why(`${JSON.stringify(raw)} is not a number`);
139064
+ return void 0;
139065
+ }
139066
+ if (unit === "px") return { px: value };
139067
+ if (unit === "%") return { percent: value };
139068
+ if ((unit === "vw" || unit === "vh") && options.viewport) {
139069
+ return { px: value / 100 * (unit === "vw" ? options.viewport.width : options.viewport.height) };
139070
+ }
139071
+ why(`the unit "${unit}" is not in the model${unit === "vw" || unit === "vh" ? " without a viewport" : ""}`);
139072
+ return void 0;
139073
+ }
139074
+ function pxOf(length, name, why) {
139075
+ if (!length) return 0;
139076
+ if (length.percent) {
139077
+ why(`${name} in percent is not in the model`);
139078
+ return 0;
139079
+ }
139080
+ return length.px ?? 0;
139081
+ }
139082
+ function numberOf(raw) {
139083
+ const v = typeof raw === "object" && raw !== null && "value" in raw ? raw.value : raw;
139084
+ if (v === void 0 || v === null || v === "") return void 0;
139085
+ const n = typeof v === "number" ? v : parseFloat(String(v));
139086
+ return Number.isFinite(n) ? n : void 0;
139087
+ }
139088
+ function layoutOf(spec, params, options = {}) {
139089
+ const outside = [];
139090
+ const byRole = /* @__PURE__ */ new Map();
139091
+ for (const [name, decl] of Object.entries(spec.inputs)) {
139092
+ const role = decl.layout;
139093
+ if (role) byRole.set(role, name);
139094
+ const cls = decl.portability;
139095
+ const set = params[name] !== void 0 && params[name] !== decl.default;
139096
+ if (set && cls && !role && !(name in BOX_NEUTRAL) && (UNMAPPED_CLASSES.includes(cls) || cls === "layout-expressible")) {
139097
+ outside.push(`${name} (${cls}) is set`);
139098
+ }
139099
+ }
139100
+ const has4 = (role) => byRole.has(role);
139101
+ const value = (role) => {
139102
+ const name = byRole.get(role);
139103
+ if (name === void 0) return void 0;
139104
+ return params[name] !== void 0 ? params[name] : spec.inputs[name].default;
139105
+ };
139106
+ const length = (role, unit) => readLength(value(role), unit, options, (r) => outside.push(`${byRole.get(role)}: ${r}`));
139107
+ const px2 = (role) => pxOf(length(role, "px"), byRole.get(role) ?? role, (r) => outside.push(r));
139108
+ const present = value("present") !== false;
139109
+ const position = value("place");
139110
+ if (position === "fixed" || position === "sticky") outside.push(`position is ${position}`);
139111
+ const parentAxis = options.parentAxis;
139112
+ const overlay = parentAxis === "overlay" || position === "absolute" || position === "fixed";
139113
+ const margin = {
139114
+ top: px2("margin-top"),
139115
+ right: px2("margin-right"),
139116
+ bottom: px2("margin-bottom"),
139117
+ left: px2("margin-left")
139118
+ };
139119
+ const mode = has4("size-mode") ? String(value("size-mode")) : "explicit";
139120
+ const assigns = {
139121
+ width: mode === "explicit" || mode === "contentHeight",
139122
+ height: mode === "explicit" || mode === "contentWidth"
139123
+ };
139124
+ const sizeRule = (dim) => {
139125
+ if (has4("diameter")) {
139126
+ const d = length("diameter", "px");
139127
+ return d ? { kind: "length", length: d } : { kind: "hug" };
139128
+ }
139129
+ if (!has4(dim) || !assigns[dim]) return { kind: "hug" };
139130
+ const l = length(dim, "%");
139131
+ if (!l) return { kind: "hug" };
139132
+ if (!l.percent) return { kind: "length", length: l };
139133
+ const fixed = Boolean(params[byRole.get(dim) ?? ""]?.isFixed);
139134
+ const along = parentAxis === "row" && dim === "width" || parentAxis === "column" && dim === "height";
139135
+ const lessMargins = dim === "width" ? margin.left + margin.right : margin.top + margin.bottom;
139136
+ if (!overlay && along && !fixed) return { kind: "fill", weight: l.percent, basis: { percent: l.percent } };
139137
+ if (parentAxis === void 0 && !overlay) return { kind: "length", length: l };
139138
+ return { kind: "length", length: lessMargins ? { percent: l.percent, px: -lessMargins } : { percent: l.percent } };
139139
+ };
139140
+ const child = { width: sizeRule("width"), height: sizeRule("height"), margin };
139141
+ for (const [role, key] of [
139142
+ ["min-width", "minWidth"],
139143
+ ["max-width", "maxWidth"],
139144
+ ["min-height", "minHeight"],
139145
+ ["max-height", "maxHeight"]
139146
+ ]) {
139147
+ const l = has4(role) ? length(role, "%") : void 0;
139148
+ if (l) child[key] = l;
139149
+ }
139150
+ const alignX = value("align-x");
139151
+ const alignY = value("align-y");
139152
+ if (overlay) {
139153
+ child.place = "overlay";
139154
+ child.overlayAlign = { x: OVERLAY_ALIGN[alignX ?? "left"] ?? "start", y: OVERLAY_ALIGN[alignY ?? "top"] ?? "start" };
139155
+ } else if (parentAxis !== void 0) {
139156
+ const [mainAlign, crossAlign, before2, after3] = parentAxis === "row" ? [alignX, alignY, margin.left, margin.right] : [alignY, alignX, margin.top, margin.bottom];
139157
+ const startish = parentAxis === "row" ? "left" : "top";
139158
+ const endish = parentAxis === "row" ? "right" : "bottom";
139159
+ if (mainAlign === startish && !after3) child.spacer = { after: true };
139160
+ else if (mainAlign === "center") child.spacer = { before: !before2, after: !after3 };
139161
+ else if (mainAlign === endish && !before2) child.spacer = { before: true };
139162
+ if (crossAlign && SELF_ALIGN[crossAlign]) child.alignSelf = SELF_ALIGN[crossAlign];
139163
+ }
139164
+ const transform = {};
139165
+ const tx = has4("translate-x") ? length("translate-x", "px") : void 0;
139166
+ const ty = has4("translate-y") ? length("translate-y", "px") : void 0;
139167
+ if (tx && (tx.px || tx.percent)) transform.x = tx;
139168
+ if (ty && (ty.px || ty.percent)) transform.y = ty;
139169
+ const rotation = has4("rotation") ? numberOf(value("rotation")) : void 0;
139170
+ if (rotation) transform.rotation = rotation;
139171
+ const scale = has4("scale") ? numberOf(value("scale")) : void 0;
139172
+ if (scale !== void 0 && scale !== 1) transform.scale = scale;
139173
+ if (Object.keys(transform).length > 0) {
139174
+ const ox = has4("origin-x") ? length("origin-x", "%") : void 0;
139175
+ const oy = has4("origin-y") ? length("origin-y", "%") : void 0;
139176
+ if (ox) transform.originX = ox;
139177
+ if (oy) transform.originY = oy;
139178
+ child.transform = transform;
139179
+ }
139180
+ let parent;
139181
+ if (has4("axis")) {
139182
+ const dir = value("axis");
139183
+ const axis = dir === "row" ? "row" : dir === "none" ? "overlay" : "column";
139184
+ const borderSide = (side) => {
139185
+ const style = (has4(`border-${side}-style`) ? value(`border-${side}-style`) : void 0) ?? value("border-style");
139186
+ if (style === void 0 || style === "none") return 0;
139187
+ const w = has4(`border-${side}-width`) && value(`border-${side}-width`) !== void 0 ? px2(`border-${side}-width`) : px2("border-width");
139188
+ return w;
139189
+ };
139190
+ const gap = axis === "row" ? has4("gap-column") ? px2("gap-column") : 0 : axis === "column" ? has4("gap-row") ? px2("gap-row") : 0 : 0;
139191
+ parent = {
139192
+ axis,
139193
+ mainAlign: MAIN_ALIGN[String(value("main-align") ?? "flex-start")] ?? "start",
139194
+ crossAlign: CROSS_ALIGN[String(value("cross-align") ?? "flex-start")] ?? "start",
139195
+ gap,
139196
+ padding: { top: px2("padding-top"), right: px2("padding-right"), bottom: px2("padding-bottom"), left: px2("padding-left") },
139197
+ border: { top: borderSide("top"), right: borderSide("right"), bottom: borderSide("bottom"), left: borderSide("left") },
139198
+ clip: value("clip") === true,
139199
+ scroll: value("scroll") === true
139200
+ };
139201
+ }
139202
+ return { present, child, parent, outside };
139203
+ }
139204
+ var UNMAPPED_CLASSES, BOX_NEUTRAL, MAIN_ALIGN, CROSS_ALIGN, SELF_ALIGN, OVERLAY_ALIGN;
138636
139205
  var init_layout_map = __esm({
138637
139206
  "../nodegx-node-spec/src/layout-map.ts"() {
138638
139207
  "use strict";
139208
+ UNMAPPED_CLASSES = ["layout-not-shared", "web-only"];
139209
+ BOX_NEUTRAL = {
139210
+ as: "the Tag: the runtime's sheet resets a Text's margins (`.ndl-visual-text { margin: 0 }`), and a Group's section, nav or span lays out as its div does; an h1\u2013h6 changes only the browser font, the Text content (layout-fixtures/measured/tags.json, s71)"
139211
+ };
139212
+ MAIN_ALIGN = {
139213
+ "flex-start": "start",
139214
+ "flex-end": "end",
139215
+ center: "center",
139216
+ "space-between": "space-between",
139217
+ "space-around": "space-around",
139218
+ "space-evenly": "space-evenly"
139219
+ };
139220
+ CROSS_ALIGN = { "flex-start": "start", "flex-end": "end", center: "center", stretch: "stretch" };
139221
+ SELF_ALIGN = { left: "start", top: "start", center: "center", right: "end", bottom: "end" };
139222
+ OVERLAY_ALIGN = { left: "start", top: "start", center: "center", right: "end", bottom: "end" };
139223
+ }
139224
+ });
139225
+
139226
+ // ../nodegx-node-spec/src/draw.ts
139227
+ function createDrawer(specs2) {
139228
+ const isVisualType = (type) => {
139229
+ const spec = specs2[type];
139230
+ return !!spec && Object.values(spec.inputs).some((d) => d.layout !== void 0);
139231
+ };
139232
+ function draw(input) {
139233
+ const { viewport } = input;
139234
+ const components = input.components ?? {};
139235
+ const componentOf = (path5) => components[path5] ?? components[path5.replace(/^\/+/, "")] ?? components["/" + path5.replace(/^\/+/, "")];
139236
+ const isInstance = (type) => componentOf(type) !== void 0 || type.startsWith("/") && !specs2[type];
139237
+ const rootComp = Array.isArray(input.nodes) ? { nodes: input.nodes } : input.nodes;
139238
+ const needs = /* @__PURE__ */ new Map();
139239
+ const limits = /* @__PURE__ */ new Map();
139240
+ const add = (m, path5, what) => {
139241
+ const list2 = m.get(path5) ?? m.set(path5, []).get(path5);
139242
+ if (!list2.includes(what)) list2.push(what);
139243
+ };
139244
+ const need = (path5, what) => add(needs, path5, what);
139245
+ const limit = (path5, what) => add(limits, path5, what);
139246
+ const inTree = (n) => isVisualType(n.type) || n.type === PAGE || isInstance(n.type) || n.type === REPEATER;
139247
+ const fileChildren = (comp, node) => {
139248
+ const byId = new Map(comp.nodes.map((n) => [n.id, n]));
139249
+ const listed = (node.children ?? []).map((id) => byId.get(id)).filter((n) => !!n && inTree(n));
139250
+ const byParent = comp.nodes.filter((n) => n.parent === node.id && inTree(n) && !listed.includes(n));
139251
+ return [...listed, ...byParent];
139252
+ };
139253
+ const fileRoots = (comp) => {
139254
+ const byId = new Map(comp.nodes.map((n) => [n.id, n]));
139255
+ if (comp.visualRoots && comp.visualRoots.length > 0) return comp.visualRoots.map((id) => byId.get(id)).filter((n) => !!n && inTree(n));
139256
+ return comp.nodes.filter((n) => inTree(n) && (!n.parent || !byId.has(n.parent)));
139257
+ };
139258
+ const expand = (nodes, comp, prefix) => {
139259
+ const out2 = [];
139260
+ for (const n of nodes) {
139261
+ const path5 = prefix ? `${prefix}/${n.id}` : n.id;
139262
+ if (n.type === REPEATER) {
139263
+ need(path5, ROWS_NEED);
139264
+ continue;
139265
+ }
139266
+ if (isInstance(n.type)) {
139267
+ const placed = componentOf(n.type);
139268
+ if (!placed) {
139269
+ need(path5, `the component ${n.type} is not among the files given, so nothing is drawn in its place`);
139270
+ continue;
139271
+ }
139272
+ out2.push(...expand(fileRoots(placed), placed, path5));
139273
+ continue;
139274
+ }
139275
+ if (n.type === PAGE) {
139276
+ need(path5, PAGE_NEED);
139277
+ out2.push({ node: n, path: path5, prefix, comp, kind: "page" });
139278
+ continue;
139279
+ }
139280
+ if (n.type === ROUTER) {
139281
+ need(path5, ROUTER_NEED);
139282
+ out2.push({ node: n, path: path5, prefix, comp, kind: "router" });
139283
+ continue;
139284
+ }
139285
+ out2.push({ node: n, path: path5, prefix, comp, kind: "visual" });
139286
+ }
139287
+ return out2;
139288
+ };
139289
+ const childItems = (item) => {
139290
+ if (item.kind === "router") {
139291
+ const pages = item.node.parameters?.pages;
139292
+ const wanted = input.pageOf?.(item.node, item.path) ?? pages?.startPage ?? pages?.routes?.[0];
139293
+ if (!wanted) {
139294
+ need(item.path, "the Router lists no page, so nothing is drawn in it");
139295
+ return [];
139296
+ }
139297
+ const page = componentOf(wanted);
139298
+ if (!page) {
139299
+ need(item.path, `the Router's page ${wanted} is not among the files given, so nothing is drawn in it`);
139300
+ return [];
139301
+ }
139302
+ return expand(fileRoots(page), page, item.path);
139303
+ }
139304
+ return expand(fileChildren(item.comp, item.node), item.comp, item.prefix);
139305
+ };
139306
+ const params = (item) => ({ ...item.node.parameters ?? {}, ...input.values?.[item.node.id] ?? {}, ...input.values?.[item.path] ?? {} });
139307
+ const read2 = (item) => (port5) => {
139308
+ const p = params(item);
139309
+ return p[port5] !== void 0 ? p[port5] : specs2[item.node.type]?.inputs[port5]?.default;
139310
+ };
139311
+ const opts = (parentAxis) => ({ parentAxis, viewport, token: input.token });
139312
+ const lay = (item, parentAxis) => {
139313
+ if (item.kind === "visual") return layoutOf(specs2[item.node.type], params(item), opts(parentAxis));
139314
+ const base = item.kind === "router" ? layoutOf(specs2[ROUTER], params(item), opts(parentAxis)) : void 0;
139315
+ const clip = item.kind === "router" ? read2(item)("clip") : void 0;
139316
+ return {
139317
+ present: base ? base.present : true,
139318
+ child: { width: { kind: "fill", weight: 1 }, height: { kind: "fill", weight: 1 }, margin: { top: 0, right: 0, bottom: 0, left: 0 } },
139319
+ parent: {
139320
+ axis: "column",
139321
+ mainAlign: "start",
139322
+ crossAlign: item.kind === "page" ? "start" : "stretch",
139323
+ gap: 0,
139324
+ padding: { top: 0, right: 0, bottom: 0, left: 0 },
139325
+ border: { top: 0, right: 0, bottom: 0, left: 0 },
139326
+ clip: clip === "clip",
139327
+ scroll: clip === "scroll"
139328
+ },
139329
+ outside: base ? base.outside : []
139330
+ };
139331
+ };
139332
+ const contents = /* @__PURE__ */ new Map();
139333
+ const kidsCache = /* @__PURE__ */ new Map();
139334
+ const kidsOf = (item) => kidsCache.get(item.path) ?? kidsCache.set(item.path, childItems(item)).get(item.path);
139335
+ const contentOf = (item, parentAxis) => {
139336
+ const self2 = lay(item, parentAxis);
139337
+ const kids = kidsOf(item).filter((c) => lay(c, void 0).present);
139338
+ if (kids.length > 0 && self2.parent) {
139339
+ const axis = self2.parent.axis;
139340
+ return hugSize(self2.parent, kids.map((c) => ({ ...lay(c, axis).child, content: contentOf(c, axis) })));
139341
+ }
139342
+ if (!usesContent(self2.child)) return { width: 0, height: 0 };
139343
+ if (item.kind !== "visual") return { width: 0, height: 0 };
139344
+ const measured = input.measure?.(item.node, read2(item), { path: item.path });
139345
+ if (!measured) {
139346
+ need(item.path, "its content size (nobody measured it)");
139347
+ return { width: 0, height: 0 };
139348
+ }
139349
+ for (const w of measured.needs ?? []) need(item.path, w);
139350
+ for (const w of measured.limits ?? []) limit(item.path, w);
139351
+ contents.set(item.path, measured.size);
139352
+ return measured.size;
139353
+ };
139354
+ const out = [];
139355
+ const place = (item, at, drawnAt, parentAxis, parentPath, underTransform) => {
139356
+ const self2 = lay(item, parentAxis);
139357
+ const content = contents.get(item.path);
139358
+ if (content && content.width > at.width + 0.5 && self2.child.height.kind === "hug") {
139359
+ limit(item.path, `its content is wider than its box (${round(content.width)} px in ${round(at.width)} px): in a browser the text wraps and the box grows down; the model takes one content size per leaf`);
139360
+ }
139361
+ if (underTransform) limit(item.path, "an ancestor is transformed: its drawn box moves with it, and the model compounds no transforms");
139362
+ out.push({
139363
+ id: item.node.id,
139364
+ path: item.path,
139365
+ type: item.node.type,
139366
+ label: item.node.label,
139367
+ parent: parentPath,
139368
+ x: at.x,
139369
+ y: at.y,
139370
+ width: at.width,
139371
+ height: at.height,
139372
+ ...drawnAt ? { drawn: drawnAt } : {},
139373
+ present: true,
139374
+ outside: self2.outside,
139375
+ needs: needs.get(item.path) ?? [],
139376
+ limits: limits.get(item.path) ?? []
139377
+ });
139378
+ const kids = kidsOf(item);
139379
+ if (kids.length === 0 || !self2.parent) return;
139380
+ const axis = self2.parent.axis;
139381
+ const placed = kids.map((c) => ({ c, l: lay(c, axis) }));
139382
+ const shown = placed.filter((p) => p.l.present);
139383
+ const children = shown.map((p) => ({ ...p.l.child, content: contentOf(p.c, axis) }));
139384
+ const result2 = layoutChildren({ ...self2.parent, width: at.width, height: at.height }, children);
139385
+ shown.forEach((p, i) => {
139386
+ const b = result2.children[i];
139387
+ const box = { x: at.x + b.x, y: at.y + b.y, width: b.width, height: b.height };
139388
+ const own = p.l.child.transform !== void 0;
139389
+ const drawn3 = own ? { x: at.x + b.drawn.x, y: at.y + b.drawn.y, width: b.drawn.width, height: b.drawn.height } : void 0;
139390
+ place(p.c, box, drawn3, axis, item.path, underTransform || own);
139391
+ });
139392
+ for (const p of placed) {
139393
+ if (p.l.present) continue;
139394
+ out.push({ id: p.c.node.id, path: p.c.path, type: p.c.node.type, label: p.c.node.label, parent: item.path, x: 0, y: 0, width: 0, height: 0, present: false, outside: [], needs: needs.get(p.c.path) ?? [], limits: [] });
139395
+ }
139396
+ };
139397
+ const ROOT_AXIS = "column";
139398
+ const roots = expand(fileRoots(rootComp), rootComp, "");
139399
+ const rootKids = roots.map((r) => ({ r, l: lay(r, ROOT_AXIS) })).filter((p) => p.l.present);
139400
+ const rootChild = (child) => {
139401
+ if (!input.bodyScroll) return child;
139402
+ const h = child.height;
139403
+ const unreferenced = h.kind === "fill" || h.kind === "length" && h.length.percent !== void 0;
139404
+ return unreferenced ? { ...child, height: { kind: "hug" } } : child;
139405
+ };
139406
+ const topChildren = rootKids.map((p) => ({ ...rootChild(p.l.child), content: contentOf(p.r, ROOT_AXIS) }));
139407
+ const rootHeight = input.bodyScroll ? Math.max(viewport.height, hugSize({ axis: ROOT_AXIS }, topChildren).height) : viewport.height;
139408
+ const top = layoutChildren({ axis: ROOT_AXIS, width: viewport.width, height: rootHeight }, topChildren);
139409
+ rootKids.forEach((p, i) => {
139410
+ const b = top.children[i];
139411
+ const own = p.l.child.transform !== void 0;
139412
+ place(p.r, { x: b.x, y: b.y, width: b.width, height: b.height }, own ? { x: b.drawn.x, y: b.drawn.y, width: b.drawn.width, height: b.drawn.height } : void 0, ROOT_AXIS, void 0, own);
139413
+ });
139414
+ for (const p of roots) if (!rootKids.some((k) => k.r === p)) out.push({ id: p.node.id, path: p.path, type: p.node.type, label: p.node.label, x: 0, y: 0, width: 0, height: 0, present: false, outside: [], needs: needs.get(p.path) ?? [], limits: [] });
139415
+ for (const [path5, list2] of needs) if (!out.some((b) => b.path === path5)) out.push({ id: path5.slice(path5.lastIndexOf("/") + 1), path: path5, type: typeAt(path5), x: 0, y: 0, width: 0, height: 0, present: false, outside: [], needs: list2, limits: limits.get(path5) ?? [] });
139416
+ return { boxes: out, roots: roots.map((r) => r.path), needs: [input.bodyScroll ? BODY_SCROLL_NEED : ROOT_NEED, VALUES_NEED] };
139417
+ function typeAt(path5) {
139418
+ const ids = path5.split("/");
139419
+ let comp = rootComp;
139420
+ let node;
139421
+ for (const id of ids) {
139422
+ node = comp?.nodes.find((n) => n.id === id);
139423
+ if (!node) return "?";
139424
+ if (isInstance(node.type)) comp = componentOf(node.type);
139425
+ else if (node.type === ROUTER) {
139426
+ const pages = node.parameters?.pages;
139427
+ const wanted = input.pageOf?.(node, path5) ?? pages?.startPage ?? pages?.routes?.[0];
139428
+ comp = wanted ? componentOf(wanted) : void 0;
139429
+ }
139430
+ }
139431
+ return node?.type ?? "?";
139432
+ }
139433
+ }
139434
+ return { draw, isVisualType };
139435
+ }
139436
+ function usesContent(child) {
139437
+ const on = (r) => r.kind === "hug" || r.kind === "fill" && r.basis === "hug";
139438
+ return on(child.width) || on(child.height);
139439
+ }
139440
+ var REPEATER, PAGE, ROUTER, ROOT_NEED, BODY_SCROLL_NEED, VALUES_NEED, ROUTER_NEED, PAGE_NEED, ROWS_NEED, round;
139441
+ var init_draw = __esm({
139442
+ "../nodegx-node-spec/src/draw.ts"() {
139443
+ "use strict";
139444
+ init_layout();
139445
+ init_layout_map();
139446
+ REPEATER = "For Each";
139447
+ PAGE = "Page";
139448
+ ROUTER = "Router";
139449
+ ROOT_NEED = "the root's parent: the app's root element, the viewport's size, stacked down (no node, no spec says so)";
139450
+ BODY_SCROLL_NEED = "the root's parent with Body Scroll on (the project's settings): the app's root element is as tall as its content, at least the viewport, and the document scrolls \u2014 a visual root's percent or share height is its content (no node says so; the project file does)";
139451
+ VALUES_NEED = "the values on the wires: a trace or a graph runner (a drawn screen cannot run the graph)";
139452
+ ROUTER_NEED = "the Router's box: its spec has no size or axis role; drawn as a column that fills its parent and stretches across it, its page the start page unless told (the runtime's element, read once \u2014 not a description)";
139453
+ PAGE_NEED = "the Page has no spec yet: drawn as a column that fills its parent, its children starting at the left (the runtime's element, read once \u2014 not a description)";
139454
+ ROWS_NEED = "a For Each draws one row per item: no items were given, so no row is drawn (a trace or a graph runner has them)";
139455
+ round = (n) => Math.round(n * 10) / 10;
139456
+ }
139457
+ });
139458
+
139459
+ // ../nodegx-node-spec/src/draw-full.ts
139460
+ var drawer;
139461
+ var init_draw_full = __esm({
139462
+ "../nodegx-node-spec/src/draw-full.ts"() {
139463
+ "use strict";
139464
+ init_draw();
139465
+ init_nodes();
139466
+ drawer = createDrawer(specs);
139467
+ }
139468
+ });
139469
+
139470
+ // ../nodegx-node-spec/src/draw-canvas.ts
139471
+ var init_draw_canvas = __esm({
139472
+ "../nodegx-node-spec/src/draw-canvas.ts"() {
139473
+ "use strict";
138639
139474
  }
138640
139475
  });
138641
139476
 
@@ -138646,6 +139481,44 @@ var init_settings_diagnostics = __esm({
138646
139481
  }
138647
139482
  });
138648
139483
 
139484
+ // ../nodegx-node-spec/src/honesty.ts
139485
+ var SCRIPT_TYPES, EXEMPT_TYPES, GRAPH_GRADED_TYPES, SCRIPT_TEXT_PARAMETER;
139486
+ var init_honesty = __esm({
139487
+ "../nodegx-node-spec/src/honesty.ts"() {
139488
+ "use strict";
139489
+ SCRIPT_TYPES = Object.freeze(["Expression", "JavaScriptFunction", "Javascript2", "Logic Builder"]);
139490
+ EXEMPT_TYPES = Object.freeze(["CSS Definition", "Component Children"]);
139491
+ GRAPH_GRADED_TYPES = Object.freeze([
139492
+ "Component Inputs",
139493
+ "Component Outputs",
139494
+ "Event Receiver",
139495
+ "Event Sender",
139496
+ "For Each Actions",
139497
+ "On App Error",
139498
+ "RunTasks",
139499
+ "net.noodl.ActionDispatcher",
139500
+ "net.noodl.ActionHandler",
139501
+ "net.noodl.ComponentObject",
139502
+ "net.noodl.GlobalStore",
139503
+ "net.noodl.GlobalStore.Set",
139504
+ "net.noodl.GlobalStore.Subscribe",
139505
+ "net.noodl.OptimisticUpdate",
139506
+ "net.noodl.ParentComponentObject",
139507
+ "net.noodl.SetComponentObjectProperties",
139508
+ "net.noodl.SetParentComponentObjectProperties",
139509
+ "net.noodl.StateHistory",
139510
+ "net.noodl.StateHistory.Undo",
139511
+ "net.noodl.StateSnapshot"
139512
+ ]);
139513
+ SCRIPT_TEXT_PARAMETER = Object.freeze({
139514
+ JavaScriptFunction: "functionScript",
139515
+ Expression: "expression",
139516
+ Javascript2: "code",
139517
+ "Logic Builder": "generatedCode"
139518
+ });
139519
+ }
139520
+ });
139521
+
138649
139522
  // ../nodegx-node-spec/src/browser.ts
138650
139523
  var init_browser = __esm({
138651
139524
  "../nodegx-node-spec/src/browser.ts"() {
@@ -138666,7 +139539,11 @@ var init_browser = __esm({
138666
139539
  init_runner();
138667
139540
  init_layout();
138668
139541
  init_layout_map();
139542
+ init_draw();
139543
+ init_draw_full();
139544
+ init_draw_canvas();
138669
139545
  init_settings_diagnostics();
139546
+ init_honesty();
138670
139547
  }
138671
139548
  });
138672
139549
 
@@ -139325,7 +140202,10 @@ function exportNode(node) {
139325
140202
  id: node.id,
139326
140203
  type: node.type.name,
139327
140204
  version: node.version,
139328
- variant: node.variant ? node.variant.name : void 0,
140205
+ // P123-S2-VARIANTLOST: `node.variant` is a lookup through the node library's registered modules, and a project
140206
+ // that is not `ProjectModel.instance` (the kit's in-page preview, the headless render) is not one — the name the
140207
+ // node wears is on the node itself, and the runtime looks the variant up by name.
140208
+ variant: node.variant ? node.variant.name : node.variantName || void 0,
139329
140209
  parameters: clone(node.parameters),
139330
140210
  stateParameters: node.stateParameters ? clone(node.stateParameters) : void 0,
139331
140211
  stateTransitions: node.stateTransitions ? clone(node.stateTransitions) : void 0,
@@ -139437,7 +140317,7 @@ function referencesByComponent(components) {
139437
140317
  byFullName.set(component.name, component.name);
139438
140318
  byFullName.set(component.name.substring(CLOUD_COMPONENT_PREFIX.length), component.name);
139439
140319
  }
139440
- const edges = /* @__PURE__ */ new Map();
140320
+ const edges2 = /* @__PURE__ */ new Map();
139441
140321
  for (const component of components) {
139442
140322
  const targets = /* @__PURE__ */ new Set();
139443
140323
  const note = (value) => {
@@ -139449,19 +140329,19 @@ function referencesByComponent(components) {
139449
140329
  note(node.typename);
139450
140330
  Object.values(node.parameters || {}).forEach(note);
139451
140331
  }
139452
- edges.set(component.name, targets);
140332
+ edges2.set(component.name, targets);
139453
140333
  }
139454
- return edges;
140334
+ return edges2;
139455
140335
  }
139456
140336
  function classifyCloudComponents(project) {
139457
140337
  const components = getCloudFunctionComponents(project);
139458
140338
  const endpoints = components.filter(declaresCloudEndpoint);
139459
- const edges = referencesByComponent(components);
140339
+ const edges2 = referencesByComponent(components);
139460
140340
  const reachable = /* @__PURE__ */ new Set();
139461
140341
  const queue = endpoints.map((c) => c.name);
139462
140342
  while (queue.length > 0) {
139463
140343
  const current = queue.shift();
139464
- for (const target of edges.get(current) || []) {
140344
+ for (const target of edges2.get(current) || []) {
139465
140345
  if (reachable.has(target)) continue;
139466
140346
  reachable.add(target);
139467
140347
  queue.push(target);