@weasel-js/forge 1.11.1 → 1.13.0-rc.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 (50) hide show
  1. package/README.md +110 -5
  2. package/dist/{FrameController-Cj_i-Rdk.d.ts → FrameController-Tv3jrHyT.d.ts} +3 -3
  3. package/dist/{chunk-JTCUIJPZ.js → chunk-35DOPXHZ.js} +119 -10
  4. package/dist/chunk-35DOPXHZ.js.map +1 -0
  5. package/dist/{chunk-7BQPHMA2.js → chunk-3BAM6M26.js} +2 -2
  6. package/dist/chunk-3BAM6M26.js.map +1 -0
  7. package/dist/{chunk-ZAWXMUGZ.js → chunk-5WS67HMU.js} +8 -3
  8. package/dist/chunk-5WS67HMU.js.map +1 -0
  9. package/dist/{chunk-BKPDKUDN.js → chunk-BUXUWAI4.js} +45 -60
  10. package/dist/chunk-BUXUWAI4.js.map +1 -0
  11. package/dist/chunk-ORMMZ5PV.js +59 -0
  12. package/dist/chunk-ORMMZ5PV.js.map +1 -0
  13. package/dist/chunk-RKQ277P5.js +501 -0
  14. package/dist/chunk-RKQ277P5.js.map +1 -0
  15. package/dist/chunk-STF6NQ5N.js +60 -0
  16. package/dist/chunk-STF6NQ5N.js.map +1 -0
  17. package/dist/chunk-VUPN73JP.js +16 -0
  18. package/dist/chunk-VUPN73JP.js.map +1 -0
  19. package/dist/{config-DN6IaNf6.d.ts → config-Bd6FVlh7.d.ts} +22 -5
  20. package/dist/frame/index.css +37 -0
  21. package/dist/frame/index.css.map +1 -1
  22. package/dist/frame/index.d.ts +62 -29
  23. package/dist/frame/index.js +138 -8
  24. package/dist/frame/index.js.map +1 -1
  25. package/dist/index.d.ts +28 -5
  26. package/dist/index.js +2 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/mountFrame-FdJDb3TB.d.ts +35 -0
  29. package/dist/shell/index.css +147 -2
  30. package/dist/shell/index.css.map +1 -1
  31. package/dist/shell/index.d.ts +44 -8
  32. package/dist/shell/index.js +1614 -277
  33. package/dist/shell/index.js.map +1 -1
  34. package/dist/test/index.css +58 -0
  35. package/dist/test/index.css.map +1 -0
  36. package/dist/test/index.d.ts +1 -1
  37. package/dist/test/index.js +4 -4
  38. package/dist/vite/index.js +571 -121
  39. package/dist/vite/index.js.map +1 -1
  40. package/package.json +13 -8
  41. package/dist/chunk-5DXUNLV2.js +0 -6
  42. package/dist/chunk-5DXUNLV2.js.map +0 -1
  43. package/dist/chunk-7BQPHMA2.js.map +0 -1
  44. package/dist/chunk-BKPDKUDN.js.map +0 -1
  45. package/dist/chunk-FZNABWJC.js +0 -282
  46. package/dist/chunk-FZNABWJC.js.map +0 -1
  47. package/dist/chunk-JTCUIJPZ.js.map +0 -1
  48. package/dist/chunk-WEQSLPKT.js +0 -9
  49. package/dist/chunk-WEQSLPKT.js.map +0 -1
  50. package/dist/chunk-ZAWXMUGZ.js.map +0 -1
@@ -0,0 +1,60 @@
1
+ import { sanitize } from './chunk-5WS67HMU.js';
2
+
3
+ // src/story/indexPages.ts
4
+ var SUFFIX = ":index";
5
+ var INDEX_NAME = "Index";
6
+ function indexId(title) {
7
+ return `${sanitize(title)}${SUFFIX}`;
8
+ }
9
+ function isIndexId(id) {
10
+ return id.endsWith(SUFFIX);
11
+ }
12
+ function stemOf(file) {
13
+ const name = file.replaceAll("\\", "/").split("/").pop() ?? file;
14
+ return name.replace(/(?:\.(?:story|stories))?\.[^.]+$/i, "");
15
+ }
16
+ function primaryFile(title, files) {
17
+ const name = (title.split("/").pop() ?? title).toLowerCase();
18
+ return files.find((file) => stemOf(file).toLowerCase() === name) ?? files[0];
19
+ }
20
+ function byComponent(stories) {
21
+ const byTitle = /* @__PURE__ */ new Map();
22
+ for (const entry of stories) {
23
+ const group = byTitle.get(entry.title);
24
+ if (group) group.push(entry);
25
+ else byTitle.set(entry.title, [entry]);
26
+ }
27
+ return [...byTitle].flatMap(([title, group]) => {
28
+ const primary = primaryFile(title, [...new Set(group.map((entry) => entry.file))]);
29
+ return [...group.filter((entry) => entry.file === primary), ...group.filter((entry) => entry.file !== primary)];
30
+ });
31
+ }
32
+ function indexEntries(stories) {
33
+ const byTitle = /* @__PURE__ */ new Map();
34
+ for (const entry of byComponent(stories)) {
35
+ const page = byTitle.get(entry.title);
36
+ if (page) {
37
+ if (page.tags) {
38
+ const common = page.tags.filter((tag) => entry.tags?.includes(tag));
39
+ if (common.length > 0) page.tags = common;
40
+ else delete page.tags;
41
+ }
42
+ continue;
43
+ }
44
+ byTitle.set(entry.title, {
45
+ id: indexId(entry.title),
46
+ title: entry.title,
47
+ name: INDEX_NAME,
48
+ exportName: "",
49
+ file: entry.file,
50
+ ...entry.componentDescription === void 0 ? {} : { componentDescription: entry.componentDescription },
51
+ ...entry.componentName === void 0 ? {} : { componentName: entry.componentName },
52
+ ...entry.tags === void 0 ? {} : { tags: entry.tags }
53
+ });
54
+ }
55
+ return [...byTitle.values()];
56
+ }
57
+
58
+ export { byComponent, indexEntries, indexId, isIndexId };
59
+ //# sourceMappingURL=chunk-STF6NQ5N.js.map
60
+ //# sourceMappingURL=chunk-STF6NQ5N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/story/indexPages.ts"],"names":[],"mappings":"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","file":"chunk-STF6NQ5N.js","sourcesContent":["import { sanitize } from './ids';\nimport type { IndexEntry } from './types';\n\nconst SUFFIX = ':index';\n\n/** The name an index page goes by in the sidebar and in its trial's title. */\nexport const INDEX_NAME = 'Index';\n\n/** The id of `title`'s index page. A colon never survives `sanitize`, so no story id can collide with it. */\nexport function indexId(title: string): string {\n return `${sanitize(title)}${SUFFIX}`;\n}\n\nexport function isIndexId(id: string): boolean {\n return id.endsWith(SUFFIX);\n}\n\n/** A story file's name with its `.stories.tsx`-style suffix removed: `Bar` for `/x/Bar.stories.tsx`. */\nfunction stemOf(file: string): string {\n const name = file.replaceAll('\\\\', '/').split('/').pop() ?? file;\n return name.replace(/(?:\\.(?:story|stories))?\\.[^.]+$/i, '');\n}\n\n/**\n * The file that speaks for a title several story files share: the one named for the title's last segment, so\n * `Bar.stories.tsx` over `lab/BarArc.stories.tsx` for `ui/Bar`, else the first. Its meta describes the component.\n */\nexport function primaryFile(title: string, files: readonly string[]): string | undefined {\n const name = (title.split('/').pop() ?? title).toLowerCase();\n return files.find((file) => stemOf(file).toLowerCase() === name) ?? files[0];\n}\n\n/** `stories` grouped by title, in the order titles first appear, each title's primary file's stories first. */\nexport function byComponent(stories: readonly IndexEntry[]): IndexEntry[] {\n const byTitle = new Map<string, IndexEntry[]>();\n for (const entry of stories) {\n const group = byTitle.get(entry.title);\n if (group) group.push(entry);\n else byTitle.set(entry.title, [entry]);\n }\n return [...byTitle].flatMap(([title, group]) => {\n const primary = primaryFile(title, [...new Set(group.map((entry) => entry.file))]);\n return [...group.filter((entry) => entry.file === primary), ...group.filter((entry) => entry.file !== primary)];\n });\n}\n\n/**\n * One index-page entry per title in `stories`, in the order titles first appear. It routes and runs like a story:\n * its `file` is the title's primary file, its descriptions are that file's, and its tags are those every story\n * of the title carries.\n */\nexport function indexEntries(stories: readonly IndexEntry[]): IndexEntry[] {\n const byTitle = new Map<string, IndexEntry>();\n for (const entry of byComponent(stories)) {\n const page = byTitle.get(entry.title);\n if (page) {\n if (page.tags) {\n const common = page.tags.filter((tag) => entry.tags?.includes(tag));\n if (common.length > 0) page.tags = common;\n else delete page.tags;\n }\n continue;\n }\n byTitle.set(entry.title, {\n id: indexId(entry.title),\n title: entry.title,\n name: INDEX_NAME,\n exportName: '',\n file: entry.file,\n ...(entry.componentDescription === undefined ? {} : { componentDescription: entry.componentDescription }),\n ...(entry.componentName === undefined ? {} : { componentName: entry.componentName }),\n ...(entry.tags === undefined ? {} : { tags: entry.tags }),\n });\n }\n return [...byTitle.values()];\n}\n"]}
@@ -0,0 +1,16 @@
1
+ // src/story/tags.ts
2
+ var GALLERY_TAG = "gallery";
3
+ function isGallery(entry) {
4
+ return entry.tags?.includes(GALLERY_TAG) ?? false;
5
+ }
6
+ var LAB_TAG = "lab";
7
+ function isLab(entry) {
8
+ return entry.tags?.includes(LAB_TAG) ?? false;
9
+ }
10
+ function taggedIds(entries) {
11
+ return { galleries: entries.filter(isGallery).map((e) => e.id), labs: entries.filter(isLab).map((e) => e.id) };
12
+ }
13
+
14
+ export { GALLERY_TAG, LAB_TAG, isGallery, isLab, taggedIds };
15
+ //# sourceMappingURL=chunk-VUPN73JP.js.map
16
+ //# sourceMappingURL=chunk-VUPN73JP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/story/tags.ts"],"names":[],"mappings":";AACO,IAAM,WAAA,GAAc;AAEpB,SAAS,UAAU,KAAA,EAA8C;AACtE,EAAA,OAAO,KAAA,CAAM,IAAA,EAAM,QAAA,CAAS,WAAW,CAAA,IAAK,KAAA;AAC9C;AAGO,IAAM,OAAA,GAAU;AAEhB,SAAS,MAAM,KAAA,EAA8C;AAClE,EAAA,OAAO,KAAA,CAAM,IAAA,EAAM,QAAA,CAAS,OAAO,CAAA,IAAK,KAAA;AAC1C;AAGO,SAAS,UAAU,OAAA,EAGxB;AACA,EAAA,OAAO,EAAE,WAAW,OAAA,CAAQ,MAAA,CAAO,SAAS,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,GAAG,IAAA,EAAM,OAAA,CAAQ,OAAO,KAAK,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAA,EAAE;AAC/G","file":"chunk-VUPN73JP.js","sourcesContent":["/** The meta tag marking a story file as a gallery: a catalog of many components, or of every permutation of one. */\nexport const GALLERY_TAG = 'gallery';\n\nexport function isGallery(entry: { tags?: readonly string[] }): boolean {\n return entry.tags?.includes(GALLERY_TAG) ?? false;\n}\n\n/** The meta tag marking a story file as a lab: a whole screen for exercising a component, not a specimen of it. */\nexport const LAB_TAG = 'lab';\n\nexport function isLab(entry: { tags?: readonly string[] }): boolean {\n return entry.tags?.includes(LAB_TAG) ?? false;\n}\n\n/** The ids among `entries` tagged as galleries and as labs, as an index page is handed them. */\nexport function taggedIds(entries: readonly { id: string; tags?: readonly string[] }[]): {\n galleries: string[];\n labs: string[];\n} {\n return { galleries: entries.filter(isGallery).map((e) => e.id), labs: entries.filter(isLab).map((e) => e.id) };\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  import { LabMode, LabContribution, LabPage } from '@weasel-js/labkit';
2
2
  import { Theme } from '@weasel-js/theme';
3
3
  import { ControlRenderer } from '@weasel-js/labkit/config';
4
- import { G as Globals, F as FrameSetup } from './FrameController-Cj_i-Rdk.js';
4
+ import { G as Globals, F as FrameSetup } from './FrameController-Tv3jrHyT.js';
5
5
  import { IconRef } from '@weasel-js/icons';
6
- import { StanceProps } from '@weasel-js/ui/stance';
6
+ import { AppearanceProps } from '@weasel-js/ui/appearance';
7
7
 
8
8
  /** One global the workshop's toolbar sets for every story, and a trial may pin for its own. */
9
9
  interface GlobalDeclaration {
@@ -28,6 +28,12 @@ interface GlobalDeclaration {
28
28
  * pin follows the lab or names a value; it cannot follow the app on its own.
29
29
  */
30
30
  follows?: (chrome: LabChrome) => string;
31
+ /**
32
+ * The value opposite `value`, for the global a lens's opposite-mode comparison flips (`ShellConfig.loupe`'s
33
+ * `modeGlobal`); undefined when it has none. `App` and `auto` are resolved to the mode they show before this is
34
+ * asked. Absent, the lens swaps `light` and `dark`.
35
+ */
36
+ opposite?: (value: string, globals: Globals) => string | undefined;
31
37
  }
32
38
  /** The lab chrome's own settings a global can follow. */
33
39
  interface LabChrome {
@@ -43,8 +49,8 @@ interface Library {
43
49
  /** A stretch of path every story file of the library has, with forward slashes: `/packages/ui/`. */
44
50
  path: string;
45
51
  label: string;
46
- /** The tone of the library's badge, as a `<Badge>` takes one. Default: the badge's own muted tone. */
47
- tone?: StanceProps['tone'];
52
+ /** The tint of the library's badge, as a `<Badge>` takes one. Default: the badge's own low emphasis. */
53
+ tint?: AppearanceProps['tint'];
48
54
  }
49
55
  /** A project's libraries, as the tree's package filter, the index page header, and Get Info ask about them. */
50
56
  interface Libraries {
@@ -53,7 +59,7 @@ interface Libraries {
53
59
  file: string;
54
60
  title: string;
55
61
  }): string;
56
- toneOf(label: string): StanceProps['tone'];
62
+ tintOf(label: string): AppearanceProps['tint'];
57
63
  }
58
64
 
59
65
  /** A shell config module: what the workshop adds to the lab. Only the workshop page imports it. */
@@ -78,6 +84,17 @@ interface ShellConfig {
78
84
  pages?: readonly LabPage[];
79
85
  /** Which of `pages` the workshop is, when the URL alone cannot say, as across dev servers on different ports. */
80
86
  path?: string;
87
+ /** What a Pixels capture may load besides the dev server's own pages, such as the origins of a web-font service
88
+ * the stories' fonts come from. Every other request a capture makes is refused. The dev server reads the list
89
+ * from this module's source, as it does `title`, so write each origin as a string. */
90
+ capture?: {
91
+ allowOrigins?: readonly string[];
92
+ };
93
+ /** The workshop's lenses. `modeGlobal` names the global holding the color mode, which a lens's opposite-mode
94
+ * comparison flips through its declaration's `opposite`. Default `mode`. */
95
+ loupe?: {
96
+ modeGlobal?: string;
97
+ };
81
98
  }
82
99
  /** A frame config module's default export. The workshop page imports it as well as every frame document, so CSS it imports reaches both; the plugin's `frameStyles` is for CSS meant for frames alone. */
83
100
  declare function defineFrameConfig(config: FrameSetup): FrameSetup;
@@ -73,6 +73,24 @@ body {
73
73
  margin-top: calc(var(--wzl-space-lg) * 1.5);
74
74
  border-top: var(--wzl-border-w) solid var(--wzl-border);
75
75
  }
76
+ .fg-index-story__main {
77
+ display: flex;
78
+ flex-wrap: wrap;
79
+ align-items: flex-start;
80
+ gap: var(--wzl-space-8) calc(var(--wzl-space-lg) * 1.5);
81
+ }
82
+ .fg-index-story__text {
83
+ flex: 1 1 20rem;
84
+ min-width: 0;
85
+ }
86
+ .fg-index-story__main > .fg-index-cell {
87
+ flex: 0 1 auto;
88
+ margin: 0;
89
+ }
90
+ .fg-index-story__main > .fg-index-cell--padded,
91
+ .fg-index-story__main > .fg-index-cell--fullscreen {
92
+ flex: 2 1 28rem;
93
+ }
76
94
  .fg-index-story__head {
77
95
  display: flex;
78
96
  align-items: center;
@@ -87,6 +105,14 @@ body {
87
105
  text-transform: var(--wzl-panel-title-case);
88
106
  color: var(--wzl-panel-title-color);
89
107
  }
108
+ .fg-index-story__link {
109
+ color: inherit;
110
+ text-decoration: none;
111
+ }
112
+ .fg-index-story__link:hover {
113
+ color: var(--wzl-accent-fg);
114
+ text-decoration: underline;
115
+ }
90
116
  .fg-index-cell {
91
117
  margin: var(--wzl-space-8) 0 0;
92
118
  width: fit-content;
@@ -213,4 +239,15 @@ body {
213
239
  margin: var(--wzl-space-8) 0 0;
214
240
  font-size: var(--wzl-font-size-sm);
215
241
  }
242
+
243
+ /* src/frame/capture/capture.css */
244
+ .fg-story[data-fg-capture] {
245
+ position: relative;
246
+ box-sizing: border-box;
247
+ width: var(--fg-capture-w);
248
+ height: var(--fg-capture-h);
249
+ overflow: hidden;
250
+ contain: paint;
251
+ container-type: size;
252
+ }
216
253
  /*# sourceMappingURL=index.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/frame/frame.css","../../src/frame/index/index.css"],"sourcesContent":["html,\nbody {\n margin: 0;\n}\n\n.fg-frame {\n box-sizing: border-box;\n}\n\n.fg-frame--centered {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n}\n\n.fg-frame--padded {\n padding: var(--wzl-space-8);\n}\n\n.fg-frame--fullscreen {\n height: 100vh;\n padding: 0;\n}\n\n.fg-frame__fault {\n margin: 0;\n padding: var(--wzl-space-8);\n white-space: pre-wrap;\n border: 1px solid #f2b8b5;\n border-radius: 4px;\n background: #fdecea;\n color: #8c1d18;\n font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n",".fg-index {\n box-sizing: border-box;\n max-width: 1200px;\n margin: 0 auto;\n padding: var(--wzl-space-lg);\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size);\n color: var(--wzl-fg);\n}\n\n.fg-index__head {\n margin-bottom: calc(var(--wzl-space-lg) * 2);\n}\n\n.fg-index__path {\n margin: 0 0 var(--wzl-space-2);\n font-size: var(--wzl-font-size-lg);\n color: var(--wzl-fg-muted);\n}\n\n.fg-index__title {\n margin: 0;\n font-family: var(--wzl-panel-title-font);\n font-size: calc(var(--wzl-font-size-xl) * 1.6);\n font-weight: var(--wzl-font-weight-medium);\n letter-spacing: var(--wzl-panel-title-tracking);\n text-transform: var(--wzl-panel-title-case);\n}\n\n.fg-index__description,\n.fg-index-story__description {\n max-width: 72ch;\n margin: var(--wzl-space-4) 0 0;\n font-family: var(--wzl-font-body);\n line-height: 1.5;\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-story {\n padding-top: calc(var(--wzl-space-lg) * 1.5);\n margin-top: calc(var(--wzl-space-lg) * 1.5);\n border-top: var(--wzl-border-w) solid var(--wzl-border);\n}\n\n.fg-index-story__head {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-6);\n}\n\n.fg-index-story__name {\n margin: 0;\n font-family: var(--wzl-panel-title-font);\n font-size: var(--wzl-font-size-xl);\n font-weight: var(--wzl-panel-title-weight);\n letter-spacing: var(--wzl-panel-title-tracking);\n text-transform: var(--wzl-panel-title-case);\n color: var(--wzl-panel-title-color);\n}\n\n.fg-index-cell {\n margin: var(--wzl-space-8) 0 0;\n width: fit-content;\n /* A fluid specimen (a slider, a tiled workspace) has no width of its own,\n so hugging it alone collapses the cell to its padding. */\n min-width: min(100%, 20rem);\n max-width: 100%;\n}\n\n.fg-index-cell__stage {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 96px;\n padding: var(--wzl-space-2);\n border: var(--wzl-border-w) solid var(--wzl-border);\n border-radius: var(--wzl-radius-md);\n background: var(--wzl-surface);\n overflow: auto;\n}\n\n/* A padded story sits top-left as it would on a page: the column's width, and\n none of the minimum height that gives a centered one presence. */\n.fg-index-cell--padded {\n width: auto;\n}\n\n.fg-index-cell--padded > .fg-index-cell__stage {\n display: block;\n min-height: 0;\n}\n\n.fg-index-cell--fullscreen {\n width: auto;\n}\n\n.fg-index-cell--fullscreen > .fg-index-cell__stage {\n display: block;\n height: 360px;\n padding: 0;\n}\n\n.fg-index-cell__label {\n margin-top: var(--wzl-space-3);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-variants__row {\n margin-top: calc(var(--wzl-space-lg) * 1.25);\n}\n\n/* Set as the kit's panel titles are, at the small size. */\n.fg-index-variants__label {\n margin: 0;\n font-family: var(--wzl-panel-title-font);\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-panel-title-weight);\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-variants__cells {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: var(--wzl-space-6);\n}\n\n.fg-index-variants__cells > .fg-index-cell {\n margin-top: var(--wzl-space-4);\n}\n\n.fg-index__library {\n margin-inline-end: var(--wzl-space-4);\n vertical-align: middle;\n}\n\n.fg-index__title-row {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-6);\n}\n\n.fg-index-history {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wzl-space-2) var(--wzl-space-8);\n margin: var(--wzl-space-8) 0 0;\n font-size: var(--wzl-font-size-sm);\n}\n\n.fg-index-history__item {\n display: flex;\n gap: var(--wzl-space-3);\n}\n\n.fg-index-history__label {\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-history__value {\n margin: 0;\n}\n\n.fg-index-deps {\n display: grid;\n grid-template-columns: max-content 1fr;\n gap: var(--wzl-space-3) var(--wzl-space-8);\n margin: var(--wzl-space-8) 0 0;\n font-size: var(--wzl-font-size-sm);\n}\n\n.fg-index-deps__row {\n display: contents;\n}\n\n.fg-index-deps__label {\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-deps__value {\n margin: 0;\n}\n\n.fg-index-deps__list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wzl-space-2) var(--wzl-space-6);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.fg-index-deps__link {\n color: var(--wzl-accent-fg);\n text-decoration: none;\n}\n\n.fg-index-deps__link:hover {\n text-decoration: underline;\n}\n\n.fg-index-deps__prefix,\n.fg-index-deps__empty,\n.fg-index-deps__none {\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-deps__none {\n max-width: 72ch;\n margin: var(--wzl-space-8) 0 0;\n font-size: var(--wzl-font-size-sm);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/frame/frame.css","../../src/frame/index/index.css","../../src/frame/capture/capture.css"],"sourcesContent":["html,\nbody {\n margin: 0;\n}\n\n.fg-frame {\n box-sizing: border-box;\n}\n\n.fg-frame--centered {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n}\n\n.fg-frame--padded {\n padding: var(--wzl-space-8);\n}\n\n.fg-frame--fullscreen {\n height: 100vh;\n padding: 0;\n}\n\n.fg-frame__fault {\n margin: 0;\n padding: var(--wzl-space-8);\n white-space: pre-wrap;\n border: 1px solid #f2b8b5;\n border-radius: 4px;\n background: #fdecea;\n color: #8c1d18;\n font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n",".fg-index {\n box-sizing: border-box;\n max-width: 1200px;\n margin: 0 auto;\n padding: var(--wzl-space-lg);\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size);\n color: var(--wzl-fg);\n}\n\n.fg-index__head {\n margin-bottom: calc(var(--wzl-space-lg) * 2);\n}\n\n.fg-index__path {\n margin: 0 0 var(--wzl-space-2);\n font-size: var(--wzl-font-size-lg);\n color: var(--wzl-fg-muted);\n}\n\n.fg-index__title {\n margin: 0;\n font-family: var(--wzl-panel-title-font);\n font-size: calc(var(--wzl-font-size-xl) * 1.6);\n font-weight: var(--wzl-font-weight-medium);\n letter-spacing: var(--wzl-panel-title-tracking);\n text-transform: var(--wzl-panel-title-case);\n}\n\n.fg-index__description,\n.fg-index-story__description {\n max-width: 72ch;\n margin: var(--wzl-space-4) 0 0;\n font-family: var(--wzl-font-body);\n line-height: 1.5;\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-story {\n padding-top: calc(var(--wzl-space-lg) * 1.5);\n margin-top: calc(var(--wzl-space-lg) * 1.5);\n border-top: var(--wzl-border-w) solid var(--wzl-border);\n}\n\n/* Text and example share a row while both fit, and the example wraps under the text rather than squeezing it. */\n.fg-index-story__main {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: var(--wzl-space-8) calc(var(--wzl-space-lg) * 1.5);\n}\n\n.fg-index-story__text {\n flex: 1 1 20rem;\n min-width: 0;\n}\n\n.fg-index-story__main > .fg-index-cell {\n flex: 0 1 auto;\n margin: 0;\n}\n\n.fg-index-story__main > .fg-index-cell--padded,\n.fg-index-story__main > .fg-index-cell--fullscreen {\n flex: 2 1 28rem;\n}\n\n.fg-index-story__head {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-6);\n}\n\n.fg-index-story__name {\n margin: 0;\n font-family: var(--wzl-panel-title-font);\n font-size: var(--wzl-font-size-xl);\n font-weight: var(--wzl-panel-title-weight);\n letter-spacing: var(--wzl-panel-title-tracking);\n text-transform: var(--wzl-panel-title-case);\n color: var(--wzl-panel-title-color);\n}\n\n.fg-index-story__link {\n color: inherit;\n text-decoration: none;\n}\n\n.fg-index-story__link:hover {\n color: var(--wzl-accent-fg);\n text-decoration: underline;\n}\n\n.fg-index-cell {\n margin: var(--wzl-space-8) 0 0;\n width: fit-content;\n /* A fluid specimen (a slider, a tiled workspace) has no width of its own,\n so hugging it alone collapses the cell to its padding. */\n min-width: min(100%, 20rem);\n max-width: 100%;\n}\n\n.fg-index-cell__stage {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 96px;\n padding: var(--wzl-space-2);\n border: var(--wzl-border-w) solid var(--wzl-border);\n border-radius: var(--wzl-radius-md);\n background: var(--wzl-surface);\n overflow: auto;\n}\n\n/* A padded story sits top-left as it would on a page: the column's width, and\n none of the minimum height that gives a centered one presence. */\n.fg-index-cell--padded {\n width: auto;\n}\n\n.fg-index-cell--padded > .fg-index-cell__stage {\n display: block;\n min-height: 0;\n}\n\n.fg-index-cell--fullscreen {\n width: auto;\n}\n\n.fg-index-cell--fullscreen > .fg-index-cell__stage {\n display: block;\n height: 360px;\n padding: 0;\n}\n\n.fg-index-cell__label {\n margin-top: var(--wzl-space-3);\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-variants__row {\n margin-top: calc(var(--wzl-space-lg) * 1.25);\n}\n\n/* Set as the kit's panel titles are, at the small size. */\n.fg-index-variants__label {\n margin: 0;\n font-family: var(--wzl-panel-title-font);\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-panel-title-weight);\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-variants__cells {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: var(--wzl-space-6);\n}\n\n.fg-index-variants__cells > .fg-index-cell {\n margin-top: var(--wzl-space-4);\n}\n\n.fg-index__library {\n margin-inline-end: var(--wzl-space-4);\n vertical-align: middle;\n}\n\n.fg-index__title-row {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-6);\n}\n\n.fg-index-history {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wzl-space-2) var(--wzl-space-8);\n margin: var(--wzl-space-8) 0 0;\n font-size: var(--wzl-font-size-sm);\n}\n\n.fg-index-history__item {\n display: flex;\n gap: var(--wzl-space-3);\n}\n\n.fg-index-history__label {\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-history__value {\n margin: 0;\n}\n\n.fg-index-deps {\n display: grid;\n grid-template-columns: max-content 1fr;\n gap: var(--wzl-space-3) var(--wzl-space-8);\n margin: var(--wzl-space-8) 0 0;\n font-size: var(--wzl-font-size-sm);\n}\n\n.fg-index-deps__row {\n display: contents;\n}\n\n.fg-index-deps__label {\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-deps__value {\n margin: 0;\n}\n\n.fg-index-deps__list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wzl-space-2) var(--wzl-space-6);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.fg-index-deps__link {\n color: var(--wzl-accent-fg);\n text-decoration: none;\n}\n\n.fg-index-deps__link:hover {\n text-decoration: underline;\n}\n\n.fg-index-deps__prefix,\n.fg-index-deps__empty,\n.fg-index-deps__none {\n color: var(--wzl-fg-muted);\n}\n\n.fg-index-deps__none {\n max-width: 72ch;\n margin: var(--wzl-space-8) 0 0;\n font-size: var(--wzl-font-size-sm);\n}\n","/* The capture box is the host's size exactly, whatever else styles a `.fg-story`. An isolated story's frame wrapper\n measures `100vh` against the page, which the capture sizes to the box. */\n.fg-story[data-fg-capture] {\n position: relative;\n box-sizing: border-box;\n width: var(--fg-capture-w);\n height: var(--fg-capture-h);\n overflow: hidden;\n contain: paint;\n container-type: size;\n}\n"],"mappings":"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","names":[]}
@@ -1,39 +1,72 @@
1
- import { F as FrameSetup, L as LoadedStory } from '../FrameController-Cj_i-Rdk.js';
2
- export { v as Channel, w as ConfigAnswers, x as FaultPhase, n as FromFrame, y as StartFrameOptions, o as ToFrame, z as reportImportFault, B as startFrame } from '../FrameController-Cj_i-Rdk.js';
1
+ import { G as Globals, L as Layout, I as IndexEntry, F as FrameSetup } from '../FrameController-Tv3jrHyT.js';
2
+ export { C as Channel, a as ConfigAnswers, b as FaultPhase, c as FromFrame, S as StartFrameOptions, T as ToFrame, r as reportImportFault, s as startFrame } from '../FrameController-Tv3jrHyT.js';
3
+ import { F as FrameImporters } from '../mountFrame-FdJDb3TB.js';
4
+ export { a as FrameIndexEntry, M as MountFrameOptions, m as mountFrame } from '../mountFrame-FdJDb3TB.js';
5
+ import { Theme } from '@weasel-js/theme';
3
6
  import '@weasel-js/icons';
4
7
  import '@weasel-js/labkit/config';
5
8
  import 'react';
6
9
 
7
- /** Story modules by file path, each loaded on first use. */
8
- interface FrameImporters {
9
- [file: string]: () => Promise<Record<string, unknown>>;
10
+ /** Everything the screen renders a story from, as the capture endpoint takes it. */
11
+ interface CapturePayload {
12
+ /** The story's index id. */
13
+ story: string;
14
+ /** Picks the document: an isolated story renders under frame styles, any other under shell styles. */
15
+ isolate: boolean;
16
+ /** The story's config, with forge's `$` keys stripped and a comparison's args laid over. */
17
+ config: unknown;
18
+ /** Must survive JSON. */
19
+ state: unknown;
20
+ globals: Globals;
21
+ /** CSS variable overrides, by property name. */
22
+ overrides: Readonly<Record<string, string>>;
23
+ /** Null for a story with no timeline, and for an isolated story. */
24
+ playhead: number | null;
25
+ /** The host's size in CSS pixels. */
26
+ box: {
27
+ width: number;
28
+ height: number;
29
+ };
30
+ layout: Layout;
31
+ /** Device pixel ratio, in [1, 3]. */
32
+ dpr: number;
33
+ /** Where the story's scroller stands in its host, in CSS pixels, so the capture shows the patch the screen does. */
34
+ scroll: {
35
+ left: number;
36
+ top: number;
37
+ };
38
+ /** The workshop lab's theme as the screen shows it: the lab's globals, which the shell config's `labTheme` reads,
39
+ * and its resolved color mode. An isolated story's document has no lab, and ignores it. */
40
+ lab: {
41
+ globals: Globals;
42
+ mode: 'light' | 'dark';
43
+ };
44
+ /** The OS color scheme the screen sees, which the capture page emulates: a story or a lab in `auto` mode follows
45
+ * `prefers-color-scheme`, and headless Chromium would otherwise answer light. */
46
+ colorScheme: 'light' | 'dark';
10
47
  }
11
- /** One story as a frame document needs to find it, before its module is loaded. */
12
- interface FrameIndexEntry {
13
- id: string;
14
- /** The story's title as indexed, which titles the file's stories when its meta names none. */
15
- title: string;
16
- file: string;
17
- exportName: string;
18
- /** The JSDoc above the story's export, which its index page shows. */
19
- description?: string;
20
- /** The JSDoc above the file's meta, which heads the index page. */
21
- componentDescription?: string;
22
- /** The meta's `tags`. */
23
- tags?: readonly string[];
24
- }
25
- /** Inputs to `mountFrame`. `load` replaces the default module loader, `loadStories`. */
26
- interface MountFrameOptions {
27
- index: readonly FrameIndexEntry[];
48
+
49
+ /** Inputs to `mountCapture`, as a capture page's entry module passes them. */
50
+ interface MountCaptureOptions {
51
+ index: readonly IndexEntry[];
28
52
  importers: FrameImporters;
29
- setup?: FrameSetup;
30
- load?: (mod: Record<string, unknown>, title: string, parameters?: Record<string, unknown>) => LoadedStory[];
53
+ setup: FrameSetup;
54
+ /** The shell config's `labTheme`; only the shell's capture page has one. */
55
+ labTheme?: ((globals: Globals) => Theme) | undefined;
56
+ }
57
+ declare global {
58
+ interface Window {
59
+ /** Set by `mountCapture`: renders one payload and resolves once it has painted. */
60
+ __forgeCapture?: {
61
+ render(payload: CapturePayload): Promise<void>;
62
+ };
63
+ }
31
64
  }
32
-
33
65
  /**
34
- * The frame document's entry: asks its parent for a port and shows what the handoff names, or, from a shell that
35
- * names nothing, what the hash does. The hash is read at once, so its module loads during the handshake.
66
+ * A capture page's entry: exposes `window.__forgeCapture.render(payload)`, which loads the payload's story, renders
67
+ * it afresh into `#root`, and resolves once it has painted. Rejects for a story the index does not hold, and with
68
+ * whatever throws outside the story's own error boundary.
36
69
  */
37
- declare function mountFrame(options: MountFrameOptions): Promise<void>;
70
+ declare function mountCapture({ index, importers, setup, labTheme }: MountCaptureOptions): void;
38
71
 
39
- export { type FrameImporters, type FrameIndexEntry, FrameSetup, type MountFrameOptions, mountFrame };
72
+ export { FrameImporters, FrameSetup, type MountCaptureOptions, mountCapture };
@@ -1,14 +1,19 @@
1
- import { describeSchema, createKitRoot, openChannel, isIndexId, TITLE_LIBRARIES, indexId, answerSchema, IndexPage } from '../chunk-BKPDKUDN.js';
2
- import { isGallery } from '../chunk-WEQSLPKT.js';
3
- import { createOverrides, createGlobalsTarget, createA11yHighlighter, loadStories, indexRenderOf, scanCssVars, auditElement, runAxe, StoryHost, resolveCssVar } from '../chunk-JTCUIJPZ.js';
4
- import '../chunk-L5XM5VKB.js';
5
- import '../chunk-ZAWXMUGZ.js';
1
+ import { describeSchema, createKitRoot, openChannel, TITLE_LIBRARIES, LAB_DENSITY, answerSchema, IndexPage } from '../chunk-BUXUWAI4.js';
2
+ import { isIndexId, indexId } from '../chunk-STF6NQ5N.js';
3
+ import { taggedIds, isGallery } from '../chunk-VUPN73JP.js';
4
+ import { createOverrides, createGlobalsTarget, createA11yHighlighter, loadStories, indexRenderOf, scanCssVars, auditElement, runAxe, useHostConditions, useStoryClock, StoryHost, resolveCssVar } from '../chunk-35DOPXHZ.js';
5
+ import { ClockContext } from '../chunk-L5XM5VKB.js';
6
+ import '../chunk-5WS67HMU.js';
6
7
  import '../chunk-KEQBGDUZ.js';
7
8
  import { FRAME_HELLO, stableStringify, PORT_HANDOFF } from '../chunk-GH5RE5X5.js';
8
9
  import { flushSync } from 'react-dom';
9
10
  import { jsx } from 'react/jsx-runtime';
10
11
  import { f } from '@weasel-js/labkit/config';
11
- import { Component } from 'react';
12
+ import { Component, useState, useMemo, useLayoutEffect, useRef, useEffect } from 'react';
13
+ import { useLatest, useVisibleRaf } from '@weasel-js/core';
14
+ import { interstellarTheme, LabRoot } from '@weasel-js/labkit';
15
+ import { ThemeProvider } from '@weasel-js/theme/react';
16
+ import { OverlayPortalProvider } from '@weasel-js/ui/portal-host';
12
17
 
13
18
  var VARS_SETTLE_MS = 100;
14
19
  function fault(phase, error, seq) {
@@ -207,6 +212,7 @@ function startIndex(options) {
207
212
  descriptions: options.descriptions,
208
213
  gallery: options.gallery ?? false,
209
214
  galleries: options.galleries ?? [],
215
+ labs: options.labs ?? [],
210
216
  globals,
211
217
  decorators: setup.decorators ?? [],
212
218
  open: (id) => send({ type: "open", id }),
@@ -312,7 +318,7 @@ async function mountFrame(options) {
312
318
  // The project's library table is the shell config's, which a frame document does not load.
313
319
  library: TITLE_LIBRARIES.of({ file: entries[0]?.file ?? "", title }),
314
320
  gallery: entries.every(isGallery),
315
- galleries: entries.filter(isGallery).map((e) => e.id),
321
+ ...taggedIds(entries),
316
322
  stories: stories2,
317
323
  descriptions: Object.fromEntries(entries.flatMap((e) => e.description ? [[e.id, e.description]] : [])),
318
324
  render: mods.map(indexRenderOf).find((render) => render !== null) ?? null,
@@ -334,7 +340,131 @@ async function mountFrame(options) {
334
340
  reportImportFault(channel, error);
335
341
  }
336
342
  }
343
+ var HOST_ID = "capture";
344
+ var NO_WRITE = () => {
345
+ };
346
+ var boxStyle = ({ width, height }) => ({ ["--fg-capture-w"]: `${width}px`, ["--fg-capture-h"]: `${height}px` });
347
+ function scrollTo(host, { left, top }) {
348
+ const scroller = host?.querySelector(":scope > .fg-story__scroll");
349
+ if (!scroller) return;
350
+ scroller.scrollLeft = left;
351
+ scroller.scrollTop = top;
352
+ }
353
+ function CaptureView({ story, payload, setup, labTheme, onRendered }) {
354
+ const [host, setHost] = useState(null);
355
+ useHostConditions(host, { hostId: HOST_ID, globals: payload.globals, overrides: payload.overrides, setup });
356
+ const spec = useMemo(() => story.timeline?.(payload.config) ?? null, [story, payload.config]);
357
+ const clock = useStoryClock(spec, () => payload.playhead);
358
+ const state = useMemo(
359
+ () => payload.state === null && story.initialState ? story.initialState(payload.config) : payload.state,
360
+ [payload.state, payload.config, story]
361
+ );
362
+ const ctx = {
363
+ config: payload.config,
364
+ setConfig: NO_WRITE,
365
+ state,
366
+ setState: NO_WRITE,
367
+ globals: payload.globals,
368
+ title: story.title,
369
+ name: story.name
370
+ };
371
+ const latest = useLatest({ onRendered, host, scroll: payload.scroll });
372
+ useLayoutEffect(() => scrollTo(host, payload.scroll), [host, payload.scroll]);
373
+ const ticks = useRef(0);
374
+ const raf = useVisibleRaf(
375
+ (_now, loop) => {
376
+ ticks.current += 1;
377
+ scrollTo(latest.current.host, latest.current.scroll);
378
+ if (ticks.current < 2) loop.request();
379
+ else latest.current.onRendered();
380
+ },
381
+ { dangerouslyRunWhenHidden: true }
382
+ );
383
+ useEffect(() => {
384
+ if (!host) return;
385
+ let canceled = false;
386
+ const start = () => {
387
+ if (!canceled) raf.request();
388
+ };
389
+ const { fonts } = host.ownerDocument;
390
+ if (fonts) void fonts.ready.then(start, start);
391
+ else start();
392
+ return () => {
393
+ canceled = true;
394
+ raf.cancel();
395
+ };
396
+ }, [host, raf]);
397
+ const content = host ? /* @__PURE__ */ jsx(ClockContext.Provider, { value: clock, children: /* @__PURE__ */ jsx(StoryHost, { story, ctx, decorators: setup.decorators ?? [], resetKey: 0, onError: NO_WRITE }) }) : null;
398
+ const box = /* @__PURE__ */ jsx(
399
+ "div",
400
+ {
401
+ ref: setHost,
402
+ className: "fg-story",
403
+ "data-fg-host": HOST_ID,
404
+ "data-fg-capture": "",
405
+ "data-fg-layout": payload.layout,
406
+ style: boxStyle(payload.box),
407
+ children: payload.isolate ? /* @__PURE__ */ jsx("div", { className: `fg-frame fg-frame--${payload.layout}`, children: content }) : /* @__PURE__ */ jsx("div", { className: "fg-story__scroll", children: host ? /* @__PURE__ */ jsx(OverlayPortalProvider, { container: host, contained: true, children: content }) : null })
408
+ }
409
+ );
410
+ if (payload.isolate) return box;
411
+ return /* @__PURE__ */ jsx(
412
+ ThemeProvider,
413
+ {
414
+ theme: labTheme?.(payload.lab.globals) ?? interstellarTheme,
415
+ selection: { mode: payload.lab.mode, density: LAB_DENSITY },
416
+ children: /* @__PURE__ */ jsx(LabRoot, { children: box })
417
+ }
418
+ );
419
+ }
420
+ var CaptureBoundary = class extends Component {
421
+ state = { failed: false };
422
+ static getDerivedStateFromError() {
423
+ return { failed: true };
424
+ }
425
+ componentDidCatch(error) {
426
+ this.props.onError(error);
427
+ }
428
+ render() {
429
+ return this.state.failed ? null : this.props.children;
430
+ }
431
+ };
432
+ function mountCapture({ index, importers, setup, labTheme }) {
433
+ const container = document.getElementById("root") ?? document.body;
434
+ const root = createKitRoot(container, setup.icons);
435
+ const prepared = /* @__PURE__ */ new Map();
436
+ let renders = 0;
437
+ const storyOf = (id) => {
438
+ let loading = prepared.get(id);
439
+ if (!loading) {
440
+ loading = (async () => {
441
+ const entry = index.find((e) => e.id === id);
442
+ if (!entry) throw new Error(`No story with id "${id}"`);
443
+ const importer = importers[entry.file];
444
+ if (!importer) throw new Error(`No importer for ${entry.file}`);
445
+ const story = loadStories(await importer(), entry.title, setup.parameters).find((s) => s.exportName === entry.exportName);
446
+ if (!story) throw new Error(`${entry.file} has no story export "${entry.exportName}"`);
447
+ await setup.prepare?.(story);
448
+ return story;
449
+ })();
450
+ prepared.set(id, loading);
451
+ loading.catch(() => prepared.delete(id));
452
+ }
453
+ return loading;
454
+ };
455
+ window.__forgeCapture = {
456
+ async render(payload) {
457
+ const story = await storyOf(payload.story);
458
+ renders += 1;
459
+ await new Promise((resolve, reject) => {
460
+ root.render(
461
+ /* @__PURE__ */ jsx(CaptureBoundary, { onError: reject, children: /* @__PURE__ */ jsx(CaptureView, { story, payload, setup, labTheme, onRendered: resolve }) }, renders)
462
+ );
463
+ });
464
+ }
465
+ };
466
+ }
337
467
 
338
- export { mountFrame, reportImportFault, startFrame };
468
+ export { mountCapture, mountFrame, reportImportFault, startFrame };
339
469
  //# sourceMappingURL=index.js.map
340
470
  //# sourceMappingURL=index.js.map