@weasel-js/forge 1.13.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{FrameController-DgfVaENb.d.ts → FrameController-BM6xuEfY.d.ts} +6 -10
- package/dist/{chunk-7XJNVJB5.js → chunk-B26DWEYT.js} +3 -3
- package/dist/{chunk-7XJNVJB5.js.map → chunk-B26DWEYT.js.map} +1 -1
- package/dist/{chunk-3BAM6M26.js → chunk-FPF2JSWB.js} +3 -3
- package/dist/chunk-FPF2JSWB.js.map +1 -0
- package/dist/{chunk-ORMMZ5PV.js → chunk-MRYXJTDO.js} +7 -3
- package/dist/chunk-MRYXJTDO.js.map +1 -0
- package/dist/{chunk-GHPMNT2A.js → chunk-PGLOYXDP.js} +184 -184
- package/dist/chunk-PGLOYXDP.js.map +1 -0
- package/dist/{chunk-WFT5IOHX.js → chunk-PPB7VP6R.js} +5 -4
- package/dist/chunk-PPB7VP6R.js.map +1 -0
- package/dist/{chunk-COT4I347.js → chunk-SFKDHXVE.js} +26 -4
- package/dist/chunk-SFKDHXVE.js.map +1 -0
- package/dist/chunk-STK2AZH2.js +11 -0
- package/dist/chunk-STK2AZH2.js.map +1 -0
- package/dist/chunk-TT4ZOYB6.js +143 -0
- package/dist/chunk-TT4ZOYB6.js.map +1 -0
- package/dist/{chunk-XIBKYSRU.js → chunk-UDEUT7AG.js} +124 -80
- package/dist/chunk-UDEUT7AG.js.map +1 -0
- package/dist/{config-6fWf87Tt.d.ts → config-DOjWyIui.d.ts} +1 -1
- package/dist/csf/preview-api.js +2 -2
- package/dist/frame/index.css +0 -2
- package/dist/frame/index.css.map +1 -1
- package/dist/frame/index.d.ts +4 -4
- package/dist/frame/index.js +9 -6
- package/dist/frame/index.js.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.js +4 -4
- package/dist/{mountFrame-Bs7u1ro-.d.ts → mountFrame-CRPAjg1M.d.ts} +1 -1
- package/dist/shell/index.css +164 -147
- package/dist/shell/index.css.map +1 -1
- package/dist/shell/index.d.ts +23 -5
- package/dist/shell/index.js +454 -318
- package/dist/shell/index.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +8 -10
- package/dist/test/index.js.map +1 -1
- package/dist/vite/index.js +118 -30
- package/dist/vite/index.js.map +1 -1
- package/package.json +11 -9
- package/dist/chunk-2ZBZRBHI.js +0 -10
- package/dist/chunk-2ZBZRBHI.js.map +0 -1
- package/dist/chunk-3BAM6M26.js.map +0 -1
- package/dist/chunk-COT4I347.js.map +0 -1
- package/dist/chunk-GHPMNT2A.js.map +0 -1
- package/dist/chunk-L5XM5VKB.js +0 -47
- package/dist/chunk-L5XM5VKB.js.map +0 -1
- package/dist/chunk-ORMMZ5PV.js.map +0 -1
- package/dist/chunk-WFT5IOHX.js.map +0 -1
- package/dist/chunk-XIBKYSRU.js.map +0 -1
package/dist/shell/index.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
import { parseRoute, formatRoute, paramsToKnobs, knobPaths, sameKnob, knobsToParams, PLAYHEAD_PARAM, reservedParams } from '../chunk-
|
|
2
|
-
import { DEFAULT_TITLE, CAPTURE_PATH, messageOf, MIN_DPR, MAX_DPR } from '../chunk-
|
|
3
|
-
import { TITLE_LIBRARIES, loadIndexBundle, librariesFrom, LibrariesContext, LAB_DENSITY, createKitRoot, schemaFromDescription, LibraryBadge, Description, IndexPage, indexEnvOf, LabBadge, openChannel } from '../chunk-
|
|
1
|
+
import { parseRoute, formatRoute, paramsToKnobs, knobPaths, sameKnob, paramsToPins, knobsToParams, PLAYHEAD_PARAM, pinParams, withoutPins, reservedParams } from '../chunk-SFKDHXVE.js';
|
|
2
|
+
import { DEFAULT_TITLE, ISSUES_PATH, issueLabel, CAPTURE_PATH, messageOf, MIN_DPR, MAX_DPR } from '../chunk-MRYXJTDO.js';
|
|
3
|
+
import { TITLE_LIBRARIES, loadIndexBundle, librariesFrom, LibrariesContext, LAB_DENSITY, createKitRoot, schemaFromDescription, LibraryBadge, Description, IndexPage, indexEnvOf, LabBadge, openChannel } from '../chunk-PPB7VP6R.js';
|
|
4
4
|
import { isIndexId, indexId, indexEntries } from '../chunk-RVJHUY7W.js';
|
|
5
5
|
import { isGallery, isLab } from '../chunk-VUPN73JP.js';
|
|
6
|
-
import { useTrialFrame, createTrialFrames,
|
|
7
|
-
export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-
|
|
8
|
-
import { labGlobals, withGlobals,
|
|
9
|
-
import { loadStories,
|
|
10
|
-
import '../chunk-
|
|
6
|
+
import { useTrialFrame, createTrialFrames, createStoryHosts, StoryGlobalsContext, TrialFramesContext, StoryHostsContext, StoryTrial, LiveLensesContext, trialAt, describeLens, createLiveLenses, useCopyConfig, useCssOverrides, IN_VIEW_MARGIN, LiveFrameCopies, TrialHost, LiveCopies, resolveContextOf, resolveComparison, scrollerOf, withArgs } from '../chunk-UDEUT7AG.js';
|
|
7
|
+
export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-UDEUT7AG.js';
|
|
8
|
+
import { labGlobals, withGlobals, storyConfig, followApp, GLOBALS_KEY, effectiveGlobals, labOptions, isForgePath, isGlobalsPath } from '../chunk-FPF2JSWB.js';
|
|
9
|
+
import { loadStories, applyGlobalsTo, scanCssVars, varRefs, cssRulesOf } from '../chunk-PGLOYXDP.js';
|
|
10
|
+
import { clockSpecOf, shownTime } from '../chunk-TT4ZOYB6.js';
|
|
11
11
|
import '../chunk-5WS67HMU.js';
|
|
12
|
-
import '../chunk-
|
|
13
|
-
import { stableStringify, FRAME_HELLO, PORT_HANDOFF, PROTOCOL_VERSION } from '../chunk-
|
|
14
|
-
import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, useTrialState, ToggleBar as ToggleBar$1, Input, TokenPanel, useLabEntries, inferTokenType, refitScale } from '@weasel-js/labkit';
|
|
12
|
+
import '../chunk-B26DWEYT.js';
|
|
13
|
+
import { stableStringify, DEFAULT_LAYOUT, FRAME_HELLO, PORT_HANDOFF, PROTOCOL_VERSION } from '../chunk-STK2AZH2.js';
|
|
14
|
+
import { useTrialId, Button, Lab, useLabContext, TrialIdContext, TrialTransport, usePersistedState, useTrialClockHandle, Select, useTrialState, ToggleBar as ToggleBar$1, Input as Input$1, TokenPanel, useLabEntries, inferTokenType, refitScale } from '@weasel-js/labkit';
|
|
15
15
|
import num from '@weasel-js/theme/numeric.module.css';
|
|
16
16
|
import { createContext, useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect, useId, useContext, useReducer, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
|
|
17
17
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
18
18
|
import { createEmitter } from '@weasel-js/registry';
|
|
19
19
|
import { f, valueAtPath, isConfigBranch } from '@weasel-js/labkit/config';
|
|
20
|
-
import { Slider } from '@weasel-js/ui/components/Slider';
|
|
21
|
-
import { Transport } from '@weasel-js/ui/components/Timeline';
|
|
22
20
|
import { TOKEN_MANIFEST, themeAxes, weaselTheme, THEME_SOURCES, resolveTheme, isColorModePreference, isByAxis } from '@weasel-js/theme';
|
|
23
21
|
import { scale, httpThemeApi } from '@weasel-js/theme/engine';
|
|
24
22
|
import { useLatest, useActionsRegistry, useVisibleRaf, effectivePose, useMenuHost, useMenus, sceneToAdapter, parseColor, contrastRatio, wcagLevels, historyKey, isPlainObject, createHistory, keymapShortcuts, actionBindings, keySpecShortcut, keymapLeaderId, swapOp } from '@weasel-js/core';
|
|
@@ -29,14 +27,39 @@ import { TOKEN_CATEGORIES, tokenCategory } from '@weasel-js/ui/components/TokenP
|
|
|
29
27
|
import { ToggleBar } from '@weasel-js/ui/components/ToggleBar';
|
|
30
28
|
import { useResolvedColorMode } from '@weasel-js/theme/react';
|
|
31
29
|
import { formatShortcut } from '@weasel-js/ui/components/ToolPalette';
|
|
30
|
+
import { Input } from '@weasel-js/ui/components/Input';
|
|
31
|
+
import { TextArea } from '@weasel-js/ui/components/TextArea';
|
|
32
32
|
import { Dialog } from '@weasel-js/ui/components/Dialog';
|
|
33
33
|
import { createLensScene, settingsOf, resolveSource, LensOverlay, LensActions, lensIdsOf, reticleAt, lensesOf, MAX_LENSES, addLensOps, unavailableText, useShownSource, requestsOf, BODY_SIZE, setLensOps, readBitmap, LENS_REST_MS, probeUnderLens } from '@weasel-js/labkit/loupe';
|
|
34
34
|
import { KeymapHint } from '@weasel-js/ui/components/KeymapHint';
|
|
35
35
|
import { pref } from '@weasel-js/prefs';
|
|
36
36
|
import { splitTopLevel, specificity, compareSpecificity } from '@weasel-js/svg';
|
|
37
|
+
import { holdAt } from '@weasel-js/clock';
|
|
37
38
|
import { Callout } from '@weasel-js/ui/components/Callout';
|
|
38
|
-
import {
|
|
39
|
+
import { Tree } from '@weasel-js/ui/components/Tree';
|
|
40
|
+
import { useNearViewport } from '@weasel-js/react';
|
|
39
41
|
|
|
42
|
+
// src/shell/a11y/reasons.ts
|
|
43
|
+
var PLAIN = {
|
|
44
|
+
bgOverlap: "axe couldn't tell the background: another element lies on top of this one.",
|
|
45
|
+
pseudoContent: "axe couldn't tell the background: a ::before or ::after box paints over or behind this text.",
|
|
46
|
+
bgGradient: "axe couldn't tell the background: it is a gradient.",
|
|
47
|
+
bgImage: "axe couldn't tell the background: it is an image.",
|
|
48
|
+
imgNode: "axe couldn't tell the background: an image or canvas sits behind this text.",
|
|
49
|
+
elmPartiallyObscured: "axe couldn't tell the background: it covers only part of this text.",
|
|
50
|
+
elmPartiallyObscuring: "axe couldn't tell the background: this text runs across more than one element.",
|
|
51
|
+
outsideViewport: "axe couldn't see this text: it is scrolled out of view.",
|
|
52
|
+
blended: "Couldn't tell the colors: this text or something around it is see-through, filtered, or blended.",
|
|
53
|
+
fgAlpha: "axe couldn't tell the text color: it is partly see-through.",
|
|
54
|
+
complexTextShadows: "axe couldn't tell the contrast: the text has a shadow.",
|
|
55
|
+
equalRatio: "The text is the same color as its background, so it may be hidden on purpose.",
|
|
56
|
+
shortTextContent: "Fails contrast, but is a single character that may be a symbol rather than text.",
|
|
57
|
+
nonBmp: "Holds only symbols or icons, not text."
|
|
58
|
+
};
|
|
59
|
+
function plainReason(reason) {
|
|
60
|
+
if (!reason) return null;
|
|
61
|
+
return (reason.key ? PLAIN[reason.key] : void 0) ?? reason.message ?? null;
|
|
62
|
+
}
|
|
40
63
|
function pointAt(point, nodes) {
|
|
41
64
|
const enter = (event) => {
|
|
42
65
|
event.stopPropagation();
|
|
@@ -50,7 +73,13 @@ function Finding({ finding, point }) {
|
|
|
50
73
|
/* @__PURE__ */ jsx("a", { href: finding.helpUrl, target: "_blank", rel: "noreferrer", children: finding.help }),
|
|
51
74
|
finding.impact ? /* @__PURE__ */ jsx("span", { className: "fg-a11y__impact", "data-impact": finding.impact, children: finding.impact }) : null
|
|
52
75
|
] }),
|
|
53
|
-
/* @__PURE__ */ jsx("ul", { className: "fg-a11y__nodes", children: finding.nodes.map((node) =>
|
|
76
|
+
/* @__PURE__ */ jsx("ul", { className: "fg-a11y__nodes", children: finding.nodes.map((node) => {
|
|
77
|
+
const reason = plainReason(node.reason);
|
|
78
|
+
return /* @__PURE__ */ jsxs("li", { ...pointAt(point, [node]), children: [
|
|
79
|
+
/* @__PURE__ */ jsx("code", { children: node.target.join(" ") }),
|
|
80
|
+
reason ? /* @__PURE__ */ jsx("p", { className: "fg-a11y__reason", children: reason }) : null
|
|
81
|
+
] }, `${finding.id}:${node.target.join(",")}`);
|
|
82
|
+
}) })
|
|
54
83
|
] });
|
|
55
84
|
}
|
|
56
85
|
function Findings({ title, findings, point }) {
|
|
@@ -185,6 +214,50 @@ function createStoryChanges() {
|
|
|
185
214
|
return { subscribe: changes.subscribe, emit: changes.emit };
|
|
186
215
|
}
|
|
187
216
|
var DependenciesContext = createContext(null);
|
|
217
|
+
|
|
218
|
+
// src/shell/breadcrumb.ts
|
|
219
|
+
var bare = (label) => label.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "");
|
|
220
|
+
function restatesTitle(title, name) {
|
|
221
|
+
return bare(title.slice(title.lastIndexOf("/") + 1)) === bare(name);
|
|
222
|
+
}
|
|
223
|
+
function crumbs(title, name) {
|
|
224
|
+
const segments = title.split("/");
|
|
225
|
+
const steps = segments.map((label, i) => ({ label, path: segments.slice(0, i + 1).join("/") }));
|
|
226
|
+
if (restatesTitle(title, name)) steps[steps.length - 1] = { label: segments[segments.length - 1], path: null };
|
|
227
|
+
else steps.push({ label: name, path: null });
|
|
228
|
+
return steps.filter((step, i) => i === 0 || step.label.toLowerCase() !== "index");
|
|
229
|
+
}
|
|
230
|
+
function breadcrumb(title, name) {
|
|
231
|
+
return crumbs(title, name).map((step) => step.label).join(" > ");
|
|
232
|
+
}
|
|
233
|
+
var reveals = createEmitter();
|
|
234
|
+
function revealInTree(path) {
|
|
235
|
+
reveals.emit(path);
|
|
236
|
+
}
|
|
237
|
+
function onRevealInTree(listener) {
|
|
238
|
+
return reveals.subscribe(listener);
|
|
239
|
+
}
|
|
240
|
+
function Crumbs({ entry }) {
|
|
241
|
+
const steps = crumbs(entry.title, entry.name);
|
|
242
|
+
return /* @__PURE__ */ jsx("span", { className: "fg-crumbs", children: steps.map((step, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
243
|
+
i > 0 ? /* @__PURE__ */ jsx("span", { className: "fg-crumbs__sep", "aria-hidden": "true", children: "\u203A" }) : null,
|
|
244
|
+
step.path === null ? /* @__PURE__ */ jsx("span", { className: "fg-crumbs__here", children: step.label }) : /* @__PURE__ */ jsx(
|
|
245
|
+
"button",
|
|
246
|
+
{
|
|
247
|
+
type: "button",
|
|
248
|
+
className: "fg-crumbs__step",
|
|
249
|
+
title: `Show ${step.label} in the story tree`,
|
|
250
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
251
|
+
onClick: () => revealInTree(step.path),
|
|
252
|
+
children: step.label
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
] }, step.path ?? `${step.label}:${i}`)) });
|
|
256
|
+
}
|
|
257
|
+
function crumbTitle(entry) {
|
|
258
|
+
const title = breadcrumb(entry.title, entry.name);
|
|
259
|
+
return { title, renderTitle: (shown) => shown === title ? /* @__PURE__ */ jsx(Crumbs, { entry }) : shown };
|
|
260
|
+
}
|
|
188
261
|
function readRoute() {
|
|
189
262
|
return parseRoute(location.hash).story;
|
|
190
263
|
}
|
|
@@ -236,108 +309,6 @@ function useRoute() {
|
|
|
236
309
|
}, []);
|
|
237
310
|
return [route, setRoute];
|
|
238
311
|
}
|
|
239
|
-
|
|
240
|
-
// src/timeline/playheadUrl.ts
|
|
241
|
-
function formatSeconds(ms) {
|
|
242
|
-
const rounded = Math.round(ms) / 1e3;
|
|
243
|
-
return String(rounded === 0 ? 0 : rounded);
|
|
244
|
-
}
|
|
245
|
-
function parseSeconds(text) {
|
|
246
|
-
if (text == null || !/^\s*-?(\d+\.?\d*|\.\d+)\s*$/.test(text)) return null;
|
|
247
|
-
return Math.round(Number(text) * 1e3);
|
|
248
|
-
}
|
|
249
|
-
function readPlayheadParam() {
|
|
250
|
-
return parseSeconds(readRouteParams()[PLAYHEAD_PARAM]);
|
|
251
|
-
}
|
|
252
|
-
function writePlayheadParam(ms) {
|
|
253
|
-
replaceRouteParams(
|
|
254
|
-
({ [PLAYHEAD_PARAM]: _held, ...rest }) => ms === null ? rest : { ...rest, [PLAYHEAD_PARAM]: formatSeconds(ms) }
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
function TransportBar({ clock }) {
|
|
258
|
-
const { time, playing, loop, rate, span } = useSyncExternalStore(clock.subscribe, clock.get);
|
|
259
|
-
return /* @__PURE__ */ jsxs("div", { className: "fg-transport", children: [
|
|
260
|
-
/* @__PURE__ */ jsx(
|
|
261
|
-
Slider,
|
|
262
|
-
{
|
|
263
|
-
className: "fg-transport__scrub",
|
|
264
|
-
ariaLabel: "Scrub",
|
|
265
|
-
thumbShape: "dot",
|
|
266
|
-
trackHeight: "var(--wzl-slider-track-h)",
|
|
267
|
-
thumbSize: "var(--wzl-slider-thumb-size)",
|
|
268
|
-
readoutPlacement: "none",
|
|
269
|
-
min: span.start,
|
|
270
|
-
max: span.end > span.start ? span.end : span.start + 1,
|
|
271
|
-
thumbs: [{ value: time, valueText: `${formatSeconds(time)} seconds` }],
|
|
272
|
-
onInput: ([thumb]) => {
|
|
273
|
-
if (thumb) clock.seek(thumb.value);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
),
|
|
277
|
-
/* @__PURE__ */ jsx(
|
|
278
|
-
Transport,
|
|
279
|
-
{
|
|
280
|
-
paused: !playing,
|
|
281
|
-
loop,
|
|
282
|
-
rate,
|
|
283
|
-
playhead: time,
|
|
284
|
-
duration: span.end,
|
|
285
|
-
onPlay: clock.play,
|
|
286
|
-
onPause: clock.pause,
|
|
287
|
-
onLoopChange: (next) => clock.setLoop(next !== false && next !== 0),
|
|
288
|
-
onRateChange: clock.setRate
|
|
289
|
-
}
|
|
290
|
-
)
|
|
291
|
-
] });
|
|
292
|
-
}
|
|
293
|
-
function TrialTransport({ trialId }) {
|
|
294
|
-
const clock = useTrialClock(trialId);
|
|
295
|
-
return clock ? /* @__PURE__ */ jsx(TransportBar, { clock }) : null;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// src/shell/breadcrumb.ts
|
|
299
|
-
var bare = (label) => label.toLowerCase().replace(/[^\p{L}\p{N}]/gu, "");
|
|
300
|
-
function restatesTitle(title, name) {
|
|
301
|
-
return bare(title.slice(title.lastIndexOf("/") + 1)) === bare(name);
|
|
302
|
-
}
|
|
303
|
-
function crumbs(title, name) {
|
|
304
|
-
const segments = title.split("/");
|
|
305
|
-
const steps = segments.map((label, i) => ({ label, path: segments.slice(0, i + 1).join("/") }));
|
|
306
|
-
if (restatesTitle(title, name)) steps[steps.length - 1] = { label: segments[segments.length - 1], path: null };
|
|
307
|
-
else steps.push({ label: name, path: null });
|
|
308
|
-
return steps.filter((step, i) => i === 0 || step.label.toLowerCase() !== "index");
|
|
309
|
-
}
|
|
310
|
-
function breadcrumb(title, name) {
|
|
311
|
-
return crumbs(title, name).map((step) => step.label).join(" > ");
|
|
312
|
-
}
|
|
313
|
-
var reveals = createEmitter();
|
|
314
|
-
function revealInTree(path) {
|
|
315
|
-
reveals.emit(path);
|
|
316
|
-
}
|
|
317
|
-
function onRevealInTree(listener) {
|
|
318
|
-
return reveals.subscribe(listener);
|
|
319
|
-
}
|
|
320
|
-
function Crumbs({ entry }) {
|
|
321
|
-
const steps = crumbs(entry.title, entry.name);
|
|
322
|
-
return /* @__PURE__ */ jsx("span", { className: "fg-crumbs", children: steps.map((step, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
323
|
-
i > 0 ? /* @__PURE__ */ jsx("span", { className: "fg-crumbs__sep", "aria-hidden": "true", children: "\u203A" }) : null,
|
|
324
|
-
step.path === null ? /* @__PURE__ */ jsx("span", { className: "fg-crumbs__here", children: step.label }) : /* @__PURE__ */ jsx(
|
|
325
|
-
"button",
|
|
326
|
-
{
|
|
327
|
-
type: "button",
|
|
328
|
-
className: "fg-crumbs__step",
|
|
329
|
-
title: `Show ${step.label} in the story tree`,
|
|
330
|
-
onPointerDown: (event) => event.stopPropagation(),
|
|
331
|
-
onClick: () => revealInTree(step.path),
|
|
332
|
-
children: step.label
|
|
333
|
-
}
|
|
334
|
-
)
|
|
335
|
-
] }, step.path ?? `${step.label}:${i}`)) });
|
|
336
|
-
}
|
|
337
|
-
function crumbTitle(entry) {
|
|
338
|
-
const title = breadcrumb(entry.title, entry.name);
|
|
339
|
-
return { title, renderTitle: (shown) => shown === title ? /* @__PURE__ */ jsx(Crumbs, { entry }) : shown };
|
|
340
|
-
}
|
|
341
312
|
function IndexTrial({ bundle, setup, config }) {
|
|
342
313
|
const lab = useLabContext();
|
|
343
314
|
const trialId = useContext(TrialIdContext);
|
|
@@ -353,14 +324,18 @@ function IndexTrial({ bundle, setup, config }) {
|
|
|
353
324
|
/* @__PURE__ */ jsx(LiveCopies, { trialId, layout: "fullscreen", setup, trialConfig: config, children: render })
|
|
354
325
|
] });
|
|
355
326
|
}
|
|
356
|
-
function CaptureInputs({ entry, layout, config, state }) {
|
|
327
|
+
function CaptureInputs({ entry, layout, config, state, timed = false }) {
|
|
357
328
|
const capture = useContext(LoupeCaptureContext);
|
|
358
329
|
const trialId = useContext(TrialIdContext);
|
|
359
330
|
const [overrides] = useCssOverrides();
|
|
360
331
|
const withdraw = useRef(null);
|
|
332
|
+
const handle = useTrialClockHandle();
|
|
333
|
+
const clock = timed ? handle?.clock ?? null : null;
|
|
361
334
|
useEffect(() => {
|
|
362
|
-
if (capture
|
|
363
|
-
|
|
335
|
+
if (!capture || !trialId) return;
|
|
336
|
+
const playhead = () => clock && clock.rate === 0 ? shownTime(clock) : null;
|
|
337
|
+
withdraw.current = capture.trials.publish(trialId, { entry, layout, config, state, overrides, playhead });
|
|
338
|
+
}, [capture, trialId, entry, layout, config, state, overrides, clock]);
|
|
364
339
|
useEffect(
|
|
365
340
|
() => () => {
|
|
366
341
|
withdraw.current?.();
|
|
@@ -381,14 +356,81 @@ function LoupeProvider({ captureBase, revision = STILL, children }) {
|
|
|
381
356
|
);
|
|
382
357
|
return /* @__PURE__ */ jsx(LiveLensesContext.Provider, { value: lenses, children: /* @__PURE__ */ jsx(LoupeCaptureContext.Provider, { value: capture, children }) });
|
|
383
358
|
}
|
|
384
|
-
|
|
385
|
-
|
|
359
|
+
|
|
360
|
+
// src/shell/storyMarks.ts
|
|
361
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
362
|
+
function documentCss(doc) {
|
|
363
|
+
const out = [];
|
|
364
|
+
for (const sheet of Array.from(doc.styleSheets)) {
|
|
365
|
+
try {
|
|
366
|
+
for (const rule of Array.from(sheet.cssRules)) out.push(rule.cssText);
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return out.join("\n");
|
|
371
|
+
}
|
|
372
|
+
function storyBox(host) {
|
|
373
|
+
const w = Math.max(1, host?.offsetWidth ?? 0);
|
|
374
|
+
const h = Math.max(1, host?.offsetHeight ?? 0);
|
|
375
|
+
const scroller = host?.querySelector(":scope > .fg-story__scroll") ?? null;
|
|
376
|
+
return {
|
|
377
|
+
content: { w: Math.max(w, scroller?.scrollWidth ?? 0), h: Math.max(h, scroller?.scrollHeight ?? 0) },
|
|
378
|
+
shown: { x: scroller?.scrollLeft ?? 0, y: scroller?.scrollTop ?? 0, w, h }
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function captureHost(host) {
|
|
382
|
+
const doc = host.ownerDocument;
|
|
383
|
+
const { w, h } = storyBox(host).content;
|
|
384
|
+
const root = doc.createElement("div");
|
|
385
|
+
root.setAttribute("data-fg-capture", "");
|
|
386
|
+
const tokens = scanCssVars(doc, void 0, host).filter((v) => v.value).map((v) => `${v.name}: ${v.value};`).join("");
|
|
387
|
+
const style = doc.createElement("style");
|
|
388
|
+
style.textContent = [
|
|
389
|
+
documentCss(doc),
|
|
390
|
+
`[data-fg-capture]{${tokens}}`,
|
|
391
|
+
`[data-fg-capture] > .fg-story{width:${w}px;height:${h}px}`
|
|
392
|
+
].join("\n");
|
|
393
|
+
const copy = host.cloneNode(true);
|
|
394
|
+
copy.removeAttribute("data-pending");
|
|
395
|
+
root.append(style, copy);
|
|
396
|
+
const inner = new XMLSerializer().serializeToString(root);
|
|
397
|
+
return {
|
|
398
|
+
kind: "svg",
|
|
399
|
+
markup: `<svg xmlns="${SVG_NS}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}"><foreignObject x="0" y="0" width="${w}" height="${h}">${inner}</foreignObject></svg>`
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
function storyMarks(hosts) {
|
|
403
|
+
return {
|
|
404
|
+
targets: (_state, _config, trial) => {
|
|
405
|
+
const ref = hosts.ref(trial.id);
|
|
406
|
+
const { content, shown } = storyBox(ref.current);
|
|
407
|
+
return [
|
|
408
|
+
{
|
|
409
|
+
id: "story",
|
|
410
|
+
ref,
|
|
411
|
+
content,
|
|
412
|
+
shown,
|
|
413
|
+
// Out of view, a host holds no content; it is rendered for the capture rather than scrolled to, which
|
|
414
|
+
// would move the reader's view.
|
|
415
|
+
base: async () => {
|
|
416
|
+
const release = await hosts.show(trial.id);
|
|
417
|
+
try {
|
|
418
|
+
if (!ref.current) throw new Error("[forge] the story has no host to capture");
|
|
419
|
+
return captureHost(ref.current);
|
|
420
|
+
} finally {
|
|
421
|
+
release();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
];
|
|
426
|
+
}
|
|
427
|
+
};
|
|
386
428
|
}
|
|
387
429
|
function documentInstrument(options) {
|
|
388
|
-
const { entry, loaded, setup, globals = {} } = options;
|
|
430
|
+
const { entry, loaded, setup, globals = {}, hosts } = options;
|
|
389
431
|
const schema = loaded.kind === "story" ? loaded.story.config : f.schema({});
|
|
390
|
-
const
|
|
391
|
-
const config = withGlobals(
|
|
432
|
+
const timeline = loaded.kind === "story" ? loaded.story.timeline : null;
|
|
433
|
+
const config = withGlobals(schema, globals);
|
|
392
434
|
const initialState = loaded.kind === "story" ? loaded.story.initialState : null;
|
|
393
435
|
return {
|
|
394
436
|
name: entry.id,
|
|
@@ -404,12 +446,17 @@ function documentInstrument(options) {
|
|
|
404
446
|
entry,
|
|
405
447
|
layout: loaded.kind === "story" ? loaded.story.layout : "fullscreen",
|
|
406
448
|
config: ctx.config,
|
|
407
|
-
state: ctx.state
|
|
449
|
+
state: ctx.state,
|
|
450
|
+
timed: timeline !== null
|
|
408
451
|
}
|
|
409
452
|
)
|
|
410
453
|
] }),
|
|
454
|
+
...hosts ? { annotations: storyMarks(hosts) } : {},
|
|
411
455
|
...loaded.kind === "story" && loaded.story.viewport ? { stage: { size: { width: loaded.story.viewport.width, height: loaded.story.viewport.height } } } : {},
|
|
412
|
-
...
|
|
456
|
+
...timeline ? {
|
|
457
|
+
clock: (c) => clockSpecOf(timeline(storyConfig(c))),
|
|
458
|
+
chrome: [{ id: "fg-transport", region: "status", render: (c) => /* @__PURE__ */ jsx(TrialTransport, { trialId: c.trialId, className: "fg-transport" }) }]
|
|
459
|
+
} : {}
|
|
413
460
|
};
|
|
414
461
|
}
|
|
415
462
|
function Fault({ phase, message }) {
|
|
@@ -1046,7 +1093,7 @@ function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
|
|
|
1046
1093
|
}
|
|
1047
1094
|
),
|
|
1048
1095
|
/* @__PURE__ */ jsx(
|
|
1049
|
-
Input,
|
|
1096
|
+
Input$1,
|
|
1050
1097
|
{
|
|
1051
1098
|
className: "fg-css-vars__filter",
|
|
1052
1099
|
"aria-label": "Filter",
|
|
@@ -1268,6 +1315,137 @@ function infoItem(open) {
|
|
|
1268
1315
|
onActivate: open
|
|
1269
1316
|
};
|
|
1270
1317
|
}
|
|
1318
|
+
var dateFormat = new Intl.DateTimeFormat(void 0, { dateStyle: "medium" });
|
|
1319
|
+
function IssueList({ component, issues, note }) {
|
|
1320
|
+
if (!issues) return /* @__PURE__ */ jsx("p", { className: "fg-issues__note", children: note ?? "Not connected to an issue tracker." });
|
|
1321
|
+
if (issues.length === 0) return /* @__PURE__ */ jsxs("p", { className: "fg-issues__note", children: [
|
|
1322
|
+
"No open issues for ",
|
|
1323
|
+
component,
|
|
1324
|
+
"."
|
|
1325
|
+
] });
|
|
1326
|
+
return /* @__PURE__ */ jsx("ul", { className: "fg-issues__list", children: issues.map((issue) => /* @__PURE__ */ jsxs("li", { className: "fg-issues__item", children: [
|
|
1327
|
+
/* @__PURE__ */ jsxs("a", { href: issue.url, target: "_blank", rel: "noreferrer", className: "fg-issues__link", children: [
|
|
1328
|
+
/* @__PURE__ */ jsxs("span", { className: `fg-issues__number ${num.numeric}`, children: [
|
|
1329
|
+
"#",
|
|
1330
|
+
issue.number
|
|
1331
|
+
] }),
|
|
1332
|
+
/* @__PURE__ */ jsx("span", { className: "fg-issues__title", children: issue.title })
|
|
1333
|
+
] }),
|
|
1334
|
+
/* @__PURE__ */ jsx("time", { className: `fg-issues__opened ${num.numeric}`, dateTime: issue.opened, children: dateFormat.format(new Date(issue.opened)) })
|
|
1335
|
+
] }, issue.number)) });
|
|
1336
|
+
}
|
|
1337
|
+
function IssuesPanel({ component, storyId, issues, listNote, onReport }) {
|
|
1338
|
+
const [title, setTitle] = useState("");
|
|
1339
|
+
const [body, setBody] = useState("");
|
|
1340
|
+
const canReport = onReport !== void 0 && title.trim() !== "";
|
|
1341
|
+
const submit = (event) => {
|
|
1342
|
+
event.preventDefault();
|
|
1343
|
+
if (!canReport) return;
|
|
1344
|
+
onReport({ component, storyId, title: title.trim(), body });
|
|
1345
|
+
setTitle("");
|
|
1346
|
+
setBody("");
|
|
1347
|
+
};
|
|
1348
|
+
return /* @__PURE__ */ jsxs("div", { className: "fg-issues", children: [
|
|
1349
|
+
/* @__PURE__ */ jsxs("section", { className: "fg-issues__group", "aria-label": "Open issues", children: [
|
|
1350
|
+
/* @__PURE__ */ jsx("h3", { className: "fg-issues__heading", children: "Open" }),
|
|
1351
|
+
/* @__PURE__ */ jsx(IssueList, { component, ...issues ? { issues } : {}, ...listNote ? { note: listNote } : {} })
|
|
1352
|
+
] }),
|
|
1353
|
+
/* @__PURE__ */ jsxs("form", { className: "fg-issues__group", "aria-label": "Report an issue", onSubmit: submit, children: [
|
|
1354
|
+
/* @__PURE__ */ jsx("h3", { className: "fg-issues__heading", children: "Report" }),
|
|
1355
|
+
/* @__PURE__ */ jsx(Input, { label: "Title", value: title, onChange: setTitle }),
|
|
1356
|
+
/* @__PURE__ */ jsx(TextArea, { label: "Description", value: body, onChange: setBody, placeholder: "What did you expect, and what happened?" }),
|
|
1357
|
+
/* @__PURE__ */ jsx("div", { className: "fg-issues__actions", children: /* @__PURE__ */ jsx(Button$1, { size: "sm", type: "submit", disabled: !canReport, children: "Report" }) }),
|
|
1358
|
+
onReport ? null : /* @__PURE__ */ jsx("p", { className: "fg-issues__note", children: "Reporting is not connected yet." })
|
|
1359
|
+
] })
|
|
1360
|
+
] });
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// src/shell/issues/tracker.ts
|
|
1364
|
+
async function fetchIssues(base, component, signal) {
|
|
1365
|
+
const res = await fetch(`${base}${ISSUES_PATH}?${new URLSearchParams({ component })}`, signal ? { signal } : {});
|
|
1366
|
+
const type = res.headers.get("Content-Type") ?? "";
|
|
1367
|
+
if (!type.includes("application/json")) throw new Error(`The dev server answered ${res.status} with no issues.`);
|
|
1368
|
+
return await res.json();
|
|
1369
|
+
}
|
|
1370
|
+
function reopenLink(page, story, schema, config, globals) {
|
|
1371
|
+
return `${page}${formatRoute({ story, params: { ...knobsToParams(schema, config), ...pinParams(globals) } })}`;
|
|
1372
|
+
}
|
|
1373
|
+
function reportBody(report, context) {
|
|
1374
|
+
const code = (text) => `\`${text}\``;
|
|
1375
|
+
const checkout = [
|
|
1376
|
+
context.branch === null ? null : `**Branch:** ${code(context.branch)}`,
|
|
1377
|
+
context.commit === null ? null : `**Commit:** ${context.commit}`
|
|
1378
|
+
].filter((line) => line !== null);
|
|
1379
|
+
return [
|
|
1380
|
+
report.body.trim(),
|
|
1381
|
+
"---",
|
|
1382
|
+
[`**Story:** ${code(report.storyId)}`, `**Reopen:** ${context.link}`, ...checkout].join("\n"),
|
|
1383
|
+
"<!-- Paste a screenshot here. -->"
|
|
1384
|
+
].filter((part) => part !== "").join("\n\n");
|
|
1385
|
+
}
|
|
1386
|
+
function newIssueUrl(repo, report, context) {
|
|
1387
|
+
const params = new URLSearchParams({
|
|
1388
|
+
title: report.title,
|
|
1389
|
+
body: reportBody(report, context),
|
|
1390
|
+
labels: issueLabel(report.component)
|
|
1391
|
+
});
|
|
1392
|
+
return `${repo.url}/issues/new?${params}`;
|
|
1393
|
+
}
|
|
1394
|
+
var couldNot = (reason) => ({ note: `Could not list issues: ${reason}` });
|
|
1395
|
+
var listingOf = (answer) => answer.issues ? { issues: answer.issues } : answer.unavailable ? { note: answer.unavailable } : couldNot(answer.error ?? "no reason given");
|
|
1396
|
+
var NO_KNOBS = { nodes: {} };
|
|
1397
|
+
function TrackedIssues({ base, storyId, component }) {
|
|
1398
|
+
const lab = useLabContext();
|
|
1399
|
+
const labGlobals2 = useContext(StoryGlobalsContext);
|
|
1400
|
+
const [answer, setAnswer] = useState(null);
|
|
1401
|
+
const [listing, setListing] = useState({ note: "Listing issues\u2026" });
|
|
1402
|
+
useEffect(() => {
|
|
1403
|
+
const abort = new AbortController();
|
|
1404
|
+
fetchIssues(base, component, abort.signal).then(
|
|
1405
|
+
(next) => {
|
|
1406
|
+
setAnswer(next);
|
|
1407
|
+
setListing(listingOf(next));
|
|
1408
|
+
},
|
|
1409
|
+
(err) => {
|
|
1410
|
+
if (!abort.signal.aborted) setListing(couldNot(err instanceof Error ? err.message : String(err)));
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1413
|
+
return () => abort.abort();
|
|
1414
|
+
}, [base, component]);
|
|
1415
|
+
const report = (made) => {
|
|
1416
|
+
if (!answer?.repo) return;
|
|
1417
|
+
const trial = lab.trials.find((t) => t.id === lab.focusedTrialId);
|
|
1418
|
+
const config = trial?.config ?? null;
|
|
1419
|
+
const schema = lab.instruments.find((instrument) => instrument.name === storyId)?.config;
|
|
1420
|
+
const globals = effectiveGlobals(labGlobals2, config?.[GLOBALS_KEY]);
|
|
1421
|
+
const page = `${location.origin}${location.pathname}${location.search}`;
|
|
1422
|
+
const link = reopenLink(page, storyId, schema ?? NO_KNOBS, config, globals);
|
|
1423
|
+
window.open(newIssueUrl(answer.repo, made, { link, branch: answer.branch, commit: answer.commit }), "_blank", "noopener");
|
|
1424
|
+
};
|
|
1425
|
+
return /* @__PURE__ */ jsx(
|
|
1426
|
+
IssuesPanel,
|
|
1427
|
+
{
|
|
1428
|
+
component,
|
|
1429
|
+
storyId,
|
|
1430
|
+
..."issues" in listing ? { issues: listing.issues } : { listNote: listing.note },
|
|
1431
|
+
...answer?.repo ? { onReport: report } : {}
|
|
1432
|
+
}
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
function FocusedIssues({ index, base }) {
|
|
1436
|
+
const { focusedTrialId, trials } = useLabContext();
|
|
1437
|
+
const record = trials.find((trial) => trial.id === focusedTrialId);
|
|
1438
|
+
const entry = record && index.find((e) => e.id === record.instrumentName);
|
|
1439
|
+
if (!entry) return null;
|
|
1440
|
+
const component = entry.componentName ?? entry.title.split("/").at(-1) ?? entry.title;
|
|
1441
|
+
return /* @__PURE__ */ jsxs("section", { className: "fg-issues-focus", "aria-label": "Issues", children: [
|
|
1442
|
+
/* @__PURE__ */ jsx("p", { className: "fg-issues__component", children: component }),
|
|
1443
|
+
base === void 0 ? /* @__PURE__ */ jsx(IssuesPanel, { component, storyId: entry.id }, entry.id) : /* @__PURE__ */ jsx(TrackedIssues, { base, storyId: entry.id, component }, entry.id)
|
|
1444
|
+
] });
|
|
1445
|
+
}
|
|
1446
|
+
function issuesSection(index, base) {
|
|
1447
|
+
return { id: "fg-issues", region: "aside", item: { title: "Issues", body: /* @__PURE__ */ jsx(FocusedIssues, { index, base }) } };
|
|
1448
|
+
}
|
|
1271
1449
|
var GLOBALS = "$globals";
|
|
1272
1450
|
function leaves(shape, prefix = "") {
|
|
1273
1451
|
const out = [];
|
|
@@ -2048,14 +2226,13 @@ function withoutUndefined(value) {
|
|
|
2048
2226
|
}
|
|
2049
2227
|
var clampDpr = (dpr) => Math.min(Math.max(dpr, MIN_DPR), MAX_DPR);
|
|
2050
2228
|
function capturePayload(input) {
|
|
2051
|
-
const { entry, layout, config, state, globals, overrides, host, dpr, resolved, lab, colorScheme } = input;
|
|
2229
|
+
const { entry, layout, config, state, globals, overrides, host, dpr, resolved, lab, colorScheme, playhead } = input;
|
|
2052
2230
|
if (!sendable(state)) return { unavailable: STATE_UNSENDABLE };
|
|
2053
2231
|
const sent = withoutUndefined(withArgs(storyConfig(config), resolved.args));
|
|
2054
2232
|
if (!sendable(sent)) return { unavailable: CONFIG_UNSENDABLE };
|
|
2055
2233
|
const box = { width: host.offsetWidth, height: host.offsetHeight };
|
|
2056
2234
|
if (!(box.width > 0 && box.height > 0)) return { unavailable: NO_BOX };
|
|
2057
2235
|
const isolate = entry.isolate !== void 0;
|
|
2058
|
-
const kept2 = config?.[PLAYHEAD_KEY];
|
|
2059
2236
|
return {
|
|
2060
2237
|
story: entry.id,
|
|
2061
2238
|
isolate,
|
|
@@ -2063,7 +2240,7 @@ function capturePayload(input) {
|
|
|
2063
2240
|
state,
|
|
2064
2241
|
globals,
|
|
2065
2242
|
overrides,
|
|
2066
|
-
playhead:
|
|
2243
|
+
playhead: isolate ? null : playhead,
|
|
2067
2244
|
box,
|
|
2068
2245
|
layout,
|
|
2069
2246
|
dpr: clampDpr(resolved.dpr ?? dpr),
|
|
@@ -2078,8 +2255,10 @@ function trialPayload(trial, lab, hit, dpr, c) {
|
|
|
2078
2255
|
const own = effectiveGlobals(lab.labValues, trial.config?.[GLOBALS_KEY]);
|
|
2079
2256
|
const resolved = resolveComparison(c, own, ctx);
|
|
2080
2257
|
const colorScheme = ctx.prefersDark ? "dark" : "light";
|
|
2258
|
+
const { playhead, ...inputs } = trial;
|
|
2081
2259
|
return capturePayload({
|
|
2082
|
-
...
|
|
2260
|
+
...inputs,
|
|
2261
|
+
playhead: playhead(),
|
|
2083
2262
|
globals: resolved.globals,
|
|
2084
2263
|
host: hit.host,
|
|
2085
2264
|
dpr,
|
|
@@ -2536,20 +2715,41 @@ function ForgeLoupe({ declarations, modeGlobal }) {
|
|
|
2536
2715
|
function routedTrial(trials, focusedTrialId, route) {
|
|
2537
2716
|
return trials.find((t) => t.id === focusedTrialId && t.instrumentName === route) ?? trials.find((t) => t.instrumentName === route);
|
|
2538
2717
|
}
|
|
2718
|
+
|
|
2719
|
+
// src/timeline/playheadUrl.ts
|
|
2720
|
+
function formatSeconds(ms) {
|
|
2721
|
+
const rounded = Math.round(ms) / 1e3;
|
|
2722
|
+
return String(rounded === 0 ? 0 : rounded);
|
|
2723
|
+
}
|
|
2724
|
+
function parseSeconds(text) {
|
|
2725
|
+
if (text == null || !/^\s*-?(\d+\.?\d*|\.\d+)\s*$/.test(text)) return null;
|
|
2726
|
+
return Math.round(Number(text) * 1e3);
|
|
2727
|
+
}
|
|
2728
|
+
function readPlayheadParam() {
|
|
2729
|
+
return parseSeconds(readRouteParams()[PLAYHEAD_PARAM]);
|
|
2730
|
+
}
|
|
2731
|
+
function writePlayheadParam(ms) {
|
|
2732
|
+
replaceRouteParams(
|
|
2733
|
+
({ [PLAYHEAD_PARAM]: _held, ...rest }) => ms === null ? rest : { ...rest, [PLAYHEAD_PARAM]: formatSeconds(ms) }
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2539
2736
|
function PlayheadSync({ trialId, route, appliedRef }) {
|
|
2540
|
-
const
|
|
2737
|
+
const handle = useTrialClockHandle(trialId);
|
|
2541
2738
|
useEffect(() => {
|
|
2542
|
-
if (!
|
|
2739
|
+
if (!handle) return;
|
|
2740
|
+
const { clock } = handle;
|
|
2741
|
+
let hold = null;
|
|
2543
2742
|
const fromUrl = () => {
|
|
2544
2743
|
const t = readPlayheadParam();
|
|
2545
|
-
if (t === null || t === clock
|
|
2546
|
-
|
|
2744
|
+
if (t === null || t === shownTime(clock)) return;
|
|
2745
|
+
hold?.release();
|
|
2746
|
+
hold = holdAt(handle, t);
|
|
2547
2747
|
};
|
|
2548
2748
|
let held;
|
|
2549
2749
|
const toUrl = () => {
|
|
2550
|
-
if (
|
|
2551
|
-
const
|
|
2552
|
-
const next =
|
|
2750
|
+
if (hold && hold.pending() !== null) return;
|
|
2751
|
+
const time = shownTime(clock);
|
|
2752
|
+
const next = clock.rate !== 0 || time === clock.origin ? null : time;
|
|
2553
2753
|
if (next === held) return;
|
|
2554
2754
|
held = next;
|
|
2555
2755
|
writePlayheadParam(next);
|
|
@@ -2569,9 +2769,10 @@ function PlayheadSync({ trialId, route, appliedRef }) {
|
|
|
2569
2769
|
window.addEventListener("hashchange", onHashChange);
|
|
2570
2770
|
return () => {
|
|
2571
2771
|
off();
|
|
2772
|
+
hold?.release();
|
|
2572
2773
|
window.removeEventListener("hashchange", onHashChange);
|
|
2573
2774
|
};
|
|
2574
|
-
}, [
|
|
2775
|
+
}, [handle, route, appliedRef]);
|
|
2575
2776
|
return null;
|
|
2576
2777
|
}
|
|
2577
2778
|
function RoutePlayhead() {
|
|
@@ -2726,7 +2927,8 @@ function KnobSync({ schema, config }) {
|
|
|
2726
2927
|
if (reading.current && takeRouteFromTrial()) reading.current = false;
|
|
2727
2928
|
if (reading.current) {
|
|
2728
2929
|
reading.current = false;
|
|
2729
|
-
const
|
|
2930
|
+
const params = readRouteParams();
|
|
2931
|
+
const knobs2 = paramsToKnobs(schema, params);
|
|
2730
2932
|
let wrote = false;
|
|
2731
2933
|
for (const { path, leaf } of knobPaths(schema)) {
|
|
2732
2934
|
const target = path in knobs2 ? knobs2[path] : leaf.default;
|
|
@@ -2735,10 +2937,15 @@ function KnobSync({ schema, config }) {
|
|
|
2735
2937
|
setConfig(path, target);
|
|
2736
2938
|
wrote = true;
|
|
2737
2939
|
}
|
|
2940
|
+
for (const [path, pin] of Object.entries(paramsToPins(schema, params))) {
|
|
2941
|
+
if (valueAtPath(config, path) === pin) continue;
|
|
2942
|
+
setConfig(path, pin);
|
|
2943
|
+
wrote = true;
|
|
2944
|
+
}
|
|
2738
2945
|
if (wrote) return;
|
|
2739
2946
|
}
|
|
2740
2947
|
const knobs = knobsToParams(schema, config);
|
|
2741
|
-
replaceRouteParams((params) => ({ ...knobs, ...reservedParams(params) }));
|
|
2948
|
+
replaceRouteParams((params) => ({ ...knobs, ...withoutPins(reservedParams(params)) }));
|
|
2742
2949
|
}, [schema, config, setConfig, typed]);
|
|
2743
2950
|
return null;
|
|
2744
2951
|
}
|
|
@@ -2956,6 +3163,8 @@ var VIEWS = [
|
|
|
2956
3163
|
{ value: "gallery", label: "Gallery" }
|
|
2957
3164
|
];
|
|
2958
3165
|
var keyOf = (node) => node.kind === "folder" ? `folder:${node.path}` : `story:${node.entry.id}`;
|
|
3166
|
+
var pathOf = (folderKey) => folderKey.slice("folder:".length);
|
|
3167
|
+
var hrefOf = (entry) => `#/${encodeURIComponent(entry.id)}`;
|
|
2959
3168
|
function ancestorsOf(entry) {
|
|
2960
3169
|
const paths = /* @__PURE__ */ new Set();
|
|
2961
3170
|
if (!entry) return paths;
|
|
@@ -2969,7 +3178,8 @@ function ancestorsOf(entry) {
|
|
|
2969
3178
|
function LibraryBadge2({ library }) {
|
|
2970
3179
|
return /* @__PURE__ */ jsx(LibraryBadge, { library, className: "fg-tree__tag" });
|
|
2971
3180
|
}
|
|
2972
|
-
function
|
|
3181
|
+
function rowTags(lab, library) {
|
|
3182
|
+
if (!lab && !library) return void 0;
|
|
2973
3183
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2974
3184
|
lab ? /* @__PURE__ */ jsx(LabBadge, { className: "fg-tree__lab" }) : null,
|
|
2975
3185
|
library ? /* @__PURE__ */ jsx(LibraryBadge2, { library }) : null
|
|
@@ -2982,8 +3192,6 @@ function StoryTree({ ctx, index }) {
|
|
|
2982
3192
|
const [folds, setFolds] = usePersistedState("fg-tree-open", {}, { scope: "lab" });
|
|
2983
3193
|
const [chosen, setView] = usePersistedState("fg-tree-view", "components", { scope: "lab" });
|
|
2984
3194
|
const [hidden, setHidden] = usePersistedState("fg-tree-libraries", {}, { scope: "lab" });
|
|
2985
|
-
const [focusKey, setFocusKey] = useState(null);
|
|
2986
|
-
const items = useRef(/* @__PURE__ */ new Map());
|
|
2987
3195
|
const table = useContext(LibrariesContext);
|
|
2988
3196
|
const libraries = useMemo(() => librariesIn(index, table), [index, table]);
|
|
2989
3197
|
const views = useMemo(() => {
|
|
@@ -3011,24 +3219,45 @@ function StoryTree({ ctx, index }) {
|
|
|
3011
3219
|
const routed = routable.find((entry) => entry.id === route);
|
|
3012
3220
|
const routeAncestors = useMemo(() => ancestorsOf(routed), [routed]);
|
|
3013
3221
|
const filtering = query.trim() !== "";
|
|
3014
|
-
const
|
|
3015
|
-
const
|
|
3016
|
-
const
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3222
|
+
const openKey = ctx.trials.map((trial) => trial.instrumentName).join("\n");
|
|
3223
|
+
const openIds = useMemo(() => new Set(openKey.split("\n")), [openKey]);
|
|
3224
|
+
const { nodes, byId, folders: folderIds } = useMemo(() => {
|
|
3225
|
+
const ids = /* @__PURE__ */ new Map();
|
|
3226
|
+
const folders = [];
|
|
3227
|
+
const convert = (list) => list.map((node) => {
|
|
3228
|
+
const id = keyOf(node);
|
|
3229
|
+
ids.set(id, node);
|
|
3230
|
+
if (node.kind === "folder") {
|
|
3231
|
+
folders.push(id);
|
|
3232
|
+
return {
|
|
3233
|
+
id,
|
|
3234
|
+
label: node.label,
|
|
3235
|
+
className: "fg-tree__folder",
|
|
3236
|
+
href: node.index ? hrefOf(node.index) : void 0,
|
|
3237
|
+
current: !!node.index && openIds.has(node.index.id),
|
|
3238
|
+
trailing: rowTags(!!node.index && isLab(node.index), node.tag),
|
|
3239
|
+
children: convert(node.children)
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
return {
|
|
3243
|
+
id,
|
|
3244
|
+
label: node.label ?? node.entry.name,
|
|
3245
|
+
className: "fg-tree__story",
|
|
3246
|
+
href: hrefOf(node.entry),
|
|
3247
|
+
current: openIds.has(node.entry.id),
|
|
3248
|
+
trailing: rowTags(isLab(node.entry), node.tag)
|
|
3249
|
+
};
|
|
3250
|
+
});
|
|
3251
|
+
return { nodes: convert(shown), byId: ids, folders };
|
|
3252
|
+
}, [shown, openIds]);
|
|
3253
|
+
const expanded = useMemo(
|
|
3254
|
+
() => new Set(folderIds.filter((id) => filtering || (folds[pathOf(id)] ?? routeAncestors.has(pathOf(id))))),
|
|
3255
|
+
[folderIds, filtering, folds, routeAncestors]
|
|
3256
|
+
);
|
|
3257
|
+
const onExpandedChange = (next) => {
|
|
3258
|
+
if (filtering) return;
|
|
3259
|
+
const changed = folderIds.filter((id) => next.has(id) !== expanded.has(id));
|
|
3260
|
+
if (changed.length) setFolds((prev) => ({ ...prev, ...Object.fromEntries(changed.map((id) => [pathOf(id), next.has(id)])) }));
|
|
3032
3261
|
};
|
|
3033
3262
|
const activate = (entry, another) => {
|
|
3034
3263
|
const focused = ctx.trials.find((trial) => trial.id === ctx.focusedTrialId);
|
|
@@ -3044,20 +3273,29 @@ function StoryTree({ ctx, index }) {
|
|
|
3044
3273
|
}
|
|
3045
3274
|
setRoute2(entry.id);
|
|
3046
3275
|
};
|
|
3047
|
-
const
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3276
|
+
const openIndex = (path, page, another) => {
|
|
3277
|
+
const was = folds[path];
|
|
3278
|
+
setFolds(({ [path]: _toggled, ...rest2 }) => was ? { ...rest2, [path]: was } : rest2);
|
|
3279
|
+
activate(page, another);
|
|
3280
|
+
};
|
|
3281
|
+
const onAction = (id, mods) => {
|
|
3282
|
+
const node = byId.get(id);
|
|
3283
|
+
if (node?.kind === "story") activate(node.entry, mods.shiftKey);
|
|
3284
|
+
else if (node?.index) openIndex(node.path, node.index, mods.shiftKey);
|
|
3056
3285
|
};
|
|
3286
|
+
const treeEl = useRef(null);
|
|
3057
3287
|
const scrollTo = useRef(null);
|
|
3288
|
+
useEffect(() => {
|
|
3289
|
+
const id = scrollTo.current;
|
|
3290
|
+
if (id == null) return;
|
|
3291
|
+
const row = [...treeEl.current?.querySelectorAll("[data-tree-id]") ?? []].find((el) => el.dataset.treeId === id);
|
|
3292
|
+
if (!row) return;
|
|
3293
|
+
scrollTo.current = null;
|
|
3294
|
+
row.firstElementChild?.scrollIntoView({ block: "nearest" });
|
|
3295
|
+
});
|
|
3058
3296
|
const reveal = (path) => {
|
|
3059
|
-
const find = (
|
|
3060
|
-
for (const node2 of
|
|
3297
|
+
const find = (list, at) => {
|
|
3298
|
+
for (const node2 of list) {
|
|
3061
3299
|
if (node2.kind !== "folder") continue;
|
|
3062
3300
|
if (node2.path === at) return node2;
|
|
3063
3301
|
const inner = find(node2.children, at);
|
|
@@ -3085,135 +3323,11 @@ function StoryTree({ ctx, index }) {
|
|
|
3085
3323
|
const segments = found.path.split("/");
|
|
3086
3324
|
const above = segments.slice(0, -1).map((_, i) => segments.slice(0, i + 1).join("/"));
|
|
3087
3325
|
setFolds((prev) => ({ ...prev, ...Object.fromEntries(above.map((p) => [p, true])) }));
|
|
3088
|
-
setFocusKey(keyOf(found));
|
|
3089
3326
|
scrollTo.current = keyOf(found);
|
|
3090
|
-
if (found.index)
|
|
3327
|
+
if (found.index) openIndex(found.path, found.index, false);
|
|
3091
3328
|
else setFolds((prev) => ({ ...prev, [found.path]: true }));
|
|
3092
3329
|
};
|
|
3093
3330
|
useEffect(() => onRevealInTree(reveal));
|
|
3094
|
-
const onKeyDown = (event) => {
|
|
3095
|
-
const at = rows.findIndex((row2) => row2.key === active);
|
|
3096
|
-
const row = rows[at];
|
|
3097
|
-
if (!row) return;
|
|
3098
|
-
const { node } = row;
|
|
3099
|
-
switch (event.key) {
|
|
3100
|
-
case "ArrowDown":
|
|
3101
|
-
moveTo(rows[at + 1]?.key);
|
|
3102
|
-
break;
|
|
3103
|
-
case "ArrowUp":
|
|
3104
|
-
moveTo(rows[at - 1]?.key);
|
|
3105
|
-
break;
|
|
3106
|
-
case "Home":
|
|
3107
|
-
moveTo(rows[0]?.key);
|
|
3108
|
-
break;
|
|
3109
|
-
case "End":
|
|
3110
|
-
moveTo(rows[rows.length - 1]?.key);
|
|
3111
|
-
break;
|
|
3112
|
-
case "ArrowRight":
|
|
3113
|
-
if (node.kind !== "folder") break;
|
|
3114
|
-
if (!isOpen(node.path)) setOpen(node.path, true);
|
|
3115
|
-
else if (rows[at + 1]?.parent === row.key) moveTo(rows[at + 1]?.key);
|
|
3116
|
-
break;
|
|
3117
|
-
case "ArrowLeft":
|
|
3118
|
-
if (node.kind === "folder" && isOpen(node.path) && !filtering) setOpen(node.path, false);
|
|
3119
|
-
else moveTo(row.parent ?? void 0);
|
|
3120
|
-
break;
|
|
3121
|
-
case "Enter":
|
|
3122
|
-
case " ":
|
|
3123
|
-
if (node.kind === "story") activate(node.entry, event.shiftKey);
|
|
3124
|
-
else openFolder(node, event.shiftKey);
|
|
3125
|
-
break;
|
|
3126
|
-
default:
|
|
3127
|
-
return;
|
|
3128
|
-
}
|
|
3129
|
-
event.preventDefault();
|
|
3130
|
-
};
|
|
3131
|
-
const itemProps = (node, level) => {
|
|
3132
|
-
const key = keyOf(node);
|
|
3133
|
-
return {
|
|
3134
|
-
role: "treeitem",
|
|
3135
|
-
"aria-level": level,
|
|
3136
|
-
tabIndex: key === active ? 0 : -1,
|
|
3137
|
-
ref: (el) => {
|
|
3138
|
-
if (el) items.current.set(key, el);
|
|
3139
|
-
else items.current.delete(key);
|
|
3140
|
-
if (el && scrollTo.current === key) {
|
|
3141
|
-
scrollTo.current = null;
|
|
3142
|
-
el.scrollIntoView({ block: "nearest" });
|
|
3143
|
-
}
|
|
3144
|
-
},
|
|
3145
|
-
// A folder item contains its children, so their focus bubbles through it.
|
|
3146
|
-
onFocus: (event) => {
|
|
3147
|
-
if (event.target === event.currentTarget) setFocusKey(key);
|
|
3148
|
-
}
|
|
3149
|
-
};
|
|
3150
|
-
};
|
|
3151
|
-
const renderNodes = (nodes, level) => nodes.map((node) => {
|
|
3152
|
-
if (node.kind === "folder") {
|
|
3153
|
-
const open = isOpen(node.path);
|
|
3154
|
-
return /* @__PURE__ */ jsxs(
|
|
3155
|
-
"div",
|
|
3156
|
-
{
|
|
3157
|
-
...itemProps(node, level),
|
|
3158
|
-
"aria-expanded": open,
|
|
3159
|
-
"aria-label": node.label,
|
|
3160
|
-
"aria-current": node.index && openIds.has(node.index.id) ? "true" : void 0,
|
|
3161
|
-
className: "fg-tree__node",
|
|
3162
|
-
children: [
|
|
3163
|
-
/* @__PURE__ */ jsxs(
|
|
3164
|
-
"div",
|
|
3165
|
-
{
|
|
3166
|
-
className: "fg-tree__item fg-tree__folder",
|
|
3167
|
-
onClick: (event) => {
|
|
3168
|
-
setFocusKey(keyOf(node));
|
|
3169
|
-
openFolder(node, event.shiftKey);
|
|
3170
|
-
},
|
|
3171
|
-
children: [
|
|
3172
|
-
/* @__PURE__ */ jsx(
|
|
3173
|
-
"span",
|
|
3174
|
-
{
|
|
3175
|
-
"aria-hidden": "true",
|
|
3176
|
-
className: "fg-tree__fold",
|
|
3177
|
-
onClick: (event) => {
|
|
3178
|
-
event.stopPropagation();
|
|
3179
|
-
setOpen(node.path, !open);
|
|
3180
|
-
},
|
|
3181
|
-
children: /* @__PURE__ */ jsx(DisclosureMark, { open })
|
|
3182
|
-
}
|
|
3183
|
-
),
|
|
3184
|
-
/* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label }),
|
|
3185
|
-
/* @__PURE__ */ jsx(RowTags, { lab: !!node.index && isLab(node.index), library: node.tag })
|
|
3186
|
-
]
|
|
3187
|
-
}
|
|
3188
|
-
),
|
|
3189
|
-
open ? /* @__PURE__ */ jsx("div", { role: "group", className: "fg-tree__group", children: renderNodes(node.children, level + 1) }) : null
|
|
3190
|
-
]
|
|
3191
|
-
},
|
|
3192
|
-
keyOf(node)
|
|
3193
|
-
);
|
|
3194
|
-
}
|
|
3195
|
-
const { entry } = node;
|
|
3196
|
-
return /* @__PURE__ */ jsxs(
|
|
3197
|
-
"a",
|
|
3198
|
-
{
|
|
3199
|
-
...itemProps(node, level),
|
|
3200
|
-
href: `#/${encodeURIComponent(entry.id)}`,
|
|
3201
|
-
"aria-current": openIds.has(entry.id) ? "true" : void 0,
|
|
3202
|
-
className: "fg-tree__item fg-tree__story",
|
|
3203
|
-
onClick: (event) => {
|
|
3204
|
-
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
3205
|
-
event.preventDefault();
|
|
3206
|
-
setFocusKey(keyOf(node));
|
|
3207
|
-
activate(entry, event.shiftKey);
|
|
3208
|
-
},
|
|
3209
|
-
children: [
|
|
3210
|
-
/* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label ?? entry.name }),
|
|
3211
|
-
/* @__PURE__ */ jsx(RowTags, { lab: isLab(entry), library: node.tag })
|
|
3212
|
-
]
|
|
3213
|
-
},
|
|
3214
|
-
keyOf(node)
|
|
3215
|
-
);
|
|
3216
|
-
});
|
|
3217
3331
|
return /* @__PURE__ */ jsxs("section", { className: "fg-tree", children: [
|
|
3218
3332
|
/* @__PURE__ */ jsx("h2", { id: headingId, className: "fg-tree__heading", children: "Stories" }),
|
|
3219
3333
|
route && !routed ? /* @__PURE__ */ jsxs("p", { className: "fg-route-miss", children: [
|
|
@@ -3255,7 +3369,19 @@ function StoryTree({ ctx, index }) {
|
|
|
3255
3369
|
onChange: (event) => setQuery(event.target.value)
|
|
3256
3370
|
}
|
|
3257
3371
|
),
|
|
3258
|
-
/* @__PURE__ */ jsx(
|
|
3372
|
+
/* @__PURE__ */ jsx(
|
|
3373
|
+
Tree,
|
|
3374
|
+
{
|
|
3375
|
+
ref: treeEl,
|
|
3376
|
+
"aria-labelledby": headingId,
|
|
3377
|
+
className: "fg-tree__items",
|
|
3378
|
+
nodes,
|
|
3379
|
+
empty: filtering ? "No stories match" : "No stories",
|
|
3380
|
+
expandedIds: expanded,
|
|
3381
|
+
onExpandedChange,
|
|
3382
|
+
onAction
|
|
3383
|
+
}
|
|
3384
|
+
)
|
|
3259
3385
|
] });
|
|
3260
3386
|
}
|
|
3261
3387
|
var KEPT_CONFIGS = 50;
|
|
@@ -3618,6 +3744,10 @@ function FrameView(props) {
|
|
|
3618
3744
|
() => copyId && setup && hostRef.current ? applyGlobalsTo(hostRef.current, copyId, globals, setup) : void 0,
|
|
3619
3745
|
[copyId, globals, setup]
|
|
3620
3746
|
);
|
|
3747
|
+
const factor = copy?.factor;
|
|
3748
|
+
useEffect(() => {
|
|
3749
|
+
if (factor !== void 0) iframeRef.current?.contentWindow?.dispatchEvent(new Event("resize"));
|
|
3750
|
+
}, [factor]);
|
|
3621
3751
|
const main = copy?.main;
|
|
3622
3752
|
useEffect(() => {
|
|
3623
3753
|
if (!main || pending2) return void 0;
|
|
@@ -3662,7 +3792,7 @@ function storyInstrument(options) {
|
|
|
3662
3792
|
...setup ? { setup } : {}
|
|
3663
3793
|
}
|
|
3664
3794
|
),
|
|
3665
|
-
/* @__PURE__ */ jsx(CaptureInputs, { entry, layout: ready?.layout ??
|
|
3795
|
+
/* @__PURE__ */ jsx(CaptureInputs, { entry, layout: ready?.layout ?? DEFAULT_LAYOUT, config: ctx.config, state: ctx.state })
|
|
3666
3796
|
] }),
|
|
3667
3797
|
// Taken from the index until the frame reports, so the trial does not move the frame into a stage and reload it.
|
|
3668
3798
|
...viewport ? { stage: { size: { width: viewport.width, height: viewport.height } } } : {}
|
|
@@ -3675,7 +3805,7 @@ var NO_SETUP = {};
|
|
|
3675
3805
|
var pruned = (map, ids) => [...map.keys()].every((id) => ids.has(id)) ? map : new Map([...map].filter(([id]) => ids.has(id)));
|
|
3676
3806
|
var toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
3677
3807
|
function useStoryRegistry(index, options) {
|
|
3678
|
-
const { frameUrl, globals = NO_GLOBALS2, importers, setup = NO_SETUP, libraries = TITLE_LIBRARIES, load = loadStories } = options;
|
|
3808
|
+
const { frameUrl, globals = NO_GLOBALS2, importers, setup = NO_SETUP, libraries = TITLE_LIBRARIES, load = loadStories, hosts } = options;
|
|
3679
3809
|
const [readies, setReadies] = useState(() => /* @__PURE__ */ new Map());
|
|
3680
3810
|
const [loaded, setLoaded] = useState(() => /* @__PURE__ */ new Map());
|
|
3681
3811
|
const [faults, setFaults] = useState(() => /* @__PURE__ */ new Map());
|
|
@@ -3798,14 +3928,14 @@ function useStoryRegistry(index, options) {
|
|
|
3798
3928
|
inDocument,
|
|
3799
3929
|
generation,
|
|
3800
3930
|
revision,
|
|
3801
|
-
instrument: !inDocument ? storyInstrument({ entry, ready, answers, frameUrl, onReady, globals, setup }) : had ? documentInstrument({ entry, loaded: had, setup, globals }) : pendingInstrument({ entry, globals, load: request, generation, ...fault ? { fault } : {} })
|
|
3931
|
+
instrument: !inDocument ? storyInstrument({ entry, ready, answers, frameUrl, onReady, globals, setup }) : had ? documentInstrument({ entry, loaded: had, setup, globals, ...hosts ? { hosts } : {} }) : pendingInstrument({ entry, globals, load: request, generation, ...fault ? { fault } : {} })
|
|
3802
3932
|
};
|
|
3803
3933
|
built.set(entry.id, kept2);
|
|
3804
3934
|
return kept2.instrument;
|
|
3805
3935
|
});
|
|
3806
3936
|
const unchanged = list.length === previous.list.length && list.every((i, n) => i === previous.list[n]);
|
|
3807
3937
|
return { built, books, list: unchanged ? previous.list : list };
|
|
3808
|
-
}, [index, readies, loaded, faults, generations, revisions2, frameUrl, globals, setup, importers, onReady, request]);
|
|
3938
|
+
}, [index, readies, loaded, faults, generations, revisions2, frameUrl, globals, setup, hosts, importers, onReady, request]);
|
|
3809
3939
|
useLayoutEffect(() => {
|
|
3810
3940
|
committed.current = cache;
|
|
3811
3941
|
}, [cache]);
|
|
@@ -3844,6 +3974,19 @@ function useDocumentTitle(title) {
|
|
|
3844
3974
|
document.title = title;
|
|
3845
3975
|
}, [title]);
|
|
3846
3976
|
}
|
|
3977
|
+
function useFramePool(frameUrl, framed) {
|
|
3978
|
+
const [pool, setPool] = useState(null);
|
|
3979
|
+
useEffect(() => {
|
|
3980
|
+
if (!framed) return;
|
|
3981
|
+
const next = createFramePool(frameUrl);
|
|
3982
|
+
setPool(next);
|
|
3983
|
+
return () => {
|
|
3984
|
+
next.dispose();
|
|
3985
|
+
setPool(null);
|
|
3986
|
+
};
|
|
3987
|
+
}, [frameUrl, framed]);
|
|
3988
|
+
return pool;
|
|
3989
|
+
}
|
|
3847
3990
|
function NoStories({ stories }) {
|
|
3848
3991
|
return /* @__PURE__ */ jsxs("div", { className: "fg-empty", children: [
|
|
3849
3992
|
/* @__PURE__ */ jsx("p", { className: "fg-empty__title", children: "No stories found" }),
|
|
@@ -3856,18 +3999,8 @@ function Workshop(props) {
|
|
|
3856
3999
|
const title = config?.title ?? DEFAULT_TITLE;
|
|
3857
4000
|
useDocumentTitle(title);
|
|
3858
4001
|
const [frames2] = useState(createTrialFrames);
|
|
3859
|
-
const [
|
|
3860
|
-
const
|
|
3861
|
-
const framed = importers === void 0 || index.some((entry) => entry.isolate !== void 0);
|
|
3862
|
-
useEffect(() => {
|
|
3863
|
-
if (!framed) return;
|
|
3864
|
-
const next = createFramePool(frameUrl);
|
|
3865
|
-
setPool(next);
|
|
3866
|
-
return () => {
|
|
3867
|
-
next.dispose();
|
|
3868
|
-
setPool(null);
|
|
3869
|
-
};
|
|
3870
|
-
}, [frameUrl, framed]);
|
|
4002
|
+
const [hosts] = useState(createStoryHosts);
|
|
4003
|
+
const pool = useFramePool(frameUrl, importers === void 0 || index.some((entry) => entry.isolate !== void 0));
|
|
3871
4004
|
const entries = useMemo(() => [...index, ...indexEntries(index)], [index]);
|
|
3872
4005
|
const table = config?.libraries;
|
|
3873
4006
|
const libraries = useMemo(() => librariesFrom(table), [table]);
|
|
@@ -3875,6 +4008,7 @@ function Workshop(props) {
|
|
|
3875
4008
|
frameUrl,
|
|
3876
4009
|
globals: declarations,
|
|
3877
4010
|
libraries,
|
|
4011
|
+
hosts,
|
|
3878
4012
|
...importers ? { importers } : {},
|
|
3879
4013
|
...setup ? { setup } : {}
|
|
3880
4014
|
});
|
|
@@ -3904,18 +4038,19 @@ function Workshop(props) {
|
|
|
3904
4038
|
{ id: "fg-stories", region: "sidebar", render: (ctx) => /* @__PURE__ */ jsx(StoryTree, { ctx, index }) },
|
|
3905
4039
|
...showsCssVars ? cssVarsChrome2 : [],
|
|
3906
4040
|
A11Y_SECTION,
|
|
4041
|
+
issuesSection(index, props.issuesBase),
|
|
3907
4042
|
...keysChrome2,
|
|
3908
4043
|
...Object.keys(declarations).length > 0 ? [{ id: "fg-globals", region: "header", render: () => /* @__PURE__ */ jsx(GlobalsToolbar, { declarations }) }] : [],
|
|
3909
4044
|
{ id: "fg-info", region: "header", group: "story", item: infoCommand },
|
|
3910
4045
|
{ id: "fg-loupe", region: "overlay", render: () => /* @__PURE__ */ jsx(ForgeLoupe, { declarations, modeGlobal }) },
|
|
3911
4046
|
...config?.labChrome ?? []
|
|
3912
4047
|
],
|
|
3913
|
-
[index, infoCommand, declarations, showsCssVars, cssVarsChrome2, keysChrome2, config?.labChrome, modeGlobal]
|
|
4048
|
+
[index, props.issuesBase, infoCommand, declarations, showsCssVars, cssVarsChrome2, keysChrome2, config?.labChrome, modeGlobal]
|
|
3914
4049
|
);
|
|
3915
4050
|
const [menus, setMenus] = useState();
|
|
3916
4051
|
const first = index[0];
|
|
3917
4052
|
if (!first) return /* @__PURE__ */ jsx(NoStories, { stories });
|
|
3918
|
-
return /* @__PURE__ */ jsx(LibrariesContext.Provider, { value: libraries, children: /* @__PURE__ */ jsx(StoryGlobalsContext.Provider, { value: labValues, children: /* @__PURE__ */ jsx(DependenciesContext.Provider, { value: dependencies ?? null, children: /* @__PURE__ */ jsx(FramePoolContext.Provider, { value: pool, children: /* @__PURE__ */ jsx(TrialFramesContext.Provider, { value: frames2, children: /* @__PURE__ */ jsx(
|
|
4053
|
+
return /* @__PURE__ */ jsx(LibrariesContext.Provider, { value: libraries, children: /* @__PURE__ */ jsx(StoryGlobalsContext.Provider, { value: labValues, children: /* @__PURE__ */ jsx(DependenciesContext.Provider, { value: dependencies ?? null, children: /* @__PURE__ */ jsx(FramePoolContext.Provider, { value: pool, children: /* @__PURE__ */ jsx(TrialFramesContext.Provider, { value: frames2, children: /* @__PURE__ */ jsx(StoryHostsContext.Provider, { value: hosts, children: /* @__PURE__ */ jsx(LoupeProvider, { captureBase: props.captureBase, revision: props.revision, children: /* @__PURE__ */ jsxs(
|
|
3919
4054
|
Lab,
|
|
3920
4055
|
{
|
|
3921
4056
|
title,
|
|
@@ -3927,6 +4062,7 @@ function Workshop(props) {
|
|
|
3927
4062
|
...storage ? { storage } : {},
|
|
3928
4063
|
labChrome,
|
|
3929
4064
|
addTrial: false,
|
|
4065
|
+
marks: false,
|
|
3930
4066
|
...menus ? { menus } : {},
|
|
3931
4067
|
...config?.controls ? { controls: config.controls } : {},
|
|
3932
4068
|
...config?.pages ? { pages: config.pages } : {},
|