@weasel-js/forge 1.5.0 → 1.5.2
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/dist/{FrameController-Ba3LaWfq.d.ts → FrameController-eZM3hlcE.d.ts} +74 -1
- package/dist/{chunk-O5OQZQRZ.js → chunk-2JDYOBWM.js} +5 -5
- package/dist/chunk-2JDYOBWM.js.map +1 -0
- package/dist/{chunk-KKF7ZX5E.js → chunk-5YHEO26U.js} +133 -23
- package/dist/chunk-5YHEO26U.js.map +1 -0
- package/dist/{chunk-UZCXH724.js → chunk-6SRQIHJQ.js} +3 -8
- package/dist/chunk-6SRQIHJQ.js.map +1 -0
- package/dist/{chunk-IOBG7GZQ.js → chunk-FTFR2YPM.js} +2 -2
- package/dist/chunk-FTFR2YPM.js.map +1 -0
- package/dist/chunk-TLO6UHF2.js +103 -0
- package/dist/chunk-TLO6UHF2.js.map +1 -0
- package/dist/{config-DdumOfyd.d.ts → config-CSVP6duU.d.ts} +6 -2
- package/dist/csf/preview-api.js +2 -2
- package/dist/frame/index.d.ts +5 -4
- package/dist/frame/index.js +5 -5
- package/dist/index.d.ts +97 -5
- package/dist/index.js.map +1 -1
- package/dist/shell/index.css +209 -12
- package/dist/shell/index.css.map +1 -1
- package/dist/shell/index.d.ts +62 -4
- package/dist/shell/index.js +712 -182
- package/dist/shell/index.js.map +1 -1
- package/dist/test/index.d.ts +2 -2
- package/dist/test/index.js +7 -7
- package/dist/test/index.js.map +1 -1
- package/dist/test/play.d.ts +25 -0
- package/dist/test/play.js +14 -0
- package/dist/test/play.js.map +1 -0
- package/dist/vite/index.js +72 -20
- package/dist/vite/index.js.map +1 -1
- package/package.json +14 -5
- package/dist/chunk-IOBG7GZQ.js.map +0 -1
- package/dist/chunk-KKF7ZX5E.js.map +0 -1
- package/dist/chunk-KKVTFMWV.js +0 -59
- package/dist/chunk-KKVTFMWV.js.map +0 -1
- package/dist/chunk-O5OQZQRZ.js.map +0 -1
- package/dist/chunk-UZCXH724.js.map +0 -1
package/dist/shell/index.js
CHANGED
|
@@ -1,106 +1,62 @@
|
|
|
1
|
-
import { schemaFromDescription, openChannel } from '../chunk-
|
|
2
|
-
import { stableStringify, PORT_HANDOFF, PROTOCOL_VERSION } from '../chunk-
|
|
1
|
+
import { schemaFromDescription, openChannel } from '../chunk-2JDYOBWM.js';
|
|
2
|
+
import { stableStringify, PORT_HANDOFF, PROTOCOL_VERSION } from '../chunk-FTFR2YPM.js';
|
|
3
|
+
import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, inferTokenType, ToggleBar as ToggleBar$1, Input, TokenPanel, refitScale } from '@weasel-js/labkit';
|
|
4
|
+
import { createContext, useContext, useCallback, useSyncExternalStore, useState, useRef, useEffect, useMemo, useLayoutEffect, useId } from 'react';
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
6
|
import { createRoot } from 'react-dom/client';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { f } from '@weasel-js/labkit/config';
|
|
7
|
+
import { InfoIcon, ToggleBar, Checkbox, Dialog } from '@weasel-js/ui';
|
|
8
|
+
import { TOKEN_MANIFEST, THEME_SOURCES } from '@weasel-js/theme';
|
|
9
|
+
import { scale } from '@weasel-js/theme/engine';
|
|
10
|
+
import { f, valueAtPath, isConfigBranch } from '@weasel-js/labkit/config';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
var HEX = /^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;
|
|
12
|
-
var RGB = /^rgba?\(\s*(\d{1,3})[\s,]+(\d{1,3})[\s,]+(\d{1,3})\b/i;
|
|
13
|
-
var COLOR_FUNCTION = /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|color-mix)\(.+\)$/i;
|
|
14
|
-
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
15
|
-
"transparent",
|
|
16
|
-
"currentcolor",
|
|
17
|
-
"black",
|
|
18
|
-
"white",
|
|
19
|
-
"gray",
|
|
20
|
-
"silver",
|
|
21
|
-
"red",
|
|
22
|
-
"orange",
|
|
23
|
-
"yellow",
|
|
24
|
-
"gold",
|
|
25
|
-
"lime",
|
|
26
|
-
"green",
|
|
27
|
-
"teal",
|
|
28
|
-
"cyan",
|
|
29
|
-
"blue",
|
|
30
|
-
"navy",
|
|
31
|
-
"purple",
|
|
32
|
-
"magenta",
|
|
33
|
-
"pink",
|
|
34
|
-
"brown"
|
|
35
|
-
]);
|
|
36
|
-
function browserSupports() {
|
|
37
|
-
return typeof CSS !== "undefined" && typeof CSS.supports === "function" ? (value) => CSS.supports("color", value) : null;
|
|
38
|
-
}
|
|
39
|
-
function parsesAsColor(value, supports = browserSupports()) {
|
|
40
|
-
const v = value.trim();
|
|
41
|
-
if (!v || v.includes("var(")) return false;
|
|
42
|
-
if (supports) return supports(v);
|
|
43
|
-
return HEX.test(v) || COLOR_FUNCTION.test(v) || KEYWORDS.has(v.toLowerCase());
|
|
44
|
-
}
|
|
45
|
-
function hexOf(v) {
|
|
46
|
-
if (HEX.test(v)) {
|
|
47
|
-
const digits = v.slice(1).toLowerCase();
|
|
48
|
-
return `#${digits.length <= 4 ? [...digits.slice(0, 3)].map((d) => d + d).join("") : digits.slice(0, 6)}`;
|
|
49
|
-
}
|
|
50
|
-
const rgb = RGB.exec(v);
|
|
51
|
-
return rgb ? `#${rgb.slice(1, 4).map((n) => Math.min(255, Number(n)).toString(16).padStart(2, "0")).join("")}` : null;
|
|
52
|
-
}
|
|
53
|
-
var paint;
|
|
54
|
-
function serialized(value) {
|
|
55
|
-
if (paint === void 0) {
|
|
56
|
-
try {
|
|
57
|
-
paint = document.createElement("canvas").getContext("2d");
|
|
58
|
-
} catch {
|
|
59
|
-
paint = null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (!paint) return null;
|
|
63
|
-
paint.fillStyle = "#000000";
|
|
64
|
-
paint.fillStyle = value;
|
|
65
|
-
const onBlack = String(paint.fillStyle);
|
|
66
|
-
paint.fillStyle = "#ffffff";
|
|
67
|
-
paint.fillStyle = value;
|
|
68
|
-
return onBlack === String(paint.fillStyle) ? onBlack : null;
|
|
69
|
-
}
|
|
70
|
-
function toHex(value) {
|
|
71
|
-
const v = value.trim();
|
|
72
|
-
const direct = hexOf(v);
|
|
73
|
-
if (direct) return direct;
|
|
74
|
-
const browser = typeof document === "undefined" ? null : serialized(v);
|
|
75
|
-
return browser && browser !== v ? hexOf(browser) : null;
|
|
76
|
-
}
|
|
77
|
-
var NONE = {};
|
|
78
|
-
function useCssOverrides() {
|
|
79
|
-
return usePersistedState("fg-css-overrides", NONE, { scope: "trial" });
|
|
80
|
-
}
|
|
81
|
-
var NO_FRAME = { send: null, vars: [] };
|
|
12
|
+
var NO_FRAME = { send: null, vars: [], audit: null, a11y: null, capture: null, size: null };
|
|
82
13
|
function createTrialFrames() {
|
|
83
14
|
const frames = /* @__PURE__ */ new Map();
|
|
15
|
+
const hosts = /* @__PURE__ */ new Map();
|
|
84
16
|
const listeners = /* @__PURE__ */ new Set();
|
|
85
17
|
const changed = () => {
|
|
86
18
|
for (const listener of [...listeners]) listener();
|
|
87
19
|
};
|
|
20
|
+
const patch = (trialId, next) => {
|
|
21
|
+
const frame = frames.get(trialId);
|
|
22
|
+
if (!frame) return;
|
|
23
|
+
frames.set(trialId, { ...frame, ...next });
|
|
24
|
+
changed();
|
|
25
|
+
};
|
|
88
26
|
return {
|
|
89
|
-
connect(trialId,
|
|
90
|
-
frames.
|
|
27
|
+
connect(trialId, calls) {
|
|
28
|
+
const held = frames.get(trialId);
|
|
29
|
+
frames.set(trialId, {
|
|
30
|
+
send: calls.send,
|
|
31
|
+
vars: [],
|
|
32
|
+
audit: calls.audit,
|
|
33
|
+
a11y: null,
|
|
34
|
+
capture: calls.capture,
|
|
35
|
+
size: held?.size ?? null
|
|
36
|
+
});
|
|
91
37
|
changed();
|
|
92
38
|
return () => {
|
|
93
|
-
if (frames.get(trialId)?.send !== send) return;
|
|
39
|
+
if (frames.get(trialId)?.send !== calls.send) return;
|
|
94
40
|
frames.delete(trialId);
|
|
95
41
|
changed();
|
|
96
42
|
};
|
|
97
43
|
},
|
|
98
|
-
report(trialId, vars) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
frames.
|
|
44
|
+
report: (trialId, vars) => patch(trialId, { vars }),
|
|
45
|
+
reportA11y: (trialId, a11y) => patch(trialId, { a11y }),
|
|
46
|
+
reportSize(trialId, size) {
|
|
47
|
+
const frame = frames.get(trialId) ?? NO_FRAME;
|
|
48
|
+
if (frame.size?.width === size.width && frame.size?.height === size.height) return;
|
|
49
|
+
frames.set(trialId, { ...frame, size });
|
|
102
50
|
changed();
|
|
103
51
|
},
|
|
52
|
+
hostRef(trialId) {
|
|
53
|
+
let ref = hosts.get(trialId);
|
|
54
|
+
if (!ref) {
|
|
55
|
+
ref = { current: null };
|
|
56
|
+
hosts.set(trialId, ref);
|
|
57
|
+
}
|
|
58
|
+
return ref;
|
|
59
|
+
},
|
|
104
60
|
get: (trialId) => frames.get(trialId) ?? NO_FRAME,
|
|
105
61
|
subscribe(listener) {
|
|
106
62
|
listeners.add(listener);
|
|
@@ -116,35 +72,164 @@ function useTrialFrame(trialId) {
|
|
|
116
72
|
const read = () => frames?.get(trialId) ?? NO_FRAME;
|
|
117
73
|
return useSyncExternalStore(subscribe, read, read);
|
|
118
74
|
}
|
|
75
|
+
function Finding({ finding }) {
|
|
76
|
+
return /* @__PURE__ */ jsxs("li", { className: "fg-a11y__finding", children: [
|
|
77
|
+
/* @__PURE__ */ jsxs("p", { className: "fg-a11y__help", children: [
|
|
78
|
+
/* @__PURE__ */ jsx("a", { href: finding.helpUrl, target: "_blank", rel: "noreferrer", children: finding.help }),
|
|
79
|
+
finding.impact ? /* @__PURE__ */ jsx("span", { className: "fg-a11y__impact", "data-impact": finding.impact, children: finding.impact }) : null
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsx("ul", { className: "fg-a11y__nodes", children: finding.nodes.map((node) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("code", { children: node.target.join(" ") }) }, `${finding.id}:${node.target.join(",")}`)) })
|
|
82
|
+
] });
|
|
83
|
+
}
|
|
84
|
+
function Findings({ title, findings }) {
|
|
85
|
+
if (findings.length === 0) return null;
|
|
86
|
+
return /* @__PURE__ */ jsxs("section", { className: "fg-a11y__group", children: [
|
|
87
|
+
/* @__PURE__ */ jsxs("h3", { className: "fg-a11y__group-title", children: [
|
|
88
|
+
title,
|
|
89
|
+
" (",
|
|
90
|
+
findings.length,
|
|
91
|
+
")"
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsx("ul", { className: "fg-a11y__findings", children: findings.map((finding) => /* @__PURE__ */ jsx(Finding, { finding }, finding.id)) })
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
function A11yPanel() {
|
|
97
|
+
const trialId = useTrialId();
|
|
98
|
+
const frame = useTrialFrame(trialId);
|
|
99
|
+
const [running, setRunning] = useState(false);
|
|
100
|
+
const audit = frame.audit;
|
|
101
|
+
const ran = useRef(null);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (!audit || ran.current === audit) return;
|
|
104
|
+
ran.current = audit;
|
|
105
|
+
setRunning(true);
|
|
106
|
+
void audit().finally(() => setRunning(false));
|
|
107
|
+
}, [audit]);
|
|
108
|
+
const rerun = () => {
|
|
109
|
+
if (!audit) return;
|
|
110
|
+
setRunning(true);
|
|
111
|
+
void audit().finally(() => setRunning(false));
|
|
112
|
+
};
|
|
113
|
+
const outcome = frame.a11y;
|
|
114
|
+
return /* @__PURE__ */ jsxs("div", { className: "fg-a11y", children: [
|
|
115
|
+
/* @__PURE__ */ jsx("div", { className: "fg-a11y__controls", children: /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: rerun, disabled: !audit || running, children: running ? "Checking\u2026" : "Re-check" }) }),
|
|
116
|
+
!audit ? /* @__PURE__ */ jsx("p", { className: "fg-a11y__note", children: "No frame is connected." }) : null,
|
|
117
|
+
audit && !outcome ? /* @__PURE__ */ jsx("p", { className: "fg-a11y__note", children: running ? "Running axe\u2026" : "Not checked yet." }) : null,
|
|
118
|
+
outcome && !outcome.ok ? /* @__PURE__ */ jsxs("p", { className: "fg-a11y__note", role: "alert", children: [
|
|
119
|
+
"axe could not run: ",
|
|
120
|
+
outcome.message
|
|
121
|
+
] }) : null,
|
|
122
|
+
outcome?.ok ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
|
+
/* @__PURE__ */ jsxs("p", { className: "fg-a11y__tally", children: [
|
|
124
|
+
outcome.report.violations.length,
|
|
125
|
+
" violations \xB7 ",
|
|
126
|
+
outcome.report.incomplete.length,
|
|
127
|
+
" to review \xB7",
|
|
128
|
+
" ",
|
|
129
|
+
outcome.report.passes,
|
|
130
|
+
" passes"
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsx(Findings, { title: "Violations", findings: outcome.report.violations }),
|
|
133
|
+
/* @__PURE__ */ jsx(Findings, { title: "Needs review", findings: outcome.report.incomplete })
|
|
134
|
+
] }) : null
|
|
135
|
+
] });
|
|
136
|
+
}
|
|
137
|
+
function FocusedA11y() {
|
|
138
|
+
const { focusedTrialId, trials, instruments } = useLabContext();
|
|
139
|
+
const record = trials.find((trial) => trial.id === focusedTrialId);
|
|
140
|
+
if (!record) return null;
|
|
141
|
+
const instrument = instruments.find((i) => i.name === record.instrumentName);
|
|
142
|
+
return /* @__PURE__ */ jsxs("section", { className: "fg-a11y-focus", "aria-label": "Accessibility", children: [
|
|
143
|
+
/* @__PURE__ */ jsx("p", { className: "fg-a11y__trial", children: record.title ?? instrument?.title ?? record.instrumentName }),
|
|
144
|
+
/* @__PURE__ */ jsx(TrialIdContext.Provider, { value: record.id, children: /* @__PURE__ */ jsx(A11yPanel, {}) })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
var A11Y_SECTION = {
|
|
148
|
+
id: "fg-a11y",
|
|
149
|
+
region: "aside",
|
|
150
|
+
item: { title: "Accessibility", body: /* @__PURE__ */ jsx(FocusedA11y, {}) }
|
|
151
|
+
};
|
|
152
|
+
var NONE = {};
|
|
153
|
+
function useCssOverrides() {
|
|
154
|
+
return usePersistedState("fg-css-overrides", NONE, { scope: "trial" });
|
|
155
|
+
}
|
|
119
156
|
var TABS = [
|
|
120
157
|
{ value: "theme", label: "Theme" },
|
|
121
158
|
{ value: "story", label: "Story" }
|
|
122
159
|
];
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
160
|
+
var MANIFEST = new Map(TOKEN_MANIFEST.map((token) => [token.name, token]));
|
|
161
|
+
var literal = (v) => typeof v === "number";
|
|
162
|
+
var SCALE_SOURCES = Object.entries(THEME_SOURCES.weasel?.scales ?? {}).flatMap(([name, def]) => {
|
|
163
|
+
if (!Array.isArray(def.steps)) return [];
|
|
164
|
+
const steps = def.steps;
|
|
165
|
+
const tokens = steps.map((step) => `--wzl-${name}-${step}`);
|
|
166
|
+
const group = MANIFEST.get(tokens[0] ?? "")?.group;
|
|
167
|
+
if (!group) return [];
|
|
168
|
+
const factors = Array.isArray(def.factors) && def.factors.every(literal) ? def.factors : null;
|
|
169
|
+
const rule = def.factors !== void 0 ? factors ? { kind: "factors", factors } : "factors" : def.ratio !== void 0 ? literal(def.ratio) ? { kind: "ratio", ratio: def.ratio } : "ratio" : literal(def.step) ? { kind: "step", step: def.step } : "step";
|
|
170
|
+
return [{ name, group, steps, tokens, rule }];
|
|
134
171
|
});
|
|
172
|
+
function standingScale(source, amounts) {
|
|
173
|
+
const seed = { tokens: source.tokens, base: amounts[0] ?? 0, rule: { kind: "step", step: 0 } };
|
|
174
|
+
if (typeof source.rule === "string") return refitScale(seed, source.rule, amounts);
|
|
175
|
+
const { rule } = source;
|
|
176
|
+
if (rule.kind !== "factors") return { ...seed, rule };
|
|
177
|
+
let at = 0;
|
|
178
|
+
rule.factors.forEach((f3, i) => {
|
|
179
|
+
if (Math.abs(f3 - 1) < Math.abs((rule.factors[at] ?? 1) - 1)) at = i;
|
|
180
|
+
});
|
|
181
|
+
const base = (amounts[at] ?? 0) / (rule.factors[at] ?? 1);
|
|
182
|
+
return { ...seed, base: Math.round(base * 100) / 100, rule };
|
|
183
|
+
}
|
|
184
|
+
function groupOf(name) {
|
|
185
|
+
const segments = name.replace(/^--/, "").split("-");
|
|
186
|
+
return segments.length > 1 ? segments.slice(0, -1).join("-") : name.replace(/^--/, "");
|
|
187
|
+
}
|
|
135
188
|
function CssVarsPanel() {
|
|
136
189
|
const trialId = useTrialId();
|
|
137
190
|
const frame = useTrialFrame(trialId);
|
|
138
191
|
const [overrides, setOverrides] = useCssOverrides();
|
|
192
|
+
const [folds, setFolds] = usePersistedState(
|
|
193
|
+
"fg-css-vars-folds",
|
|
194
|
+
{},
|
|
195
|
+
{ scope: "lab" }
|
|
196
|
+
);
|
|
197
|
+
const [edited, setEdited] = usePersistedState("fg-css-vars-scales", {}, { scope: "lab" });
|
|
139
198
|
const [tab, setTab] = useState("theme");
|
|
140
199
|
const [filter, setFilter] = useState("");
|
|
141
|
-
const
|
|
142
|
-
if (tab === "story")
|
|
200
|
+
const tokens = useMemo(() => {
|
|
201
|
+
if (tab === "story") {
|
|
202
|
+
return frame.vars.map(({ name, value }) => {
|
|
203
|
+
const known = MANIFEST.get(name);
|
|
204
|
+
return { name, type: known?.type ?? inferTokenType(value), group: known?.group ?? groupOf(name), value };
|
|
205
|
+
});
|
|
206
|
+
}
|
|
143
207
|
const reported = new Map(frame.vars.map((v) => [v.name, v.value]));
|
|
144
|
-
return TOKEN_MANIFEST.map((token) => ({
|
|
208
|
+
return TOKEN_MANIFEST.map((token) => ({
|
|
209
|
+
name: token.name,
|
|
210
|
+
type: token.type,
|
|
211
|
+
group: token.group,
|
|
212
|
+
value: reported.get(token.name) ?? token.defaultValue,
|
|
213
|
+
description: token.description
|
|
214
|
+
}));
|
|
145
215
|
}, [tab, frame.vars]);
|
|
146
216
|
const query = filter.trim().toLowerCase();
|
|
147
|
-
const shown =
|
|
217
|
+
const shown = tokens.map((token) => {
|
|
218
|
+
const override = overrides[token.name];
|
|
219
|
+
return override === void 0 ? token : { ...token, value: override, overridden: true };
|
|
220
|
+
}).filter((token) => !query || token.name.toLowerCase().includes(query) || token.value.toLowerCase().includes(query));
|
|
221
|
+
const scales = useMemo(() => {
|
|
222
|
+
if (tab !== "theme") return void 0;
|
|
223
|
+
const values = new Map(shown.map((t) => [t.name, t.value]));
|
|
224
|
+
const out = {};
|
|
225
|
+
for (const source of SCALE_SOURCES) {
|
|
226
|
+
const amounts = source.tokens.map((name) => Number.parseFloat(values.get(name) ?? ""));
|
|
227
|
+
if (amounts.some(Number.isNaN)) continue;
|
|
228
|
+
const own = source.tokens.some((name) => overrides[name] !== void 0) ? edited[source.group] : void 0;
|
|
229
|
+
out[source.group] = own ?? standingScale(source, amounts);
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}, [tab, shown, overrides, edited]);
|
|
148
233
|
const send = frame.send;
|
|
149
234
|
const write = useCallback(
|
|
150
235
|
(name, value) => {
|
|
@@ -156,37 +241,272 @@ function CssVarsPanel() {
|
|
|
156
241
|
},
|
|
157
242
|
[setOverrides, send]
|
|
158
243
|
);
|
|
244
|
+
const onScaleChange = useCallback(
|
|
245
|
+
(group, next) => {
|
|
246
|
+
const source = SCALE_SOURCES.find((sc) => sc.group === group);
|
|
247
|
+
if (!source) return;
|
|
248
|
+
setEdited((prev) => ({ ...prev, [group]: next }));
|
|
249
|
+
const { rule } = next;
|
|
250
|
+
const values = scale(source.steps, {
|
|
251
|
+
base: next.base,
|
|
252
|
+
...rule.kind === "factors" ? { factors: rule.factors } : rule.kind === "ratio" ? { ratio: rule.ratio } : { step: rule.step }
|
|
253
|
+
});
|
|
254
|
+
source.steps.forEach((step, i) => {
|
|
255
|
+
const value = values[step];
|
|
256
|
+
const name = source.tokens[i];
|
|
257
|
+
if (value !== void 0 && name !== void 0) write(name, value);
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
[setEdited, write]
|
|
261
|
+
);
|
|
262
|
+
const onCollapsedChange = useCallback(
|
|
263
|
+
(category, collapsed) => setFolds((prev) => ({ ...prev, [category]: collapsed })),
|
|
264
|
+
[setFolds]
|
|
265
|
+
);
|
|
159
266
|
return /* @__PURE__ */ jsxs("div", { className: "fg-css-vars", children: [
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
267
|
+
/* @__PURE__ */ jsxs("div", { className: "fg-css-vars__controls", children: [
|
|
268
|
+
/* @__PURE__ */ jsx(
|
|
269
|
+
ToggleBar$1,
|
|
270
|
+
{
|
|
271
|
+
ariaLabel: "Variables",
|
|
272
|
+
variant: "flat",
|
|
273
|
+
items: TABS,
|
|
274
|
+
value: tab,
|
|
275
|
+
onChange: (next) => {
|
|
276
|
+
if (next) setTab(next);
|
|
277
|
+
}
|
|
169
278
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
shown.length === 0 ? /* @__PURE__ */ jsx("p", { className: "fg-css-vars__empty", children:
|
|
174
|
-
|
|
279
|
+
),
|
|
280
|
+
/* @__PURE__ */ jsx(Input, { "aria-label": "Filter", placeholder: "Filter by name or value", value: filter, onChange: setFilter })
|
|
281
|
+
] }),
|
|
282
|
+
shown.length === 0 ? /* @__PURE__ */ jsx("p", { className: "fg-css-vars__empty", children: tokens.length === 0 ? "The story\u2019s frame has reported no variables." : "No variables match." }) : /* @__PURE__ */ jsx(
|
|
283
|
+
TokenPanel,
|
|
175
284
|
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
285
|
+
tokens: shown,
|
|
286
|
+
onChange: write,
|
|
287
|
+
scales,
|
|
288
|
+
onScaleChange,
|
|
289
|
+
collapsed: folds,
|
|
290
|
+
onCollapsedChange
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
] });
|
|
294
|
+
}
|
|
295
|
+
function FocusedCssVars() {
|
|
296
|
+
const { focusedTrialId, trials, instruments } = useLabContext();
|
|
297
|
+
const record = trials.find((trial) => trial.id === focusedTrialId);
|
|
298
|
+
if (!record) return null;
|
|
299
|
+
const instrument = instruments.find((i) => i.name === record.instrumentName);
|
|
300
|
+
return /* @__PURE__ */ jsxs("section", { className: "fg-css-vars-focus", "aria-label": "CSS Vars", children: [
|
|
301
|
+
/* @__PURE__ */ jsx("p", { className: "fg-css-vars__trial", children: record.title ?? instrument?.title ?? record.instrumentName }),
|
|
302
|
+
/* @__PURE__ */ jsx(TrialIdContext.Provider, { value: record.id, children: /* @__PURE__ */ jsx(CssVarsPanel, {}) })
|
|
183
303
|
] });
|
|
184
304
|
}
|
|
185
305
|
var CSS_VARS_SECTION = {
|
|
186
306
|
id: "fg-css-vars",
|
|
187
|
-
region: "
|
|
188
|
-
item: { title: "CSS Vars", body: /* @__PURE__ */ jsx(
|
|
307
|
+
region: "aside",
|
|
308
|
+
item: { title: "CSS Vars", body: /* @__PURE__ */ jsx(FocusedCssVars, {}) }
|
|
189
309
|
};
|
|
310
|
+
|
|
311
|
+
// src/shell/tree/buildComponents.ts
|
|
312
|
+
var LIBRARIES = [
|
|
313
|
+
["/packages/ui/", "weasel-ui"],
|
|
314
|
+
["/packages/labkit/", "labkit"],
|
|
315
|
+
["/packages/forge/", "forge"],
|
|
316
|
+
["/apps/draw/", "draw"],
|
|
317
|
+
["/apps/forge/", "forge"]
|
|
318
|
+
];
|
|
319
|
+
function libraryOf(entry) {
|
|
320
|
+
const file = entry.file.replaceAll("\\", "/");
|
|
321
|
+
for (const [marker, label] of LIBRARIES) {
|
|
322
|
+
if (file.includes(marker)) return label;
|
|
323
|
+
}
|
|
324
|
+
return entry.title.split("/")[0];
|
|
325
|
+
}
|
|
326
|
+
function buildComponents(index) {
|
|
327
|
+
const byTitle = /* @__PURE__ */ new Map();
|
|
328
|
+
for (const entry of index) {
|
|
329
|
+
let row = byTitle.get(entry.title);
|
|
330
|
+
if (!row) {
|
|
331
|
+
const segments = entry.title.split("/");
|
|
332
|
+
row = {
|
|
333
|
+
path: entry.title,
|
|
334
|
+
label: segments[segments.length - 1],
|
|
335
|
+
library: libraryOf(entry),
|
|
336
|
+
entries: []
|
|
337
|
+
};
|
|
338
|
+
byTitle.set(entry.title, row);
|
|
339
|
+
}
|
|
340
|
+
row.entries.push(entry);
|
|
341
|
+
}
|
|
342
|
+
const rows = [...byTitle.values()];
|
|
343
|
+
disambiguate(rows);
|
|
344
|
+
return rows.sort(
|
|
345
|
+
(a, b) => a.label.localeCompare(b.label) || a.library.localeCompare(b.library) || a.path.localeCompare(b.path)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
function disambiguate(rows) {
|
|
349
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
350
|
+
for (const row of rows) {
|
|
351
|
+
const group = byLabel.get(row.label);
|
|
352
|
+
if (group) group.push(row);
|
|
353
|
+
else byLabel.set(row.label, [row]);
|
|
354
|
+
}
|
|
355
|
+
for (const group of byLabel.values()) {
|
|
356
|
+
if (group.length < 2) continue;
|
|
357
|
+
for (const row of group) {
|
|
358
|
+
const segments = row.path.split("/");
|
|
359
|
+
for (let take = 2; take <= segments.length; take++) {
|
|
360
|
+
row.label = segments.slice(-take).join("/");
|
|
361
|
+
if (group.every((other) => other === row || !other.path.endsWith(`/${row.label}`))) break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function filterComponents(rows, query) {
|
|
367
|
+
const needle = query.trim().toLowerCase();
|
|
368
|
+
if (!needle) return [...rows];
|
|
369
|
+
const out = [];
|
|
370
|
+
for (const row of rows) {
|
|
371
|
+
if (`${row.path}/${row.library}`.toLowerCase().includes(needle)) {
|
|
372
|
+
out.push(row);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const entries = row.entries.filter((entry) => entry.name.toLowerCase().includes(needle));
|
|
376
|
+
if (entries.length > 0) out.push({ ...row, entries });
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
}
|
|
380
|
+
function componentNodes(rows) {
|
|
381
|
+
return rows.map(
|
|
382
|
+
(row) => row.entries.length === 1 ? { kind: "story", entry: row.entries[0], tag: row.library, label: row.label } : {
|
|
383
|
+
kind: "folder",
|
|
384
|
+
label: row.label,
|
|
385
|
+
path: row.path,
|
|
386
|
+
tag: row.library,
|
|
387
|
+
children: row.entries.map((entry) => ({ kind: "story", entry }))
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
function librariesIn(index) {
|
|
392
|
+
return [...new Set(index.map(libraryOf))].sort((a, b) => a.localeCompare(b));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/shell/info/dossier.ts
|
|
396
|
+
var GLOBALS = "$globals";
|
|
397
|
+
function leaves(shape, prefix = "") {
|
|
398
|
+
const out = [];
|
|
399
|
+
for (const [key, entry] of Object.entries(shape)) {
|
|
400
|
+
if (prefix === "" && key === GLOBALS) continue;
|
|
401
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
402
|
+
if (isConfigBranch(entry)) out.push(...leaves(entry.children, path));
|
|
403
|
+
else if (!entry.annotations.hidden) out.push({ path, node: entry });
|
|
404
|
+
}
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
function storyDossier(input) {
|
|
408
|
+
const { entry, instrument, config, ready = true } = input;
|
|
409
|
+
const schema = ready ? instrument?.config : void 0;
|
|
410
|
+
const args = schema ? leaves(schema.nodes).map(({ path, node }) => {
|
|
411
|
+
const value = valueAtPath(config, path);
|
|
412
|
+
return {
|
|
413
|
+
path,
|
|
414
|
+
label: node.annotations.name ?? path.split(".").pop() ?? path,
|
|
415
|
+
kind: node.kind,
|
|
416
|
+
default: node.default,
|
|
417
|
+
value: value === void 0 ? node.default : value,
|
|
418
|
+
...node.annotations.description === void 0 ? {} : { description: node.annotations.description }
|
|
419
|
+
};
|
|
420
|
+
}) : [];
|
|
421
|
+
return {
|
|
422
|
+
id: entry.id,
|
|
423
|
+
title: entry.title,
|
|
424
|
+
name: entry.name,
|
|
425
|
+
exportName: entry.exportName,
|
|
426
|
+
file: entry.file,
|
|
427
|
+
library: libraryOf(entry),
|
|
428
|
+
...entry.componentName === void 0 ? {} : { componentName: entry.componentName },
|
|
429
|
+
...entry.description === void 0 ? {} : { description: entry.description },
|
|
430
|
+
...entry.componentDescription === void 0 ? {} : { componentDescription: entry.componentDescription },
|
|
431
|
+
args,
|
|
432
|
+
argsPending: !schema
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function printed(value) {
|
|
436
|
+
if (value === void 0 || value === null) return "\u2014";
|
|
437
|
+
if (typeof value === "string") return value === "" ? '""' : value;
|
|
438
|
+
if (typeof value === "function") return "\u0192";
|
|
439
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
440
|
+
return String(value);
|
|
441
|
+
}
|
|
442
|
+
function Row({ label, children }) {
|
|
443
|
+
return /* @__PURE__ */ jsxs("div", { className: "fg-info__row", children: [
|
|
444
|
+
/* @__PURE__ */ jsx("dt", { className: "fg-info__key", children: label }),
|
|
445
|
+
/* @__PURE__ */ jsx("dd", { className: "fg-info__value", children })
|
|
446
|
+
] });
|
|
447
|
+
}
|
|
448
|
+
function ArgRows({ args }) {
|
|
449
|
+
return /* @__PURE__ */ jsxs("table", { className: "fg-info__args", children: [
|
|
450
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
451
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: "Name" }),
|
|
452
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: "Kind" }),
|
|
453
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: "Value" }),
|
|
454
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: "Default" })
|
|
455
|
+
] }) }),
|
|
456
|
+
/* @__PURE__ */ jsx("tbody", { children: args.map((arg) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
457
|
+
/* @__PURE__ */ jsxs("th", { scope: "row", children: [
|
|
458
|
+
/* @__PURE__ */ jsx("span", { className: "fg-info__arg-name", children: arg.label }),
|
|
459
|
+
arg.description ? /* @__PURE__ */ jsx("span", { className: "fg-info__arg-doc", children: arg.description }) : null
|
|
460
|
+
] }),
|
|
461
|
+
/* @__PURE__ */ jsx("td", { className: "fg-info__arg-kind", children: arg.kind ?? "\u2014" }),
|
|
462
|
+
/* @__PURE__ */ jsx("td", { className: "fg-info__value-cell", children: printed(arg.value) }),
|
|
463
|
+
/* @__PURE__ */ jsx("td", { className: "fg-info__value-cell", children: printed(arg.default) })
|
|
464
|
+
] }, arg.path)) })
|
|
465
|
+
] });
|
|
466
|
+
}
|
|
467
|
+
function StoryDossier({ dossier }) {
|
|
468
|
+
return /* @__PURE__ */ jsxs("div", { className: "fg-info", children: [
|
|
469
|
+
dossier.description || dossier.componentDescription ? /* @__PURE__ */ jsxs("section", { className: "fg-info__section", "aria-label": "Docs", children: [
|
|
470
|
+
dossier.description ? /* @__PURE__ */ jsx("p", { className: "fg-info__doc", children: dossier.description }) : null,
|
|
471
|
+
dossier.componentDescription ? /* @__PURE__ */ jsx("p", { className: "fg-info__doc fg-info__doc--component", children: dossier.componentDescription }) : null
|
|
472
|
+
] }) : null,
|
|
473
|
+
/* @__PURE__ */ jsx("section", { className: "fg-info__section", "aria-label": "Identity", children: /* @__PURE__ */ jsxs("dl", { className: "fg-info__rows", children: [
|
|
474
|
+
/* @__PURE__ */ jsx(Row, { label: "Story", children: dossier.name }),
|
|
475
|
+
/* @__PURE__ */ jsx(Row, { label: "Component", children: dossier.componentName ?? dossier.title.split("/").pop() }),
|
|
476
|
+
/* @__PURE__ */ jsx(Row, { label: "Library", children: dossier.library }),
|
|
477
|
+
/* @__PURE__ */ jsx(Row, { label: "Title", children: dossier.title }),
|
|
478
|
+
/* @__PURE__ */ jsx(Row, { label: "Export", children: /* @__PURE__ */ jsx("code", { children: dossier.exportName }) }),
|
|
479
|
+
/* @__PURE__ */ jsx(Row, { label: "Id", children: /* @__PURE__ */ jsx("code", { children: dossier.id }) }),
|
|
480
|
+
/* @__PURE__ */ jsx(Row, { label: "File", children: /* @__PURE__ */ jsx("code", { className: "fg-info__path", children: dossier.file }) })
|
|
481
|
+
] }) }),
|
|
482
|
+
/* @__PURE__ */ jsxs("section", { className: "fg-info__section", "aria-label": "Args", children: [
|
|
483
|
+
/* @__PURE__ */ jsx("h3", { className: "fg-info__heading", children: "Args" }),
|
|
484
|
+
dossier.argsPending ? /* @__PURE__ */ jsx("p", { className: "fg-info__empty", children: "Open this story to read its args." }) : dossier.args.length === 0 ? /* @__PURE__ */ jsx("p", { className: "fg-info__empty", children: "This story takes no args." }) : /* @__PURE__ */ jsx(ArgRows, { args: dossier.args })
|
|
485
|
+
] })
|
|
486
|
+
] });
|
|
487
|
+
}
|
|
488
|
+
function StoryInfoDialog({ index, isReady, isOpen, onOpenChange }) {
|
|
489
|
+
const { focusedTrialId, trials, instruments } = useLabContext();
|
|
490
|
+
const trial = trials.find((record) => record.id === focusedTrialId);
|
|
491
|
+
const entry = trial ? index.find((candidate) => candidate.id === trial.instrumentName) : void 0;
|
|
492
|
+
if (!entry) return null;
|
|
493
|
+
const dossier = storyDossier({
|
|
494
|
+
entry,
|
|
495
|
+
instrument: instruments.find((candidate) => candidate.name === entry.id),
|
|
496
|
+
config: trial?.config,
|
|
497
|
+
ready: isReady(entry.id)
|
|
498
|
+
});
|
|
499
|
+
return /* @__PURE__ */ jsx(
|
|
500
|
+
Dialog,
|
|
501
|
+
{
|
|
502
|
+
isOpen,
|
|
503
|
+
onOpenChange,
|
|
504
|
+
title: `${dossier.title} / ${dossier.name}`,
|
|
505
|
+
className: "fg-info-dialog",
|
|
506
|
+
children: /* @__PURE__ */ jsx(StoryDossier, { dossier })
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
}
|
|
190
510
|
var GLOBALS_KEY = "$globals";
|
|
191
511
|
var FOLLOW_LAB = "lab";
|
|
192
512
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -217,13 +537,13 @@ function storyConfig(config, previous) {
|
|
|
217
537
|
function withGlobals(schema, declarations) {
|
|
218
538
|
const entries = Object.entries(declarations);
|
|
219
539
|
if (entries.length === 0) return schema;
|
|
220
|
-
const
|
|
540
|
+
const leaves2 = Object.fromEntries(
|
|
221
541
|
entries.map(([key, declaration]) => [
|
|
222
542
|
key,
|
|
223
543
|
f.enum(FOLLOW_LAB, [{ value: FOLLOW_LAB, label: "Lab" }, ...declaration.options]).label(declaration.label)
|
|
224
544
|
])
|
|
225
545
|
);
|
|
226
|
-
const group = f.group(
|
|
546
|
+
const group = f.group(leaves2).label("").section("Globals", { layout: "inline", pack: "pairs" });
|
|
227
547
|
return f.schema({ ...schema.nodes, [GLOBALS_KEY]: group });
|
|
228
548
|
}
|
|
229
549
|
var RECORD = "fg-globals";
|
|
@@ -239,13 +559,13 @@ function useLabGlobals(declarations) {
|
|
|
239
559
|
function GlobalsToolbar({ declarations }) {
|
|
240
560
|
const [values, set] = useLabGlobals(declarations);
|
|
241
561
|
return /* @__PURE__ */ jsx("div", { className: "fg-globals", role: "toolbar", "aria-label": "Globals", children: Object.entries(declarations).map(([key, declaration]) => /* @__PURE__ */ jsx(
|
|
242
|
-
|
|
562
|
+
Select,
|
|
243
563
|
{
|
|
244
|
-
className: "fg-globals__select",
|
|
245
564
|
"aria-label": declaration.label,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
565
|
+
width: "fit",
|
|
566
|
+
selectedKey: String(values[key]),
|
|
567
|
+
onSelectionChange: (value) => set(key, value),
|
|
568
|
+
options: declaration.options
|
|
249
569
|
},
|
|
250
570
|
key
|
|
251
571
|
)) });
|
|
@@ -341,6 +661,10 @@ function filterTree(nodes, query) {
|
|
|
341
661
|
});
|
|
342
662
|
return walk(nodes);
|
|
343
663
|
}
|
|
664
|
+
var VIEWS = [
|
|
665
|
+
{ value: "tree", label: "Tree" },
|
|
666
|
+
{ value: "components", label: "Components" }
|
|
667
|
+
];
|
|
344
668
|
var keyOf = (node) => node.kind === "folder" ? `folder:${node.path}` : `story:${node.entry.id}`;
|
|
345
669
|
function ancestorsOf(entry) {
|
|
346
670
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -357,14 +681,22 @@ function StoryTree({ ctx, index }) {
|
|
|
357
681
|
const [route, setRoute] = useRoute();
|
|
358
682
|
const [query, setQuery] = useState("");
|
|
359
683
|
const [folds, setFolds] = usePersistedState("fg-tree-open", {}, { scope: "lab" });
|
|
684
|
+
const [view, setView] = usePersistedState("fg-tree-view", "tree", { scope: "lab" });
|
|
685
|
+
const [hidden, setHidden] = usePersistedState("fg-tree-libraries", {}, { scope: "lab" });
|
|
360
686
|
const [focusKey, setFocusKey] = useState(null);
|
|
361
687
|
const items = useRef(/* @__PURE__ */ new Map());
|
|
362
|
-
const
|
|
363
|
-
const
|
|
364
|
-
const
|
|
688
|
+
const libraries = useMemo(() => librariesIn(index), [index]);
|
|
689
|
+
const kept = useMemo(() => index.filter((entry) => !hidden[libraryOf(entry)]), [index, hidden]);
|
|
690
|
+
const tree = useMemo(() => buildTree(kept), [kept]);
|
|
691
|
+
const components = useMemo(() => buildComponents(kept), [kept]);
|
|
692
|
+
const shown = useMemo(
|
|
693
|
+
() => view === "components" ? componentNodes(filterComponents(components, query)) : filterTree(tree, query),
|
|
694
|
+
[view, components, tree, query]
|
|
695
|
+
);
|
|
696
|
+
const routed = kept.find((entry) => entry.id === route);
|
|
365
697
|
const routeAncestors = useMemo(() => ancestorsOf(routed), [routed]);
|
|
366
698
|
const filtering = query.trim() !== "";
|
|
367
|
-
const isOpen = (path) => filtering || (folds[path] ?? routeAncestors.has(path));
|
|
699
|
+
const isOpen = (path) => filtering || (folds[path] ?? (view === "tree" && routeAncestors.has(path)));
|
|
368
700
|
const rows = [];
|
|
369
701
|
const collect = (nodes, parent) => {
|
|
370
702
|
for (const node of nodes) {
|
|
@@ -383,10 +715,11 @@ function StoryTree({ ctx, index }) {
|
|
|
383
715
|
setFocusKey(key);
|
|
384
716
|
items.current.get(key)?.focus();
|
|
385
717
|
};
|
|
386
|
-
const activate = (entry,
|
|
387
|
-
const
|
|
388
|
-
if (
|
|
389
|
-
else
|
|
718
|
+
const activate = (entry, another) => {
|
|
719
|
+
const focused = ctx.trials.find((trial) => trial.id === ctx.focusedTrialId);
|
|
720
|
+
if (another || !focused) ctx.addTrial(entry.id);
|
|
721
|
+
else if (focused.instrumentName === entry.id) revealTrial(focused.id);
|
|
722
|
+
else ctx.swapTrial(focused.id, entry.id);
|
|
390
723
|
setRoute(entry.id);
|
|
391
724
|
};
|
|
392
725
|
const onKeyDown = (event) => {
|
|
@@ -419,7 +752,7 @@ function StoryTree({ ctx, index }) {
|
|
|
419
752
|
case "Enter":
|
|
420
753
|
case " ":
|
|
421
754
|
if (node.kind === "folder") setOpen(node.path, !isOpen(node.path));
|
|
422
|
-
else activate(node.entry, event.
|
|
755
|
+
else activate(node.entry, event.shiftKey);
|
|
423
756
|
break;
|
|
424
757
|
default:
|
|
425
758
|
return;
|
|
@@ -453,7 +786,10 @@ function StoryTree({ ctx, index }) {
|
|
|
453
786
|
"aria-label": node.label,
|
|
454
787
|
className: "fg-tree__node",
|
|
455
788
|
children: [
|
|
456
|
-
/* @__PURE__ */
|
|
789
|
+
/* @__PURE__ */ jsxs("div", { className: "fg-tree__item fg-tree__folder", onClick: () => setOpen(node.path, !open), children: [
|
|
790
|
+
/* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label }),
|
|
791
|
+
node.tag ? /* @__PURE__ */ jsx("span", { className: "fg-tree__tag", children: node.tag }) : null
|
|
792
|
+
] }),
|
|
457
793
|
open ? /* @__PURE__ */ jsx("div", { role: "group", className: "fg-tree__group", children: renderNodes(node.children, level + 1) }) : null
|
|
458
794
|
]
|
|
459
795
|
},
|
|
@@ -461,7 +797,7 @@ function StoryTree({ ctx, index }) {
|
|
|
461
797
|
);
|
|
462
798
|
}
|
|
463
799
|
const { entry } = node;
|
|
464
|
-
return /* @__PURE__ */
|
|
800
|
+
return /* @__PURE__ */ jsxs(
|
|
465
801
|
"a",
|
|
466
802
|
{
|
|
467
803
|
...itemProps(node, level),
|
|
@@ -469,12 +805,15 @@ function StoryTree({ ctx, index }) {
|
|
|
469
805
|
"aria-current": openIds.has(entry.id) ? "true" : void 0,
|
|
470
806
|
className: "fg-tree__item fg-tree__story",
|
|
471
807
|
onClick: (event) => {
|
|
472
|
-
if (event.button !== 0 || event.
|
|
808
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.altKey) return;
|
|
473
809
|
event.preventDefault();
|
|
474
810
|
setFocusKey(keyOf(node));
|
|
475
|
-
activate(entry, event.
|
|
811
|
+
activate(entry, event.shiftKey);
|
|
476
812
|
},
|
|
477
|
-
children:
|
|
813
|
+
children: [
|
|
814
|
+
/* @__PURE__ */ jsx("span", { className: "fg-tree__label", children: node.label ?? entry.name }),
|
|
815
|
+
node.tag ? /* @__PURE__ */ jsx("span", { className: "fg-tree__tag", children: node.tag }) : null
|
|
816
|
+
]
|
|
478
817
|
},
|
|
479
818
|
keyOf(node)
|
|
480
819
|
);
|
|
@@ -485,6 +824,30 @@ function StoryTree({ ctx, index }) {
|
|
|
485
824
|
"No story ",
|
|
486
825
|
/* @__PURE__ */ jsx("code", { children: route })
|
|
487
826
|
] }) : null,
|
|
827
|
+
/* @__PURE__ */ jsx(
|
|
828
|
+
ToggleBar,
|
|
829
|
+
{
|
|
830
|
+
ariaLabel: "Sidebar view",
|
|
831
|
+
variant: "flat",
|
|
832
|
+
items: VIEWS,
|
|
833
|
+
value: view,
|
|
834
|
+
onChange: (next) => {
|
|
835
|
+
if (next) setView(next);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
),
|
|
839
|
+
/* @__PURE__ */ jsxs("fieldset", { className: "fg-tree__libraries", children: [
|
|
840
|
+
/* @__PURE__ */ jsx("legend", { className: "fg-tree__libraries-legend", children: "Packages" }),
|
|
841
|
+
libraries.map((library) => /* @__PURE__ */ jsx(
|
|
842
|
+
Checkbox,
|
|
843
|
+
{
|
|
844
|
+
isSelected: !hidden[library],
|
|
845
|
+
onChange: (on) => setHidden((prev) => ({ ...prev, [library]: !on })),
|
|
846
|
+
children: library
|
|
847
|
+
},
|
|
848
|
+
library
|
|
849
|
+
))
|
|
850
|
+
] }),
|
|
488
851
|
/* @__PURE__ */ jsx(
|
|
489
852
|
"input",
|
|
490
853
|
{
|
|
@@ -504,26 +867,36 @@ var KEPT_CONFIGS = 50;
|
|
|
504
867
|
var UNANSWERED = stableStringify({ hidden: [], errors: {} });
|
|
505
868
|
function createAnswerBook() {
|
|
506
869
|
const byConfig = /* @__PURE__ */ new Map();
|
|
507
|
-
let errors = {};
|
|
508
870
|
const listeners = /* @__PURE__ */ new Set();
|
|
871
|
+
const held = /* @__PURE__ */ new Map();
|
|
872
|
+
const keyOf2 = (config) => stableStringify(storyConfig(config));
|
|
873
|
+
const answerFor = (config) => byConfig.get(keyOf2(config));
|
|
509
874
|
return {
|
|
510
875
|
record(answers) {
|
|
511
876
|
const key = stableStringify({ hidden: answers.hidden, errors: answers.errors });
|
|
512
877
|
const changed = key !== (byConfig.get(answers.configKey)?.key ?? UNANSWERED);
|
|
513
878
|
byConfig.delete(answers.configKey);
|
|
514
|
-
byConfig.set(answers.configKey, { hidden: answers.hidden, key });
|
|
879
|
+
byConfig.set(answers.configKey, { hidden: answers.hidden, errors: answers.errors, key });
|
|
515
880
|
if (byConfig.size > KEPT_CONFIGS) {
|
|
516
881
|
const oldest = byConfig.keys().next().value;
|
|
517
882
|
if (oldest !== void 0) byConfig.delete(oldest);
|
|
518
883
|
}
|
|
519
|
-
|
|
520
|
-
|
|
884
|
+
if (changed && held.has(answers.configKey)) for (const fn of listeners) fn();
|
|
885
|
+
},
|
|
886
|
+
hold(config) {
|
|
887
|
+
const key = keyOf2(config);
|
|
888
|
+
held.set(key, (held.get(key) ?? 0) + 1);
|
|
889
|
+
return () => {
|
|
890
|
+
const count = (held.get(key) ?? 1) - 1;
|
|
891
|
+
if (count > 0) held.set(key, count);
|
|
892
|
+
else held.delete(key);
|
|
893
|
+
};
|
|
521
894
|
},
|
|
522
895
|
hidden(path, config) {
|
|
523
|
-
const hidden =
|
|
896
|
+
const hidden = answerFor(config)?.hidden;
|
|
524
897
|
return hidden?.some((h) => path === h || path.startsWith(`${h}.`)) ?? false;
|
|
525
898
|
},
|
|
526
|
-
errors: (path) => errors[path] ?? [],
|
|
899
|
+
errors: (path, config) => answerFor(config)?.errors[path] ?? [],
|
|
527
900
|
subscribe(fn) {
|
|
528
901
|
listeners.add(fn);
|
|
529
902
|
return () => {
|
|
@@ -536,18 +909,43 @@ function createAnswerBook() {
|
|
|
536
909
|
// src/shell/readyKey.ts
|
|
537
910
|
var readyKey = (ready) => stableStringify({ schema: ready.schema, layout: ready.layout, viewport: ready.viewport });
|
|
538
911
|
var START_TIMEOUT_MS = 1e4;
|
|
912
|
+
var requests = 0;
|
|
913
|
+
var IN_VIEW_MARGIN = "50%";
|
|
914
|
+
function useInView(ref) {
|
|
915
|
+
const [inView, setInView] = useState(true);
|
|
916
|
+
useEffect(() => {
|
|
917
|
+
const el = ref.current;
|
|
918
|
+
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
919
|
+
const observer = new IntersectionObserver(
|
|
920
|
+
(entries) => {
|
|
921
|
+
const last = entries.at(-1);
|
|
922
|
+
if (last) setInView(last.isIntersecting);
|
|
923
|
+
},
|
|
924
|
+
{ rootMargin: IN_VIEW_MARGIN }
|
|
925
|
+
);
|
|
926
|
+
observer.observe(el);
|
|
927
|
+
return () => observer.disconnect();
|
|
928
|
+
}, [ref]);
|
|
929
|
+
return inView;
|
|
930
|
+
}
|
|
539
931
|
function closeLink(link) {
|
|
540
932
|
if (!link.current) return;
|
|
541
933
|
clearTimeout(link.current.timer);
|
|
934
|
+
for (const settle2 of link.current.pending.values()) settle2.gone();
|
|
935
|
+
link.current.pending.clear();
|
|
542
936
|
link.current.disconnect();
|
|
543
937
|
link.current.channel.close();
|
|
544
938
|
link.current = null;
|
|
545
939
|
}
|
|
940
|
+
function settle(link, id, value) {
|
|
941
|
+
link.pending.get(id)?.resolve?.(value);
|
|
942
|
+
link.pending.delete(id);
|
|
943
|
+
}
|
|
546
944
|
function mismatchMessage(mismatch) {
|
|
547
945
|
return mismatch.reason === "version" ? `Frame speaks protocol ${String(mismatch.version)}; this workshop speaks ${PROTOCOL_VERSION}` : "Frame sent a message that is not a forge envelope";
|
|
548
946
|
}
|
|
549
947
|
function FrameView(props) {
|
|
550
|
-
const { entry, frameUrl, descriptionKey, ctx } = props;
|
|
948
|
+
const { entry, frameUrl, answers, descriptionKey, ctx } = props;
|
|
551
949
|
const labGlobals2 = useContext(StoryGlobalsContext);
|
|
552
950
|
const pins = ctx.config?.[GLOBALS_KEY];
|
|
553
951
|
const globals = useMemo(() => effectiveGlobals(labGlobals2, pins), [labGlobals2, pins]);
|
|
@@ -556,10 +954,13 @@ function FrameView(props) {
|
|
|
556
954
|
const [overrides] = useCssOverrides();
|
|
557
955
|
const src = `${frameUrl}#${entry.id}`;
|
|
558
956
|
const iframeRef = useRef(null);
|
|
957
|
+
const hostRef = useRef(null);
|
|
958
|
+
const inView = useInView(hostRef);
|
|
559
959
|
const link = useRef(null);
|
|
560
960
|
const latest = useRef({ ...props, globals, overrides });
|
|
561
961
|
latest.current = { ...props, globals, overrides };
|
|
562
962
|
const [fault, setFault] = useState(null);
|
|
963
|
+
const [pending, setPending] = useState(true);
|
|
563
964
|
const init = (current) => {
|
|
564
965
|
const { ctx: live, globals: liveGlobals } = latest.current;
|
|
565
966
|
current.awaiting = null;
|
|
@@ -568,7 +969,7 @@ function FrameView(props) {
|
|
|
568
969
|
current.channel.send({ type: "init", ...current.sent });
|
|
569
970
|
};
|
|
570
971
|
const receive = (current, msg) => {
|
|
571
|
-
const { ctx: live, answers, onReady } = latest.current;
|
|
972
|
+
const { ctx: live, answers: answers2, onReady } = latest.current;
|
|
572
973
|
switch (msg.type) {
|
|
573
974
|
case "ready": {
|
|
574
975
|
clearTimeout(current.timer);
|
|
@@ -582,6 +983,11 @@ function FrameView(props) {
|
|
|
582
983
|
if (current.awaiting === key && key === latest.current.descriptionKey) init(current);
|
|
583
984
|
break;
|
|
584
985
|
}
|
|
986
|
+
case "rendered":
|
|
987
|
+
setPending(false);
|
|
988
|
+
current.hasRendered = true;
|
|
989
|
+
for (const ask of current.queued.splice(0)) ask();
|
|
990
|
+
break;
|
|
585
991
|
case "setConfig":
|
|
586
992
|
if (!isGlobalsPath(msg.path)) live.setConfig(msg.path, msg.value);
|
|
587
993
|
break;
|
|
@@ -590,11 +996,25 @@ function FrameView(props) {
|
|
|
590
996
|
live.setState(msg.state);
|
|
591
997
|
break;
|
|
592
998
|
case "answers":
|
|
593
|
-
|
|
999
|
+
answers2.record(msg.answers);
|
|
594
1000
|
break;
|
|
595
1001
|
case "vars":
|
|
596
1002
|
if (trialId) frames?.report(trialId, msg.vars);
|
|
597
1003
|
break;
|
|
1004
|
+
case "size":
|
|
1005
|
+
if (trialId) frames?.reportSize(trialId, { width: msg.width, height: msg.height });
|
|
1006
|
+
break;
|
|
1007
|
+
case "a11y": {
|
|
1008
|
+
const outcome = msg.ok ? { ok: true, report: msg.report } : { ok: false, message: msg.message };
|
|
1009
|
+
if (trialId) frames?.reportA11y(trialId, outcome);
|
|
1010
|
+
settle(current, msg.id, outcome);
|
|
1011
|
+
break;
|
|
1012
|
+
}
|
|
1013
|
+
case "capture":
|
|
1014
|
+
if (msg.ok) settle(current, msg.id, msg.picture);
|
|
1015
|
+
else current.pending.get(msg.id)?.fail(new Error(msg.message));
|
|
1016
|
+
current.pending.delete(msg.id);
|
|
1017
|
+
break;
|
|
598
1018
|
case "fault":
|
|
599
1019
|
if (msg.phase === "render" && msg.seq !== void 0 && msg.seq < current.inputs) break;
|
|
600
1020
|
clearTimeout(current.timer);
|
|
@@ -604,6 +1024,7 @@ function FrameView(props) {
|
|
|
604
1024
|
};
|
|
605
1025
|
const onLoad = () => {
|
|
606
1026
|
closeLink(link);
|
|
1027
|
+
setPending(true);
|
|
607
1028
|
const target = iframeRef.current?.contentWindow;
|
|
608
1029
|
if (!target) return;
|
|
609
1030
|
const { port1, port2 } = new MessageChannel();
|
|
@@ -614,14 +1035,58 @@ function FrameView(props) {
|
|
|
614
1035
|
setFault({ phase: "protocol", message: mismatchMessage(mismatch) });
|
|
615
1036
|
}
|
|
616
1037
|
});
|
|
617
|
-
const
|
|
1038
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
1039
|
+
const request = (id, msg, gone) => new Promise((resolve, reject) => {
|
|
1040
|
+
waiting.set(id, {
|
|
1041
|
+
resolve,
|
|
1042
|
+
fail: reject,
|
|
1043
|
+
gone: () => gone(resolve, reject)
|
|
1044
|
+
});
|
|
1045
|
+
const ask = () => channel.send(msg);
|
|
1046
|
+
if (current.hasRendered) ask();
|
|
1047
|
+
else current.queued.push(ask);
|
|
1048
|
+
});
|
|
1049
|
+
const audit = () => {
|
|
1050
|
+
const id = `a11y-${requests += 1}`;
|
|
1051
|
+
return request(
|
|
1052
|
+
id,
|
|
1053
|
+
{ type: "a11y.run", id },
|
|
1054
|
+
(resolve) => resolve({ ok: false, message: "The frame went away" })
|
|
1055
|
+
);
|
|
1056
|
+
};
|
|
1057
|
+
const capture = () => {
|
|
1058
|
+
const id = `capture-${requests += 1}`;
|
|
1059
|
+
return request(
|
|
1060
|
+
id,
|
|
1061
|
+
{ type: "capture.run", id },
|
|
1062
|
+
(_resolve, fail) => fail(new Error("The frame could not draw itself"))
|
|
1063
|
+
);
|
|
1064
|
+
};
|
|
1065
|
+
const disconnect = frames && trialId ? frames.connect(trialId, { send: channel.send, audit, capture }) : () => {
|
|
1066
|
+
};
|
|
1067
|
+
const current = {
|
|
1068
|
+
channel,
|
|
1069
|
+
timer,
|
|
1070
|
+
awaiting: null,
|
|
1071
|
+
sent: null,
|
|
1072
|
+
inputs: 0,
|
|
1073
|
+
disconnect,
|
|
1074
|
+
pending: waiting,
|
|
1075
|
+
hasRendered: false,
|
|
1076
|
+
queued: []
|
|
618
1077
|
};
|
|
619
|
-
const current = { channel, timer, awaiting: null, sent: null, inputs: 0, disconnect };
|
|
620
1078
|
link.current = current;
|
|
621
1079
|
channel.on((msg) => receive(current, msg));
|
|
622
1080
|
target.postMessage({ type: PORT_HANDOFF }, location.origin, [port2]);
|
|
623
1081
|
};
|
|
624
1082
|
useEffect(() => () => closeLink(link), []);
|
|
1083
|
+
useEffect(() => {
|
|
1084
|
+
if (inView) return;
|
|
1085
|
+
closeLink(link);
|
|
1086
|
+
setPending(true);
|
|
1087
|
+
setFault(null);
|
|
1088
|
+
}, [inView]);
|
|
1089
|
+
useEffect(() => answers.hold(ctx.config), [answers, ctx.config]);
|
|
625
1090
|
useEffect(() => {
|
|
626
1091
|
const current = link.current;
|
|
627
1092
|
if (!current) return;
|
|
@@ -653,25 +1118,54 @@ function FrameView(props) {
|
|
|
653
1118
|
}
|
|
654
1119
|
if (input) setFault((shown) => shown?.phase === "render" ? null : shown);
|
|
655
1120
|
}, [ctx.config, ctx.state, globals, descriptionKey]);
|
|
656
|
-
return /* @__PURE__ */ jsxs(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
{
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1121
|
+
return /* @__PURE__ */ jsxs(
|
|
1122
|
+
"div",
|
|
1123
|
+
{
|
|
1124
|
+
ref: (el) => {
|
|
1125
|
+
hostRef.current = el;
|
|
1126
|
+
if (trialId && frames) frames.hostRef(trialId).current = el;
|
|
1127
|
+
},
|
|
1128
|
+
className: "fg-frame-host",
|
|
1129
|
+
children: [
|
|
1130
|
+
inView ? /* @__PURE__ */ jsx(
|
|
1131
|
+
"iframe",
|
|
1132
|
+
{
|
|
1133
|
+
ref: iframeRef,
|
|
1134
|
+
className: "fg-frame-view",
|
|
1135
|
+
src,
|
|
1136
|
+
title: `${entry.title} / ${entry.name}`,
|
|
1137
|
+
onLoad,
|
|
1138
|
+
"data-pending": pending && !fault ? "" : void 0
|
|
1139
|
+
}
|
|
1140
|
+
) : null,
|
|
1141
|
+
fault ? /* @__PURE__ */ jsxs("div", { className: "fg-fault", role: "alert", children: [
|
|
1142
|
+
fault.phase ? /* @__PURE__ */ jsx("span", { className: "fg-fault__phase", children: fault.phase }) : null,
|
|
1143
|
+
/* @__PURE__ */ jsx("p", { className: "fg-fault__message", children: fault.message })
|
|
1144
|
+
] }) : null
|
|
1145
|
+
]
|
|
1146
|
+
}
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
function annotationsOn(frames) {
|
|
1150
|
+
return {
|
|
1151
|
+
targets: (_state, _config, trial) => {
|
|
1152
|
+
const ref = frames.hostRef(trial.id);
|
|
1153
|
+
const frame = frames.get(trial.id);
|
|
1154
|
+
const box = frame.size ?? { width: ref.current?.clientWidth ?? 0, height: ref.current?.clientHeight ?? 0 };
|
|
1155
|
+
const capture = frame.capture;
|
|
1156
|
+
return [
|
|
1157
|
+
{
|
|
1158
|
+
id: "story",
|
|
1159
|
+
ref,
|
|
1160
|
+
content: { w: Math.max(1, box.width), h: Math.max(1, box.height) },
|
|
1161
|
+
...capture ? { base: () => capture() } : {}
|
|
1162
|
+
}
|
|
1163
|
+
];
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
672
1166
|
}
|
|
673
1167
|
function storyInstrument(options) {
|
|
674
|
-
const { entry, ready, answers, frameUrl, onReady, globals = {} } = options;
|
|
1168
|
+
const { entry, ready, answers, frameUrl, onReady, globals = {}, frames } = options;
|
|
675
1169
|
const config = withGlobals(
|
|
676
1170
|
ready ? schemaFromDescription(ready.schema, answers) : f.schema({}),
|
|
677
1171
|
globals
|
|
@@ -694,6 +1188,7 @@ function storyInstrument(options) {
|
|
|
694
1188
|
ctx
|
|
695
1189
|
}
|
|
696
1190
|
),
|
|
1191
|
+
...frames ? { annotations: annotationsOn(frames) } : {},
|
|
697
1192
|
...ready?.viewport ? { stage: { size: { width: ready.viewport.width, height: ready.viewport.height } } } : {}
|
|
698
1193
|
};
|
|
699
1194
|
}
|
|
@@ -702,7 +1197,7 @@ function storyInstrument(options) {
|
|
|
702
1197
|
var NO_GLOBALS = {};
|
|
703
1198
|
var pruned = (map, ids) => [...map.keys()].every((id) => ids.has(id)) ? map : new Map([...map].filter(([id]) => ids.has(id)));
|
|
704
1199
|
function useStoryRegistry(index, options) {
|
|
705
|
-
const { frameUrl, globals = NO_GLOBALS } = options;
|
|
1200
|
+
const { frameUrl, globals = NO_GLOBALS, frames } = options;
|
|
706
1201
|
const [readies, setReadies] = useState(() => /* @__PURE__ */ new Map());
|
|
707
1202
|
const [revisions, setRevisions] = useState(() => /* @__PURE__ */ new Map());
|
|
708
1203
|
const onReady = useCallback((entry, ready) => {
|
|
@@ -729,20 +1224,21 @@ function useStoryRegistry(index, options) {
|
|
|
729
1224
|
const answers = previous.books.get(entry.id) ?? createAnswerBook();
|
|
730
1225
|
books.set(entry.id, answers);
|
|
731
1226
|
const held = previous.built.get(entry.id);
|
|
732
|
-
const kept = held && held.entryKey === entryKey && held.ready === ready && held.frameUrl === frameUrl && held.globals === globals && held.revision === revision ? held : {
|
|
1227
|
+
const kept = held && held.entryKey === entryKey && held.ready === ready && held.frameUrl === frameUrl && held.globals === globals && held.frames === frames && held.revision === revision ? held : {
|
|
733
1228
|
entryKey,
|
|
734
1229
|
ready,
|
|
735
1230
|
frameUrl,
|
|
736
1231
|
globals,
|
|
1232
|
+
frames,
|
|
737
1233
|
revision,
|
|
738
|
-
instrument: storyInstrument({ entry, ready, answers, frameUrl, onReady, globals })
|
|
1234
|
+
instrument: storyInstrument({ entry, ready, answers, frameUrl, onReady, globals, ...frames ? { frames } : {} })
|
|
739
1235
|
};
|
|
740
1236
|
built.set(entry.id, kept);
|
|
741
1237
|
return kept.instrument;
|
|
742
1238
|
});
|
|
743
1239
|
const unchanged = list.length === previous.list.length && list.every((i, n) => i === previous.list[n]);
|
|
744
1240
|
return { built, books, list: unchanged ? previous.list : list };
|
|
745
|
-
}, [index, readies, revisions, frameUrl, globals, onReady]);
|
|
1241
|
+
}, [index, readies, revisions, frameUrl, globals, frames, onReady]);
|
|
746
1242
|
useLayoutEffect(() => {
|
|
747
1243
|
committed.current = cache;
|
|
748
1244
|
}, [cache]);
|
|
@@ -754,10 +1250,10 @@ function useStoryRegistry(index, options) {
|
|
|
754
1250
|
for (const off of offs) off();
|
|
755
1251
|
};
|
|
756
1252
|
}, [cache.books]);
|
|
757
|
-
|
|
1253
|
+
const isReady = useCallback((id) => readies.has(id), [readies]);
|
|
1254
|
+
return { instruments: cache.list, isReady, onReady };
|
|
758
1255
|
}
|
|
759
1256
|
var NO_DECLARATIONS = {};
|
|
760
|
-
var TRIAL_CHROME = [CSS_VARS_SECTION];
|
|
761
1257
|
function initialStory(index, fallback) {
|
|
762
1258
|
const route = readRoute();
|
|
763
1259
|
return route !== null && index.some((entry) => entry.id === route) ? route : fallback;
|
|
@@ -773,11 +1269,27 @@ function RouteOpener({ index }) {
|
|
|
773
1269
|
}, [route, index, lab]);
|
|
774
1270
|
return null;
|
|
775
1271
|
}
|
|
1272
|
+
function useInfoShortcut(open) {
|
|
1273
|
+
useEffect(() => {
|
|
1274
|
+
const onKeyDown = (event) => {
|
|
1275
|
+
if (event.key !== "i" && event.key !== "I") return;
|
|
1276
|
+
if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return;
|
|
1277
|
+
const target = event.target;
|
|
1278
|
+
if (target?.isContentEditable || /^(input|textarea|select)$/i.test(target?.tagName ?? "")) return;
|
|
1279
|
+
event.preventDefault();
|
|
1280
|
+
open(true);
|
|
1281
|
+
};
|
|
1282
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1283
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1284
|
+
}, [open]);
|
|
1285
|
+
}
|
|
776
1286
|
function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }) {
|
|
777
1287
|
const declarations = config?.globals ?? NO_DECLARATIONS;
|
|
778
|
-
const registry = useStoryRegistry(index, { frameUrl, globals: declarations });
|
|
779
1288
|
const [frames] = useState(createTrialFrames);
|
|
1289
|
+
const registry = useStoryRegistry(index, { frameUrl, globals: declarations, frames });
|
|
780
1290
|
const [labValues, setLabValues] = useState(() => labGlobals(declarations, void 0));
|
|
1291
|
+
const [infoOpen, setInfoOpen] = useState(false);
|
|
1292
|
+
useInfoShortcut(setInfoOpen);
|
|
781
1293
|
const reportLabValues = useCallback(
|
|
782
1294
|
(next) => setLabValues((prev) => stableStringify(prev) === stableStringify(next) ? prev : next),
|
|
783
1295
|
[]
|
|
@@ -785,6 +1297,13 @@ function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }
|
|
|
785
1297
|
const labChrome = useMemo(
|
|
786
1298
|
() => [
|
|
787
1299
|
{ id: "fg-stories", region: "sidebar", render: (ctx) => /* @__PURE__ */ jsx(StoryTree, { ctx, index }) },
|
|
1300
|
+
{
|
|
1301
|
+
id: "fg-info",
|
|
1302
|
+
region: "palette",
|
|
1303
|
+
item: { icon: InfoIcon, label: "Info", shortcut: "\u2318I", onActivate: () => setInfoOpen(true) }
|
|
1304
|
+
},
|
|
1305
|
+
CSS_VARS_SECTION,
|
|
1306
|
+
A11Y_SECTION,
|
|
788
1307
|
...Object.keys(declarations).length > 0 ? [{ id: "fg-globals", region: "header", render: () => /* @__PURE__ */ jsx(GlobalsToolbar, { declarations }) }] : [],
|
|
789
1308
|
...config?.labChrome ?? []
|
|
790
1309
|
],
|
|
@@ -801,16 +1320,27 @@ function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }
|
|
|
801
1320
|
Lab,
|
|
802
1321
|
{
|
|
803
1322
|
title: "weaselforge",
|
|
1323
|
+
density: "roomy",
|
|
804
1324
|
instruments: registry.instruments,
|
|
805
1325
|
defaultInstrument: initialStory(index, first.id),
|
|
806
1326
|
storageKey: storageKey ?? "weaselforge",
|
|
807
1327
|
...storage ? { storage } : {},
|
|
808
1328
|
labChrome,
|
|
809
|
-
chrome: TRIAL_CHROME,
|
|
810
1329
|
addTrial: false,
|
|
811
1330
|
...config?.controls ? { controls: config.controls } : {},
|
|
1331
|
+
...config?.pages ? { pages: config.pages } : {},
|
|
1332
|
+
...config?.path !== void 0 ? { path: config.path } : {},
|
|
812
1333
|
children: [
|
|
813
1334
|
/* @__PURE__ */ jsx(RouteOpener, { index }),
|
|
1335
|
+
/* @__PURE__ */ jsx(
|
|
1336
|
+
StoryInfoDialog,
|
|
1337
|
+
{
|
|
1338
|
+
index,
|
|
1339
|
+
isReady: registry.isReady,
|
|
1340
|
+
isOpen: infoOpen,
|
|
1341
|
+
onOpenChange: setInfoOpen
|
|
1342
|
+
}
|
|
1343
|
+
),
|
|
814
1344
|
/* @__PURE__ */ jsx(LabGlobals, { declarations, onChange: reportLabValues })
|
|
815
1345
|
]
|
|
816
1346
|
}
|
|
@@ -831,6 +1361,6 @@ function mountWorkshop({ container, ...props }) {
|
|
|
831
1361
|
};
|
|
832
1362
|
}
|
|
833
1363
|
|
|
834
|
-
export { StoryGlobalsContext, Workshop, mountWorkshop, useStoryRegistry };
|
|
1364
|
+
export { A11Y_SECTION, A11yPanel, StoryGlobalsContext, TrialFramesContext, Workshop, mountWorkshop, useStoryRegistry, useTrialFrame };
|
|
835
1365
|
//# sourceMappingURL=index.js.map
|
|
836
1366
|
//# sourceMappingURL=index.js.map
|