@weasel-js/forge 1.6.1 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +35 -2
  2. package/dist/{FrameController-BspC9Zr5.d.ts → FrameController-RdGggdGA.d.ts} +34 -1
  3. package/dist/chunk-JNJGIUYU.js +77 -0
  4. package/dist/chunk-JNJGIUYU.js.map +1 -0
  5. package/dist/{chunk-CSAXZIMV.js → chunk-KIFCYY4F.js} +98 -11
  6. package/dist/chunk-KIFCYY4F.js.map +1 -0
  7. package/dist/{chunk-OLWZURU3.js → chunk-NYBMOSSR.js} +11 -12
  8. package/dist/chunk-NYBMOSSR.js.map +1 -0
  9. package/dist/{chunk-DIOLWCH7.js → chunk-SEK6EKHO.js} +38 -94
  10. package/dist/chunk-SEK6EKHO.js.map +1 -0
  11. package/dist/chunk-WEQSLPKT.js +9 -0
  12. package/dist/chunk-WEQSLPKT.js.map +1 -0
  13. package/dist/{chunk-2GXKNXTJ.js → chunk-Y3WZ2EWR.js} +2 -2
  14. package/dist/chunk-Y3WZ2EWR.js.map +1 -0
  15. package/dist/{chunk-JXIWZFD4.js → chunk-YKN3IAEX.js} +12 -10
  16. package/dist/chunk-YKN3IAEX.js.map +1 -0
  17. package/dist/{config-C5Vq5Kjj.d.ts → config-BJIgUhiI.d.ts} +16 -3
  18. package/dist/csf/preview-api.js +1 -1
  19. package/dist/frame/index.css +63 -7
  20. package/dist/frame/index.css.map +1 -1
  21. package/dist/frame/index.d.ts +7 -2
  22. package/dist/frame/index.js +11 -4
  23. package/dist/frame/index.js.map +1 -1
  24. package/dist/index.d.ts +14 -4
  25. package/dist/index.js +3 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/shell/index.css +165 -3
  28. package/dist/shell/index.css.map +1 -1
  29. package/dist/shell/index.d.ts +44 -31
  30. package/dist/shell/index.js +178 -202
  31. package/dist/shell/index.js.map +1 -1
  32. package/dist/test/index.d.ts +2 -1
  33. package/dist/test/index.js +5 -4
  34. package/dist/test/index.js.map +1 -1
  35. package/dist/vite/index.d.ts +7 -1
  36. package/dist/vite/index.js +367 -32
  37. package/dist/vite/index.js.map +1 -1
  38. package/package.json +10 -10
  39. package/dist/chunk-2GXKNXTJ.js.map +0 -1
  40. package/dist/chunk-CSAXZIMV.js.map +0 -1
  41. package/dist/chunk-DIOLWCH7.js.map +0 -1
  42. package/dist/chunk-JXIWZFD4.js.map +0 -1
  43. package/dist/chunk-OLWZURU3.js.map +0 -1
@@ -1,18 +1,21 @@
1
- import { isIndexId, indexId, indexEntries, schemaFromDescription, openChannel, IndexPage } from '../chunk-CSAXZIMV.js';
2
- import { useTrialFrame, createTrialFrames, labGlobals, StoryGlobalsContext, TrialFramesContext, withGlobals, StoryTrial, storyConfig, GLOBALS_KEY, effectiveGlobals, useCssOverrides, TrialHost, shownOptions, isGlobalsPath } from '../chunk-DIOLWCH7.js';
3
- export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-DIOLWCH7.js';
4
- import { loadStories, indexRenderOf } from '../chunk-OLWZURU3.js';
5
- import '../chunk-2GXKNXTJ.js';
1
+ import { isIndexId, indexId, libraryOf, indexEntries, schemaFromDescription, LibraryBadge, openChannel, IndexPage } from '../chunk-KIFCYY4F.js';
2
+ import { isGallery } from '../chunk-WEQSLPKT.js';
3
+ import { useTrialFrame, createTrialFrames, StoryGlobalsContext, TrialFramesContext, StoryTrial, useCssOverrides, TrialHost } from '../chunk-SEK6EKHO.js';
4
+ export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-SEK6EKHO.js';
5
+ import { labGlobals, withGlobals, followApp, storyConfig, GLOBALS_KEY, effectiveGlobals, labOptions, isGlobalsPath } from '../chunk-JNJGIUYU.js';
6
+ import { loadStories, indexRenderOf } from '../chunk-NYBMOSSR.js';
7
+ import '../chunk-Y3WZ2EWR.js';
6
8
  import '../chunk-HRGPFQBW.js';
7
- import { stableStringify, PORT_HANDOFF, FRAME_HELLO, PROTOCOL_VERSION } from '../chunk-JXIWZFD4.js';
9
+ import { stableStringify, PORT_HANDOFF, FRAME_HELLO, PROTOCOL_VERSION } from '../chunk-YKN3IAEX.js';
8
10
  import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, inferTokenType, ToggleBar as ToggleBar$1, Input, TokenPanel, refitScale } from '@weasel-js/labkit';
9
11
  import { createContext, useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect, useId, useContext } from 'react';
10
12
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
13
  import { createRoot } from 'react-dom/client';
12
- import { InfoIcon, ToggleBar, Checkbox, Dialog, Badge, DisclosureMark, Button as Button$1, TuneIcon, Callout } from '@weasel-js/ui';
14
+ import { InfoIcon, ToggleBar, Checkbox, Dialog, DisclosureMark, Button as Button$1, TuneIcon, Callout } from '@weasel-js/ui';
13
15
  import { TOKEN_MANIFEST, THEME_SOURCES, isByAxis } from '@weasel-js/theme';
14
16
  import { scale, httpThemeApi } from '@weasel-js/theme/engine';
15
17
  import { f, valueAtPath, isConfigBranch } from '@weasel-js/labkit/config';
18
+ import { useLatest } from '@weasel-js/core';
16
19
 
17
20
  function Finding({ finding }) {
18
21
  return /* @__PURE__ */ jsxs("li", { className: "fg-a11y__finding", children: [
@@ -105,6 +108,7 @@ function createStoryChanges() {
105
108
  }
106
109
  };
107
110
  }
111
+ var DependenciesContext = createContext(null);
108
112
  var SEED_REF = /^\{seeds\.([^}]+)\}$/;
109
113
  var RULE_KEYS = { factors: "factors", ratio: "ratio", step: "step" };
110
114
  function selectionFor(axes, globals) {
@@ -445,113 +449,6 @@ function breadcrumb(title, name) {
445
449
  const segments = [...title.split("/"), name].filter((segment, i) => i === 0 || segment.toLowerCase() !== "index");
446
450
  return segments.join(" > ");
447
451
  }
448
-
449
- // src/shell/tree/buildComponents.ts
450
- var LIBRARIES = [
451
- ["/packages/ui/", "ui"],
452
- ["/packages/labkit/", "labkit"],
453
- ["/packages/forge/", "forge"],
454
- ["/apps/draw/", "draw"],
455
- ["/apps/forge/", "forge"]
456
- ];
457
- function libraryOf(entry) {
458
- const file = entry.file.replaceAll("\\", "/");
459
- for (const [marker, label] of LIBRARIES) {
460
- if (file.includes(marker)) return label;
461
- }
462
- return entry.title.split("/")[0];
463
- }
464
- function buildComponents(index) {
465
- const byTitle = /* @__PURE__ */ new Map();
466
- for (const entry of index) {
467
- let row = byTitle.get(entry.title);
468
- if (!row) {
469
- const segments = entry.title.split("/");
470
- row = {
471
- path: entry.title,
472
- label: segments[segments.length - 1],
473
- library: libraryOf(entry),
474
- index: indexEntries([entry])[0],
475
- entries: []
476
- };
477
- byTitle.set(entry.title, row);
478
- }
479
- row.entries.push(entry);
480
- }
481
- const rows = [...byTitle.values()];
482
- disambiguate(rows);
483
- return rows.sort(
484
- (a, b) => a.label.localeCompare(b.label) || a.library.localeCompare(b.library) || a.path.localeCompare(b.path)
485
- );
486
- }
487
- function disambiguate(rows) {
488
- const byLabel = /* @__PURE__ */ new Map();
489
- for (const row of rows) {
490
- const key = `${row.library}\0${row.label}`;
491
- const group = byLabel.get(key);
492
- if (group) group.push(row);
493
- else byLabel.set(key, [row]);
494
- }
495
- for (const group of byLabel.values()) {
496
- if (group.length < 2) continue;
497
- for (const row of group) {
498
- const segments = row.path.split("/");
499
- for (let take = 2; take <= segments.length; take++) {
500
- row.label = segments.slice(-take).join("/");
501
- if (group.every((other) => other === row || !other.path.endsWith(`/${row.label}`))) break;
502
- }
503
- }
504
- }
505
- }
506
- function filterComponents(rows, query) {
507
- const needle = query.trim().toLowerCase();
508
- if (!needle) return [...rows];
509
- const out = [];
510
- for (const row of rows) {
511
- if (`${row.path}/${row.library}`.toLowerCase().includes(needle)) {
512
- out.push(row);
513
- continue;
514
- }
515
- const entries = row.entries.filter((entry) => entry.name.toLowerCase().includes(needle));
516
- if (entries.length > 0) out.push({ path: row.path, label: row.label, library: row.library, entries });
517
- }
518
- return out;
519
- }
520
- function componentNodes(rows) {
521
- const root = [];
522
- const folders = /* @__PURE__ */ new Map();
523
- for (const row of rows) {
524
- const labels = row.label.split("/");
525
- const title = row.path.split("/");
526
- let siblings = root;
527
- let tag = row.library;
528
- for (let i = 0; i < labels.length - 1; i++) {
529
- const path = title.slice(0, title.length - labels.length + i + 1).join("/");
530
- let folder = folders.get(path);
531
- if (!folder) {
532
- folder = { kind: "folder", label: labels[i], path, tag, children: [] };
533
- folders.set(path, folder);
534
- siblings.push(folder);
535
- }
536
- siblings = folder.children;
537
- tag = void 0;
538
- }
539
- siblings.push({
540
- kind: "folder",
541
- label: labels[labels.length - 1],
542
- path: row.path,
543
- tag,
544
- ...row.index ? { index: row.index } : {},
545
- children: row.entries.map((entry) => ({ kind: "story", entry }))
546
- });
547
- }
548
- return root;
549
- }
550
- function librariesIn(index) {
551
- return [...new Set(index.map(libraryOf))].sort((a, b) => a.localeCompare(b));
552
- }
553
-
554
- // src/shell/info/dossier.ts
555
452
  var GLOBALS = "$globals";
556
453
  function leaves(shape, prefix = "") {
557
454
  const out = [];
@@ -745,20 +642,21 @@ function GlobalSelect({ name, declaration, values, set, labeled = false }) {
745
642
  width: labeled ? "fill" : "fit",
746
643
  selectedKey: String(values[name]),
747
644
  onSelectionChange: (value) => set(name, value),
748
- options: shownOptions(declaration, values)
645
+ options: labOptions(declaration, values)
749
646
  }
750
647
  );
751
648
  }
752
649
  function GlobalsToolbar({ declarations }) {
753
650
  const [values, set] = useLabGlobals(declarations);
754
651
  const entries = Object.entries(declarations);
755
- return /* @__PURE__ */ jsx("div", { className: "fg-globals", role: "toolbar", "aria-label": "Globals", children: entries.filter(([, declaration]) => declaration.under === void 0).map(([key, declaration]) => {
756
- const nested = entries.filter(([, other]) => other.under === key);
757
- return /* @__PURE__ */ jsxs("div", { className: "fg-globals__item", children: [
758
- /* @__PURE__ */ jsx(GlobalSelect, { name: key, declaration, values, set }),
759
- nested.length > 0 ? /* @__PURE__ */ jsx(NestedGlobals, { label: declaration.label, nested, values, set }) : null
760
- ] }, key);
761
- }) });
652
+ const top = entries.filter(([, declaration]) => declaration.under === void 0);
653
+ return /* @__PURE__ */ jsxs("div", { className: "fg-globals", role: "toolbar", "aria-label": "Globals", children: [
654
+ top.map(([key, declaration]) => /* @__PURE__ */ jsx("div", { className: "fg-globals__item", children: /* @__PURE__ */ jsx(GlobalSelect, { name: key, declaration, values, set }) }, key)),
655
+ top.map(([key, declaration]) => {
656
+ const nested = entries.filter(([, other]) => other.under === key);
657
+ return nested.length > 0 ? /* @__PURE__ */ jsx(NestedGlobals, { label: declaration.label, nested, values, set }, key) : null;
658
+ })
659
+ ] });
762
660
  }
763
661
  function NestedGlobals({
764
662
  label,
@@ -802,7 +700,9 @@ function LabGlobals({
802
700
  onChange
803
701
  }) {
804
702
  const [values] = useLabGlobals(declarations);
805
- useEffect(() => onChange(values), [values, onChange]);
703
+ const { mode } = useLabContext();
704
+ const resolved = useMemo(() => followApp(declarations, values, { mode }), [declarations, values, mode]);
705
+ useEffect(() => onChange(resolved), [resolved, onChange]);
806
706
  return null;
807
707
  }
808
708
 
@@ -872,6 +772,17 @@ function sortFolder(nodes) {
872
772
  for (const folder of folders) folder.children = sortFolder(folder.children);
873
773
  return [...folders, ...nodes.filter((n) => n.kind === "story")];
874
774
  }
775
+ function foldGalleries(nodes) {
776
+ return nodes.map((node) => {
777
+ if (node.kind !== "folder") return node;
778
+ const children = foldGalleries(node.children);
779
+ const [only] = children;
780
+ if (!node.index && children.length === 1 && only?.kind === "folder" && only.path === `${node.path}/${only.label}` && only.index && isGallery(only.index)) {
781
+ return { ...only, label: node.label, tag: node.tag ?? only.tag };
782
+ }
783
+ return { ...node, children };
784
+ });
785
+ }
875
786
  function buildTree(index) {
876
787
  const root = [];
877
788
  const folders = /* @__PURE__ */ new Map();
@@ -908,9 +819,102 @@ function filterTree(nodes, query) {
908
819
  });
909
820
  return walk(nodes);
910
821
  }
822
+
823
+ // src/shell/tree/buildComponents.ts
824
+ function buildComponents(index) {
825
+ const byTitle = /* @__PURE__ */ new Map();
826
+ const pages = new Map(indexEntries(index).map((page) => [page.title, page]));
827
+ for (const entry of index) {
828
+ let row = byTitle.get(entry.title);
829
+ if (!row) {
830
+ const segments = entry.title.split("/");
831
+ row = {
832
+ path: entry.title,
833
+ label: segments[segments.length - 1],
834
+ library: libraryOf(entry),
835
+ index: pages.get(entry.title),
836
+ entries: []
837
+ };
838
+ byTitle.set(entry.title, row);
839
+ }
840
+ row.entries.push(entry);
841
+ }
842
+ const rows = [...byTitle.values()];
843
+ disambiguate(rows);
844
+ return rows.sort(
845
+ (a, b) => a.label.localeCompare(b.label) || a.library.localeCompare(b.library) || a.path.localeCompare(b.path)
846
+ );
847
+ }
848
+ function disambiguate(rows) {
849
+ const byLabel = /* @__PURE__ */ new Map();
850
+ for (const row of rows) {
851
+ const key = `${row.library}\0${row.label}`;
852
+ const group = byLabel.get(key);
853
+ if (group) group.push(row);
854
+ else byLabel.set(key, [row]);
855
+ }
856
+ for (const group of byLabel.values()) {
857
+ if (group.length < 2) continue;
858
+ for (const row of group) {
859
+ const segments = row.path.split("/");
860
+ for (let take = 2; take <= segments.length; take++) {
861
+ row.label = segments.slice(-take).join("/");
862
+ if (group.every((other) => other === row || !other.path.endsWith(`/${row.label}`))) break;
863
+ }
864
+ }
865
+ }
866
+ }
867
+ function filterComponents(rows, query) {
868
+ const needle = query.trim().toLowerCase();
869
+ if (!needle) return [...rows];
870
+ const out = [];
871
+ for (const row of rows) {
872
+ if (`${row.path}/${row.library}`.toLowerCase().includes(needle)) {
873
+ out.push(row);
874
+ continue;
875
+ }
876
+ const entries = row.entries.filter((entry) => entry.name.toLowerCase().includes(needle));
877
+ if (entries.length > 0) out.push({ path: row.path, label: row.label, library: row.library, entries });
878
+ }
879
+ return out;
880
+ }
881
+ function componentNodes(rows) {
882
+ const root = [];
883
+ const folders = /* @__PURE__ */ new Map();
884
+ for (const row of rows) {
885
+ const labels = row.label.split("/");
886
+ const title = row.path.split("/");
887
+ let siblings = root;
888
+ let tag = row.library;
889
+ for (let i = 0; i < labels.length - 1; i++) {
890
+ const path = title.slice(0, title.length - labels.length + i + 1).join("/");
891
+ let folder = folders.get(path);
892
+ if (!folder) {
893
+ folder = { kind: "folder", label: labels[i], path, tag, children: [] };
894
+ folders.set(path, folder);
895
+ siblings.push(folder);
896
+ }
897
+ siblings = folder.children;
898
+ tag = void 0;
899
+ }
900
+ siblings.push({
901
+ kind: "folder",
902
+ label: labels[labels.length - 1],
903
+ path: row.path,
904
+ tag,
905
+ ...row.index ? { index: row.index } : {},
906
+ children: row.entries.map((entry) => ({ kind: "story", entry }))
907
+ });
908
+ }
909
+ return foldGalleries(root);
910
+ }
911
+ function librariesIn(index) {
912
+ return [...new Set(index.map(libraryOf))].sort((a, b) => a.localeCompare(b));
913
+ }
911
914
  var VIEWS = [
912
915
  { value: "tree", label: "Tree" },
913
- { value: "components", label: "Components" }
916
+ { value: "components", label: "Components" },
917
+ { value: "gallery", label: "Gallery" }
914
918
  ];
915
919
  var keyOf = (node) => node.kind === "folder" ? `folder:${node.path}` : `story:${node.entry.id}`;
916
920
  function ancestorsOf(entry) {
@@ -923,14 +927,8 @@ function ancestorsOf(entry) {
923
927
  }
924
928
  return paths;
925
929
  }
926
- var LIBRARY_TONES = {
927
- ui: "color-mix(in oklab, var(--wzl-swatch-blue), var(--wzl-fg) 35%)",
928
- labkit: "color-mix(in oklab, var(--wzl-swatch-teal), var(--wzl-fg) 45%)",
929
- forge: "color-mix(in oklab, var(--wzl-swatch-amber), var(--wzl-fg) 40%)",
930
- draw: "color-mix(in oklab, var(--wzl-swatch-rose), var(--wzl-fg) 35%)"
931
- };
932
- function LibraryBadge({ library }) {
933
- return /* @__PURE__ */ jsx(Badge, { status: "muted", tone: LIBRARY_TONES[library], variant: "subtle", size: "sm", className: "fg-tree__tag", children: library });
930
+ function LibraryBadge2({ library }) {
931
+ return /* @__PURE__ */ jsx(LibraryBadge, { library, className: "fg-tree__tag" });
934
932
  }
935
933
  function StoryTree({ ctx, index }) {
936
934
  const headingId = useId();
@@ -943,11 +941,18 @@ function StoryTree({ ctx, index }) {
943
941
  const items = useRef(/* @__PURE__ */ new Map());
944
942
  const libraries = useMemo(() => librariesIn(index), [index]);
945
943
  const kept = useMemo(() => index.filter((entry) => !hidden[libraryOf(entry)]), [index, hidden]);
946
- const tree = useMemo(() => buildTree(kept), [kept]);
947
- const components = useMemo(() => buildComponents(kept), [kept]);
944
+ const [galleryEntries, rest] = useMemo(() => {
945
+ const yes = [];
946
+ const no = [];
947
+ for (const entry of kept) (isGallery(entry) ? yes : no).push(entry);
948
+ return [yes, no];
949
+ }, [kept]);
950
+ const tree = useMemo(() => buildTree(rest), [rest]);
951
+ const components = useMemo(() => buildComponents(rest), [rest]);
952
+ const galleries = useMemo(() => buildComponents(galleryEntries), [galleryEntries]);
948
953
  const shown = useMemo(
949
- () => view === "components" ? componentNodes(filterComponents(components, query)) : filterTree(tree, query),
950
- [view, components, tree, query]
954
+ () => view === "tree" ? filterTree(tree, query) : componentNodes(filterComponents(view === "gallery" ? galleries : components, query)),
955
+ [view, components, galleries, tree, query]
951
956
  );
952
957
  const routable = useMemo(() => [...kept, ...indexEntries(kept)], [kept]);
953
958
  const routed = routable.find((entry) => entry.id === route);
@@ -989,7 +994,7 @@ function StoryTree({ ctx, index }) {
989
994
  return;
990
995
  }
991
996
  if (folds[node.path] === false) {
992
- setFolds(({ [node.path]: _dropped, ...rest }) => rest);
997
+ setFolds(({ [node.path]: _dropped, ...rest2 }) => rest2);
993
998
  }
994
999
  activate(node.index, another);
995
1000
  };
@@ -1080,7 +1085,7 @@ function StoryTree({ ctx, index }) {
1080
1085
  }
1081
1086
  ),
1082
1087
  /* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label }),
1083
- node.tag ? /* @__PURE__ */ jsx(LibraryBadge, { library: node.tag }) : null
1088
+ node.tag ? /* @__PURE__ */ jsx(LibraryBadge2, { library: node.tag }) : null
1084
1089
  ]
1085
1090
  }
1086
1091
  ),
@@ -1106,7 +1111,7 @@ function StoryTree({ ctx, index }) {
1106
1111
  },
1107
1112
  children: [
1108
1113
  /* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label ?? entry.name }),
1109
- node.tag ? /* @__PURE__ */ jsx(LibraryBadge, { library: node.tag }) : null
1114
+ node.tag ? /* @__PURE__ */ jsx(LibraryBadge2, { library: node.tag }) : null
1110
1115
  ]
1111
1116
  },
1112
1117
  keyOf(node)
@@ -1137,7 +1142,7 @@ function StoryTree({ ctx, index }) {
1137
1142
  {
1138
1143
  isSelected: !hidden[library],
1139
1144
  onChange: (on) => setHidden((prev) => ({ ...prev, [library]: !on })),
1140
- children: /* @__PURE__ */ jsx(LibraryBadge, { library })
1145
+ children: /* @__PURE__ */ jsx(LibraryBadge2, { library })
1141
1146
  },
1142
1147
  library
1143
1148
  ))
@@ -1202,6 +1207,9 @@ function createAnswerBook() {
1202
1207
  function IndexTrial({ bundle, setup, config }) {
1203
1208
  const lab = useLabContext();
1204
1209
  const trialId = useContext(TrialIdContext);
1210
+ const graph = useContext(DependenciesContext);
1211
+ const gallery = isGallery(bundle);
1212
+ const dependencies = gallery ? void 0 : graph?.[bundle.title];
1205
1213
  const open = (id) => {
1206
1214
  if (!trialId || !lab.instruments.some((instrument) => instrument.name === id)) return;
1207
1215
  lab.swapTrial(trialId, id);
@@ -1210,9 +1218,13 @@ function IndexTrial({ bundle, setup, config }) {
1210
1218
  const render = (globals) => {
1211
1219
  const env = {
1212
1220
  title: bundle.title,
1221
+ library: bundle.library,
1213
1222
  ...bundle.description === void 0 ? {} : { description: bundle.description },
1214
1223
  stories: bundle.stories,
1215
1224
  descriptions: bundle.descriptions,
1225
+ gallery,
1226
+ galleries: bundle.galleries,
1227
+ ...dependencies ? { dependencies } : {},
1216
1228
  globals,
1217
1229
  decorators: setup.decorators ?? [],
1218
1230
  open,
@@ -1223,26 +1235,8 @@ function IndexTrial({ bundle, setup, config }) {
1223
1235
  };
1224
1236
  return /* @__PURE__ */ jsx(TrialHost, { layout: "fullscreen", setup, config, children: render });
1225
1237
  }
1226
- function annotationsOn(frames) {
1227
- return {
1228
- targets: (_state, _config, trial) => {
1229
- const ref = frames.hostRef(trial.id);
1230
- const frame = frames.get(trial.id);
1231
- const box = frame.size ?? { width: ref.current?.clientWidth ?? 0, height: ref.current?.clientHeight ?? 0 };
1232
- const capture = frame.capture;
1233
- return [
1234
- {
1235
- id: "story",
1236
- ref,
1237
- content: { w: Math.max(1, box.width), h: Math.max(1, box.height) },
1238
- ...capture ? { base: () => capture() } : {}
1239
- }
1240
- ];
1241
- }
1242
- };
1243
- }
1244
1238
  function documentInstrument(options) {
1245
- const { entry, loaded, setup, globals = {}, frames } = options;
1239
+ const { entry, loaded, setup, globals = {} } = options;
1246
1240
  const schema = loaded.kind === "story" ? loaded.story.config : f.schema({});
1247
1241
  const config = withGlobals(schema, globals);
1248
1242
  const initialState = loaded.kind === "story" ? loaded.story.initialState : null;
@@ -1253,7 +1247,6 @@ function documentInstrument(options) {
1253
1247
  defaultConfig: () => config.defaults(),
1254
1248
  initialState: (c) => initialState?.(c) ?? null,
1255
1249
  render: (ctx) => loaded.kind === "story" ? /* @__PURE__ */ jsx(StoryTrial, { story: loaded.story, setup, ctx }) : /* @__PURE__ */ jsx(IndexTrial, { bundle: loaded.bundle, setup, config: ctx.config }),
1256
- ...frames ? { annotations: annotationsOn(frames) } : {},
1257
1250
  ...loaded.kind === "story" && loaded.story.viewport ? { stage: { size: { width: loaded.story.viewport.width, height: loaded.story.viewport.height } } } : {}
1258
1251
  };
1259
1252
  }
@@ -1335,17 +1328,16 @@ function FrameView(props) {
1335
1328
  const slotRef = useRef(null);
1336
1329
  const inView = useInView(hostRef);
1337
1330
  const link = useRef(null);
1338
- const latest = useRef({ ...props, globals, overrides, pool, lab, title });
1339
- latest.current = { ...props, globals, overrides, pool, lab, title };
1331
+ const latest = useLatest({ ...props, globals, overrides, pool, lab, title });
1340
1332
  const [fault, setFault] = useState(null);
1341
1333
  const [pending, setPending] = useState(true);
1342
- const init = (current) => {
1334
+ const init = useCallback((current) => {
1343
1335
  const { ctx: live, globals: liveGlobals } = latest.current;
1344
1336
  current.awaiting = null;
1345
1337
  current.sent = { config: storyConfig(live.config), state: live.state, globals: liveGlobals };
1346
1338
  current.inputs += 1;
1347
1339
  current.channel.send({ type: "init", ...current.sent });
1348
- };
1340
+ }, [latest]);
1349
1341
  const receive = (current, msg) => {
1350
1342
  const { ctx: live, answers: answers2, onReady } = latest.current;
1351
1343
  switch (msg.type) {
@@ -1466,8 +1458,7 @@ function FrameView(props) {
1466
1458
  port2
1467
1459
  ]);
1468
1460
  };
1469
- const connectRef = useRef(connect);
1470
- connectRef.current = connect;
1461
+ const connectRef = useLatest(connect);
1471
1462
  useEffect(() => {
1472
1463
  const slot = slotRef.current;
1473
1464
  if (!inView || !slot) return;
@@ -1502,7 +1493,7 @@ function FrameView(props) {
1502
1493
  setPending(true);
1503
1494
  setFault(null);
1504
1495
  };
1505
- }, [inView, src]);
1496
+ }, [inView, src, connectRef, latest]);
1506
1497
  useEffect(() => {
1507
1498
  iframeRef.current?.toggleAttribute("data-pending", pending && !fault);
1508
1499
  }, [pending, fault, inView, src]);
@@ -1540,7 +1531,7 @@ function FrameView(props) {
1540
1531
  input = true;
1541
1532
  }
1542
1533
  if (input) setFault((shown) => shown?.phase === "render" ? null : shown);
1543
- }, [ctx.config, ctx.state, globals, descriptionKey]);
1534
+ }, [ctx.config, ctx.state, globals, descriptionKey, init]);
1544
1535
  return /* @__PURE__ */ jsxs(
1545
1536
  "div",
1546
1537
  {
@@ -1559,26 +1550,8 @@ function FrameView(props) {
1559
1550
  }
1560
1551
  );
1561
1552
  }
1562
- function annotationsOn2(frames) {
1563
- return {
1564
- targets: (_state, _config, trial) => {
1565
- const ref = frames.hostRef(trial.id);
1566
- const frame = frames.get(trial.id);
1567
- const box = frame.size ?? { width: ref.current?.clientWidth ?? 0, height: ref.current?.clientHeight ?? 0 };
1568
- const capture = frame.capture;
1569
- return [
1570
- {
1571
- id: "story",
1572
- ref,
1573
- content: { w: Math.max(1, box.width), h: Math.max(1, box.height) },
1574
- ...capture ? { base: () => capture() } : {}
1575
- }
1576
- ];
1577
- }
1578
- };
1579
- }
1580
1553
  function storyInstrument(options) {
1581
- const { entry, ready, answers, frameUrl, onReady, globals = {}, frames } = options;
1554
+ const { entry, ready, answers, frameUrl, onReady, globals = {} } = options;
1582
1555
  const config = withGlobals(
1583
1556
  ready ? schemaFromDescription(ready.schema, answers) : f.schema({}),
1584
1557
  globals
@@ -1601,7 +1574,6 @@ function storyInstrument(options) {
1601
1574
  ctx
1602
1575
  }
1603
1576
  ),
1604
- ...frames ? { annotations: annotationsOn2(frames) } : {},
1605
1577
  ...ready?.viewport ? { stage: { size: { width: ready.viewport.width, height: ready.viewport.height } } } : {}
1606
1578
  };
1607
1579
  }
@@ -1612,7 +1584,7 @@ var NO_SETUP = {};
1612
1584
  var pruned = (map, ids) => [...map.keys()].every((id) => ids.has(id)) ? map : new Map([...map].filter(([id]) => ids.has(id)));
1613
1585
  var toError = (error) => error instanceof Error ? error : new Error(String(error));
1614
1586
  function useStoryRegistry(index, options) {
1615
- const { frameUrl, globals = NO_GLOBALS2, frames, importers, setup = NO_SETUP, load = loadStories } = options;
1587
+ const { frameUrl, globals = NO_GLOBALS2, importers, setup = NO_SETUP, load = loadStories } = options;
1616
1588
  const [readies, setReadies] = useState(() => /* @__PURE__ */ new Map());
1617
1589
  const [loaded, setLoaded] = useState(() => /* @__PURE__ */ new Map());
1618
1590
  const [faults, setFaults] = useState(() => /* @__PURE__ */ new Map());
@@ -1633,8 +1605,7 @@ function useStoryRegistry(index, options) {
1633
1605
  setRevisions((prev) => pruned(prev, ids));
1634
1606
  }, [index]);
1635
1607
  const imports = useRef(/* @__PURE__ */ new Map());
1636
- const live = useRef({ index, importers, setup, load });
1637
- live.current = { index, importers, setup, load };
1608
+ const live = useLatest({ index, importers, setup, load });
1638
1609
  const importOf = useCallback((file) => {
1639
1610
  let loading = imports.current.get(file);
1640
1611
  if (!loading) {
@@ -1643,7 +1614,7 @@ function useStoryRegistry(index, options) {
1643
1614
  imports.current.set(file, loading);
1644
1615
  }
1645
1616
  return loading;
1646
- }, []);
1617
+ }, [live]);
1647
1618
  const request = useCallback(
1648
1619
  (entry) => {
1649
1620
  const { setup: current, load: loadModule } = live.current;
@@ -1677,7 +1648,10 @@ function useStoryRegistry(index, options) {
1677
1648
  kind: "index",
1678
1649
  bundle: {
1679
1650
  title: entry.title,
1651
+ library: libraryOf(entry),
1680
1652
  ...entry.componentDescription === void 0 ? {} : { description: entry.componentDescription },
1653
+ ...entry.tags === void 0 ? {} : { tags: entry.tags },
1654
+ galleries: entries.filter(isGallery).map((e) => e.id),
1681
1655
  stories,
1682
1656
  descriptions: Object.fromEntries(entries.flatMap((e) => e.description ? [[e.id, e.description]] : [])),
1683
1657
  render: mods.map(indexRenderOf).find((render) => render !== null) ?? null
@@ -1702,7 +1676,7 @@ function useStoryRegistry(index, options) {
1702
1676
  settle2(ids, result);
1703
1677
  }).catch((error) => fail(ids, error));
1704
1678
  },
1705
- [importOf]
1679
+ [importOf, live]
1706
1680
  );
1707
1681
  const reload = useCallback(
1708
1682
  (file) => {
@@ -1713,7 +1687,7 @@ function useStoryRegistry(index, options) {
1713
1687
  setFaults((prev) => pruned(prev, new Set([...prev.keys()].filter((id) => !ids.has(id)))));
1714
1688
  setGenerations((prev) => new Map(prev).set(file, (prev.get(file) ?? 0) + 1));
1715
1689
  },
1716
- []
1690
+ [live]
1717
1691
  );
1718
1692
  const committed = useRef({ built: /* @__PURE__ */ new Map(), books: /* @__PURE__ */ new Map(), list: [] });
1719
1693
  const cache = useMemo(() => {
@@ -1731,26 +1705,25 @@ function useStoryRegistry(index, options) {
1731
1705
  const answers = previous.books.get(entry.id) ?? createAnswerBook();
1732
1706
  books.set(entry.id, answers);
1733
1707
  const held = previous.built.get(entry.id);
1734
- const kept = held && held.entryKey === entryKey && held.ready === ready && held.loaded === had && held.fault === fault && held.frameUrl === frameUrl && held.globals === globals && held.frames === frames && held.setup === setup && held.inDocument === inDocument && held.generation === generation && held.revision === revision ? held : {
1708
+ const kept = held && held.entryKey === entryKey && held.ready === ready && held.loaded === had && held.fault === fault && held.frameUrl === frameUrl && held.globals === globals && held.setup === setup && held.inDocument === inDocument && held.generation === generation && held.revision === revision ? held : {
1735
1709
  entryKey,
1736
1710
  ready,
1737
1711
  loaded: had,
1738
1712
  fault,
1739
1713
  frameUrl,
1740
1714
  globals,
1741
- frames,
1742
1715
  setup,
1743
1716
  inDocument,
1744
1717
  generation,
1745
1718
  revision,
1746
- instrument: !inDocument ? storyInstrument({ entry, ready, answers, frameUrl, onReady, globals, ...frames ? { frames } : {} }) : had ? documentInstrument({ entry, loaded: had, setup, globals, ...frames ? { frames } : {} }) : pendingInstrument({ entry, globals, load: request, generation, ...fault ? { fault } : {} })
1719
+ instrument: !inDocument ? storyInstrument({ entry, ready, answers, frameUrl, onReady, globals }) : had ? documentInstrument({ entry, loaded: had, setup, globals }) : pendingInstrument({ entry, globals, load: request, generation, ...fault ? { fault } : {} })
1747
1720
  };
1748
1721
  built.set(entry.id, kept);
1749
1722
  return kept.instrument;
1750
1723
  });
1751
1724
  const unchanged = list.length === previous.list.length && list.every((i, n) => i === previous.list[n]);
1752
1725
  return { built, books, list: unchanged ? previous.list : list };
1753
- }, [index, readies, loaded, faults, generations, revisions, frameUrl, globals, frames, setup, importers, onReady, request]);
1726
+ }, [index, readies, loaded, faults, generations, revisions, frameUrl, globals, setup, importers, onReady, request]);
1754
1727
  useLayoutEffect(() => {
1755
1728
  committed.current = cache;
1756
1729
  }, [cache]);
@@ -1802,7 +1775,7 @@ function useInfoShortcut(open) {
1802
1775
  return () => window.removeEventListener("keydown", onKeyDown);
1803
1776
  }, [open]);
1804
1777
  }
1805
- function Workshop({ index, frameUrl, importers, setup, changes, config, stories = [], storageKey, storage }) {
1778
+ function Workshop({ index, frameUrl, importers, setup, changes, config, stories = [], storageKey, storage, dependencies }) {
1806
1779
  const declarations = config?.globals ?? NO_DECLARATIONS;
1807
1780
  const [frames] = useState(createTrialFrames);
1808
1781
  const [pool, setPool] = useState(null);
@@ -1820,7 +1793,6 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
1820
1793
  const registry = useStoryRegistry(entries, {
1821
1794
  frameUrl,
1822
1795
  globals: declarations,
1823
- frames,
1824
1796
  ...importers ? { importers } : {},
1825
1797
  ...setup ? { setup } : {}
1826
1798
  });
@@ -1857,7 +1829,7 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
1857
1829
  stories.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "fg-empty__globs", "aria-label": "Story globs", children: stories.map((glob) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: glob }) }, glob)) }) : null
1858
1830
  ] });
1859
1831
  }
1860
- return /* @__PURE__ */ jsx(StoryGlobalsContext.Provider, { value: labValues, children: /* @__PURE__ */ jsx(FramePoolContext.Provider, { value: pool, children: /* @__PURE__ */ jsx(TrialFramesContext.Provider, { value: frames, children: /* @__PURE__ */ jsxs(
1832
+ return /* @__PURE__ */ jsx(StoryGlobalsContext.Provider, { value: labValues, children: /* @__PURE__ */ jsx(DependenciesContext.Provider, { value: dependencies ?? null, children: /* @__PURE__ */ jsx(FramePoolContext.Provider, { value: pool, children: /* @__PURE__ */ jsx(TrialFramesContext.Provider, { value: frames, children: /* @__PURE__ */ jsxs(
1861
1833
  Lab,
1862
1834
  {
1863
1835
  title: "weaselforge",
@@ -1886,7 +1858,7 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
1886
1858
  /* @__PURE__ */ jsx(LabGlobals, { declarations, onChange: reportLabValues })
1887
1859
  ]
1888
1860
  }
1889
- ) }) }) });
1861
+ ) }) }) }) });
1890
1862
  }
1891
1863
  function mountWorkshop({ container, ...props }) {
1892
1864
  const target = container ?? document.getElementById("root");
@@ -1905,6 +1877,10 @@ function mountWorkshop({ container, ...props }) {
1905
1877
  root.render(/* @__PURE__ */ jsx(Workshop, { ...current }));
1906
1878
  },
1907
1879
  reloadStory: (file) => changes.emit(file),
1880
+ setDependencies(dependencies) {
1881
+ current = { ...current, dependencies };
1882
+ root.render(/* @__PURE__ */ jsx(Workshop, { ...current }));
1883
+ },
1908
1884
  unmount: () => root.unmount()
1909
1885
  };
1910
1886
  }