@weasel-js/forge 1.10.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/{FrameController-UeTY7ubF.d.ts → FrameController-BRhShqut.d.ts} +5 -0
  2. package/dist/{chunk-FL2MUPUL.js → chunk-2OJAV522.js} +2 -2
  3. package/dist/chunk-2OJAV522.js.map +1 -0
  4. package/dist/{chunk-DFKCLZKS.js → chunk-7BQPHMA2.js} +2 -2
  5. package/dist/chunk-7BQPHMA2.js.map +1 -0
  6. package/dist/{chunk-DKWHGSBZ.js → chunk-DPTECNYC.js} +3 -3
  7. package/dist/{chunk-DKWHGSBZ.js.map → chunk-DPTECNYC.js.map} +1 -1
  8. package/dist/{chunk-E2FHBDNQ.js → chunk-J3GYSNCK.js} +4 -4
  9. package/dist/{chunk-E2FHBDNQ.js.map → chunk-J3GYSNCK.js.map} +1 -1
  10. package/dist/{chunk-AP3DRQLI.js → chunk-NVNTQ4AT.js} +87 -9
  11. package/dist/chunk-NVNTQ4AT.js.map +1 -0
  12. package/dist/{chunk-BIAEGWGJ.js → chunk-R5F44ECM.js} +17 -7
  13. package/dist/chunk-R5F44ECM.js.map +1 -0
  14. package/dist/{chunk-GB6PRJET.js → chunk-SQ4KCIEH.js} +3 -3
  15. package/dist/{chunk-GB6PRJET.js.map → chunk-SQ4KCIEH.js.map} +1 -1
  16. package/dist/{config-Cz7ZyGF-.d.ts → config-BwScQukB.d.ts} +5 -4
  17. package/dist/csf/preview-api.js +2 -2
  18. package/dist/frame/index.d.ts +3 -2
  19. package/dist/frame/index.js +7 -8
  20. package/dist/frame/index.js.map +1 -1
  21. package/dist/index.d.ts +5 -4
  22. package/dist/index.js +3 -3
  23. package/dist/shell/index.css +3 -0
  24. package/dist/shell/index.css.map +1 -1
  25. package/dist/shell/index.d.ts +4 -3
  26. package/dist/shell/index.js +140 -69
  27. package/dist/shell/index.js.map +1 -1
  28. package/dist/test/index.d.ts +2 -1
  29. package/dist/test/index.js +5 -5
  30. package/dist/vite/index.d.ts +1 -1
  31. package/dist/vite/index.js.map +1 -1
  32. package/package.json +8 -6
  33. package/dist/chunk-AP3DRQLI.js.map +0 -1
  34. package/dist/chunk-BIAEGWGJ.js.map +0 -1
  35. package/dist/chunk-DFKCLZKS.js.map +0 -1
  36. package/dist/chunk-FL2MUPUL.js.map +0 -1
@@ -1,27 +1,27 @@
1
- import { parseRoute, formatRoute, paramsToKnobs, knobPaths, sameKnob, knobsToParams, PLAYHEAD_PARAM, reservedParams } from '../chunk-DKWHGSBZ.js';
1
+ import { parseRoute, formatRoute, paramsToKnobs, knobPaths, sameKnob, knobsToParams, PLAYHEAD_PARAM, reservedParams } from '../chunk-DPTECNYC.js';
2
2
  import { DEFAULT_TITLE } from '../chunk-5DXUNLV2.js';
3
- import { TITLE_LIBRARIES, isIndexId, indexId, indexEntries, librariesFrom, LibrariesContext, schemaFromDescription, LibraryBadge, openChannel, IndexPage } from '../chunk-BIAEGWGJ.js';
3
+ import { TITLE_LIBRARIES, isIndexId, indexId, indexEntries, librariesFrom, LibrariesContext, createKitRoot, schemaFromDescription, LibraryBadge, openChannel, IndexPage } from '../chunk-R5F44ECM.js';
4
4
  import { isGallery } from '../chunk-WEQSLPKT.js';
5
- import { useTrialFrame, createTrialFrames, createTrialClocks, StoryGlobalsContext, TrialFramesContext, TrialClocksContext, StoryTrial, useCssOverrides, useTrialClock, TrialHost } from '../chunk-E2FHBDNQ.js';
6
- export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-E2FHBDNQ.js';
7
- import { labGlobals, withGlobals, followApp, storyConfig, GLOBALS_KEY, effectiveGlobals, PLAYHEAD_KEY, labOptions, isGlobalsPath } from '../chunk-DFKCLZKS.js';
8
- import { loadStories, indexRenderOf, useNearViewport, IN_VIEW_MARGIN } from '../chunk-AP3DRQLI.js';
5
+ import { useTrialFrame, createTrialFrames, createTrialClocks, StoryGlobalsContext, TrialFramesContext, TrialClocksContext, StoryTrial, useCssOverrides, useTrialClock, TrialHost } from '../chunk-J3GYSNCK.js';
6
+ export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-J3GYSNCK.js';
7
+ import { labGlobals, withGlobals, followApp, storyConfig, GLOBALS_KEY, effectiveGlobals, PLAYHEAD_KEY, labOptions, isGlobalsPath } from '../chunk-7BQPHMA2.js';
8
+ import { loadStories, indexRenderOf, useNearViewport, IN_VIEW_MARGIN } from '../chunk-NVNTQ4AT.js';
9
9
  import '../chunk-L5XM5VKB.js';
10
10
  import '../chunk-ZAWXMUGZ.js';
11
- import '../chunk-GB6PRJET.js';
12
- import { stableStringify, PORT_HANDOFF, FRAME_HELLO, PROTOCOL_VERSION } from '../chunk-FL2MUPUL.js';
13
- import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, useTrialState, inferTokenType, ToggleBar as ToggleBar$1, Input, TokenPanel, refitScale } from '@weasel-js/labkit';
11
+ import '../chunk-SQ4KCIEH.js';
12
+ import { stableStringify, PORT_HANDOFF, FRAME_HELLO, PROTOCOL_VERSION } from '../chunk-2OJAV522.js';
13
+ import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, useTrialState, ToggleBar as ToggleBar$1, Input, TokenPanel, inferTokenType, refitScale } from '@weasel-js/labkit';
14
14
  import num from '@weasel-js/theme/numeric.module.css';
15
15
  import { createContext, useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect, useId, useContext, Fragment as Fragment$1, useSyncExternalStore } from 'react';
16
16
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
17
- import { createRoot } from 'react-dom/client';
18
17
  import { createEmitter } from '@weasel-js/registry';
19
18
  import { f, valueAtPath, isConfigBranch } from '@weasel-js/labkit/config';
20
- import { InfoIcon, ToggleBar, Checkbox, Dialog, TOKEN_CATEGORIES, Icon, DisclosureMark, Button as Button$1, Callout, Slider, Transport, SearchIcon } from '@weasel-js/ui';
21
- import { themeAxes, weaselTheme, TOKEN_MANIFEST, THEME_SOURCES, resolveTheme, isColorModePreference, isByAxis } from '@weasel-js/theme';
19
+ import { formatShortcut, ToggleBar, Checkbox, Dialog, TOKEN_CATEGORIES, Icon, DisclosureMark, Button as Button$1, Callout, Slider, Transport } from '@weasel-js/ui';
20
+ import { TOKEN_MANIFEST, themeAxes, weaselTheme, THEME_SOURCES, resolveTheme, isColorModePreference, isByAxis } from '@weasel-js/theme';
22
21
  import { scale, httpThemeApi } from '@weasel-js/theme/engine';
23
22
  import { useLatest, historyKey, createHistory, swapOp, isPlainObject } from '@weasel-js/core';
24
23
  import { useResolvedColorMode } from '@weasel-js/theme/react';
24
+ import { pref } from '@weasel-js/prefs';
25
25
 
26
26
  function Finding({ finding }) {
27
27
  return /* @__PURE__ */ jsxs("li", { className: "fg-a11y__finding", children: [
@@ -340,12 +340,30 @@ function missingInstrument(id, globals = {}) {
340
340
  };
341
341
  }
342
342
  var ICONS = {
343
- color: "drop",
344
- type: "type",
345
- size: "measure",
346
- motion: "curveEaseInOut",
347
- depth: "layers",
348
- other: "more"
343
+ color: (
344
+ /* @icon */
345
+ "ui:drop"
346
+ ),
347
+ type: (
348
+ /* @icon */
349
+ "ui:type"
350
+ ),
351
+ size: (
352
+ /* @icon */
353
+ "core:measure"
354
+ ),
355
+ motion: (
356
+ /* @icon */
357
+ "ui:curveEaseInOut"
358
+ ),
359
+ depth: (
360
+ /* @icon */
361
+ "ui:layers"
362
+ ),
363
+ other: (
364
+ /* @icon */
365
+ "ui:more"
366
+ )
349
367
  };
350
368
  var PANEL = ".fg-css-vars";
351
369
  var HOST = ".lk-sidebar-section, .lk-panel-tile";
@@ -636,6 +654,24 @@ function useThemeSave({ trialId, globals, themeApi, scales, overrides, history:
636
654
  save
637
655
  };
638
656
  }
657
+ var MANIFEST = new Map(TOKEN_MANIFEST.map((token) => [token.name, token]));
658
+ function groupOf(name) {
659
+ const segments = name.replace(/^--/, "").split("-");
660
+ return segments.length > 1 ? segments.slice(0, -1).join("-") : name.replace(/^--/, "");
661
+ }
662
+ function useStoryTokens(vars) {
663
+ const [showUnused, setShowUnused] = usePersistedState("fg-css-vars-unused", false, { scope: "lab" });
664
+ const tokens = useMemo(
665
+ () => vars.flatMap(({ name, value, used }) => {
666
+ if (!used && !showUnused) return [];
667
+ const known = MANIFEST.get(name);
668
+ return [{ name, type: known?.type ?? inferTokenType(value), group: known?.group ?? groupOf(name), value }];
669
+ }),
670
+ [vars, showUnused]
671
+ );
672
+ const emptyText = vars.length > 0 && !showUnused ? "The story uses no variables." : "The story\u2019s frame has reported no variables.";
673
+ return { tokens, showUnused, setShowUnused, emptyText };
674
+ }
639
675
  var AXES = themeAxes(weaselTheme);
640
676
  function useThemeTokens(globals, vars) {
641
677
  const selection = useTrialSelection(AXES, globals);
@@ -714,13 +750,13 @@ var TABS = [
714
750
  { value: "theme", label: "Theme" },
715
751
  { value: "story", label: "Story" }
716
752
  ];
717
- var MANIFEST = new Map(TOKEN_MANIFEST.map((token) => [token.name, token]));
753
+ var MANIFEST2 = new Map(TOKEN_MANIFEST.map((token) => [token.name, token]));
718
754
  var literal = (v) => typeof v === "number";
719
755
  var SCALE_SOURCES = Object.entries(THEME_SOURCES[THEME_NAME]?.scales ?? {}).flatMap(([name, def]) => {
720
756
  if (!Array.isArray(def.steps)) return [];
721
757
  const steps = def.steps;
722
758
  const tokens = steps.map((step) => `${TOKEN_PREFIX}${name}-${step}`);
723
- const group = MANIFEST.get(tokens[0] ?? "")?.group;
759
+ const group = MANIFEST2.get(tokens[0] ?? "")?.group;
724
760
  if (!group) return [];
725
761
  const factors = Array.isArray(def.factors) && def.factors.every(literal) ? def.factors : null;
726
762
  const rule = def.factors !== void 0 ? factors ? { kind: "factors", factors } : "factors" : def.ratio !== void 0 ? literal(def.ratio) ? { kind: "ratio", ratio: def.ratio } : "ratio" : literal(def.step) ? { kind: "step", step: def.step } : "step";
@@ -750,10 +786,6 @@ function standingScale(source, amounts) {
750
786
  const base = (amounts[at] ?? 0) / (rule.factors[at] ?? 1);
751
787
  return { ...seed, base: Math.round(base * 100) / 100, rule };
752
788
  }
753
- function groupOf(name) {
754
- const segments = name.replace(/^--/, "").split("-");
755
- return segments.length > 1 ? segments.slice(0, -1).join("-") : name.replace(/^--/, "");
756
- }
757
789
  var NO_GLOBALS = {};
758
790
  function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
759
791
  const trialId = useTrialId();
@@ -768,13 +800,8 @@ function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
768
800
  const [tab, setTab] = useState("theme");
769
801
  const [filter, setFilter] = useState("");
770
802
  const themeTokens = useThemeTokens(globals, frame.vars);
771
- const tokens = useMemo(() => {
772
- if (tab === "theme") return themeTokens;
773
- return frame.vars.map(({ name, value }) => {
774
- const known = MANIFEST.get(name);
775
- return { name, type: known?.type ?? inferTokenType(value), group: known?.group ?? groupOf(name), value };
776
- });
777
- }, [tab, frame.vars, themeTokens]);
803
+ const story = useStoryTokens(frame.vars);
804
+ const tokens = tab === "theme" ? themeTokens : story.tokens;
778
805
  const query = filter.trim().toLowerCase();
779
806
  const shown = tokens.map((token) => {
780
807
  const override = overrides[token.name];
@@ -866,27 +893,27 @@ function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
866
893
  placeholder: "Filter",
867
894
  value: filter,
868
895
  onChange: setFilter,
869
- leadingAdornment: /* @__PURE__ */ jsx(SearchIcon, { size: 12 })
896
+ leadingAdornment: /* @__PURE__ */ jsx(Icon, { name: (
897
+ /* @icon */
898
+ "ui:search"
899
+ ), size: 12 })
870
900
  }
871
901
  ),
872
902
  /* @__PURE__ */ jsxs("div", { className: "fg-css-vars__steps", role: "group", "aria-label": "History", children: [
873
- /* @__PURE__ */ jsx(Button$1, { iconOnly: true, ariaLabel: "Undo", variant: "ghost", size: "sm", disabled: !history2.canUndo, onClick: () => history2.step("undo"), children: /* @__PURE__ */ jsx(Icon, { name: "undo", size: 14 }) }),
874
- /* @__PURE__ */ jsx(Button$1, { iconOnly: true, ariaLabel: "Redo", variant: "ghost", size: "sm", disabled: !history2.canRedo, onClick: () => history2.step("redo"), children: /* @__PURE__ */ jsx(Icon, { name: "redo", size: 14 }) })
903
+ /* @__PURE__ */ jsx(Button$1, { iconOnly: true, ariaLabel: "Undo", variant: "ghost", size: "sm", disabled: !history2.canUndo, onClick: () => history2.step("undo"), children: /* @__PURE__ */ jsx(Icon, { name: (
904
+ /* @icon */
905
+ "core:undo"
906
+ ), size: 14 }) }),
907
+ /* @__PURE__ */ jsx(Button$1, { iconOnly: true, ariaLabel: "Redo", variant: "ghost", size: "sm", disabled: !history2.canRedo, onClick: () => history2.step("redo"), children: /* @__PURE__ */ jsx(Icon, { name: (
908
+ /* @icon */
909
+ "core:redo"
910
+ ), size: 14 }) })
875
911
  ] })
876
912
  ] }),
877
- tab === "theme" && (pending || notice) ? /* @__PURE__ */ jsxs("div", { className: "fg-css-vars__save", children: [
878
- pending ? /* @__PURE__ */ jsx(Button$1, { variant: "primary", size: "sm", loading: saving, disabled: saving, onClick: () => void save(), children: "Save to theme" }) : null,
879
- notice ? /* @__PURE__ */ jsx(
880
- "p",
881
- {
882
- className: `fg-css-vars__notice fg-css-vars__notice--${notice.tone}`,
883
- role: notice.tone === "error" ? "alert" : "status",
884
- children: notice.text
885
- }
886
- ) : null
887
- ] }) : null
913
+ tab === "story" ? /* @__PURE__ */ jsx(Checkbox, { className: "fg-css-vars__unused", isSelected: story.showUnused, onChange: story.setShowUnused, children: "Show variables the story doesn\u2019t use" }) : null,
914
+ tab === "theme" ? /* @__PURE__ */ jsx(SaveRow, { pending, saving, notice, onSave: () => void save() }) : null
888
915
  ] }),
889
- shown.length === 0 ? /* @__PURE__ */ jsx("p", { className: "fg-css-vars__empty", children: tokens.length === 0 ? "The story\u2019s frame has reported no variables." : "No variables match." }) : /* @__PURE__ */ jsx("div", { className: "fg-css-vars__list", children: /* @__PURE__ */ jsx(
916
+ shown.length === 0 ? /* @__PURE__ */ jsx("p", { className: "fg-css-vars__empty", children: tokens.length > 0 ? "No variables match." : tab === "story" ? story.emptyText : "The story\u2019s frame has reported no variables." }) : /* @__PURE__ */ jsx("div", { className: "fg-css-vars__list", children: /* @__PURE__ */ jsx(
890
917
  TokenPanel,
891
918
  {
892
919
  tokens: shown,
@@ -903,6 +930,13 @@ function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
903
930
  )
904
931
  );
905
932
  }
933
+ function SaveRow({ pending, saving, notice, onSave }) {
934
+ if (!pending && !notice) return null;
935
+ return /* @__PURE__ */ jsxs("div", { className: "fg-css-vars__save", children: [
936
+ pending ? /* @__PURE__ */ jsx(Button$1, { variant: "primary", size: "sm", loading: saving, disabled: saving, onClick: onSave, children: "Save to theme" }) : null,
937
+ notice ? /* @__PURE__ */ jsx("p", { className: `fg-css-vars__notice fg-css-vars__notice--${notice.tone}`, role: notice.tone === "error" ? "alert" : "status", children: notice.text }) : null
938
+ ] });
939
+ }
906
940
  function FocusedCssVars() {
907
941
  const { focusedTrialId, trials, instruments } = useLabContext();
908
942
  const labGlobals2 = useContext(StoryGlobalsContext);
@@ -920,6 +954,19 @@ var CSS_VARS_SECTION = {
920
954
  region: "aside",
921
955
  item: { title: "CSS Vars", actions: /* @__PURE__ */ jsx(SectionJump, {}), fill: true, body: /* @__PURE__ */ jsx(FocusedCssVars, {}) }
922
956
  };
957
+ var KEYS = { kind: "key", key: "i", mods: { mod: true } };
958
+ function infoItem(open) {
959
+ return {
960
+ icon: (
961
+ /* @icon */
962
+ "ui:info"
963
+ ),
964
+ label: "Info",
965
+ shortcut: formatShortcut({ key: KEYS.key, mod: KEYS.mods.mod }),
966
+ keys: KEYS,
967
+ onActivate: open
968
+ };
969
+ }
923
970
  var GLOBALS = "$globals";
924
971
  function leaves(shape, prefix = "") {
925
972
  const out = [];
@@ -1193,7 +1240,8 @@ function GlobalsToolbar({ declarations }) {
1193
1240
  NestedGlobals,
1194
1241
  {
1195
1242
  label: declaration.label,
1196
- icon: declaration.icon ?? "tune",
1243
+ icon: declaration.icon ?? /* @icon */
1244
+ "core:tune",
1197
1245
  nested,
1198
1246
  values,
1199
1247
  set
@@ -1251,6 +1299,41 @@ function LabGlobals({
1251
1299
  useEffect(() => onChange(resolved), [resolved, onChange]);
1252
1300
  return null;
1253
1301
  }
1302
+ var leafPath = (n) => `globals.d${n}.v`;
1303
+ function globalsMenu(declarations, values, set) {
1304
+ const entries = Object.entries(declarations);
1305
+ if (entries.length === 0) return void 0;
1306
+ const submenus = {};
1307
+ const current = {};
1308
+ const keys = /* @__PURE__ */ new Map();
1309
+ entries.forEach(([key, declaration], n) => {
1310
+ const options = labOptions(declaration, values);
1311
+ submenus[`d${n}`] = pref.group(
1312
+ declaration.label,
1313
+ { v: pref.enum(declaration.label, declaration.default, options) },
1314
+ { as: "menu" }
1315
+ );
1316
+ current[leafPath(n)] = values[key];
1317
+ keys.set(leafPath(n), key);
1318
+ });
1319
+ return {
1320
+ schema: pref.group("", { globals: pref.group("Globals", submenus, { as: "menu" }) }),
1321
+ values: current,
1322
+ onChange: (path, value) => {
1323
+ const key = keys.get(path);
1324
+ if (key !== void 0 && typeof value === "string") set(key, value);
1325
+ }
1326
+ };
1327
+ }
1328
+ function GlobalsMenus({
1329
+ declarations,
1330
+ onMenus
1331
+ }) {
1332
+ const [values, set] = useLabGlobals(declarations);
1333
+ const menus = useMemo(() => globalsMenu(declarations, values, set), [declarations, values, set]);
1334
+ useEffect(() => onMenus(menus), [menus, onMenus]);
1335
+ return null;
1336
+ }
1254
1337
  function KnobSync({ schema, config }) {
1255
1338
  const { setConfig } = useTrialState();
1256
1339
  const reading = useRef(true);
@@ -2303,26 +2386,15 @@ function RouteOpener() {
2303
2386
  }, [route, lab]);
2304
2387
  return null;
2305
2388
  }
2306
- function useInfoShortcut(open) {
2389
+ function useDocumentTitle(title) {
2307
2390
  useEffect(() => {
2308
- const onKeyDown = (event) => {
2309
- if (event.key !== "i" && event.key !== "I") return;
2310
- if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return;
2311
- const target = event.target;
2312
- if (target?.isContentEditable || /^(input|textarea|select)$/i.test(target?.tagName ?? "")) return;
2313
- event.preventDefault();
2314
- open(true);
2315
- };
2316
- window.addEventListener("keydown", onKeyDown);
2317
- return () => window.removeEventListener("keydown", onKeyDown);
2318
- }, [open]);
2391
+ document.title = title;
2392
+ }, [title]);
2319
2393
  }
2320
2394
  function Workshop({ index, frameUrl, importers, setup, changes, config, stories = [], storageKey, storage, dependencies }) {
2321
2395
  const declarations = config?.globals ?? NO_DECLARATIONS;
2322
2396
  const title = config?.title ?? DEFAULT_TITLE;
2323
- useEffect(() => {
2324
- document.title = title;
2325
- }, [title]);
2397
+ useDocumentTitle(title);
2326
2398
  const [frames] = useState(createTrialFrames);
2327
2399
  const [clocks] = useState(createTrialClocks);
2328
2400
  const [pool, setPool] = useState(null);
@@ -2358,15 +2430,11 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
2358
2430
  const themeFor = config?.labTheme;
2359
2431
  const labTheme = useMemo(() => themeFor?.(labValues), [themeFor, labValues]);
2360
2432
  const [infoOpen, setInfoOpen] = useState(false);
2361
- useInfoShortcut(setInfoOpen);
2362
2433
  const reportLabValues = useCallback(
2363
2434
  (next) => setLabValues((prev) => stableStringify(prev) === stableStringify(next) ? prev : next),
2364
2435
  []
2365
2436
  );
2366
- const infoCommand = useMemo(
2367
- () => ({ icon: InfoIcon, label: "Info", shortcut: "\u2318I", onActivate: () => setInfoOpen(true) }),
2368
- []
2369
- );
2437
+ const infoCommand = useMemo(() => infoItem(() => setInfoOpen(true)), []);
2370
2438
  const labChrome = useMemo(
2371
2439
  () => [
2372
2440
  { id: "fg-stories", region: "sidebar", render: (ctx) => /* @__PURE__ */ jsx(StoryTree, { ctx, index }) },
@@ -2378,6 +2446,7 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
2378
2446
  ],
2379
2447
  [index, infoCommand, declarations, config?.cssVars, config?.labChrome]
2380
2448
  );
2449
+ const [menus, setMenus] = useState();
2381
2450
  const first = index[0];
2382
2451
  if (!first) {
2383
2452
  return /* @__PURE__ */ jsxs("div", { className: "fg-empty", children: [
@@ -2397,6 +2466,7 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
2397
2466
  ...storage ? { storage } : {},
2398
2467
  labChrome,
2399
2468
  addTrial: false,
2469
+ ...menus ? { menus } : {},
2400
2470
  ...config?.controls ? { controls: config.controls } : {},
2401
2471
  ...config?.pages ? { pages: config.pages } : {},
2402
2472
  ...config?.path !== void 0 ? { path: config.path } : {},
@@ -2413,7 +2483,8 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
2413
2483
  onOpenChange: setInfoOpen
2414
2484
  }
2415
2485
  ),
2416
- /* @__PURE__ */ jsx(LabGlobals, { declarations, onChange: reportLabValues })
2486
+ /* @__PURE__ */ jsx(LabGlobals, { declarations, onChange: reportLabValues }),
2487
+ /* @__PURE__ */ jsx(GlobalsMenus, { declarations, onMenus: setMenus })
2417
2488
  ]
2418
2489
  }
2419
2490
  ) }) }) }) }) }) });
@@ -2421,7 +2492,7 @@ function Workshop({ index, frameUrl, importers, setup, changes, config, stories
2421
2492
  function mountWorkshop({ container, ...props }) {
2422
2493
  const target = container ?? document.getElementById("root");
2423
2494
  if (!target) throw new Error("mountWorkshop: no container given and no #root element");
2424
- const root = createRoot(target);
2495
+ const root = createKitRoot(target, props.setup?.icons);
2425
2496
  const changes = createStoryChanges();
2426
2497
  let current = { ...props, changes };
2427
2498
  root.render(/* @__PURE__ */ jsx(Workshop, { ...current }));