@weasel-js/forge 1.5.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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/dist/FrameController-Ba3LaWfq.d.ts +196 -0
  4. package/dist/chunk-2GXKNXTJ.js +15 -0
  5. package/dist/chunk-2GXKNXTJ.js.map +1 -0
  6. package/dist/chunk-IOBG7GZQ.js +13 -0
  7. package/dist/chunk-IOBG7GZQ.js.map +1 -0
  8. package/dist/chunk-KKF7ZX5E.js +572 -0
  9. package/dist/chunk-KKF7ZX5E.js.map +1 -0
  10. package/dist/chunk-KKVTFMWV.js +59 -0
  11. package/dist/chunk-KKVTFMWV.js.map +1 -0
  12. package/dist/chunk-O5OQZQRZ.js +118 -0
  13. package/dist/chunk-O5OQZQRZ.js.map +1 -0
  14. package/dist/chunk-UZCXH724.js +24 -0
  15. package/dist/chunk-UZCXH724.js.map +1 -0
  16. package/dist/cli.js +107 -0
  17. package/dist/config-DdumOfyd.d.ts +28 -0
  18. package/dist/csf/preview-api.d.ts +9 -0
  19. package/dist/csf/preview-api.js +48 -0
  20. package/dist/csf/preview-api.js.map +1 -0
  21. package/dist/frame/index.css +35 -0
  22. package/dist/frame/index.css.map +1 -0
  23. package/dist/frame/index.d.ts +24 -0
  24. package/dist/frame/index.js +8 -0
  25. package/dist/frame/index.js.map +1 -0
  26. package/dist/index.d.ts +11 -0
  27. package/dist/index.js +14 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/shell/index.css +198 -0
  30. package/dist/shell/index.css.map +1 -0
  31. package/dist/shell/index.d.ts +51 -0
  32. package/dist/shell/index.js +836 -0
  33. package/dist/shell/index.js.map +1 -0
  34. package/dist/test/index.d.ts +17 -0
  35. package/dist/test/index.js +132 -0
  36. package/dist/test/index.js.map +1 -0
  37. package/dist/vite/index.d.ts +29 -0
  38. package/dist/vite/index.js +392 -0
  39. package/dist/vite/index.js.map +1 -0
  40. package/package.json +87 -0
@@ -0,0 +1,572 @@
1
+ import { isNative } from './chunk-2GXKNXTJ.js';
2
+ import { titleFromFile, storyNameFromExport, storyId } from './chunk-UZCXH724.js';
3
+ import { describeSchema, openChannel, answerSchema } from './chunk-O5OQZQRZ.js';
4
+ import { isPortSafe, appliedLocal, coverOf, ArgsContext } from './chunk-KKVTFMWV.js';
5
+ import { stableStringify, PORT_HANDOFF } from './chunk-IOBG7GZQ.js';
6
+ import { flushSync } from 'react-dom';
7
+ import { createRoot } from 'react-dom/client';
8
+ import { createContext, createElement, Component, useContext, useState, useEffect } from 'react';
9
+ import { jsx } from 'react/jsx-runtime';
10
+ import { f } from '@weasel-js/labkit/config';
11
+
12
+ // src/frame/cssVars.ts
13
+ var VAR_REF = /var\(\s*(--[\w-]+)/g;
14
+ function collect(text, names) {
15
+ if (!text.includes("var(")) return;
16
+ for (const [, name] of text.matchAll(VAR_REF)) names.add(name);
17
+ }
18
+ function walkRules(sheet, names) {
19
+ let rules;
20
+ try {
21
+ rules = sheet.cssRules;
22
+ } catch {
23
+ return;
24
+ }
25
+ for (const rule of Array.from(rules)) {
26
+ const imported = rule.styleSheet;
27
+ if (imported) walkRules(imported, names);
28
+ else collect(rule.cssText, names);
29
+ }
30
+ }
31
+ function scanCssVars(doc, overridden = () => false) {
32
+ const names = /* @__PURE__ */ new Set();
33
+ for (const el of Array.from(doc.querySelectorAll("[style]"))) collect(el.getAttribute("style") ?? "", names);
34
+ for (const sheet of Array.from(doc.styleSheets)) walkRules(sheet, names);
35
+ const values = resolveValues(doc, names);
36
+ return [...names].sort().map((name) => ({ name, value: values.get(name) ?? "", overridden: overridden(name) }));
37
+ }
38
+ function resolveValues(doc, names) {
39
+ const view = doc.defaultView;
40
+ const values = /* @__PURE__ */ new Map();
41
+ if (!view) return values;
42
+ const root = view.getComputedStyle(doc.documentElement);
43
+ const missing = [];
44
+ for (const name of names) {
45
+ const value = root.getPropertyValue(name).trim();
46
+ if (value) values.set(name, value);
47
+ else missing.push(name);
48
+ }
49
+ for (const el of missing.length > 0 ? Array.from((doc.body ?? doc.documentElement).querySelectorAll("*")) : []) {
50
+ const style = view.getComputedStyle(el);
51
+ for (let i = missing.length - 1; i >= 0; i--) {
52
+ const value = style.getPropertyValue(missing[i]).trim();
53
+ if (!value) continue;
54
+ values.set(missing[i], value);
55
+ missing.splice(i, 1);
56
+ }
57
+ if (missing.length === 0) break;
58
+ }
59
+ return values;
60
+ }
61
+ function resolveCssVar(doc, name) {
62
+ return resolveValues(doc, [name]).get(name) ?? "";
63
+ }
64
+ function createOverrides(doc, scope = ":root") {
65
+ const values = /* @__PURE__ */ new Map();
66
+ const style = doc.createElement("style");
67
+ style.setAttribute("data-fg-overrides", "");
68
+ const keepLast = () => {
69
+ if (style.parentNode === doc.head) {
70
+ let next = style.nextElementSibling;
71
+ while (next?.hasAttribute("data-fg-overrides")) next = next.nextElementSibling;
72
+ if (next === null) return;
73
+ }
74
+ doc.head.append(style);
75
+ };
76
+ const write = () => {
77
+ const decls = [...values].map(([name, value]) => ` ${name}: ${value};`);
78
+ style.textContent = decls.length > 0 ? `:is(${scope}):not(#fg-overrides) {
79
+ ${decls.join("\n")}
80
+ }
81
+ ` : "";
82
+ };
83
+ keepLast();
84
+ const observer = typeof MutationObserver === "undefined" ? null : new MutationObserver(keepLast);
85
+ observer?.observe(doc.head, { childList: true });
86
+ return {
87
+ set(name, value) {
88
+ if (value === null) values.delete(name);
89
+ else values.set(name, value);
90
+ write();
91
+ },
92
+ has: (name) => values.has(name),
93
+ owns: (node) => style.contains(node),
94
+ dispose() {
95
+ observer?.disconnect();
96
+ style.remove();
97
+ }
98
+ };
99
+ }
100
+ function RenderStory({ story, ctx }) {
101
+ return story.render(ctx);
102
+ }
103
+ function Decorated({ story, ctx, decorators }) {
104
+ let inner = () => /* @__PURE__ */ jsx(RenderStory, { story, ctx });
105
+ for (const decorate of [...story.decorators, ...decorators]) {
106
+ const wrapped = inner;
107
+ inner = () => decorate(wrapped, ctx);
108
+ }
109
+ return inner();
110
+ }
111
+ var StoryHost = class extends Component {
112
+ state = { error: null, failed: false, resetKey: this.props.resetKey };
113
+ static getDerivedStateFromProps(props, state) {
114
+ return props.resetKey === state.resetKey ? null : { error: null, failed: false, resetKey: props.resetKey };
115
+ }
116
+ static getDerivedStateFromError(error) {
117
+ return { error, failed: true };
118
+ }
119
+ componentDidCatch(error) {
120
+ this.props.onError(error);
121
+ }
122
+ render() {
123
+ if (this.state.failed) {
124
+ const { error } = this.state;
125
+ return /* @__PURE__ */ jsx("pre", { className: "fg-frame__fault", children: error instanceof Error ? error.message : String(error) });
126
+ }
127
+ const { story, ctx, decorators } = this.props;
128
+ return /* @__PURE__ */ jsx(Decorated, { story, ctx, decorators });
129
+ }
130
+ };
131
+ var VARS_SETTLE_MS = 100;
132
+ function fault(phase, error, seq) {
133
+ return {
134
+ type: "fault",
135
+ phase,
136
+ message: error instanceof Error ? error.message : String(error),
137
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {},
138
+ ...seq === void 0 ? {} : { seq }
139
+ };
140
+ }
141
+ function startFrame({ story, channel, container, setup = {} }) {
142
+ const { send } = channel;
143
+ send({ type: "ready", schema: describeSchema(story.config), layout: story.layout, viewport: story.viewport });
144
+ const wrapper = document.createElement("div");
145
+ wrapper.className = `fg-frame fg-frame--${story.layout}`;
146
+ container.append(wrapper);
147
+ const root = createRoot(wrapper);
148
+ const decorators = setup.decorators ?? [];
149
+ let initialized = false;
150
+ let config;
151
+ let state = null;
152
+ let globals = {};
153
+ let answeredKey = null;
154
+ let resetKey = 0;
155
+ let seq = 0;
156
+ const overrides = createOverrides(document, setup.cssVarsScope);
157
+ let lastVars = null;
158
+ let reported = null;
159
+ const publishVars = (vars) => {
160
+ lastVars = vars;
161
+ const key = JSON.stringify(vars);
162
+ if (key === reported) return;
163
+ reported = key;
164
+ send({ type: "vars", vars });
165
+ };
166
+ const reportVars = () => publishVars(scanCssVars(document, overrides.has));
167
+ const ownMutation = (record) => overrides.owns(record.target) || record.type === "childList" && [...record.addedNodes, ...record.removedNodes].length > 0 && [...record.addedNodes, ...record.removedNodes].every(overrides.owns);
168
+ let settleTimer;
169
+ const mutations = typeof MutationObserver === "undefined" ? null : new MutationObserver((records) => {
170
+ if (records.every(ownMutation)) return;
171
+ clearTimeout(settleTimer);
172
+ settleTimer = setTimeout(reportVars, VARS_SETTLE_MS);
173
+ });
174
+ mutations?.observe(document.documentElement, {
175
+ subtree: true,
176
+ childList: true,
177
+ characterData: true,
178
+ attributes: true,
179
+ attributeFilter: ["style", "class"]
180
+ });
181
+ const setConfig = (path, value) => send({ type: "setConfig", path, value });
182
+ const setState = (next) => {
183
+ state = typeof next === "function" ? next(state) : next;
184
+ send({ type: "setState", state });
185
+ render();
186
+ };
187
+ const render = () => {
188
+ const ctx = { config, setConfig, state, setState, globals, title: story.title, name: story.name };
189
+ const at = seq;
190
+ root.render(
191
+ /* @__PURE__ */ jsx(
192
+ StoryHost,
193
+ {
194
+ story,
195
+ ctx,
196
+ decorators,
197
+ resetKey,
198
+ onError: (error) => send(fault("render", error, at))
199
+ }
200
+ )
201
+ );
202
+ const key = stableStringify(config);
203
+ if (key !== answeredKey) {
204
+ answeredKey = key;
205
+ send({ type: "answers", answers: { configKey: key, ...answerSchema(story.config, config) } });
206
+ }
207
+ };
208
+ const input = (apply) => {
209
+ seq += 1;
210
+ try {
211
+ apply();
212
+ if (!initialized) return;
213
+ resetKey += 1;
214
+ flushSync(render);
215
+ } catch (error) {
216
+ send(fault("render", error, seq));
217
+ return;
218
+ }
219
+ reportVars();
220
+ };
221
+ const play = async () => {
222
+ try {
223
+ await story.play?.({ canvasElement: wrapper, config, globals });
224
+ send({ type: "played", ok: true });
225
+ } catch (error) {
226
+ send({ type: "played", ok: false, message: error instanceof Error ? error.message : String(error) });
227
+ send(fault("play", error));
228
+ }
229
+ };
230
+ const off = channel.on((msg) => {
231
+ switch (msg.type) {
232
+ case "init":
233
+ input(() => {
234
+ initialized = true;
235
+ config = msg.config;
236
+ globals = msg.globals;
237
+ state = msg.state;
238
+ if (state === null && story.initialState) {
239
+ state = story.initialState(config);
240
+ send({ type: "setState", state });
241
+ }
242
+ setup.applyGlobals?.(globals, document.documentElement);
243
+ });
244
+ break;
245
+ case "config":
246
+ input(() => {
247
+ config = msg.config;
248
+ });
249
+ break;
250
+ case "state":
251
+ input(() => {
252
+ state = msg.state;
253
+ });
254
+ break;
255
+ case "globals":
256
+ input(() => {
257
+ globals = msg.globals;
258
+ setup.applyGlobals?.(globals, document.documentElement);
259
+ });
260
+ break;
261
+ case "vars.set": {
262
+ overrides.set(msg.name, msg.value);
263
+ const at = lastVars?.findIndex((v) => v.name === msg.name) ?? -1;
264
+ if (!lastVars || at < 0) break;
265
+ const vars = lastVars.slice();
266
+ vars[at] = { name: msg.name, value: resolveCssVar(document, msg.name), overridden: overrides.has(msg.name) };
267
+ publishVars(vars);
268
+ break;
269
+ }
270
+ case "play":
271
+ void play();
272
+ break;
273
+ }
274
+ });
275
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
276
+ const { width, height } = wrapper.getBoundingClientRect();
277
+ send({ type: "size", width: Math.ceil(width), height: Math.ceil(height) });
278
+ });
279
+ observer?.observe(wrapper);
280
+ return () => {
281
+ off();
282
+ observer?.disconnect();
283
+ mutations?.disconnect();
284
+ clearTimeout(settleTimer);
285
+ overrides.dispose();
286
+ root.unmount();
287
+ wrapper.remove();
288
+ };
289
+ }
290
+ function reportImportFault(channel, error) {
291
+ channel.send(fault("import", error));
292
+ }
293
+
294
+ // src/csf/isPlainObject.ts
295
+ var isPlainObject = (value) => !!value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
296
+
297
+ // src/csf/argsToSchema.ts
298
+ function controlOf(argType) {
299
+ const { control } = argType;
300
+ if (control === false) return false;
301
+ if (typeof control === "string") return { type: control };
302
+ return { type: control?.type, min: control?.min, max: control?.max, step: control?.step };
303
+ }
304
+ function stringOptions(argType) {
305
+ const opts = argType.options;
306
+ return opts && opts.length > 0 && opts.every((o) => typeof o === "string") ? opts : null;
307
+ }
308
+ function inferred(value) {
309
+ switch (typeof value) {
310
+ case "number":
311
+ return f.number(value);
312
+ case "boolean":
313
+ return f.boolean(value);
314
+ case "string":
315
+ return f.string(value);
316
+ default:
317
+ return Array.isArray(value) || isPlainObject(value) ? f.custom("json", value) : null;
318
+ }
319
+ }
320
+ function bounded(node, control) {
321
+ let out = node;
322
+ if (control.min !== void 0 && control.max !== void 0) out = out.range(control.min, control.max);
323
+ if (control.step !== void 0) out = out.step(control.step);
324
+ return out;
325
+ }
326
+ function controlled(control, argType, has, value) {
327
+ const options = stringOptions(argType);
328
+ const def = () => has ? value : void 0;
329
+ switch (control.type) {
330
+ case "number": {
331
+ const node = bounded(f.number(def()), control);
332
+ return control.min !== void 0 && control.max !== void 0 ? node.input() : node;
333
+ }
334
+ case "range":
335
+ case "slider":
336
+ return has ? bounded(f.number(def()), control).slider() : null;
337
+ case "boolean":
338
+ return f.boolean(def());
339
+ case "switch":
340
+ return f.boolean(def()).toggle();
341
+ case "text":
342
+ case "textarea":
343
+ return f.string(def());
344
+ case "select":
345
+ case "radio":
346
+ case "inline-radio":
347
+ case void 0: {
348
+ if (!options) return control.type === void 0 && has ? inferred(value) : null;
349
+ const node = f.enum(def(), options);
350
+ return control.type === "radio" || control.type === "inline-radio" ? node.radio() : node;
351
+ }
352
+ case "object":
353
+ return has ? f.custom("json", value) : null;
354
+ default:
355
+ return has ? inferred(value) : null;
356
+ }
357
+ }
358
+ function matched(key, value, argType, matchers) {
359
+ if (typeof value !== "string" || argType?.control !== void 0 || stringOptions(argType ?? {})) return null;
360
+ return matchers.color?.test(key) ? f.color(value) : null;
361
+ }
362
+ function argsToSchema(args, argTypes, matchers = {}) {
363
+ const nodes = {};
364
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(args), ...Object.keys(argTypes)])) {
365
+ const has = key in args;
366
+ const value = args[key];
367
+ if (has && !isPortSafe(value)) continue;
368
+ const argType = argTypes[key];
369
+ const control = argType ? controlOf(argType) : false;
370
+ let node = (has ? matched(key, value, argType, matchers) : null) ?? (control ? controlled(control, argType ?? {}, has, value) : has ? inferred(value) : null);
371
+ if (!node) continue;
372
+ if (argType && (argType.control === false || argType.table?.disable)) node = node.hidden();
373
+ if (argType?.description) node = node.describe(argType.description);
374
+ if (argType?.name) node = node.label(argType.name);
375
+ nodes[key] = node;
376
+ }
377
+ return f.schema(nodes);
378
+ }
379
+
380
+ // src/csf/loadCsfModule.tsx
381
+ var LAYOUTS = ["centered", "padded", "fullscreen"];
382
+ var asArray = (value) => value === void 0 ? [] : Array.isArray(value) ? value : [value];
383
+ var withoutUndefined = (args) => Object.fromEntries(Object.entries(args).filter(([, value]) => value !== void 0));
384
+ var matches = (key, filter) => Array.isArray(filter) ? filter.includes(key) : key.match(filter) !== null;
385
+ function combineParameters(...sources) {
386
+ const out = {};
387
+ for (const source of sources) {
388
+ for (const [key, value] of Object.entries(source ?? {})) {
389
+ if (value === void 0) continue;
390
+ out[key] = isPlainObject(value) && isPlainObject(out[key]) ? combineParameters(out[key], value) : value;
391
+ }
392
+ }
393
+ return out;
394
+ }
395
+ function resolveViewport(parameters, globals) {
396
+ const selected = globals.viewport;
397
+ const options = parameters.viewport?.options;
398
+ const styles = selected?.value ? options?.[selected.value]?.styles : void 0;
399
+ const px = (v) => v && /^\d+(\.\d+)?px$/.test(v) ? Number.parseFloat(v) : Number.NaN;
400
+ const width = px(styles?.width);
401
+ const height = px(styles?.height);
402
+ if (Number.isNaN(width) || Number.isNaN(height)) return null;
403
+ return selected?.isRotated ? { width: height, height: width } : { width, height };
404
+ }
405
+ var InnerStory = createContext(null);
406
+ function Story() {
407
+ return useContext(InnerStory)?.() ?? null;
408
+ }
409
+ function Call({ fn }) {
410
+ return fn();
411
+ }
412
+ var LocalArgsContext = createContext(null);
413
+ function Scoped({
414
+ id,
415
+ build,
416
+ fn
417
+ }) {
418
+ const outer = useContext(LocalArgsContext);
419
+ const [local, setLocal] = useState({});
420
+ const shared = outer?.story === id ? outer : { story: id, local, setLocal };
421
+ const [scope, context] = build(shared);
422
+ const { config } = scope;
423
+ const prune = shared.setLocal;
424
+ useEffect(() => {
425
+ prune((prev) => {
426
+ const kept = Object.entries(prev).filter(([key, entry]) => coverOf(config[key]) === entry.over);
427
+ return kept.length === Object.keys(prev).length ? prev : Object.fromEntries(kept);
428
+ });
429
+ }, [config, prune]);
430
+ return createElement(
431
+ LocalArgsContext.Provider,
432
+ { value: shared },
433
+ createElement(ArgsContext.Provider, { value: scope }, createElement(Call, { fn: () => fn(context) }))
434
+ );
435
+ }
436
+ function loadCsfModule(mod, file, root, projectParameters = {}) {
437
+ const meta = isPlainObject(mod.default) ? mod.default : {};
438
+ const title = meta.title ?? titleFromFile(file, root);
439
+ const stories = [];
440
+ for (const [exportName, value] of Object.entries(mod)) {
441
+ if (exportName === "default" || exportName === "__namedExportsOrder" || exportName === "__esModule") continue;
442
+ if (meta.includeStories && !matches(exportName, meta.includeStories)) continue;
443
+ if (meta.excludeStories && matches(exportName, meta.excludeStories)) continue;
444
+ if (!value || typeof value !== "object" && typeof value !== "function") continue;
445
+ const spec = typeof value === "function" ? { ...value, render: value } : value;
446
+ const id = storyId(title, exportName);
447
+ const name = spec.name || spec.storyName || storyNameFromExport(exportName);
448
+ const args = { ...meta.args, ...spec.args };
449
+ const parameters = combineParameters(projectParameters, meta.parameters, spec.parameters);
450
+ const matchers = parameters.controls?.matchers;
451
+ const config = argsToSchema(
452
+ args,
453
+ combineParameters(meta.argTypes, spec.argTypes),
454
+ isPlainObject(matchers) ? matchers : {}
455
+ );
456
+ const defaults = config.defaults();
457
+ const storyGlobals = { ...meta.globals, ...spec.globals };
458
+ const component = meta.component;
459
+ const renderFn = spec.render ?? meta.render ?? (component ? (a) => createElement(component, a) : void 0);
460
+ const csfContext = (config2, globals, local = {}) => ({
461
+ args: withoutUndefined({ ...args, ...withoutUndefined(config2), ...appliedLocal(local, config2) }),
462
+ globals,
463
+ parameters,
464
+ title,
465
+ name,
466
+ id,
467
+ viewMode: "story"
468
+ });
469
+ const scoped = (ctx, fn) => createElement(Scoped, {
470
+ id,
471
+ build: ({ local, setLocal }) => {
472
+ const context = csfContext(ctx.config, ctx.globals, local);
473
+ const scope = {
474
+ args: context.args,
475
+ config: ctx.config,
476
+ defaults,
477
+ original: args,
478
+ setConfig: ctx.setConfig,
479
+ setLocal
480
+ };
481
+ return [scope, context];
482
+ },
483
+ fn
484
+ });
485
+ const adapt = (decorator) => (inner, ctx) => createElement(
486
+ InnerStory.Provider,
487
+ { value: inner },
488
+ scoped(ctx, (context) => decorator(Story, context))
489
+ );
490
+ const layout = parameters.layout;
491
+ const play = spec.play ?? meta.play;
492
+ stories.push({
493
+ id,
494
+ title,
495
+ name,
496
+ exportName,
497
+ config,
498
+ initialState: null,
499
+ render: (ctx) => {
500
+ if (!renderFn) return null;
501
+ return scoped(ctx, (context) => renderFn(context.args, context));
502
+ },
503
+ decorators: [...asArray(spec.decorators), ...asArray(meta.decorators)].map(adapt),
504
+ layout: layout && LAYOUTS.includes(layout) ? layout : "padded",
505
+ viewport: resolveViewport(parameters, storyGlobals),
506
+ play: play ? ({ canvasElement, config: config2, globals }) => play({ ...csfContext(config2, globals), canvasElement, step: async (_label, fn) => fn() }) : null
507
+ });
508
+ }
509
+ return stories;
510
+ }
511
+ function loadNativeModule(mod, file, root) {
512
+ const metaSpec = isNative(mod.default, "meta") ? mod.default : {};
513
+ const title = metaSpec.title ?? titleFromFile(file, root);
514
+ const stories = [];
515
+ for (const [exportName, value] of Object.entries(mod)) {
516
+ if (exportName === "default" || !isNative(value, "story")) continue;
517
+ const spec = value;
518
+ stories.push({
519
+ id: storyId(title, exportName),
520
+ title,
521
+ name: spec.name ?? storyNameFromExport(exportName),
522
+ exportName,
523
+ config: spec.config ?? f.schema({}),
524
+ initialState: spec.state ?? null,
525
+ render: spec.render,
526
+ decorators: [...spec.decorators ?? [], ...metaSpec.decorators ?? []],
527
+ layout: spec.layout ?? metaSpec.layout ?? "centered",
528
+ viewport: spec.viewport ?? null,
529
+ play: spec.play ?? null
530
+ });
531
+ }
532
+ return stories;
533
+ }
534
+
535
+ // src/frame/mountFrame.tsx
536
+ function loadStories(mod, file, root, parameters) {
537
+ return isNative(mod.default, "meta") ? loadNativeModule(mod, file, root) : loadCsfModule(mod, file, root, parameters);
538
+ }
539
+ function waitForPort() {
540
+ return new Promise((resolve) => {
541
+ const onMessage = (event) => {
542
+ const data = event.data;
543
+ const port = event.ports[0];
544
+ if (event.origin !== location.origin || event.source !== window.parent) return;
545
+ if (typeof data !== "object" || data === null || data.type !== PORT_HANDOFF || !port) return;
546
+ window.removeEventListener("message", onMessage);
547
+ resolve(port);
548
+ };
549
+ window.addEventListener("message", onMessage);
550
+ });
551
+ }
552
+ async function mountFrame(options) {
553
+ const channel = openChannel(await waitForPort());
554
+ const id = location.hash.slice(1);
555
+ try {
556
+ const entry = options.index.find((e) => e.id === id);
557
+ if (!entry) throw new Error(`No story with id "${id}"`);
558
+ const importer = options.importers[entry.file];
559
+ if (!importer) throw new Error(`No importer for ${entry.file}`);
560
+ const mod = await importer();
561
+ const stories = (options.load ?? loadStories)(mod, entry.file, options.root, options.setup?.parameters);
562
+ const story = stories.find((s) => s.exportName === entry.exportName);
563
+ if (!story) throw new Error(`${entry.file} has no story export "${entry.exportName}"`);
564
+ startFrame({ story, channel, container: document.getElementById("root") ?? document.body, setup: options.setup });
565
+ } catch (error) {
566
+ reportImportFault(channel, error);
567
+ }
568
+ }
569
+
570
+ export { loadStories, mountFrame, reportImportFault, startFrame };
571
+ //# sourceMappingURL=chunk-KKF7ZX5E.js.map
572
+ //# sourceMappingURL=chunk-KKF7ZX5E.js.map