@weasel-js/forge 1.5.1 → 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-BPfCt4PJ.d.ts → FrameController-eZM3hlcE.d.ts} +6 -0
- package/dist/{chunk-2MADHLRA.js → chunk-5YHEO26U.js} +12 -3
- package/dist/chunk-5YHEO26U.js.map +1 -0
- package/dist/{config-CrLpfSq9.d.ts → config-CSVP6duU.d.ts} +1 -1
- package/dist/frame/index.d.ts +2 -2
- package/dist/frame/index.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/shell/index.css +112 -0
- package/dist/shell/index.css.map +1 -1
- package/dist/shell/index.d.ts +6 -2
- package/dist/shell/index.js +355 -15
- package/dist/shell/index.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +1 -1
- package/dist/vite/index.js +26 -7
- package/dist/vite/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-2MADHLRA.js.map +0 -1
package/dist/shell/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { schemaFromDescription, openChannel } from '../chunk-2JDYOBWM.js';
|
|
2
2
|
import { stableStringify, PORT_HANDOFF, PROTOCOL_VERSION } from '../chunk-FTFR2YPM.js';
|
|
3
|
-
import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, inferTokenType, ToggleBar, Input, TokenPanel } from '@weasel-js/labkit';
|
|
3
|
+
import { useTrialId, Button, Lab, useLabContext, TrialIdContext, usePersistedState, Select, inferTokenType, ToggleBar as ToggleBar$1, Input, TokenPanel, refitScale } from '@weasel-js/labkit';
|
|
4
4
|
import { createContext, useContext, useCallback, useSyncExternalStore, useState, useRef, useEffect, useMemo, useLayoutEffect, useId } from 'react';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
import { createRoot } from 'react-dom/client';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
12
|
var NO_FRAME = { send: null, vars: [], audit: null, a11y: null, capture: null, size: null };
|
|
11
13
|
function createTrialFrames() {
|
|
@@ -156,6 +158,29 @@ var TABS = [
|
|
|
156
158
|
{ value: "story", label: "Story" }
|
|
157
159
|
];
|
|
158
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 }];
|
|
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
|
+
}
|
|
159
184
|
function groupOf(name) {
|
|
160
185
|
const segments = name.replace(/^--/, "").split("-");
|
|
161
186
|
return segments.length > 1 ? segments.slice(0, -1).join("-") : name.replace(/^--/, "");
|
|
@@ -169,6 +194,7 @@ function CssVarsPanel() {
|
|
|
169
194
|
{},
|
|
170
195
|
{ scope: "lab" }
|
|
171
196
|
);
|
|
197
|
+
const [edited, setEdited] = usePersistedState("fg-css-vars-scales", {}, { scope: "lab" });
|
|
172
198
|
const [tab, setTab] = useState("theme");
|
|
173
199
|
const [filter, setFilter] = useState("");
|
|
174
200
|
const tokens = useMemo(() => {
|
|
@@ -192,6 +218,18 @@ function CssVarsPanel() {
|
|
|
192
218
|
const override = overrides[token.name];
|
|
193
219
|
return override === void 0 ? token : { ...token, value: override, overridden: true };
|
|
194
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]);
|
|
195
233
|
const send = frame.send;
|
|
196
234
|
const write = useCallback(
|
|
197
235
|
(name, value) => {
|
|
@@ -203,6 +241,24 @@ function CssVarsPanel() {
|
|
|
203
241
|
},
|
|
204
242
|
[setOverrides, send]
|
|
205
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
|
+
);
|
|
206
262
|
const onCollapsedChange = useCallback(
|
|
207
263
|
(category, collapsed) => setFolds((prev) => ({ ...prev, [category]: collapsed })),
|
|
208
264
|
[setFolds]
|
|
@@ -210,7 +266,7 @@ function CssVarsPanel() {
|
|
|
210
266
|
return /* @__PURE__ */ jsxs("div", { className: "fg-css-vars", children: [
|
|
211
267
|
/* @__PURE__ */ jsxs("div", { className: "fg-css-vars__controls", children: [
|
|
212
268
|
/* @__PURE__ */ jsx(
|
|
213
|
-
ToggleBar,
|
|
269
|
+
ToggleBar$1,
|
|
214
270
|
{
|
|
215
271
|
ariaLabel: "Variables",
|
|
216
272
|
variant: "flat",
|
|
@@ -223,7 +279,17 @@ function CssVarsPanel() {
|
|
|
223
279
|
),
|
|
224
280
|
/* @__PURE__ */ jsx(Input, { "aria-label": "Filter", placeholder: "Filter by name or value", value: filter, onChange: setFilter })
|
|
225
281
|
] }),
|
|
226
|
-
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(
|
|
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,
|
|
284
|
+
{
|
|
285
|
+
tokens: shown,
|
|
286
|
+
onChange: write,
|
|
287
|
+
scales,
|
|
288
|
+
onScaleChange,
|
|
289
|
+
collapsed: folds,
|
|
290
|
+
onCollapsedChange
|
|
291
|
+
}
|
|
292
|
+
)
|
|
227
293
|
] });
|
|
228
294
|
}
|
|
229
295
|
function FocusedCssVars() {
|
|
@@ -241,6 +307,206 @@ var CSS_VARS_SECTION = {
|
|
|
241
307
|
region: "aside",
|
|
242
308
|
item: { title: "CSS Vars", body: /* @__PURE__ */ jsx(FocusedCssVars, {}) }
|
|
243
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
|
+
}
|
|
244
510
|
var GLOBALS_KEY = "$globals";
|
|
245
511
|
var FOLLOW_LAB = "lab";
|
|
246
512
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -271,13 +537,13 @@ function storyConfig(config, previous) {
|
|
|
271
537
|
function withGlobals(schema, declarations) {
|
|
272
538
|
const entries = Object.entries(declarations);
|
|
273
539
|
if (entries.length === 0) return schema;
|
|
274
|
-
const
|
|
540
|
+
const leaves2 = Object.fromEntries(
|
|
275
541
|
entries.map(([key, declaration]) => [
|
|
276
542
|
key,
|
|
277
543
|
f.enum(FOLLOW_LAB, [{ value: FOLLOW_LAB, label: "Lab" }, ...declaration.options]).label(declaration.label)
|
|
278
544
|
])
|
|
279
545
|
);
|
|
280
|
-
const group = f.group(
|
|
546
|
+
const group = f.group(leaves2).label("").section("Globals", { layout: "inline", pack: "pairs" });
|
|
281
547
|
return f.schema({ ...schema.nodes, [GLOBALS_KEY]: group });
|
|
282
548
|
}
|
|
283
549
|
var RECORD = "fg-globals";
|
|
@@ -395,6 +661,10 @@ function filterTree(nodes, query) {
|
|
|
395
661
|
});
|
|
396
662
|
return walk(nodes);
|
|
397
663
|
}
|
|
664
|
+
var VIEWS = [
|
|
665
|
+
{ value: "tree", label: "Tree" },
|
|
666
|
+
{ value: "components", label: "Components" }
|
|
667
|
+
];
|
|
398
668
|
var keyOf = (node) => node.kind === "folder" ? `folder:${node.path}` : `story:${node.entry.id}`;
|
|
399
669
|
function ancestorsOf(entry) {
|
|
400
670
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -411,14 +681,22 @@ function StoryTree({ ctx, index }) {
|
|
|
411
681
|
const [route, setRoute] = useRoute();
|
|
412
682
|
const [query, setQuery] = useState("");
|
|
413
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" });
|
|
414
686
|
const [focusKey, setFocusKey] = useState(null);
|
|
415
687
|
const items = useRef(/* @__PURE__ */ new Map());
|
|
416
|
-
const
|
|
417
|
-
const
|
|
418
|
-
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);
|
|
419
697
|
const routeAncestors = useMemo(() => ancestorsOf(routed), [routed]);
|
|
420
698
|
const filtering = query.trim() !== "";
|
|
421
|
-
const isOpen = (path) => filtering || (folds[path] ?? routeAncestors.has(path));
|
|
699
|
+
const isOpen = (path) => filtering || (folds[path] ?? (view === "tree" && routeAncestors.has(path)));
|
|
422
700
|
const rows = [];
|
|
423
701
|
const collect = (nodes, parent) => {
|
|
424
702
|
for (const node of nodes) {
|
|
@@ -508,7 +786,10 @@ function StoryTree({ ctx, index }) {
|
|
|
508
786
|
"aria-label": node.label,
|
|
509
787
|
className: "fg-tree__node",
|
|
510
788
|
children: [
|
|
511
|
-
/* @__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
|
+
] }),
|
|
512
793
|
open ? /* @__PURE__ */ jsx("div", { role: "group", className: "fg-tree__group", children: renderNodes(node.children, level + 1) }) : null
|
|
513
794
|
]
|
|
514
795
|
},
|
|
@@ -516,7 +797,7 @@ function StoryTree({ ctx, index }) {
|
|
|
516
797
|
);
|
|
517
798
|
}
|
|
518
799
|
const { entry } = node;
|
|
519
|
-
return /* @__PURE__ */
|
|
800
|
+
return /* @__PURE__ */ jsxs(
|
|
520
801
|
"a",
|
|
521
802
|
{
|
|
522
803
|
...itemProps(node, level),
|
|
@@ -529,7 +810,10 @@ function StoryTree({ ctx, index }) {
|
|
|
529
810
|
setFocusKey(keyOf(node));
|
|
530
811
|
activate(entry, event.shiftKey);
|
|
531
812
|
},
|
|
532
|
-
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
|
+
]
|
|
533
817
|
},
|
|
534
818
|
keyOf(node)
|
|
535
819
|
);
|
|
@@ -540,6 +824,30 @@ function StoryTree({ ctx, index }) {
|
|
|
540
824
|
"No story ",
|
|
541
825
|
/* @__PURE__ */ jsx("code", { children: route })
|
|
542
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
|
+
] }),
|
|
543
851
|
/* @__PURE__ */ jsx(
|
|
544
852
|
"input",
|
|
545
853
|
{
|
|
@@ -942,7 +1250,8 @@ function useStoryRegistry(index, options) {
|
|
|
942
1250
|
for (const off of offs) off();
|
|
943
1251
|
};
|
|
944
1252
|
}, [cache.books]);
|
|
945
|
-
|
|
1253
|
+
const isReady = useCallback((id) => readies.has(id), [readies]);
|
|
1254
|
+
return { instruments: cache.list, isReady, onReady };
|
|
946
1255
|
}
|
|
947
1256
|
var NO_DECLARATIONS = {};
|
|
948
1257
|
function initialStory(index, fallback) {
|
|
@@ -960,11 +1269,27 @@ function RouteOpener({ index }) {
|
|
|
960
1269
|
}, [route, index, lab]);
|
|
961
1270
|
return null;
|
|
962
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
|
+
}
|
|
963
1286
|
function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }) {
|
|
964
1287
|
const declarations = config?.globals ?? NO_DECLARATIONS;
|
|
965
1288
|
const [frames] = useState(createTrialFrames);
|
|
966
1289
|
const registry = useStoryRegistry(index, { frameUrl, globals: declarations, frames });
|
|
967
1290
|
const [labValues, setLabValues] = useState(() => labGlobals(declarations, void 0));
|
|
1291
|
+
const [infoOpen, setInfoOpen] = useState(false);
|
|
1292
|
+
useInfoShortcut(setInfoOpen);
|
|
968
1293
|
const reportLabValues = useCallback(
|
|
969
1294
|
(next) => setLabValues((prev) => stableStringify(prev) === stableStringify(next) ? prev : next),
|
|
970
1295
|
[]
|
|
@@ -972,6 +1297,11 @@ function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }
|
|
|
972
1297
|
const labChrome = useMemo(
|
|
973
1298
|
() => [
|
|
974
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
|
+
},
|
|
975
1305
|
CSS_VARS_SECTION,
|
|
976
1306
|
A11Y_SECTION,
|
|
977
1307
|
...Object.keys(declarations).length > 0 ? [{ id: "fg-globals", region: "header", render: () => /* @__PURE__ */ jsx(GlobalsToolbar, { declarations }) }] : [],
|
|
@@ -990,6 +1320,7 @@ function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }
|
|
|
990
1320
|
Lab,
|
|
991
1321
|
{
|
|
992
1322
|
title: "weaselforge",
|
|
1323
|
+
density: "roomy",
|
|
993
1324
|
instruments: registry.instruments,
|
|
994
1325
|
defaultInstrument: initialStory(index, first.id),
|
|
995
1326
|
storageKey: storageKey ?? "weaselforge",
|
|
@@ -1001,6 +1332,15 @@ function Workshop({ index, frameUrl, config, stories = [], storageKey, storage }
|
|
|
1001
1332
|
...config?.path !== void 0 ? { path: config.path } : {},
|
|
1002
1333
|
children: [
|
|
1003
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
|
+
),
|
|
1004
1344
|
/* @__PURE__ */ jsx(LabGlobals, { declarations, onChange: reportLabValues })
|
|
1005
1345
|
]
|
|
1006
1346
|
}
|