tutuca 0.9.113 → 0.9.114

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.
@@ -5,60 +5,88 @@ var Storybook = component({
5
5
  fields: {
6
6
  selectedSectionIndex: 0,
7
7
  sections: [],
8
+ // Retained navigation tree (SidebarGroup → SidebarEntry), built once from
9
+ // `sections` and then mutated in place: selection flips entry `selected`, the
10
+ // filter walk sets entry/group `visible`, a header click flips group `collapsed`.
11
+ // It owns its own UI state — never rebuilt or projected from `sections`.
8
12
  sidebar: [],
9
13
  filter: "",
10
14
  sectionId: null,
11
15
  exampleId: null,
12
16
  focusExample: null,
13
17
  sidebarCollapsed: false,
18
+ // The active margaui palette, and the names the switcher offers. `themes` is
19
+ // seeded by mountStorybook from its `themes` option and left EMPTY when no
20
+ // margaui theme CSS is on the page (`--no-margaui`) — an empty list hides the
21
+ // switcher, so nothing offers a theme that can't load.
14
22
  theme: "",
15
23
  themes: []
16
24
  },
17
25
  statics: {
26
+ // Build a storybook whose sidebar tree is derived from `sections` once. Use this
27
+ // instead of a bare `make` anywhere a Storybook is constructed by hand (the
28
+ // engine itself, the getRoot/Inception demo) so the sidebar is never empty.
18
29
  withSections(sections) {
19
30
  return this.make({ sections, sidebar: buildSidebar(sections) });
20
31
  }
21
32
  },
22
33
  methods: {
23
34
  selectSectionAtIndex(index) {
24
- if (this.sections.size === 0)
25
- return this;
35
+ if (this.sections.size === 0) return this;
26
36
  const safeIndex = index >= 0 && index < this.sections.size ? index : 0;
27
- return this.setSelectedSectionIndex(safeIndex).markSidebarSelected(this.sections.get(safeIndex)?.id);
37
+ return this.setSelectedSectionIndex(safeIndex).markSidebarSelected(
38
+ this.sections.get(safeIndex)?.id
39
+ );
28
40
  },
41
+ // Flip the `selected` highlight to the entry for `id`, clearing the rest.
29
42
  markSidebarSelected(id) {
30
- return this.setSidebar(this.sidebar.map((g) => g.setRows(g.rows.map((e) => e.setSelected(e.sectionId === id)))));
43
+ return this.setSidebar(
44
+ this.sidebar.map((g) => g.setRows(g.rows.map((e) => e.setSelected(e.sectionId === id))))
45
+ );
31
46
  },
47
+ // Walk the tree setting each row's `visible` from the filter AND the group's
48
+ // collapse: a row is visible iff it passes the filter (every row passes when the
49
+ // filter is empty) AND its group is open — collapse always wins, so a collapsed
50
+ // group shows no rows even while filtering. The group's own `visible` is set from
51
+ // whether any row MATCHES (ignoring collapse), so a collapsed group that still
52
+ // contains matches keeps its header and stays expandable; with no filter every
53
+ // group shows.
32
54
  applyFilterToSidebar(filter) {
33
55
  const active = (filter ?? "") !== "";
34
- return this.setSidebar(this.sidebar.map((g) => {
35
- let anyMatch = false;
36
- const rows = g.rows.map((e) => {
37
- const match = !active || fuzzyMatch(filter, `${e.title} ${e.description}`);
38
- if (match)
39
- anyMatch = true;
40
- return e.setVisible(match && !g.collapsed);
41
- });
42
- return g.setRows(rows).setVisible(active ? anyMatch : true);
43
- }));
56
+ return this.setSidebar(
57
+ this.sidebar.map((g) => {
58
+ let anyMatch = false;
59
+ const rows = g.rows.map((e) => {
60
+ const match = !active || fuzzyMatch(filter, `${e.title} ${e.description}`);
61
+ if (match) anyMatch = true;
62
+ return e.setVisible(match && !g.collapsed);
63
+ });
64
+ return g.setRows(rows).setVisible(active ? anyMatch : true);
65
+ })
66
+ );
44
67
  },
68
+ // Flip a named group's `collapsed`, recomputing its rows' visibility for the new
69
+ // state against the current filter. Collapsing always hides the rows (even while a
70
+ // filter is active — the reported chevron-flips-but-content-stays case); expanding
71
+ // re-reveals the rows that pass the filter. The group header's own `visible` is left
72
+ // as-is, so a collapsed group that matched the filter stays visible to be reopened.
45
73
  toggleSidebarGroup(name) {
46
74
  const filter = this.filter;
47
75
  const active = filter !== "";
48
- return this.setSidebar(this.sidebar.map((g) => {
49
- if (g.name !== name)
50
- return g;
51
- const collapsed = !g.collapsed;
52
- const rows = g.rows.map((e) => {
53
- const match = !active || fuzzyMatch(filter, `${e.title} ${e.description}`);
54
- return e.setVisible(match && !collapsed);
55
- });
56
- return g.setCollapsed(collapsed).setRows(rows);
57
- }));
76
+ return this.setSidebar(
77
+ this.sidebar.map((g) => {
78
+ if (g.name !== name) return g;
79
+ const collapsed = !g.collapsed;
80
+ const rows = g.rows.map((e) => {
81
+ const match = !active || fuzzyMatch(filter, `${e.title} ${e.description}`);
82
+ return e.setVisible(match && !collapsed);
83
+ });
84
+ return g.setCollapsed(collapsed).setRows(rows);
85
+ })
86
+ );
58
87
  },
59
88
  selectSectionWithId(id) {
60
- if (!id)
61
- return this.selectSectionAtIndex(this.selectedSectionIndex);
89
+ if (!id) return this.selectSectionAtIndex(this.selectedSectionIndex);
62
90
  const index = this.sections.findIndex((s) => s.id === id);
63
91
  return this.selectSectionAtIndex(index);
64
92
  },
@@ -74,12 +102,21 @@ var Storybook = component({
74
102
  return this.setSectionId(sectionId).setExampleId(exampleId).setFocusExample(example.value);
75
103
  },
76
104
  setSelectedSectionFilter(value) {
77
- if (this.sections.size === 0)
78
- return this;
105
+ if (this.sections.size === 0) return this;
79
106
  const i = this.selectedSectionIndex;
80
107
  const sections = this.sections.map((s, idx) => idx === i ? s.setFilter(value ?? "") : s);
81
108
  return this.setSections(sections);
82
109
  },
110
+ // Assemble the full URL snapshot from current state. `overrides` carries the
111
+ // change the calling handler is about to make, since `this` is still the
112
+ // pre-change state when the persistState request is issued.
113
+ //
114
+ // `theme` is deliberately NOT part of the base snapshot: it is resolved on load
115
+ // (from the OS preference when the URL says nothing), and writing that resolved
116
+ // value back would stamp `?theme=light` onto the URL the first time anyone types
117
+ // in the filter. persistState only touches the keys it is handed, so omitting it
118
+ // here leaves an already-chosen `?theme=` alone — onSelectTheme passes it as an
119
+ // override, which is the only way it enters the URL.
83
120
  toUrlState(overrides = {}) {
84
121
  const section = this.sections.get(this.selectedSectionIndex);
85
122
  return {
@@ -92,6 +129,8 @@ var Storybook = component({
92
129
  }
93
130
  },
94
131
  alter: {
132
+ // The sidebar render-each shows only visible groups (kept positional via `when`
133
+ // so a click resolves to the right group — see SidebarGroup.rowVisible).
95
134
  groupVisible(_key, group) {
96
135
  return group.visible;
97
136
  }
@@ -109,6 +148,10 @@ var Storybook = component({
109
148
  ctx.request("persistState", [this.toUrlState({ example: "" }), this, true]);
110
149
  return this.setSectionId(null).setExampleId(null).setFocusExample(null);
111
150
  },
151
+ // Switching a palette is a DOM effect (set data-theme, load the stylesheet), so it
152
+ // goes out as a request — the host handler in mountStorybook owns the document.
153
+ // Unregistered (no `themes` option), the request no-ops via the 404 path and the
154
+ // switcher isn't rendered anyway.
112
155
  onSelectTheme(value, ctx) {
113
156
  ctx.request("applyTheme", [value, this]);
114
157
  ctx.request("persistState", [this.toUrlState({ theme: value }), this, false]);
@@ -116,6 +159,8 @@ var Storybook = component({
116
159
  }
117
160
  },
118
161
  bubble: {
162
+ // A sidebar entry was clicked; it bubbles its section id. Resolve to the content
163
+ // index and select. Sidebar highlight is updated by selectSectionAtIndex.
119
164
  sectionSelected(sectionId, ctx) {
120
165
  ctx.stopPropagation();
121
166
  const section = this.sections.find((s) => s.id === sectionId);
@@ -129,6 +174,7 @@ var Storybook = component({
129
174
  transitionSections(ctx, next, oldIndex, next.selectedSectionIndex);
130
175
  return next;
131
176
  },
177
+ // Toggle a group open/closed in place — the collapse lives on the SidebarGroup.
132
178
  groupToggled(name, ctx) {
133
179
  ctx.stopPropagation();
134
180
  return this.toggleSidebarGroup(name);
@@ -154,8 +200,7 @@ var Storybook = component({
154
200
  },
155
201
  response: {
156
202
  loadState(state, err, ctx) {
157
- if (err || !state)
158
- return this;
203
+ if (err || !state) return this;
159
204
  const selected = this.selectSectionWithId(state.section).setTheme(state.theme ?? "").setFilter(state.sectionFilter ?? "").applyFilterToSidebar(state.sectionFilter ?? "").setSelectedSectionFilter(state.exampleFilter ?? "");
160
205
  const next = state.example ? selected.focusExampleByIds(state.section, state.example) : selected.setSectionId(null).setExampleId(null).setFocusExample(null);
161
206
  transitionSections(ctx, next, this.selectedSectionIndex, next.selectedSectionIndex);
@@ -228,6 +273,9 @@ var Section = component({
228
273
  id: "?",
229
274
  title: "No Title Section",
230
275
  description: "",
276
+ // Optional 2-level grouping: sections sharing a `group` name cluster under one
277
+ // collapsible sidebar header. Empty string = ungrouped (top-level, flat — the
278
+ // default and the backward-compatible behavior).
231
279
  group: "",
232
280
  items: [],
233
281
  filter: "",
@@ -236,13 +284,18 @@ var Section = component({
236
284
  statics: {
237
285
  fromData(raw) {
238
286
  if (!raw || typeof raw !== "object" || Array.isArray(raw) || raw.title == null) {
239
- throw new Error(`Section.fromData: expected a section object { title, items }, got ${JSON.stringify(raw)}. ` + `getExamples() must return a section object or an array of section objects.`);
287
+ throw new Error(
288
+ `Section.fromData: expected a section object { title, items }, got ${JSON.stringify(raw)}. getExamples() must return a section object or an array of section objects.`
289
+ );
240
290
  }
241
291
  const { id, title, description = "", group, items = [] } = raw;
242
292
  return this.make({
243
293
  id: id ?? slugify(title),
244
294
  title,
245
295
  description,
296
+ // `group` is an optional string (single 2-level grouping). A non-string is
297
+ // ignored here (rendered ungrouped) rather than coerced; the normalize layer
298
+ // (tools/core/module.js) reports it as a shape error.
246
299
  group: typeof group === "string" ? group : "",
247
300
  items: items.map((v) => Example.Class.fromData(v))
248
301
  });
@@ -264,6 +317,7 @@ var Section = component({
264
317
  }
265
318
  },
266
319
  receive: {
320
+ // First display of this section: run each example's `on.init`, mark shown.
267
321
  init(ctx) {
268
322
  fanoutLifecycle(ctx, this.items, "init");
269
323
  return this.setInitialized(true);
@@ -318,6 +372,10 @@ var SidebarEntry = component({
318
372
  var SidebarGroup = component({
319
373
  name: "SidebarGroup",
320
374
  fields: { name: "", collapsed: false, visible: true, rows: [] },
375
+ // Visibility drives the render-each `when` predicates (not `@show` on the items):
376
+ // a `when` filter renders only visible rows while keeping each row's positional key,
377
+ // so an event's path resolves to the right row. `@show` on a render-each item would
378
+ // shift the rendered set and mis-map the click.
321
379
  alter: {
322
380
  rowVisible(_key, row) {
323
381
  return row.visible;
@@ -358,6 +416,9 @@ var Example = component({
358
416
  view: "main",
359
417
  requestHandlers: null,
360
418
  on: null,
419
+ // Inspector tabs. activeTab selects which body renders; the *View fields hold
420
+ // prebuilt inspector instances (attached by mountStorybook before start, via
421
+ // src/storybook/inspect.js); the has* flags gate which tabs appear.
361
422
  activeTab: "preview",
362
423
  hasInspect: false,
363
424
  hasComponent: false,
@@ -367,9 +428,15 @@ var Example = component({
367
428
  instanceView: null,
368
429
  lintView: null,
369
430
  testView: null,
431
+ // Live dispatch activity for this example's component (`.value`): an ActivityLog
432
+ // (baked empty by src/storybook/inspect.js) the core observer appends to via
433
+ // logActivity. `hasActivity` gates the Activity tab so it only appears once
434
+ // something has actually happened.
370
435
  activityLog: null,
371
436
  hasActivity: false
372
437
  },
438
+ // Storybook-only convention (read in mountStorybook, never by core): names the
439
+ // field on an example instance that holds its per-example request-handler mocks.
373
440
  requestOverridesField: "requestHandlers",
374
441
  statics: {
375
442
  fromData({
@@ -393,6 +460,8 @@ var Example = component({
393
460
  });
394
461
  }
395
462
  },
463
+ // Lifecycle hooks: the section forwards init/resume/suspend here; each runs the
464
+ // matching `on` phase's actions against this example's component (`.value`).
396
465
  receive: {
397
466
  init(ctx) {
398
467
  this.runPhase(ctx, "init", this.on?.init);
@@ -406,6 +475,9 @@ var Example = component({
406
475
  this.runPhase(ctx, "suspend", this.on?.suspend);
407
476
  return this;
408
477
  },
478
+ // Append one activity row to the example's ActivityLog. Dispatched by the storybook
479
+ // observer at this example's node — a path OUTSIDE `.value`, so the observer's own
480
+ // filter ignores it and this append is never itself logged (no loop).
409
481
  logActivity(entry) {
410
482
  return this.setActivityLog(this.activityLog.appendEntry(entry)).setHasActivity(true);
411
483
  }
@@ -413,7 +485,9 @@ var Example = component({
413
485
  methods: {
414
486
  runPhase(ctx, name, phase) {
415
487
  if (phaseHasBubble(phase))
416
- console.warn(`storybook on.${name}: a \`bubble\` action leaves this example and is received by the storybook engine, so your component's bubble handler won't run. Use send/request/input to drive a preset state.`);
488
+ console.warn(
489
+ `storybook on.${name}: a \`bubble\` action leaves this example and is received by the storybook engine, so your component's bubble handler won't run. Use send/request/input to drive a preset state.`
490
+ );
417
491
  dispatchPhase(ctx, ctx.at.field("value").buildPath(), phase, this.value);
418
492
  }
419
493
  },
@@ -529,12 +603,9 @@ function transitionSections(ctx, sb, oldIndex, newIndex) {
529
603
  if (changed && sb.sections.get(oldIndex)?.initialized)
530
604
  ctx.at.index("sections", oldIndex).send("suspend", []);
531
605
  const target = sb.sections.get(newIndex);
532
- if (!target)
533
- return;
534
- if (!target.initialized)
535
- ctx.at.index("sections", newIndex).send("init", []);
536
- else if (changed)
537
- ctx.at.index("sections", newIndex).send("resume", []);
606
+ if (!target) return;
607
+ if (!target.initialized) ctx.at.index("sections", newIndex).send("init", []);
608
+ else if (changed) ctx.at.index("sections", newIndex).send("resume", []);
538
609
  }
539
610
  function fanoutLifecycle(ctx, items, name) {
540
611
  items.forEach((_item, j) => {
@@ -543,23 +614,18 @@ function fanoutLifecycle(ctx, items, name) {
543
614
  }
544
615
  function fuzzyMatch(query, target) {
545
616
  const q = query.toLowerCase().trim();
546
- if (q === "")
547
- return true;
617
+ if (q === "") return true;
548
618
  const t = target.toLowerCase();
549
- if (t.includes(q))
550
- return true;
619
+ if (t.includes(q)) return true;
551
620
  const budget = q.length + Math.max(1, Math.floor(q.length / 2));
552
- for (let start = 0;start < t.length; start++) {
553
- if (t[start] !== q[0])
554
- continue;
621
+ for (let start = 0; start < t.length; start++) {
622
+ if (t[start] !== q[0]) continue;
555
623
  let qi = 1;
556
624
  let ti = start + 1;
557
- for (;ti < t.length && qi < q.length; ti++) {
558
- if (t[ti] === q[qi])
559
- qi++;
625
+ for (; ti < t.length && qi < q.length; ti++) {
626
+ if (t[ti] === q[qi]) qi++;
560
627
  }
561
- if (qi === q.length && ti - start <= budget)
562
- return true;
628
+ if (qi === q.length && ti - start <= budget) return true;
563
629
  }
564
630
  return false;
565
631
  }
@@ -567,36 +633,37 @@ function slugify(str) {
567
633
  return String(str).normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
568
634
  }
569
635
  function buildSidebar(sections) {
570
- const groups = new Map;
636
+ const groups = /* @__PURE__ */ new Map();
571
637
  const singles = [];
572
638
  sections.forEach((s) => {
573
639
  const name = s.group ?? "";
574
- if (name === "")
575
- singles.push(s);
640
+ if (name === "") singles.push(s);
576
641
  else {
577
- if (!groups.has(name))
578
- groups.set(name, []);
642
+ if (!groups.has(name)) groups.set(name, []);
579
643
  groups.get(name).push(s);
580
644
  }
581
645
  });
582
646
  const buckets = [];
583
647
  for (const [name, secs] of groups)
584
648
  buckets.push({ key: name.toLowerCase(), name, sections: secs });
585
- for (const s of singles)
586
- buckets.push({ key: s.title.toLowerCase(), name: "", sections: [s] });
649
+ for (const s of singles) buckets.push({ key: s.title.toLowerCase(), name: "", sections: [s] });
587
650
  buckets.sort((a, b) => a.key.localeCompare(b.key));
588
- return buckets.map((b) => SidebarGroup.make({
589
- name: b.name,
590
- collapsed: false,
591
- visible: true,
592
- rows: b.sections.map((s) => SidebarEntry.make({
593
- sectionId: s.id,
594
- title: s.title,
595
- description: s.description,
596
- selected: false,
597
- visible: true
598
- }))
599
- }));
651
+ return buckets.map(
652
+ (b) => SidebarGroup.make({
653
+ name: b.name,
654
+ collapsed: false,
655
+ visible: true,
656
+ rows: b.sections.map(
657
+ (s) => SidebarEntry.make({
658
+ sectionId: s.id,
659
+ title: s.title,
660
+ description: s.description,
661
+ selected: false,
662
+ visible: true
663
+ })
664
+ )
665
+ })
666
+ );
600
667
  }
601
668
 
602
669
  // src/storybook/build.js
@@ -604,8 +671,7 @@ import { getComponents as getInspectorComponents } from "tutuca/components";
604
671
  function buildStorybook(modules) {
605
672
  const rawSections = modules.flatMap((m) => {
606
673
  const raw = m.getExamples?.();
607
- if (raw == null)
608
- return [];
674
+ if (raw == null) return [];
609
675
  return Array.isArray(raw) ? raw : [raw];
610
676
  });
611
677
  const sections = rawSections.map((s) => Section.Class.fromData(s)).sort((a, b) => a.title.localeCompare(b.title));
@@ -615,15 +681,15 @@ function buildStorybook(modules) {
615
681
  Example,
616
682
  SidebarGroup,
617
683
  SidebarEntry,
684
+ // ActivityLog + ActivityEntry arrive via getInspectorComponents() (tutuca/components).
618
685
  ...getInspectorComponents()
619
686
  ];
620
687
  const engineSet = new Set(engineComponents);
621
688
  const moduleComponents = modules.map((m) => {
622
- const seen = new Set;
689
+ const seen = /* @__PURE__ */ new Set();
623
690
  const out = [];
624
691
  for (const c of m.getComponents?.() ?? []) {
625
- if (!c || engineSet.has(c) || seen.has(c))
626
- continue;
692
+ if (!c || engineSet.has(c) || seen.has(c)) continue;
627
693
  seen.add(c);
628
694
  out.push(c);
629
695
  }
@@ -631,20 +697,17 @@ function buildStorybook(modules) {
631
697
  });
632
698
  const macros = {};
633
699
  const requestHandlers = {};
634
- const overrideNames = new Set;
700
+ const overrideNames = /* @__PURE__ */ new Set();
635
701
  for (const s of rawSections) {
636
702
  for (const it of s?.items ?? []) {
637
- for (const name in it?.requestHandlers ?? {})
638
- overrideNames.add(name);
703
+ for (const name in it?.requestHandlers ?? {}) overrideNames.add(name);
639
704
  }
640
705
  }
641
706
  for (const m of modules) {
642
- if (m.getMacros)
643
- Object.assign(macros, m.getMacros());
644
- if (m.getRequestHandlers)
645
- Object.assign(requestHandlers, m.getRequestHandlers());
707
+ if (m.getMacros) Object.assign(macros, m.getMacros());
708
+ if (m.getRequestHandlers) Object.assign(requestHandlers, m.getRequestHandlers());
646
709
  }
647
- const components = [...new Set([...engineComponents, ...moduleComponents.flat()])];
710
+ const components = [.../* @__PURE__ */ new Set([...engineComponents, ...moduleComponents.flat()])];
648
711
  return {
649
712
  root: Storybook.Class.withSections(sections),
650
713
  components,
@@ -656,15 +719,14 @@ function buildStorybook(modules) {
656
719
  };
657
720
  }
658
721
  function buildExampleRequestHandlers({ requestHandlers: reals, overrideNames }) {
659
- const names = new Set([...Object.keys(reals), ...overrideNames]);
722
+ const names = /* @__PURE__ */ new Set([...Object.keys(reals), ...overrideNames]);
660
723
  const makeMeta = (name) => async (...rest) => {
661
724
  const ctx = rest.at(-1);
662
725
  const args = rest.slice(0, -1);
663
726
  let override = null;
664
727
  ctx.walkPath((Comp, inst) => {
665
728
  const field = Comp.extra?.requestOverridesField;
666
- if (!field)
667
- return;
729
+ if (!field) return;
668
730
  const map = inst.get(field, null);
669
731
  if (map && name in map) {
670
732
  override = map[name];
@@ -672,15 +734,14 @@ function buildExampleRequestHandlers({ requestHandlers: reals, overrideNames })
672
734
  }
673
735
  });
674
736
  const fn = override ?? reals[name];
675
- if (!fn)
676
- throw new Error(`Request not found: ${name}`);
737
+ if (!fn) throw new Error(`Request not found: ${name}`);
677
738
  return await fn(...args, ctx);
678
739
  };
679
740
  const handlers = {};
680
- for (const name of names)
681
- handlers[name] = makeMeta(name);
741
+ for (const name of names) handlers[name] = makeMeta(name);
682
742
  return handlers;
683
743
  }
744
+
684
745
  // src/storybook/mount.js
685
746
  import { injectCss, tutuca } from "tutuca";
686
747
 
@@ -698,8 +759,7 @@ function subscribeExampleActivity(app) {
698
759
  return;
699
760
  const si = keys[0].key;
700
761
  const ii = keys[1].key;
701
- if (si === undefined || ii === undefined)
702
- return;
762
+ if (si === void 0 || ii === void 0) return;
703
763
  const entry = recordToEntry(record, { inspect, pathLabel: storybookPathLabel });
704
764
  rootDispatcher(app.transactor).at.index("sections", si).index("items", ii).send("logActivity", [entry]);
705
765
  });
@@ -708,14 +768,12 @@ function subscribeExampleActivity(app) {
708
768
  // src/storybook/inspect.js
709
769
  import { ActivityLog, buildInspectorViews, isComponentInstance } from "tutuca/components";
710
770
  function buildTestIndex(modules) {
711
- const index = new Map;
771
+ const index = /* @__PURE__ */ new Map();
712
772
  for (const m of modules) {
713
- if (typeof m.getTests !== "function")
714
- continue;
773
+ if (typeof m.getTests !== "function") continue;
715
774
  const components = m.getComponents?.() ?? [];
716
775
  for (const c of components) {
717
- if (!index.has(c.name))
718
- index.set(c.name, { getTests: m.getTests, components });
776
+ if (!index.has(c.name)) index.set(c.name, { getTests: m.getTests, components });
719
777
  }
720
778
  }
721
779
  return index;
@@ -734,10 +792,10 @@ async function buildExampleInspectors(example, scope, testIndex, dev) {
734
792
  async function attachInspectorViews(root, scope, modules, dev = null) {
735
793
  const testIndex = buildTestIndex(modules);
736
794
  let sections = root.sections;
737
- for (let si = 0;si < sections.size; si++) {
795
+ for (let si = 0; si < sections.size; si++) {
738
796
  const section = sections.get(si);
739
797
  let items = section.items;
740
- for (let ii = 0;ii < items.size; ii++) {
798
+ for (let ii = 0; ii < items.size; ii++) {
741
799
  items = items.set(ii, await buildExampleInspectors(items.get(ii), scope, testIndex, dev));
742
800
  }
743
801
  sections = sections.set(si, section.setItems(items));
@@ -783,7 +841,7 @@ var MARGAUI_THEMES = [
783
841
  "winter",
784
842
  "wireframe"
785
843
  ];
786
- var BUNDLED_THEMES = new Set(["light", "dark"]);
844
+ var BUNDLED_THEMES = /* @__PURE__ */ new Set(["light", "dark"]);
787
845
 
788
846
  // src/storybook/mount.js
789
847
  async function mountStorybook(selector, modules, { compileCss, root, persistUrl = true, dev = null, noCache = false, themes = null } = {}) {
@@ -814,8 +872,7 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
814
872
  injectCss("tutuca-storybook", await compileCss(app));
815
873
  }
816
874
  app.start({ noCache });
817
- if (dev)
818
- subscribeExampleActivity(app);
875
+ if (dev) subscribeExampleActivity(app);
819
876
  app.sendAtRoot("init", []);
820
877
  if (persistUrl) {
821
878
  window.addEventListener("popstate", () => app.sendAtRoot("init", []));
@@ -823,20 +880,16 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
823
880
  app.scopes = { root: rootScope, modules: moduleScopes };
824
881
  return app;
825
882
  function persistState(state, instance, push) {
826
- if (instance !== app.state.val)
827
- return;
883
+ if (instance !== app.state.val) return;
828
884
  const url = new URL(window.location.href);
829
885
  for (const [k, v] of Object.entries(state)) {
830
- if (v === "" || v == null)
831
- url.searchParams.delete(k);
832
- else
833
- url.searchParams.set(k, String(v));
886
+ if (v === "" || v == null) url.searchParams.delete(k);
887
+ else url.searchParams.set(k, String(v));
834
888
  }
835
889
  window.history[push ? "pushState" : "replaceState"](null, "", url);
836
890
  }
837
891
  function applyTheme(name, instance) {
838
- if (instance !== app.state.val)
839
- return;
892
+ if (instance !== app.state.val) return;
840
893
  ensureThemeLink(name);
841
894
  document.documentElement.dataset.theme = name;
842
895
  }
@@ -860,8 +913,7 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
860
913
  };
861
914
  }
862
915
  function resolveTheme(fromUrl) {
863
- if (!themeBaseUrl)
864
- return "";
916
+ if (!themeBaseUrl) return "";
865
917
  const name = MARGAUI_THEMES.includes(fromUrl) ? fromUrl : osTheme();
866
918
  ensureThemeLink(name);
867
919
  document.documentElement.dataset.theme = name;
@@ -871,8 +923,7 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
871
923
  return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
872
924
  }
873
925
  function ensureThemeLink(name) {
874
- if (BUNDLED_THEMES.has(name) || document.getElementById(themeLinkId(name)))
875
- return;
926
+ if (BUNDLED_THEMES.has(name) || document.getElementById(themeLinkId(name))) return;
876
927
  const link = document.createElement("link");
877
928
  link.rel = "stylesheet";
878
929
  link.id = themeLinkId(name);
@@ -889,15 +940,15 @@ function getComponents() {
889
940
  return [Storybook, Section, Example, SidebarGroup, SidebarEntry];
890
941
  }
891
942
  export {
892
- slugify,
893
- mountStorybook,
894
- getComponents,
895
- fuzzyMatch,
896
- buildStorybook,
897
- buildExampleRequestHandlers,
898
- Storybook,
899
- Section,
900
- MARGAUI_THEMES,
943
+ BUNDLED_THEMES,
901
944
  Example,
902
- BUNDLED_THEMES
945
+ MARGAUI_THEMES,
946
+ Section,
947
+ Storybook,
948
+ buildExampleRequestHandlers,
949
+ buildStorybook,
950
+ fuzzyMatch,
951
+ getComponents,
952
+ mountStorybook,
953
+ slugify
903
954
  };