@weasel-js/forge 1.13.0-rc.0 → 1.14.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.
- package/README.md +1 -1
- package/dist/{FrameController-Tv3jrHyT.d.ts → FrameController-BM6xuEfY.d.ts} +18 -10
- package/dist/{chunk-KEQBGDUZ.js → chunk-B26DWEYT.js} +3 -3
- package/dist/{chunk-KEQBGDUZ.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-35DOPXHZ.js → chunk-PGLOYXDP.js} +193 -190
- package/dist/chunk-PGLOYXDP.js.map +1 -0
- package/dist/{chunk-BUXUWAI4.js → chunk-PPB7VP6R.js} +62 -8
- package/dist/chunk-PPB7VP6R.js.map +1 -0
- package/dist/{chunk-STF6NQ5N.js → chunk-RVJHUY7W.js} +2 -2
- package/dist/chunk-RVJHUY7W.js.map +1 -0
- package/dist/{chunk-LTTEILXY.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-RKQ277P5.js → chunk-UDEUT7AG.js} +125 -80
- package/dist/chunk-UDEUT7AG.js.map +1 -0
- package/dist/{config-Bd6FVlh7.d.ts → config-DOjWyIui.d.ts} +3 -21
- 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 +14 -8
- package/dist/frame/index.js +114 -79
- package/dist/frame/index.js.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +4 -4
- package/dist/library-CdTj-hFp.d.ts +21 -0
- package/dist/{mountFrame-FdJDb3TB.d.ts → mountFrame-CRPAjg1M.d.ts} +1 -1
- package/dist/shell/index.css +166 -150
- package/dist/shell/index.css.map +1 -1
- package/dist/shell/index.d.ts +24 -5
- package/dist/shell/index.js +594 -402
- package/dist/shell/index.js.map +1 -1
- package/dist/test/index.css +2 -3
- package/dist/test/index.css.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 +127 -32
- package/dist/vite/index.js.map +1 -1
- package/package.json +11 -9
- package/dist/chunk-35DOPXHZ.js.map +0 -1
- package/dist/chunk-3BAM6M26.js.map +0 -1
- package/dist/chunk-BUXUWAI4.js.map +0 -1
- package/dist/chunk-GH5RE5X5.js +0 -10
- package/dist/chunk-GH5RE5X5.js.map +0 -1
- package/dist/chunk-L5XM5VKB.js +0 -47
- package/dist/chunk-L5XM5VKB.js.map +0 -1
- package/dist/chunk-LTTEILXY.js.map +0 -1
- package/dist/chunk-ORMMZ5PV.js.map +0 -1
- package/dist/chunk-RKQ277P5.js.map +0 -1
- package/dist/chunk-STF6NQ5N.js.map +0 -1
package/dist/shell/index.js
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
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, librariesFrom, LibrariesContext, LAB_DENSITY, createKitRoot, schemaFromDescription, LibraryBadge, Description, IndexPage, LabBadge, openChannel } from '../chunk-
|
|
4
|
-
import { isIndexId, indexId, indexEntries } from '../chunk-
|
|
5
|
-
import {
|
|
6
|
-
import { useTrialFrame, createTrialFrames,
|
|
7
|
-
export { StoryGlobalsContext, StoryTrial, TrialFramesContext, TrialHost, useTrialFrame } from '../chunk-
|
|
8
|
-
import { labGlobals, withGlobals,
|
|
9
|
-
import { loadStories,
|
|
10
|
-
import '../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
|
+
import { isIndexId, indexId, indexEntries } from '../chunk-RVJHUY7W.js';
|
|
5
|
+
import { isGallery, isLab } from '../chunk-VUPN73JP.js';
|
|
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
|
-
import { useLatest, useActionsRegistry, effectivePose, useMenuHost, useMenus, sceneToAdapter, parseColor, contrastRatio, wcagLevels, historyKey, isPlainObject, createHistory, keymapShortcuts, actionBindings, keySpecShortcut, keymapLeaderId, swapOp } from '@weasel-js/core';
|
|
22
|
+
import { useLatest, useActionsRegistry, useVisibleRaf, effectivePose, useMenuHost, useMenus, sceneToAdapter, parseColor, contrastRatio, wcagLevels, historyKey, isPlainObject, createHistory, keymapShortcuts, actionBindings, keySpecShortcut, keymapLeaderId, swapOp } from '@weasel-js/core';
|
|
25
23
|
import { Button as Button$1 } from '@weasel-js/ui/components/Button';
|
|
26
24
|
import { Checkbox } from '@weasel-js/ui/components/Checkbox';
|
|
27
25
|
import { Icon } from '@weasel-js/ui/components/Icon';
|
|
@@ -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,151 +309,33 @@ 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);
|
|
344
315
|
const graph = useContext(DependenciesContext);
|
|
345
|
-
const gallery = isGallery(bundle);
|
|
346
|
-
const dependencies = gallery ? void 0 : graph?.[bundle.title];
|
|
347
316
|
const open = (id) => {
|
|
348
317
|
if (!trialId || !lab.instruments.some((instrument) => instrument.name === id)) return;
|
|
349
318
|
lab.swapTrial(trialId, id);
|
|
350
319
|
setRoute(id, { inPlace: true });
|
|
351
320
|
};
|
|
352
|
-
const render = (globals) => {
|
|
353
|
-
const env = {
|
|
354
|
-
title: bundle.title,
|
|
355
|
-
library: bundle.library,
|
|
356
|
-
...bundle.description === void 0 ? {} : { description: bundle.description },
|
|
357
|
-
stories: bundle.stories,
|
|
358
|
-
descriptions: bundle.descriptions,
|
|
359
|
-
gallery,
|
|
360
|
-
galleries: bundle.galleries,
|
|
361
|
-
labs: bundle.labs,
|
|
362
|
-
...dependencies ? { dependencies } : {},
|
|
363
|
-
globals,
|
|
364
|
-
decorators: setup.decorators ?? [],
|
|
365
|
-
open,
|
|
366
|
-
// A story that throws shows its error in its own cell; the rest of the page is still worth reading.
|
|
367
|
-
onError: (error) => console.error(error)
|
|
368
|
-
};
|
|
369
|
-
return /* @__PURE__ */ jsx(IndexPage, { env, render: bundle.render });
|
|
370
|
-
};
|
|
321
|
+
const render = (globals) => /* @__PURE__ */ jsx(IndexPage, { env: indexEnvOf(bundle, { graph, globals, decorators: setup.decorators ?? [], open }), render: bundle.render });
|
|
371
322
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
372
323
|
/* @__PURE__ */ jsx(TrialHost, { layout: "fullscreen", setup, config, children: render }),
|
|
373
324
|
/* @__PURE__ */ jsx(LiveCopies, { trialId, layout: "fullscreen", setup, trialConfig: config, children: render })
|
|
374
325
|
] });
|
|
375
326
|
}
|
|
376
|
-
function CaptureInputs({ entry, layout, config, state }) {
|
|
327
|
+
function CaptureInputs({ entry, layout, config, state, timed = false }) {
|
|
377
328
|
const capture = useContext(LoupeCaptureContext);
|
|
378
329
|
const trialId = useContext(TrialIdContext);
|
|
379
330
|
const [overrides] = useCssOverrides();
|
|
380
331
|
const withdraw = useRef(null);
|
|
332
|
+
const handle = useTrialClockHandle();
|
|
333
|
+
const clock = timed ? handle?.clock ?? null : null;
|
|
381
334
|
useEffect(() => {
|
|
382
|
-
if (capture
|
|
383
|
-
|
|
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]);
|
|
384
339
|
useEffect(
|
|
385
340
|
() => () => {
|
|
386
341
|
withdraw.current?.();
|
|
@@ -401,14 +356,81 @@ function LoupeProvider({ captureBase, revision = STILL, children }) {
|
|
|
401
356
|
);
|
|
402
357
|
return /* @__PURE__ */ jsx(LiveLensesContext.Provider, { value: lenses, children: /* @__PURE__ */ jsx(LoupeCaptureContext.Provider, { value: capture, children }) });
|
|
403
358
|
}
|
|
404
|
-
|
|
405
|
-
|
|
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
|
+
};
|
|
406
428
|
}
|
|
407
429
|
function documentInstrument(options) {
|
|
408
|
-
const { entry, loaded, setup, globals = {} } = options;
|
|
430
|
+
const { entry, loaded, setup, globals = {}, hosts } = options;
|
|
409
431
|
const schema = loaded.kind === "story" ? loaded.story.config : f.schema({});
|
|
410
|
-
const
|
|
411
|
-
const config = withGlobals(
|
|
432
|
+
const timeline = loaded.kind === "story" ? loaded.story.timeline : null;
|
|
433
|
+
const config = withGlobals(schema, globals);
|
|
412
434
|
const initialState = loaded.kind === "story" ? loaded.story.initialState : null;
|
|
413
435
|
return {
|
|
414
436
|
name: entry.id,
|
|
@@ -416,12 +438,25 @@ function documentInstrument(options) {
|
|
|
416
438
|
config,
|
|
417
439
|
defaultConfig: () => config.defaults(),
|
|
418
440
|
initialState: (c) => initialState?.(c) ?? null,
|
|
419
|
-
render: (ctx) =>
|
|
420
|
-
/* @__PURE__ */ jsx(StoryTrial, { story: loaded.story, setup, ctx }, loaded.story.id),
|
|
421
|
-
/* @__PURE__ */ jsx(
|
|
422
|
-
|
|
441
|
+
render: (ctx) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
442
|
+
loaded.kind === "story" ? /* @__PURE__ */ jsx(StoryTrial, { story: loaded.story, setup, ctx }, loaded.story.id) : /* @__PURE__ */ jsx(IndexTrial, { bundle: loaded.bundle, setup, config: ctx.config }),
|
|
443
|
+
/* @__PURE__ */ jsx(
|
|
444
|
+
CaptureInputs,
|
|
445
|
+
{
|
|
446
|
+
entry,
|
|
447
|
+
layout: loaded.kind === "story" ? loaded.story.layout : "fullscreen",
|
|
448
|
+
config: ctx.config,
|
|
449
|
+
state: ctx.state,
|
|
450
|
+
timed: timeline !== null
|
|
451
|
+
}
|
|
452
|
+
)
|
|
453
|
+
] }),
|
|
454
|
+
...hosts ? { annotations: storyMarks(hosts) } : {},
|
|
423
455
|
...loaded.kind === "story" && loaded.story.viewport ? { stage: { size: { width: loaded.story.viewport.width, height: loaded.story.viewport.height } } } : {},
|
|
424
|
-
...
|
|
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
|
+
} : {}
|
|
425
460
|
};
|
|
426
461
|
}
|
|
427
462
|
function Fault({ phase, message }) {
|
|
@@ -797,10 +832,10 @@ function useThemeSave({ trialId, globals, themeApi, scales, overrides, history:
|
|
|
797
832
|
...Object.keys(tokens).map((token) => `${TOKEN_PREFIX}${token}`)
|
|
798
833
|
]);
|
|
799
834
|
const groups = new Set(scales.map((scale) => scale.group));
|
|
800
|
-
const
|
|
835
|
+
const now2 = history2.current();
|
|
801
836
|
history2.reset({
|
|
802
|
-
overrides: Object.fromEntries(Object.entries(
|
|
803
|
-
scales: Object.fromEntries(Object.entries(
|
|
837
|
+
overrides: Object.fromEntries(Object.entries(now2.overrides).filter(([name]) => !saved.has(name))),
|
|
838
|
+
scales: Object.fromEntries(Object.entries(now2.scales).filter(([group]) => !groups.has(group)))
|
|
804
839
|
});
|
|
805
840
|
setNotice(
|
|
806
841
|
result.regenerated || result.problems.length === 0 ? { outcome: "saved", text: `Saved to ${file}.` } : { outcome: "error", text: `Saved to ${file}, but the tokens were not regenerated: ${result.problems.join("; ")}` }
|
|
@@ -1010,16 +1045,16 @@ function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
|
|
|
1010
1045
|
}
|
|
1011
1046
|
});
|
|
1012
1047
|
const write = (name, value) => {
|
|
1013
|
-
const
|
|
1014
|
-
const { [name]: _dropped, ...rest } =
|
|
1015
|
-
history2.commit({ ...
|
|
1048
|
+
const now2 = history2.current();
|
|
1049
|
+
const { [name]: _dropped, ...rest } = now2.overrides;
|
|
1050
|
+
history2.commit({ ...now2, overrides: value === null ? rest : { ...rest, [name]: value } }, `token:${name}`);
|
|
1016
1051
|
};
|
|
1017
1052
|
const onScaleChange = (group, next) => {
|
|
1018
1053
|
const source = SCALE_SOURCES.find((sc) => sc.group === group);
|
|
1019
1054
|
if (!source) return;
|
|
1020
|
-
const
|
|
1055
|
+
const now2 = history2.current();
|
|
1021
1056
|
history2.commit(
|
|
1022
|
-
{ overrides: { ...
|
|
1057
|
+
{ overrides: { ...now2.overrides, ...Object.fromEntries(scaleValues(source, next)) }, scales: { ...now2.scales, [group]: next } },
|
|
1023
1058
|
`scale:${group}`
|
|
1024
1059
|
);
|
|
1025
1060
|
};
|
|
@@ -1058,7 +1093,7 @@ function CssVarsPanel({ globals = NO_GLOBALS, themeApi } = {}) {
|
|
|
1058
1093
|
}
|
|
1059
1094
|
),
|
|
1060
1095
|
/* @__PURE__ */ jsx(
|
|
1061
|
-
Input,
|
|
1096
|
+
Input$1,
|
|
1062
1097
|
{
|
|
1063
1098
|
className: "fg-css-vars__filter",
|
|
1064
1099
|
"aria-label": "Filter",
|
|
@@ -1280,6 +1315,137 @@ function infoItem(open) {
|
|
|
1280
1315
|
onActivate: open
|
|
1281
1316
|
};
|
|
1282
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
|
+
}
|
|
1283
1449
|
var GLOBALS = "$globals";
|
|
1284
1450
|
function leaves(shape, prefix = "") {
|
|
1285
1451
|
const out = [];
|
|
@@ -1583,15 +1749,40 @@ var liveCellId = (lensId, compare) => compare === null ? lensId : `${lensId}~com
|
|
|
1583
1749
|
function sameRequest(a, b) {
|
|
1584
1750
|
return a !== null && a.slot === b.slot && a.hit.host === b.hit.host && a.hit.trialId === b.hit.trialId && a.hit.local.x === b.hit.local.x && a.hit.local.y === b.hit.local.y && a.hit.scale === b.hit.scale && a.factor === b.factor && a.size.width === b.size.width && a.size.height === b.size.height && a.args === b.args && a.compare === b.compare && sameContext(a.ctx, b.ctx);
|
|
1585
1751
|
}
|
|
1586
|
-
|
|
1752
|
+
var LOOK_EVENTS = ["pointerover", "pointerout", "focusin", "focusout", "transitionend"];
|
|
1753
|
+
function watchOf(notify) {
|
|
1754
|
+
const observer = typeof MutationObserver === "undefined" ? null : new MutationObserver(notify);
|
|
1755
|
+
let watched = null;
|
|
1756
|
+
return (el) => {
|
|
1757
|
+
if (el === watched) return;
|
|
1758
|
+
observer?.disconnect();
|
|
1759
|
+
for (const type of LOOK_EVENTS) watched?.removeEventListener(type, notify);
|
|
1760
|
+
watched = el;
|
|
1761
|
+
if (!el) return;
|
|
1762
|
+
observer?.observe(el, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
1763
|
+
for (const type of LOOK_EVENTS) el.addEventListener(type, notify);
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
var now = (run) => run();
|
|
1767
|
+
function createLiveSource(lenses, resolve, conditions, schedule = now) {
|
|
1587
1768
|
const measures = /* @__PURE__ */ new Set();
|
|
1588
1769
|
const listeners2 = /* @__PURE__ */ new Set();
|
|
1770
|
+
let due = false;
|
|
1771
|
+
const flush = () => {
|
|
1772
|
+
due = false;
|
|
1773
|
+
for (const listener of [...listeners2]) listener();
|
|
1774
|
+
};
|
|
1775
|
+
const notify = () => {
|
|
1776
|
+
if (due) return;
|
|
1777
|
+
due = true;
|
|
1778
|
+
schedule(flush);
|
|
1779
|
+
};
|
|
1589
1780
|
return {
|
|
1590
1781
|
kind: "live",
|
|
1591
1782
|
produces: "dom",
|
|
1592
1783
|
label: () => "Live",
|
|
1593
1784
|
status: () => true,
|
|
1594
|
-
// A
|
|
1785
|
+
// A reader hears of what it reads: the lens's copy, and with nothing compared, the main story under the reticle.
|
|
1595
1786
|
subscribe(listener) {
|
|
1596
1787
|
listeners2.add(listener);
|
|
1597
1788
|
return () => {
|
|
@@ -1606,10 +1797,9 @@ function createLiveSource(lenses, resolve, conditions) {
|
|
|
1606
1797
|
empty.className = "fg-lens-empty";
|
|
1607
1798
|
empty.hidden = true;
|
|
1608
1799
|
slot.append(target, empty);
|
|
1609
|
-
const
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
watch?.observe(target, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
1800
|
+
const watchCopy = watchOf(notify);
|
|
1801
|
+
watchCopy(target);
|
|
1802
|
+
const watchStory = watchOf(notify);
|
|
1613
1803
|
let request = first;
|
|
1614
1804
|
let posted = null;
|
|
1615
1805
|
const post = (next) => {
|
|
@@ -1620,6 +1810,7 @@ function createLiveSource(lenses, resolve, conditions) {
|
|
|
1620
1810
|
const measure = () => {
|
|
1621
1811
|
const { lensId, reticle, conditions: compare } = request;
|
|
1622
1812
|
const hit = trialAt(reticle, doc);
|
|
1813
|
+
watchStory(compare === null ? hit?.host ?? null : null);
|
|
1623
1814
|
empty.hidden = hit !== null;
|
|
1624
1815
|
if (!hit) {
|
|
1625
1816
|
post(null);
|
|
@@ -1647,7 +1838,8 @@ function createLiveSource(lenses, resolve, conditions) {
|
|
|
1647
1838
|
measure();
|
|
1648
1839
|
},
|
|
1649
1840
|
detach() {
|
|
1650
|
-
|
|
1841
|
+
watchCopy(null);
|
|
1842
|
+
watchStory(null);
|
|
1651
1843
|
measures.delete(measure);
|
|
1652
1844
|
lenses.set(liveCellId(request.lensId, request.conditions), null);
|
|
1653
1845
|
target.remove();
|
|
@@ -1986,6 +2178,22 @@ function renderReadouts(props) {
|
|
|
1986
2178
|
readouts.includes("contrast") ? /* @__PURE__ */ jsx(ContrastReadout, { ...props }) : null
|
|
1987
2179
|
] });
|
|
1988
2180
|
}
|
|
2181
|
+
|
|
2182
|
+
// src/shell/loupe/frameScroll.ts
|
|
2183
|
+
var ORIGIN = { left: 0, top: 0 };
|
|
2184
|
+
var frames = /* @__PURE__ */ new Map();
|
|
2185
|
+
var trialOf = (host) => host.dataset.fgHost ?? "";
|
|
2186
|
+
function reportFrameScroll(host, scroll) {
|
|
2187
|
+
frames.set(trialOf(host), { left: scroll.left, top: scroll.top });
|
|
2188
|
+
host.dispatchEvent(new Event("scroll"));
|
|
2189
|
+
}
|
|
2190
|
+
function scrollOf(host) {
|
|
2191
|
+
const scroller = scrollerOf(host);
|
|
2192
|
+
if (scroller) return { left: scroller.scrollLeft, top: scroller.scrollTop };
|
|
2193
|
+
return frames.get(trialOf(host)) ?? ORIGIN;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// src/shell/loupe/capturePayload.ts
|
|
1989
2197
|
var STATE_UNSENDABLE = "this story's state cannot be sent to a capture";
|
|
1990
2198
|
var CONFIG_UNSENDABLE = "this story's config cannot be sent to a capture";
|
|
1991
2199
|
var NO_STORY = "this trial shows no story to capture";
|
|
@@ -2018,15 +2226,13 @@ function withoutUndefined(value) {
|
|
|
2018
2226
|
}
|
|
2019
2227
|
var clampDpr = (dpr) => Math.min(Math.max(dpr, MIN_DPR), MAX_DPR);
|
|
2020
2228
|
function capturePayload(input) {
|
|
2021
|
-
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;
|
|
2022
2230
|
if (!sendable(state)) return { unavailable: STATE_UNSENDABLE };
|
|
2023
2231
|
const sent = withoutUndefined(withArgs(storyConfig(config), resolved.args));
|
|
2024
2232
|
if (!sendable(sent)) return { unavailable: CONFIG_UNSENDABLE };
|
|
2025
2233
|
const box = { width: host.offsetWidth, height: host.offsetHeight };
|
|
2026
2234
|
if (!(box.width > 0 && box.height > 0)) return { unavailable: NO_BOX };
|
|
2027
2235
|
const isolate = entry.isolate !== void 0;
|
|
2028
|
-
const scroller = scrollerOf(host);
|
|
2029
|
-
const kept2 = config?.[PLAYHEAD_KEY];
|
|
2030
2236
|
return {
|
|
2031
2237
|
story: entry.id,
|
|
2032
2238
|
isolate,
|
|
@@ -2034,11 +2240,11 @@ function capturePayload(input) {
|
|
|
2034
2240
|
state,
|
|
2035
2241
|
globals,
|
|
2036
2242
|
overrides,
|
|
2037
|
-
playhead:
|
|
2243
|
+
playhead: isolate ? null : playhead,
|
|
2038
2244
|
box,
|
|
2039
2245
|
layout,
|
|
2040
2246
|
dpr: clampDpr(resolved.dpr ?? dpr),
|
|
2041
|
-
scroll:
|
|
2247
|
+
scroll: scrollOf(host),
|
|
2042
2248
|
lab,
|
|
2043
2249
|
colorScheme
|
|
2044
2250
|
};
|
|
@@ -2049,8 +2255,10 @@ function trialPayload(trial, lab, hit, dpr, c) {
|
|
|
2049
2255
|
const own = effectiveGlobals(lab.labValues, trial.config?.[GLOBALS_KEY]);
|
|
2050
2256
|
const resolved = resolveComparison(c, own, ctx);
|
|
2051
2257
|
const colorScheme = ctx.prefersDark ? "dark" : "light";
|
|
2258
|
+
const { playhead, ...inputs } = trial;
|
|
2052
2259
|
return capturePayload({
|
|
2053
|
-
...
|
|
2260
|
+
...inputs,
|
|
2261
|
+
playhead: playhead(),
|
|
2054
2262
|
globals: resolved.globals,
|
|
2055
2263
|
host: hit.host,
|
|
2056
2264
|
dpr,
|
|
@@ -2341,7 +2549,14 @@ function useLensFocus() {
|
|
|
2341
2549
|
}
|
|
2342
2550
|
function createApi(scene, refs) {
|
|
2343
2551
|
let next = 0;
|
|
2344
|
-
|
|
2552
|
+
const holds = /* @__PURE__ */ new Map();
|
|
2553
|
+
const release = (lensId) => {
|
|
2554
|
+
holds.get(lensId)?.drop();
|
|
2555
|
+
holds.delete(lensId);
|
|
2556
|
+
};
|
|
2557
|
+
scene.subscribe(() => {
|
|
2558
|
+
for (const lensId of [...holds.keys()]) if (settingsOf(scene, lensId)?.aim !== "follow") release(lensId);
|
|
2559
|
+
});
|
|
2345
2560
|
const toBody = (client, box) => ({ x: client.x - box.left, y: client.y - box.top });
|
|
2346
2561
|
const apply = (ops) => {
|
|
2347
2562
|
if (ops.length > 0) sceneToAdapter(scene).applyOps([...ops], "lens");
|
|
@@ -2373,10 +2588,11 @@ function createApi(scene, refs) {
|
|
|
2373
2588
|
if (lensId === null || !body) return;
|
|
2374
2589
|
const pose = reticleAt(scene, lensId, toBody(client, body.getBoundingClientRect()));
|
|
2375
2590
|
if (pose === null) return;
|
|
2376
|
-
hold
|
|
2591
|
+
const hold = holds.get(lensId) ?? scene.overrides.hold();
|
|
2592
|
+
holds.set(lensId, hold);
|
|
2377
2593
|
hold.set([[`${lensId}:reticle`, pose]]);
|
|
2378
2594
|
},
|
|
2379
|
-
unfollow:
|
|
2595
|
+
unfollow: release,
|
|
2380
2596
|
sourceAvailable: (kind) => refs.sources.current[kind]?.status() ?? "not provided",
|
|
2381
2597
|
openCompareMenu: (lensId) => refs.compare?.current?.(lensId)
|
|
2382
2598
|
};
|
|
@@ -2407,6 +2623,21 @@ function useRemeasure(sources, body, rerender) {
|
|
|
2407
2623
|
};
|
|
2408
2624
|
}, [sources, body, rerender]);
|
|
2409
2625
|
}
|
|
2626
|
+
function useFrameSchedule() {
|
|
2627
|
+
const next = useRef(null);
|
|
2628
|
+
const frame = useVisibleRaf(() => {
|
|
2629
|
+
const run = next.current;
|
|
2630
|
+
next.current = null;
|
|
2631
|
+
run?.();
|
|
2632
|
+
});
|
|
2633
|
+
return useCallback(
|
|
2634
|
+
(run) => {
|
|
2635
|
+
next.current = run;
|
|
2636
|
+
frame.request();
|
|
2637
|
+
},
|
|
2638
|
+
[frame]
|
|
2639
|
+
);
|
|
2640
|
+
}
|
|
2410
2641
|
function ForgeLoupe({ declarations, modeGlobal }) {
|
|
2411
2642
|
const lenses = useContext(LiveLensesContext);
|
|
2412
2643
|
const labValues = useContext(StoryGlobalsContext);
|
|
@@ -2417,8 +2648,14 @@ function ForgeLoupe({ declarations, modeGlobal }) {
|
|
|
2417
2648
|
const pointer = useRef(null);
|
|
2418
2649
|
const { focus, onFocusLens } = useLensFocus();
|
|
2419
2650
|
const conditions = useLatest({ labValues, mode, declarations, modeGlobal });
|
|
2651
|
+
const onFrame = useFrameSchedule();
|
|
2420
2652
|
const [live] = useState(
|
|
2421
|
-
() => lenses ? createLiveSource(
|
|
2653
|
+
() => lenses ? createLiveSource(
|
|
2654
|
+
lenses,
|
|
2655
|
+
resolveComparison,
|
|
2656
|
+
() => ({ base: conditions.current.labValues, ctx: resolveContextOf(conditions.current) }),
|
|
2657
|
+
onFrame
|
|
2658
|
+
) : null
|
|
2422
2659
|
);
|
|
2423
2660
|
const pixels = usePixelsSource(conditions);
|
|
2424
2661
|
const sources = useMemo(
|
|
@@ -2478,20 +2715,41 @@ function ForgeLoupe({ declarations, modeGlobal }) {
|
|
|
2478
2715
|
function routedTrial(trials, focusedTrialId, route) {
|
|
2479
2716
|
return trials.find((t) => t.id === focusedTrialId && t.instrumentName === route) ?? trials.find((t) => t.instrumentName === route);
|
|
2480
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
|
+
}
|
|
2481
2736
|
function PlayheadSync({ trialId, route, appliedRef }) {
|
|
2482
|
-
const
|
|
2737
|
+
const handle = useTrialClockHandle(trialId);
|
|
2483
2738
|
useEffect(() => {
|
|
2484
|
-
if (!
|
|
2739
|
+
if (!handle) return;
|
|
2740
|
+
const { clock } = handle;
|
|
2741
|
+
let hold = null;
|
|
2485
2742
|
const fromUrl = () => {
|
|
2486
2743
|
const t = readPlayheadParam();
|
|
2487
|
-
if (t === null || t === clock
|
|
2488
|
-
|
|
2744
|
+
if (t === null || t === shownTime(clock)) return;
|
|
2745
|
+
hold?.release();
|
|
2746
|
+
hold = holdAt(handle, t);
|
|
2489
2747
|
};
|
|
2490
2748
|
let held;
|
|
2491
2749
|
const toUrl = () => {
|
|
2492
|
-
if (
|
|
2493
|
-
const
|
|
2494
|
-
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;
|
|
2495
2753
|
if (next === held) return;
|
|
2496
2754
|
held = next;
|
|
2497
2755
|
writePlayheadParam(next);
|
|
@@ -2511,9 +2769,10 @@ function PlayheadSync({ trialId, route, appliedRef }) {
|
|
|
2511
2769
|
window.addEventListener("hashchange", onHashChange);
|
|
2512
2770
|
return () => {
|
|
2513
2771
|
off();
|
|
2772
|
+
hold?.release();
|
|
2514
2773
|
window.removeEventListener("hashchange", onHashChange);
|
|
2515
2774
|
};
|
|
2516
|
-
}, [
|
|
2775
|
+
}, [handle, route, appliedRef]);
|
|
2517
2776
|
return null;
|
|
2518
2777
|
}
|
|
2519
2778
|
function RoutePlayhead() {
|
|
@@ -2668,7 +2927,8 @@ function KnobSync({ schema, config }) {
|
|
|
2668
2927
|
if (reading.current && takeRouteFromTrial()) reading.current = false;
|
|
2669
2928
|
if (reading.current) {
|
|
2670
2929
|
reading.current = false;
|
|
2671
|
-
const
|
|
2930
|
+
const params = readRouteParams();
|
|
2931
|
+
const knobs2 = paramsToKnobs(schema, params);
|
|
2672
2932
|
let wrote = false;
|
|
2673
2933
|
for (const { path, leaf } of knobPaths(schema)) {
|
|
2674
2934
|
const target = path in knobs2 ? knobs2[path] : leaf.default;
|
|
@@ -2677,10 +2937,15 @@ function KnobSync({ schema, config }) {
|
|
|
2677
2937
|
setConfig(path, target);
|
|
2678
2938
|
wrote = true;
|
|
2679
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
|
+
}
|
|
2680
2945
|
if (wrote) return;
|
|
2681
2946
|
}
|
|
2682
2947
|
const knobs = knobsToParams(schema, config);
|
|
2683
|
-
replaceRouteParams((params) => ({ ...knobs, ...reservedParams(params) }));
|
|
2948
|
+
replaceRouteParams((params) => ({ ...knobs, ...withoutPins(reservedParams(params)) }));
|
|
2684
2949
|
}, [schema, config, setConfig, typed]);
|
|
2685
2950
|
return null;
|
|
2686
2951
|
}
|
|
@@ -2898,6 +3163,8 @@ var VIEWS = [
|
|
|
2898
3163
|
{ value: "gallery", label: "Gallery" }
|
|
2899
3164
|
];
|
|
2900
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)}`;
|
|
2901
3168
|
function ancestorsOf(entry) {
|
|
2902
3169
|
const paths = /* @__PURE__ */ new Set();
|
|
2903
3170
|
if (!entry) return paths;
|
|
@@ -2911,7 +3178,8 @@ function ancestorsOf(entry) {
|
|
|
2911
3178
|
function LibraryBadge2({ library }) {
|
|
2912
3179
|
return /* @__PURE__ */ jsx(LibraryBadge, { library, className: "fg-tree__tag" });
|
|
2913
3180
|
}
|
|
2914
|
-
function
|
|
3181
|
+
function rowTags(lab, library) {
|
|
3182
|
+
if (!lab && !library) return void 0;
|
|
2915
3183
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2916
3184
|
lab ? /* @__PURE__ */ jsx(LabBadge, { className: "fg-tree__lab" }) : null,
|
|
2917
3185
|
library ? /* @__PURE__ */ jsx(LibraryBadge2, { library }) : null
|
|
@@ -2924,8 +3192,6 @@ function StoryTree({ ctx, index }) {
|
|
|
2924
3192
|
const [folds, setFolds] = usePersistedState("fg-tree-open", {}, { scope: "lab" });
|
|
2925
3193
|
const [chosen, setView] = usePersistedState("fg-tree-view", "components", { scope: "lab" });
|
|
2926
3194
|
const [hidden, setHidden] = usePersistedState("fg-tree-libraries", {}, { scope: "lab" });
|
|
2927
|
-
const [focusKey, setFocusKey] = useState(null);
|
|
2928
|
-
const items = useRef(/* @__PURE__ */ new Map());
|
|
2929
3195
|
const table = useContext(LibrariesContext);
|
|
2930
3196
|
const libraries = useMemo(() => librariesIn(index, table), [index, table]);
|
|
2931
3197
|
const views = useMemo(() => {
|
|
@@ -2953,24 +3219,45 @@ function StoryTree({ ctx, index }) {
|
|
|
2953
3219
|
const routed = routable.find((entry) => entry.id === route);
|
|
2954
3220
|
const routeAncestors = useMemo(() => ancestorsOf(routed), [routed]);
|
|
2955
3221
|
const filtering = query.trim() !== "";
|
|
2956
|
-
const
|
|
2957
|
-
const
|
|
2958
|
-
const
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
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)])) }));
|
|
2974
3261
|
};
|
|
2975
3262
|
const activate = (entry, another) => {
|
|
2976
3263
|
const focused = ctx.trials.find((trial) => trial.id === ctx.focusedTrialId);
|
|
@@ -2986,20 +3273,29 @@ function StoryTree({ ctx, index }) {
|
|
|
2986
3273
|
}
|
|
2987
3274
|
setRoute2(entry.id);
|
|
2988
3275
|
};
|
|
2989
|
-
const
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
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);
|
|
2998
3285
|
};
|
|
3286
|
+
const treeEl = useRef(null);
|
|
2999
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
|
+
});
|
|
3000
3296
|
const reveal = (path) => {
|
|
3001
|
-
const find = (
|
|
3002
|
-
for (const node2 of
|
|
3297
|
+
const find = (list, at) => {
|
|
3298
|
+
for (const node2 of list) {
|
|
3003
3299
|
if (node2.kind !== "folder") continue;
|
|
3004
3300
|
if (node2.path === at) return node2;
|
|
3005
3301
|
const inner = find(node2.children, at);
|
|
@@ -3027,135 +3323,11 @@ function StoryTree({ ctx, index }) {
|
|
|
3027
3323
|
const segments = found.path.split("/");
|
|
3028
3324
|
const above = segments.slice(0, -1).map((_, i) => segments.slice(0, i + 1).join("/"));
|
|
3029
3325
|
setFolds((prev) => ({ ...prev, ...Object.fromEntries(above.map((p) => [p, true])) }));
|
|
3030
|
-
setFocusKey(keyOf(found));
|
|
3031
3326
|
scrollTo.current = keyOf(found);
|
|
3032
|
-
if (found.index)
|
|
3327
|
+
if (found.index) openIndex(found.path, found.index, false);
|
|
3033
3328
|
else setFolds((prev) => ({ ...prev, [found.path]: true }));
|
|
3034
3329
|
};
|
|
3035
3330
|
useEffect(() => onRevealInTree(reveal));
|
|
3036
|
-
const onKeyDown = (event) => {
|
|
3037
|
-
const at = rows.findIndex((row2) => row2.key === active);
|
|
3038
|
-
const row = rows[at];
|
|
3039
|
-
if (!row) return;
|
|
3040
|
-
const { node } = row;
|
|
3041
|
-
switch (event.key) {
|
|
3042
|
-
case "ArrowDown":
|
|
3043
|
-
moveTo(rows[at + 1]?.key);
|
|
3044
|
-
break;
|
|
3045
|
-
case "ArrowUp":
|
|
3046
|
-
moveTo(rows[at - 1]?.key);
|
|
3047
|
-
break;
|
|
3048
|
-
case "Home":
|
|
3049
|
-
moveTo(rows[0]?.key);
|
|
3050
|
-
break;
|
|
3051
|
-
case "End":
|
|
3052
|
-
moveTo(rows[rows.length - 1]?.key);
|
|
3053
|
-
break;
|
|
3054
|
-
case "ArrowRight":
|
|
3055
|
-
if (node.kind !== "folder") break;
|
|
3056
|
-
if (!isOpen(node.path)) setOpen(node.path, true);
|
|
3057
|
-
else if (rows[at + 1]?.parent === row.key) moveTo(rows[at + 1]?.key);
|
|
3058
|
-
break;
|
|
3059
|
-
case "ArrowLeft":
|
|
3060
|
-
if (node.kind === "folder" && isOpen(node.path) && !filtering) setOpen(node.path, false);
|
|
3061
|
-
else moveTo(row.parent ?? void 0);
|
|
3062
|
-
break;
|
|
3063
|
-
case "Enter":
|
|
3064
|
-
case " ":
|
|
3065
|
-
if (node.kind === "story") activate(node.entry, event.shiftKey);
|
|
3066
|
-
else openFolder(node, event.shiftKey);
|
|
3067
|
-
break;
|
|
3068
|
-
default:
|
|
3069
|
-
return;
|
|
3070
|
-
}
|
|
3071
|
-
event.preventDefault();
|
|
3072
|
-
};
|
|
3073
|
-
const itemProps = (node, level) => {
|
|
3074
|
-
const key = keyOf(node);
|
|
3075
|
-
return {
|
|
3076
|
-
role: "treeitem",
|
|
3077
|
-
"aria-level": level,
|
|
3078
|
-
tabIndex: key === active ? 0 : -1,
|
|
3079
|
-
ref: (el) => {
|
|
3080
|
-
if (el) items.current.set(key, el);
|
|
3081
|
-
else items.current.delete(key);
|
|
3082
|
-
if (el && scrollTo.current === key) {
|
|
3083
|
-
scrollTo.current = null;
|
|
3084
|
-
el.scrollIntoView({ block: "nearest" });
|
|
3085
|
-
}
|
|
3086
|
-
},
|
|
3087
|
-
// A folder item contains its children, so their focus bubbles through it.
|
|
3088
|
-
onFocus: (event) => {
|
|
3089
|
-
if (event.target === event.currentTarget) setFocusKey(key);
|
|
3090
|
-
}
|
|
3091
|
-
};
|
|
3092
|
-
};
|
|
3093
|
-
const renderNodes = (nodes, level) => nodes.map((node) => {
|
|
3094
|
-
if (node.kind === "folder") {
|
|
3095
|
-
const open = isOpen(node.path);
|
|
3096
|
-
return /* @__PURE__ */ jsxs(
|
|
3097
|
-
"div",
|
|
3098
|
-
{
|
|
3099
|
-
...itemProps(node, level),
|
|
3100
|
-
"aria-expanded": open,
|
|
3101
|
-
"aria-label": node.label,
|
|
3102
|
-
"aria-current": node.index && openIds.has(node.index.id) ? "true" : void 0,
|
|
3103
|
-
className: "fg-tree__node",
|
|
3104
|
-
children: [
|
|
3105
|
-
/* @__PURE__ */ jsxs(
|
|
3106
|
-
"div",
|
|
3107
|
-
{
|
|
3108
|
-
className: "fg-tree__item fg-tree__folder",
|
|
3109
|
-
onClick: (event) => {
|
|
3110
|
-
setFocusKey(keyOf(node));
|
|
3111
|
-
openFolder(node, event.shiftKey);
|
|
3112
|
-
},
|
|
3113
|
-
children: [
|
|
3114
|
-
/* @__PURE__ */ jsx(
|
|
3115
|
-
"span",
|
|
3116
|
-
{
|
|
3117
|
-
"aria-hidden": "true",
|
|
3118
|
-
className: "fg-tree__fold",
|
|
3119
|
-
onClick: (event) => {
|
|
3120
|
-
event.stopPropagation();
|
|
3121
|
-
setOpen(node.path, !open);
|
|
3122
|
-
},
|
|
3123
|
-
children: /* @__PURE__ */ jsx(DisclosureMark, { open })
|
|
3124
|
-
}
|
|
3125
|
-
),
|
|
3126
|
-
/* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label }),
|
|
3127
|
-
/* @__PURE__ */ jsx(RowTags, { lab: !!node.index && isLab(node.index), library: node.tag })
|
|
3128
|
-
]
|
|
3129
|
-
}
|
|
3130
|
-
),
|
|
3131
|
-
open ? /* @__PURE__ */ jsx("div", { role: "group", className: "fg-tree__group", children: renderNodes(node.children, level + 1) }) : null
|
|
3132
|
-
]
|
|
3133
|
-
},
|
|
3134
|
-
keyOf(node)
|
|
3135
|
-
);
|
|
3136
|
-
}
|
|
3137
|
-
const { entry } = node;
|
|
3138
|
-
return /* @__PURE__ */ jsxs(
|
|
3139
|
-
"a",
|
|
3140
|
-
{
|
|
3141
|
-
...itemProps(node, level),
|
|
3142
|
-
href: `#/${encodeURIComponent(entry.id)}`,
|
|
3143
|
-
"aria-current": openIds.has(entry.id) ? "true" : void 0,
|
|
3144
|
-
className: "fg-tree__item fg-tree__story",
|
|
3145
|
-
onClick: (event) => {
|
|
3146
|
-
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
3147
|
-
event.preventDefault();
|
|
3148
|
-
setFocusKey(keyOf(node));
|
|
3149
|
-
activate(entry, event.shiftKey);
|
|
3150
|
-
},
|
|
3151
|
-
children: [
|
|
3152
|
-
/* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label ?? entry.name }),
|
|
3153
|
-
/* @__PURE__ */ jsx(RowTags, { lab: isLab(entry), library: node.tag })
|
|
3154
|
-
]
|
|
3155
|
-
},
|
|
3156
|
-
keyOf(node)
|
|
3157
|
-
);
|
|
3158
|
-
});
|
|
3159
3331
|
return /* @__PURE__ */ jsxs("section", { className: "fg-tree", children: [
|
|
3160
3332
|
/* @__PURE__ */ jsx("h2", { id: headingId, className: "fg-tree__heading", children: "Stories" }),
|
|
3161
3333
|
route && !routed ? /* @__PURE__ */ jsxs("p", { className: "fg-route-miss", children: [
|
|
@@ -3197,7 +3369,19 @@ function StoryTree({ ctx, index }) {
|
|
|
3197
3369
|
onChange: (event) => setQuery(event.target.value)
|
|
3198
3370
|
}
|
|
3199
3371
|
),
|
|
3200
|
-
/* @__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
|
+
)
|
|
3201
3385
|
] });
|
|
3202
3386
|
}
|
|
3203
3387
|
var KEPT_CONFIGS = 50;
|
|
@@ -3276,7 +3460,7 @@ function sendInputs(current, sent, next) {
|
|
|
3276
3460
|
return current.inputs !== before;
|
|
3277
3461
|
}
|
|
3278
3462
|
function receive(deps, current, msg) {
|
|
3279
|
-
const { latest: latestRef, trialId, frames, setFault, setPending, init, copy } = deps;
|
|
3463
|
+
const { latest: latestRef, trialId, frames: frames2, host, setFault, setPending, init, copy } = deps;
|
|
3280
3464
|
const { ctx: live, answers, onReady } = latestRef.current;
|
|
3281
3465
|
switch (msg.type) {
|
|
3282
3466
|
case "ready": {
|
|
@@ -3311,15 +3495,18 @@ function receive(deps, current, msg) {
|
|
|
3311
3495
|
if (!copy) answers.record(msg.answers);
|
|
3312
3496
|
break;
|
|
3313
3497
|
case "vars":
|
|
3314
|
-
if (trialId)
|
|
3498
|
+
if (trialId) frames2?.report(trialId, msg.vars);
|
|
3315
3499
|
break;
|
|
3316
3500
|
case "a11y": {
|
|
3317
3501
|
const outcome = msg.ok ? { ok: true, report: msg.report } : { ok: false, message: msg.message };
|
|
3318
|
-
if (trialId)
|
|
3502
|
+
if (trialId) frames2?.reportA11y(trialId, outcome);
|
|
3319
3503
|
current.pending.get(msg.id)?.(outcome);
|
|
3320
3504
|
current.pending.delete(msg.id);
|
|
3321
3505
|
break;
|
|
3322
3506
|
}
|
|
3507
|
+
case "scroll":
|
|
3508
|
+
if (!copy && host.current) reportFrameScroll(host.current, msg);
|
|
3509
|
+
break;
|
|
3323
3510
|
case "open": {
|
|
3324
3511
|
const { lab: liveLab } = latestRef.current;
|
|
3325
3512
|
if (!trialId || !liveLab.instruments.some((instrument) => instrument.name === msg.id)) break;
|
|
@@ -3335,7 +3522,7 @@ function receive(deps, current, msg) {
|
|
|
3335
3522
|
}
|
|
3336
3523
|
}
|
|
3337
3524
|
function openLink(deps, link, frame) {
|
|
3338
|
-
const { latest: latestRef, src, trialId, frames, setFault, setPending } = deps;
|
|
3525
|
+
const { latest: latestRef, src, trialId, frames: frames2, setFault, setPending } = deps;
|
|
3339
3526
|
closeLink(link);
|
|
3340
3527
|
setPending(true);
|
|
3341
3528
|
const target = frame.contentWindow;
|
|
@@ -3356,7 +3543,7 @@ function openLink(deps, link, frame) {
|
|
|
3356
3543
|
if (current.hasRendered) ask();
|
|
3357
3544
|
else current.queued.push(ask);
|
|
3358
3545
|
});
|
|
3359
|
-
const disconnect =
|
|
3546
|
+
const disconnect = frames2 && trialId ? frames2.connect(trialId, { send: channel.send, audit }) : () => {
|
|
3360
3547
|
};
|
|
3361
3548
|
const current = {
|
|
3362
3549
|
channel,
|
|
@@ -3377,7 +3564,7 @@ function openLink(deps, link, frame) {
|
|
|
3377
3564
|
]);
|
|
3378
3565
|
}
|
|
3379
3566
|
function useFrameLink(opts) {
|
|
3380
|
-
const { latest: latestRef, src, trialId, frames, setFault, setPending, copy, config, state, globals, descriptionKey } = opts;
|
|
3567
|
+
const { latest: latestRef, src, trialId, frames: frames2, host, setFault, setPending, copy, config, state, globals, descriptionKey } = opts;
|
|
3381
3568
|
const link = useRef(null);
|
|
3382
3569
|
const init = useCallback((current) => {
|
|
3383
3570
|
const { ctx: live, globals: liveGlobals } = latestRef.current;
|
|
@@ -3386,7 +3573,7 @@ function useFrameLink(opts) {
|
|
|
3386
3573
|
current.inputs += 1;
|
|
3387
3574
|
current.channel.send({ type: "init", ...current.sent });
|
|
3388
3575
|
}, [latestRef]);
|
|
3389
|
-
const deps = { latest: latestRef, src, trialId, frames, setFault, setPending, copy, init };
|
|
3576
|
+
const deps = { latest: latestRef, src, trialId, frames: frames2, host, setFault, setPending, copy, init };
|
|
3390
3577
|
const connect = (frame) => openLink(deps, link, frame);
|
|
3391
3578
|
const adopt2 = (adopted) => {
|
|
3392
3579
|
adopted.deliver = (msg) => receive(deps, adopted, msg);
|
|
@@ -3504,7 +3691,7 @@ function FrameView(props) {
|
|
|
3504
3691
|
[copy, trialCtx, laidConfig, laidSetConfig]
|
|
3505
3692
|
);
|
|
3506
3693
|
const trialId = useContext(TrialIdContext);
|
|
3507
|
-
const
|
|
3694
|
+
const frames2 = useContext(TrialFramesContext);
|
|
3508
3695
|
const [overrides] = useCssOverrides();
|
|
3509
3696
|
const pool = useContext(FramePoolContext);
|
|
3510
3697
|
const lab = useLabContext();
|
|
@@ -3521,7 +3708,8 @@ function FrameView(props) {
|
|
|
3521
3708
|
latest: latestRef,
|
|
3522
3709
|
src,
|
|
3523
3710
|
trialId: copy ? null : trialId,
|
|
3524
|
-
frames: copy ? null :
|
|
3711
|
+
frames: copy ? null : frames2,
|
|
3712
|
+
host: hostRef,
|
|
3525
3713
|
setFault,
|
|
3526
3714
|
setPending,
|
|
3527
3715
|
copy: copy !== void 0,
|
|
@@ -3550,6 +3738,24 @@ function FrameView(props) {
|
|
|
3550
3738
|
if (iframeRef.current) iframeRef.current.title = title;
|
|
3551
3739
|
}, [title]);
|
|
3552
3740
|
useEffect(() => copy ? void 0 : answers.hold(ctx.config), [answers, ctx.config, copy]);
|
|
3741
|
+
const setup = props.setup;
|
|
3742
|
+
const copyId = copy?.id;
|
|
3743
|
+
useLayoutEffect(
|
|
3744
|
+
() => copyId && setup && hostRef.current ? applyGlobalsTo(hostRef.current, copyId, globals, setup) : void 0,
|
|
3745
|
+
[copyId, globals, setup]
|
|
3746
|
+
);
|
|
3747
|
+
const factor = copy?.factor;
|
|
3748
|
+
useEffect(() => {
|
|
3749
|
+
if (factor !== void 0) iframeRef.current?.contentWindow?.dispatchEvent(new Event("resize"));
|
|
3750
|
+
}, [factor]);
|
|
3751
|
+
const main = copy?.main;
|
|
3752
|
+
useEffect(() => {
|
|
3753
|
+
if (!main || pending2) return void 0;
|
|
3754
|
+
const follow = () => link.current?.channel.send({ type: "scroll", ...scrollOf(main) });
|
|
3755
|
+
follow();
|
|
3756
|
+
main.addEventListener("scroll", follow, { passive: true });
|
|
3757
|
+
return () => main.removeEventListener("scroll", follow);
|
|
3758
|
+
}, [main, pending2, link]);
|
|
3553
3759
|
return /* @__PURE__ */ jsxs("div", { ref: hostRef, className: "fg-frame-host", "data-fg-host": copy?.id ?? trialId ?? void 0, "data-fg-copy": copy ? "" : void 0, children: [
|
|
3554
3760
|
/* @__PURE__ */ jsx("div", { ref: slotRef, className: "fg-frame-slot" }),
|
|
3555
3761
|
fault ? /* @__PURE__ */ jsxs("div", { className: "fg-fault", role: "alert", children: [
|
|
@@ -3560,7 +3766,7 @@ function FrameView(props) {
|
|
|
3560
3766
|
] });
|
|
3561
3767
|
}
|
|
3562
3768
|
function storyInstrument(options) {
|
|
3563
|
-
const { entry, ready, answers, frameUrl, onReady, globals = {} } = options;
|
|
3769
|
+
const { entry, ready, answers, frameUrl, onReady, globals = {}, setup } = options;
|
|
3564
3770
|
const config = withGlobals(
|
|
3565
3771
|
ready ? schemaFromDescription(ready.schema, answers) : f.schema({}),
|
|
3566
3772
|
globals
|
|
@@ -3582,10 +3788,11 @@ function storyInstrument(options) {
|
|
|
3582
3788
|
answers,
|
|
3583
3789
|
onReady,
|
|
3584
3790
|
descriptionKey,
|
|
3585
|
-
ctx
|
|
3791
|
+
ctx,
|
|
3792
|
+
...setup ? { setup } : {}
|
|
3586
3793
|
}
|
|
3587
3794
|
),
|
|
3588
|
-
/* @__PURE__ */ jsx(CaptureInputs, { entry, layout: ready?.layout ??
|
|
3795
|
+
/* @__PURE__ */ jsx(CaptureInputs, { entry, layout: ready?.layout ?? DEFAULT_LAYOUT, config: ctx.config, state: ctx.state })
|
|
3589
3796
|
] }),
|
|
3590
3797
|
// Taken from the index until the frame reports, so the trial does not move the frame into a stage and reload it.
|
|
3591
3798
|
...viewport ? { stage: { size: { width: viewport.width, height: viewport.height } } } : {}
|
|
@@ -3598,7 +3805,7 @@ var NO_SETUP = {};
|
|
|
3598
3805
|
var pruned = (map, ids) => [...map.keys()].every((id) => ids.has(id)) ? map : new Map([...map].filter(([id]) => ids.has(id)));
|
|
3599
3806
|
var toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
3600
3807
|
function useStoryRegistry(index, options) {
|
|
3601
|
-
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;
|
|
3602
3809
|
const [readies, setReadies] = useState(() => /* @__PURE__ */ new Map());
|
|
3603
3810
|
const [loaded, setLoaded] = useState(() => /* @__PURE__ */ new Map());
|
|
3604
3811
|
const [faults, setFaults] = useState(() => /* @__PURE__ */ new Map());
|
|
@@ -3654,30 +3861,9 @@ function useStoryRegistry(index, options) {
|
|
|
3654
3861
|
});
|
|
3655
3862
|
};
|
|
3656
3863
|
if (isIndexId(entry.id)) {
|
|
3657
|
-
const
|
|
3658
|
-
const
|
|
3659
|
-
void
|
|
3660
|
-
const byFile = new Map(files.map((file, i) => [file, loadModule(mods[i] ?? {}, entry.title, current.parameters)]));
|
|
3661
|
-
const stories = entries.flatMap(
|
|
3662
|
-
(e) => byFile.get(e.file)?.find((s) => s.exportName === e.exportName) ?? []
|
|
3663
|
-
);
|
|
3664
|
-
for (const story of stories) await current.prepare?.(story);
|
|
3665
|
-
const result = /* @__PURE__ */ new Map();
|
|
3666
|
-
result.set(entry.id, {
|
|
3667
|
-
kind: "index",
|
|
3668
|
-
bundle: {
|
|
3669
|
-
title: entry.title,
|
|
3670
|
-
library: liveRef.current.libraries.of(entry),
|
|
3671
|
-
...entry.componentDescription === void 0 ? {} : { description: entry.componentDescription },
|
|
3672
|
-
...entry.tags === void 0 ? {} : { tags: entry.tags },
|
|
3673
|
-
...taggedIds(entries),
|
|
3674
|
-
stories,
|
|
3675
|
-
descriptions: Object.fromEntries(entries.flatMap((e) => e.description ? [[e.id, e.description]] : [])),
|
|
3676
|
-
render: mods.map(indexRenderOf).find((render) => render !== null) ?? null
|
|
3677
|
-
}
|
|
3678
|
-
});
|
|
3679
|
-
settle([entry.id], result);
|
|
3680
|
-
}).catch((error) => fail([entry.id], error));
|
|
3864
|
+
const { index: entries, libraries: libraries2 } = liveRef.current;
|
|
3865
|
+
const source = { index: entries, importOf, load: loadModule, parameters: current.parameters, prepare: current.prepare, libraries: libraries2 };
|
|
3866
|
+
void loadIndexBundle(entry.id, source).then((bundle) => settle([entry.id], /* @__PURE__ */ new Map([[entry.id, { kind: "index", bundle }]]))).catch((error) => fail([entry.id], error));
|
|
3681
3867
|
return;
|
|
3682
3868
|
}
|
|
3683
3869
|
const siblings = liveRef.current.index.filter((e) => e.file === entry.file && !isIndexId(e.id));
|
|
@@ -3742,14 +3928,14 @@ function useStoryRegistry(index, options) {
|
|
|
3742
3928
|
inDocument,
|
|
3743
3929
|
generation,
|
|
3744
3930
|
revision,
|
|
3745
|
-
instrument: !inDocument ? storyInstrument({ entry, ready, answers, frameUrl, onReady, globals }) : 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 } : {} })
|
|
3746
3932
|
};
|
|
3747
3933
|
built.set(entry.id, kept2);
|
|
3748
3934
|
return kept2.instrument;
|
|
3749
3935
|
});
|
|
3750
3936
|
const unchanged = list.length === previous.list.length && list.every((i, n) => i === previous.list[n]);
|
|
3751
3937
|
return { built, books, list: unchanged ? previous.list : list };
|
|
3752
|
-
}, [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]);
|
|
3753
3939
|
useLayoutEffect(() => {
|
|
3754
3940
|
committed.current = cache;
|
|
3755
3941
|
}, [cache]);
|
|
@@ -3788,6 +3974,19 @@ function useDocumentTitle(title) {
|
|
|
3788
3974
|
document.title = title;
|
|
3789
3975
|
}, [title]);
|
|
3790
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
|
+
}
|
|
3791
3990
|
function NoStories({ stories }) {
|
|
3792
3991
|
return /* @__PURE__ */ jsxs("div", { className: "fg-empty", children: [
|
|
3793
3992
|
/* @__PURE__ */ jsx("p", { className: "fg-empty__title", children: "No stories found" }),
|
|
@@ -3799,19 +3998,9 @@ function Workshop(props) {
|
|
|
3799
3998
|
const declarations = config?.globals ?? NO_DECLARATIONS;
|
|
3800
3999
|
const title = config?.title ?? DEFAULT_TITLE;
|
|
3801
4000
|
useDocumentTitle(title);
|
|
3802
|
-
const [
|
|
3803
|
-
const [
|
|
3804
|
-
const
|
|
3805
|
-
const framed = importers === void 0 || index.some((entry) => entry.isolate !== void 0);
|
|
3806
|
-
useEffect(() => {
|
|
3807
|
-
if (!framed) return;
|
|
3808
|
-
const next = createFramePool(frameUrl);
|
|
3809
|
-
setPool(next);
|
|
3810
|
-
return () => {
|
|
3811
|
-
next.dispose();
|
|
3812
|
-
setPool(null);
|
|
3813
|
-
};
|
|
3814
|
-
}, [frameUrl, framed]);
|
|
4001
|
+
const [frames2] = useState(createTrialFrames);
|
|
4002
|
+
const [hosts] = useState(createStoryHosts);
|
|
4003
|
+
const pool = useFramePool(frameUrl, importers === void 0 || index.some((entry) => entry.isolate !== void 0));
|
|
3815
4004
|
const entries = useMemo(() => [...index, ...indexEntries(index)], [index]);
|
|
3816
4005
|
const table = config?.libraries;
|
|
3817
4006
|
const libraries = useMemo(() => librariesFrom(table), [table]);
|
|
@@ -3819,6 +4008,7 @@ function Workshop(props) {
|
|
|
3819
4008
|
frameUrl,
|
|
3820
4009
|
globals: declarations,
|
|
3821
4010
|
libraries,
|
|
4011
|
+
hosts,
|
|
3822
4012
|
...importers ? { importers } : {},
|
|
3823
4013
|
...setup ? { setup } : {}
|
|
3824
4014
|
});
|
|
@@ -3848,18 +4038,19 @@ function Workshop(props) {
|
|
|
3848
4038
|
{ id: "fg-stories", region: "sidebar", render: (ctx) => /* @__PURE__ */ jsx(StoryTree, { ctx, index }) },
|
|
3849
4039
|
...showsCssVars ? cssVarsChrome2 : [],
|
|
3850
4040
|
A11Y_SECTION,
|
|
4041
|
+
issuesSection(index, props.issuesBase),
|
|
3851
4042
|
...keysChrome2,
|
|
3852
4043
|
...Object.keys(declarations).length > 0 ? [{ id: "fg-globals", region: "header", render: () => /* @__PURE__ */ jsx(GlobalsToolbar, { declarations }) }] : [],
|
|
3853
4044
|
{ id: "fg-info", region: "header", group: "story", item: infoCommand },
|
|
3854
4045
|
{ id: "fg-loupe", region: "overlay", render: () => /* @__PURE__ */ jsx(ForgeLoupe, { declarations, modeGlobal }) },
|
|
3855
4046
|
...config?.labChrome ?? []
|
|
3856
4047
|
],
|
|
3857
|
-
[index, infoCommand, declarations, showsCssVars, cssVarsChrome2, keysChrome2, config?.labChrome, modeGlobal]
|
|
4048
|
+
[index, props.issuesBase, infoCommand, declarations, showsCssVars, cssVarsChrome2, keysChrome2, config?.labChrome, modeGlobal]
|
|
3858
4049
|
);
|
|
3859
4050
|
const [menus, setMenus] = useState();
|
|
3860
4051
|
const first = index[0];
|
|
3861
4052
|
if (!first) return /* @__PURE__ */ jsx(NoStories, { stories });
|
|
3862
|
-
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:
|
|
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(
|
|
3863
4054
|
Lab,
|
|
3864
4055
|
{
|
|
3865
4056
|
title,
|
|
@@ -3871,6 +4062,7 @@ function Workshop(props) {
|
|
|
3871
4062
|
...storage ? { storage } : {},
|
|
3872
4063
|
labChrome,
|
|
3873
4064
|
addTrial: false,
|
|
4065
|
+
marks: false,
|
|
3874
4066
|
...menus ? { menus } : {},
|
|
3875
4067
|
...config?.controls ? { controls: config.controls } : {},
|
|
3876
4068
|
...config?.pages ? { pages: config.pages } : {},
|