@weasel-js/labkit 1.1.0 → 1.3.0-pre.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 +27 -0
- package/dist/_dts/{DrawCommand-BkZztJsW.d.ts → DrawCommand-DBB45NfN.d.ts} +244 -58
- package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
- package/dist/_dts/index-DgcNdEdh.d.ts +314 -0
- package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
- package/dist/_dts/types-AHlQxBNN.d.ts +162 -0
- package/dist/_dts/types-DJ79Tg5J.d.ts +56 -0
- package/dist/_dts/{useTrialState-DYe2vUwN.d.ts → useTrialState-CsGMjhu9.d.ts} +6 -92
- package/dist/canvas/index.d.ts +8 -3
- package/dist/canvas/index.js +2 -1
- package/dist/chrome/index.d.ts +86 -0
- package/dist/chrome/index.js +8 -0
- package/dist/chrome/index.js.map +1 -0
- package/dist/{chunk-3TYUJR7Z.js → chunk-4TMMVIDM.js} +9 -3
- package/dist/chunk-4TMMVIDM.js.map +1 -0
- package/dist/chunk-HFDVGF4X.js +170 -0
- package/dist/chunk-HFDVGF4X.js.map +1 -0
- package/dist/chunk-LN6JDUGB.js +106 -0
- package/dist/chunk-LN6JDUGB.js.map +1 -0
- package/dist/{chunk-574LJAV4.js → chunk-NS54R52R.js} +31 -21
- package/dist/chunk-NS54R52R.js.map +1 -0
- package/dist/{chunk-NRKWVTVT.js → chunk-O2BULFHX.js} +5 -14
- package/dist/chunk-O2BULFHX.js.map +1 -0
- package/dist/chunk-O7NVSCLN.js +266 -0
- package/dist/chunk-O7NVSCLN.js.map +1 -0
- package/dist/chunk-PO6L3NSH.js +366 -0
- package/dist/chunk-PO6L3NSH.js.map +1 -0
- package/dist/chunk-PQJ5B2U2.js +27 -0
- package/dist/chunk-PQJ5B2U2.js.map +1 -0
- package/dist/chunk-RVNN4CHQ.js +212 -0
- package/dist/chunk-RVNN4CHQ.js.map +1 -0
- package/dist/chunk-TLGRYALP.js +6151 -0
- package/dist/chunk-TLGRYALP.js.map +1 -0
- package/dist/{chunk-SFL7NFKN.js → chunk-V7PRSIRQ.js} +15 -7
- package/dist/chunk-V7PRSIRQ.js.map +1 -0
- package/dist/{chunk-DJLDIRFN.js → chunk-Z6HJ5FEM.js} +9626 -6050
- package/dist/chunk-Z6HJ5FEM.js.map +1 -0
- package/dist/chunk-ZZAYVX4X.js +505 -0
- package/dist/chunk-ZZAYVX4X.js.map +1 -0
- package/dist/controls/index.d.ts +19 -5
- package/dist/controls/index.js +3 -1
- package/dist/dragdrop/index.d.ts +5 -2
- package/dist/index.d.ts +307 -107
- package/dist/index.js +797 -540
- package/dist/index.js.map +1 -1
- package/dist/job/index.d.ts +13 -0
- package/dist/job/index.js +3 -0
- package/dist/job/index.js.map +1 -0
- package/dist/layers/index.d.ts +6 -3
- package/dist/layers/index.js +2 -1
- package/dist/passthrough/weasel-canvas.d.ts +69 -13
- package/dist/passthrough/weasel-canvas.js +1 -1
- package/dist/passthrough/weasel-ui.d.ts +3804 -3661
- package/dist/passthrough/weasel-ui.js +2 -2
- package/dist/primitives/index.d.ts +102 -8
- package/dist/primitives/index.js +4 -1
- package/dist/state/index.d.ts +3 -2
- package/dist/state/index.js +2 -2
- package/dist/styles.css +543 -144
- package/dist/surface/index.d.ts +77 -0
- package/dist/surface/index.js +4 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/ui/layers/index.js +4 -3
- package/dist/undo/index.d.ts +5 -2
- package/package.json +24 -7
- package/src/canvas/AGENTS.md +12 -0
- package/src/canvas/CanvasStack.tsx +6 -2
- package/src/canvas/useLayerScheduler.ts +22 -15
- package/src/canvas/useOrbit.test.ts +71 -0
- package/src/canvas/useOrbit.ts +149 -0
- package/src/canvas/usePanZoom.test.ts +80 -0
- package/src/canvas/usePanZoom.ts +16 -2
- package/src/chrome/ChromeRegions.stories.tsx +106 -0
- package/src/chrome/builtins.test.ts +153 -0
- package/src/chrome/builtins.tsx +205 -0
- package/src/chrome/index.ts +25 -0
- package/src/chrome/merge.test.ts +51 -0
- package/src/chrome/merge.ts +52 -0
- package/src/chrome/regions/PaletteRegion.less +14 -0
- package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
- package/src/chrome/regions/PaletteRegion.tsx +41 -0
- package/src/chrome/regions/SidebarRegion.tsx +56 -0
- package/src/chrome/regions/StatusRegion.tsx +31 -0
- package/src/chrome/regions/TitleBarRegion.tsx +41 -0
- package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
- package/src/chrome/regions/ToolbarRegion.tsx +71 -0
- package/src/chrome/regions/ViewportRegion.less +42 -0
- package/src/chrome/regions/ViewportRegion.tsx +37 -0
- package/src/chrome/regions/regions.test.tsx +81 -0
- package/src/chrome/types.ts +127 -0
- package/src/config/builder.test.ts +87 -0
- package/src/config/builder.ts +224 -0
- package/src/config/fromConfigField.test.ts +93 -0
- package/src/config/fromConfigField.ts +74 -0
- package/src/config/index.ts +22 -0
- package/src/config/resolve.test.ts +120 -0
- package/src/config/resolve.ts +67 -0
- package/src/config/rules.ts +68 -0
- package/src/config/types.ts +108 -0
- package/src/config/useConfigSchema.ts +30 -0
- package/src/config/visible.ts +20 -0
- package/src/controls/ControlPanel.less +4 -58
- package/src/controls/ControlPanel.stories.tsx +86 -0
- package/src/controls/ControlPanel.test.tsx +223 -10
- package/src/controls/ControlPanel.tsx +202 -203
- package/src/dragdrop/DragDropRuntime.tsx +36 -18
- package/src/dragdrop/DragGhost.less +1 -1
- package/src/dragdrop/Palette.less +1 -1
- package/src/index.test.ts +18 -0
- package/src/index.ts +38 -3
- package/src/instrument/SineWave.smoke.test.tsx +9 -1
- package/src/instrument/defineInstrument.test.ts +38 -1
- package/src/instrument/defineInstrument.ts +31 -4
- package/src/instrument/index.ts +1 -0
- package/src/instrument/types.ts +38 -3
- package/src/instrument/validateConfigSchema.test.ts +13 -3
- package/src/instrument/validateConfigSchema.ts +7 -10
- package/src/job/index.ts +3 -0
- package/src/job/types.ts +47 -0
- package/src/job/useJob.test.tsx +210 -0
- package/src/job/useJob.ts +134 -0
- package/src/lab/Lab.chrome.test.tsx +65 -0
- package/src/lab/Lab.stories.tsx +1 -2
- package/src/lab/Lab.tsx +58 -15
- package/src/lab/LabContext.ts +6 -0
- package/src/lab/LabFullChrome.stories.tsx +308 -0
- package/src/lab/LabHeader.test.tsx +42 -0
- package/src/lab/LabHeader.tsx +57 -0
- package/src/lab/LabPalette.test.tsx +35 -0
- package/src/lab/LabPalette.tsx +34 -0
- package/src/lab/LabShell.less +50 -3
- package/src/lab/Workspace.less +1 -30
- package/src/lab/Workspace.surface.test.tsx +49 -0
- package/src/lab/Workspace.test.tsx +20 -10
- package/src/lab/Workspace.tsx +19 -9
- package/src/lab/index.ts +2 -0
- package/src/layers/AGENTS.md +1 -1
- package/src/layers/LayerList.less +31 -7
- package/src/layers/LayerList.tsx +17 -4
- package/src/passthrough/weasel-ui.ts +23 -0
- package/src/primitives/DragHandleGlyph.tsx +29 -0
- package/src/primitives/FloatingPanel.less +28 -0
- package/src/primitives/FloatingPanel.stories.tsx +46 -0
- package/src/primitives/FloatingPanel.test.tsx +240 -0
- package/src/primitives/FloatingPanel.tsx +190 -0
- package/src/primitives/FpsMeter.test.tsx +44 -0
- package/src/primitives/FpsMeter.tsx +20 -9
- package/src/primitives/JobProgress.less +69 -0
- package/src/primitives/JobProgress.stories.tsx +48 -0
- package/src/primitives/JobProgress.tsx +65 -0
- package/src/primitives/Legend.less +47 -0
- package/src/primitives/Legend.stories.tsx +32 -0
- package/src/primitives/Legend.test.tsx +57 -0
- package/src/primitives/Legend.tsx +40 -0
- package/src/primitives/Sidebar.less +32 -2
- package/src/primitives/StatusBar.less +7 -0
- package/src/primitives/StatusBar.tsx +10 -3
- package/src/primitives/Toolbar.less +50 -3
- package/src/primitives/Toolbar.test.tsx +67 -1
- package/src/primitives/Toolbar.tsx +72 -6
- package/src/primitives/ZoomControl.less +31 -0
- package/src/primitives/ZoomControl.tsx +76 -0
- package/src/primitives/floating.entry.test.tsx +14 -0
- package/src/primitives/index.ts +10 -2
- package/src/primitives/useRovingTabIndex.test.ts +27 -0
- package/src/primitives/useRovingTabIndex.ts +79 -0
- package/src/state/store.ts +16 -2
- package/src/state/toolSlot.test.ts +36 -0
- package/src/state/types.ts +8 -2
- package/src/state/view.test.ts +127 -0
- package/src/state/view.ts +18 -0
- package/src/styles.less +6 -0
- package/src/surface/AGENTS.md +64 -0
- package/src/surface/SurfaceContext.ts +5 -0
- package/src/surface/composeRects.test.ts +50 -0
- package/src/surface/composeRects.ts +19 -0
- package/src/surface/deviceRect.test.ts +40 -0
- package/src/surface/deviceRect.ts +19 -0
- package/src/surface/index.ts +7 -0
- package/src/surface/rect.ts +16 -0
- package/src/surface/useSurfaceTile.test.tsx +67 -0
- package/src/surface/useSurfaceTile.ts +32 -0
- package/src/surface/useTiledSurface.test.tsx +231 -0
- package/src/surface/useTiledSurface.ts +161 -0
- package/src/test-setup.ts +26 -0
- package/src/theme/base.less +19 -2
- package/src/theme/interstellar.tokens.json +6 -2
- package/src/tools/types.ts +24 -0
- package/src/trial/Trial.config.test.tsx +113 -0
- package/src/trial/Trial.job.test.tsx +73 -0
- package/src/trial/Trial.less +121 -4
- package/src/trial/Trial.stories.tsx +9 -1
- package/src/trial/Trial.test.tsx +67 -96
- package/src/trial/Trial.tsx +112 -45
- package/src/trial/TrialChrome.tsx +110 -64
- package/src/trial/TrialDragContext.ts +10 -0
- package/src/trial/TrialTitleBar.tsx +41 -0
- package/src/trial/index.ts +1 -15
- package/src/trial/trialChrome.borders.test.ts +38 -0
- package/src/ui/layers/LayerStack.less +9 -9
- package/src/ui/layers/LayerStack.tsx +1 -13
- package/src/ui/properties/CurveField.less +2 -2
- package/src/ui/properties/CurveField.test.tsx +11 -11
- package/src/ui/properties/PropertyGroup.less +2 -2
- package/src/ui/properties/PropertyPanel.less +55 -41
- package/src/ui/properties/SpeechBalloonPanels.stories.tsx +4 -2
- package/src/ui/properties/storyLayouts.tsx +2 -2
- package/dist/_dts/index-iAP6XbH3.d.ts +0 -132
- package/dist/_dts/types-x92Kfeme.d.ts +0 -62
- package/dist/chunk-3TYUJR7Z.js.map +0 -1
- package/dist/chunk-574LJAV4.js.map +0 -1
- package/dist/chunk-5R2ATYPJ.js +0 -201
- package/dist/chunk-5R2ATYPJ.js.map +0 -1
- package/dist/chunk-C6GJKPUI.js +0 -5986
- package/dist/chunk-C6GJKPUI.js.map +0 -1
- package/dist/chunk-DJLDIRFN.js.map +0 -1
- package/dist/chunk-NRKWVTVT.js.map +0 -1
- package/dist/chunk-PMAU3SEE.js +0 -126
- package/dist/chunk-PMAU3SEE.js.map +0 -1
- package/dist/chunk-SFL7NFKN.js.map +0 -1
- package/src/instrument/capabilityDetector.test.ts +0 -64
- package/src/instrument/capabilityDetector.ts +0 -20
- package/src/trial/DefaultSidebar.tsx +0 -29
- package/src/trial/DefaultStatusBar.tsx +0 -19
- package/src/trial/DefaultToolbar.tsx +0 -92
- package/src/trial/slotTypes.ts +0 -53
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { v, f2 } from './chunk-TLGRYALP.js';
|
|
2
|
+
import { CanvasStackContext } from './chunk-CPUJ3QXL.js';
|
|
3
|
+
import { useVisibleRaf } from './chunk-Z6HJ5FEM.js';
|
|
4
|
+
import { useState, useRef, useEffect, useContext, useCallback } from 'react';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
// src/primitives/fpsAverage.ts
|
|
8
|
+
function rollingAverage(samples) {
|
|
9
|
+
if (samples.length === 0) return 0;
|
|
10
|
+
const sum = samples.reduce((acc, n) => acc + n, 0);
|
|
11
|
+
return Math.round(sum / samples.length);
|
|
12
|
+
}
|
|
13
|
+
var SAMPLE_WINDOW = 30;
|
|
14
|
+
function FpsMeter() {
|
|
15
|
+
const [fps, setFps] = useState(0);
|
|
16
|
+
const samplesRef = useRef([]);
|
|
17
|
+
const lastTimeRef = useRef(null);
|
|
18
|
+
const rootRef = useRef(null);
|
|
19
|
+
const loop = useVisibleRaf(
|
|
20
|
+
(time) => {
|
|
21
|
+
const last = lastTimeRef.current;
|
|
22
|
+
if (last !== null) {
|
|
23
|
+
const delta = time - last;
|
|
24
|
+
if (delta > 0) {
|
|
25
|
+
const samples = samplesRef.current;
|
|
26
|
+
samples.push(1e3 / delta);
|
|
27
|
+
if (samples.length > SAMPLE_WINDOW) samples.shift();
|
|
28
|
+
setFps(rollingAverage(samples));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
lastTimeRef.current = time;
|
|
32
|
+
loop.request();
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
target: rootRef,
|
|
36
|
+
// The gap spent suspended is not a frame that took an hour to draw.
|
|
37
|
+
onResume: () => {
|
|
38
|
+
lastTimeRef.current = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
loop.request();
|
|
44
|
+
return () => loop.cancel();
|
|
45
|
+
}, [loop]);
|
|
46
|
+
return /* @__PURE__ */ jsx("div", { className: "lk-fps-meter", ref: rootRef, children: /* @__PURE__ */ jsxs("span", { className: "lk-fps-meter-value", children: [
|
|
47
|
+
"FPS ",
|
|
48
|
+
fps
|
|
49
|
+
] }) });
|
|
50
|
+
}
|
|
51
|
+
function niceNumber(n) {
|
|
52
|
+
if (n <= 0) return 1;
|
|
53
|
+
const exp = Math.floor(Math.log10(n));
|
|
54
|
+
const fraction = n / 10 ** exp;
|
|
55
|
+
let nice;
|
|
56
|
+
if (fraction < 1.5) nice = 1;
|
|
57
|
+
else if (fraction < 3.5) nice = 2;
|
|
58
|
+
else if (fraction < 7.5) nice = 5;
|
|
59
|
+
else nice = 10;
|
|
60
|
+
return nice * 10 ** exp;
|
|
61
|
+
}
|
|
62
|
+
function ScaleIndicator({
|
|
63
|
+
zoom,
|
|
64
|
+
pixelsPerUnit = 1,
|
|
65
|
+
unit = "px",
|
|
66
|
+
targetWidth = 100
|
|
67
|
+
}) {
|
|
68
|
+
const ctxValue = useContext(CanvasStackContext);
|
|
69
|
+
const effectiveZoom = zoom ?? ctxValue?.view.zoom ?? 1;
|
|
70
|
+
const effectivePxPerUnit = pixelsPerUnit * effectiveZoom;
|
|
71
|
+
const targetUnits = targetWidth / effectivePxPerUnit;
|
|
72
|
+
const niceUnits = niceNumber(targetUnits);
|
|
73
|
+
const barWidth = niceUnits * effectivePxPerUnit;
|
|
74
|
+
return /* @__PURE__ */ jsxs("div", { className: "lk-scale-indicator", role: "img", "aria-label": `Scale: ${niceUnits} ${unit}`, children: [
|
|
75
|
+
/* @__PURE__ */ jsx("div", { className: "lk-scale-indicator-bar", style: { width: `${barWidth}px` } }),
|
|
76
|
+
/* @__PURE__ */ jsxs("span", { className: "lk-scale-indicator-label", children: [
|
|
77
|
+
niceUnits,
|
|
78
|
+
" ",
|
|
79
|
+
unit
|
|
80
|
+
] })
|
|
81
|
+
] });
|
|
82
|
+
}
|
|
83
|
+
var toTrack = (zoom) => Math.log2(zoom);
|
|
84
|
+
var fromTrack = (v2) => 2 ** v2;
|
|
85
|
+
var clamp = (v2, lo, hi) => Math.max(lo, Math.min(hi, v2));
|
|
86
|
+
function ZoomControl({
|
|
87
|
+
zoom,
|
|
88
|
+
onZoomChange,
|
|
89
|
+
onZoomCommit,
|
|
90
|
+
min = 0.1,
|
|
91
|
+
max = 8,
|
|
92
|
+
className
|
|
93
|
+
}) {
|
|
94
|
+
const commit = onZoomCommit ?? onZoomChange;
|
|
95
|
+
const emit = (track, done) => {
|
|
96
|
+
const next = clamp(fromTrack(track), min, max);
|
|
97
|
+
(done ? commit : onZoomChange)(next);
|
|
98
|
+
};
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", { className: `lk-zoom${className ? ` ${className}` : ""}`, children: [
|
|
100
|
+
/* @__PURE__ */ jsx(
|
|
101
|
+
v,
|
|
102
|
+
{
|
|
103
|
+
className: "lk-zoom__slider",
|
|
104
|
+
ariaLabel: "Zoom",
|
|
105
|
+
min: toTrack(min),
|
|
106
|
+
max: toTrack(max),
|
|
107
|
+
stops: [-2, -1, 0, 1, 2],
|
|
108
|
+
thumbs: [{ value: clamp(toTrack(zoom), toTrack(min), toTrack(max)) }],
|
|
109
|
+
onInput: (next) => emit(next[0].value, false),
|
|
110
|
+
onChange: (next) => emit(next[0].value, true),
|
|
111
|
+
readoutPlacement: "none"
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
f2,
|
|
116
|
+
{
|
|
117
|
+
className: "lk-zoom__field",
|
|
118
|
+
"aria-label": "Zoom",
|
|
119
|
+
value: zoom,
|
|
120
|
+
minValue: min,
|
|
121
|
+
maxValue: max,
|
|
122
|
+
step: 0.01,
|
|
123
|
+
hideSteppers: true,
|
|
124
|
+
formatOptions: { style: "percent", maximumFractionDigits: 0 },
|
|
125
|
+
onChange: (next) => {
|
|
126
|
+
if (!Number.isFinite(next)) return;
|
|
127
|
+
commit(clamp(next, min, max));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] });
|
|
132
|
+
}
|
|
133
|
+
function StatusBar({ children }) {
|
|
134
|
+
return /* @__PURE__ */ jsx("div", { className: "lk-status-bar", children });
|
|
135
|
+
}
|
|
136
|
+
function Section({ children, end }) {
|
|
137
|
+
return /* @__PURE__ */ jsx("span", { className: `lk-status-bar-section${end ? " lk-status-bar-section--end" : ""}`, children });
|
|
138
|
+
}
|
|
139
|
+
StatusBar.Section = Section;
|
|
140
|
+
function nextIndex(current, key, count) {
|
|
141
|
+
if (count === 0) return null;
|
|
142
|
+
switch (key) {
|
|
143
|
+
case "ArrowRight":
|
|
144
|
+
return (current + 1) % count;
|
|
145
|
+
case "ArrowLeft":
|
|
146
|
+
return (current - 1 + count) % count;
|
|
147
|
+
case "Home":
|
|
148
|
+
return 0;
|
|
149
|
+
case "End":
|
|
150
|
+
return count - 1;
|
|
151
|
+
default:
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function useRovingTabIndex() {
|
|
156
|
+
const ref = useRef(null);
|
|
157
|
+
const stop = useRef(0);
|
|
158
|
+
const items = useCallback(
|
|
159
|
+
() => ref.current ? Array.from(
|
|
160
|
+
ref.current.querySelectorAll('button:not([disabled]), [role="button"]')
|
|
161
|
+
) : [],
|
|
162
|
+
[]
|
|
163
|
+
);
|
|
164
|
+
const setTabStop = useCallback(
|
|
165
|
+
(index) => {
|
|
166
|
+
const found = items();
|
|
167
|
+
const at = Math.min(Math.max(index, 0), Math.max(found.length - 1, 0));
|
|
168
|
+
stop.current = at;
|
|
169
|
+
for (const [i, el] of found.entries()) el.tabIndex = i === at ? 0 : -1;
|
|
170
|
+
},
|
|
171
|
+
[items]
|
|
172
|
+
);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
const resync = () => {
|
|
175
|
+
const focused = items().indexOf(document.activeElement);
|
|
176
|
+
setTabStop(focused === -1 ? stop.current : focused);
|
|
177
|
+
};
|
|
178
|
+
resync();
|
|
179
|
+
if (!ref.current) return;
|
|
180
|
+
const mo = new MutationObserver(resync);
|
|
181
|
+
mo.observe(ref.current, { childList: true, subtree: true, attributeFilter: ["disabled"] });
|
|
182
|
+
return () => mo.disconnect();
|
|
183
|
+
}, [items, setTabStop]);
|
|
184
|
+
const onKeyDown = useCallback(
|
|
185
|
+
(e) => {
|
|
186
|
+
const found = items();
|
|
187
|
+
const current = found.indexOf(document.activeElement);
|
|
188
|
+
const next = nextIndex(current === -1 ? 0 : current, e.key, found.length);
|
|
189
|
+
if (next === null) return;
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
found[next]?.focus();
|
|
192
|
+
setTabStop(next);
|
|
193
|
+
},
|
|
194
|
+
[items, setTabStop]
|
|
195
|
+
);
|
|
196
|
+
return { ref, onKeyDown };
|
|
197
|
+
}
|
|
198
|
+
function Toolbar({ children, "aria-label": ariaLabel }) {
|
|
199
|
+
const { ref, onKeyDown } = useRovingTabIndex();
|
|
200
|
+
return /* @__PURE__ */ jsx(
|
|
201
|
+
"div",
|
|
202
|
+
{
|
|
203
|
+
ref,
|
|
204
|
+
className: "lk-toolbar",
|
|
205
|
+
role: "toolbar",
|
|
206
|
+
"aria-label": ariaLabel,
|
|
207
|
+
onKeyDown,
|
|
208
|
+
children
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
function Title({ children }) {
|
|
213
|
+
return /* @__PURE__ */ jsx("span", { className: "lk-toolbar-title", children });
|
|
214
|
+
}
|
|
215
|
+
function Group({ children, end, "aria-label": ariaLabel }) {
|
|
216
|
+
return (
|
|
217
|
+
// biome-ignore lint/a11y/useSemanticElements: a group inside role="toolbar" is the APG pattern; the rule is local and cannot see the parent. <fieldset> means form controls, needs a <legend> to be named, and its UA min-width breaks flex children.
|
|
218
|
+
/* @__PURE__ */ jsx(
|
|
219
|
+
"div",
|
|
220
|
+
{
|
|
221
|
+
className: `lk-toolbar-group${end ? " lk-toolbar-group--end" : ""}`,
|
|
222
|
+
role: "group",
|
|
223
|
+
"aria-label": ariaLabel,
|
|
224
|
+
children
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
function Button({
|
|
230
|
+
children,
|
|
231
|
+
onClick,
|
|
232
|
+
disabled,
|
|
233
|
+
title,
|
|
234
|
+
"aria-label": ariaLabel,
|
|
235
|
+
variant = "default",
|
|
236
|
+
iconOnly
|
|
237
|
+
}) {
|
|
238
|
+
const cls = [
|
|
239
|
+
"lk-toolbar-button",
|
|
240
|
+
variant === "danger" && "lk-toolbar-button--danger",
|
|
241
|
+
iconOnly && "lk-toolbar-button--icon"
|
|
242
|
+
].filter(Boolean).join(" ");
|
|
243
|
+
return /* @__PURE__ */ jsx(
|
|
244
|
+
"button",
|
|
245
|
+
{
|
|
246
|
+
type: "button",
|
|
247
|
+
className: cls,
|
|
248
|
+
onClick,
|
|
249
|
+
disabled,
|
|
250
|
+
title,
|
|
251
|
+
"aria-label": ariaLabel ?? title,
|
|
252
|
+
children
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
function Spacer() {
|
|
257
|
+
return /* @__PURE__ */ jsx("span", { className: "lk-toolbar-spacer", "aria-hidden": "true" });
|
|
258
|
+
}
|
|
259
|
+
Toolbar.Title = Title;
|
|
260
|
+
Toolbar.Group = Group;
|
|
261
|
+
Toolbar.Button = Button;
|
|
262
|
+
Toolbar.Spacer = Spacer;
|
|
263
|
+
|
|
264
|
+
export { FpsMeter, ScaleIndicator, StatusBar, Toolbar, ZoomControl };
|
|
265
|
+
//# sourceMappingURL=chunk-O7NVSCLN.js.map
|
|
266
|
+
//# sourceMappingURL=chunk-O7NVSCLN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/primitives/fpsAverage.ts","../src/primitives/FpsMeter.tsx","../src/primitives/ScaleIndicator.tsx","../src/primitives/ZoomControl.tsx","../src/primitives/StatusBar.tsx","../src/primitives/useRovingTabIndex.ts","../src/primitives/Toolbar.tsx"],"names":["jsxs","jsx","v","f","useRef","useEffect"],"mappings":"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","file":"chunk-O7NVSCLN.js","sourcesContent":["export function rollingAverage(samples: readonly number[]): number {\n if (samples.length === 0) return 0;\n const sum = samples.reduce((acc, n) => acc + n, 0);\n return Math.round(sum / samples.length);\n}\n","import { useVisibleRaf } from '@weasel-js/core';\nimport { useEffect, useRef, useState } from 'react';\nimport { rollingAverage } from './fpsAverage';\n\nconst SAMPLE_WINDOW = 30;\n\n/** A live frame-rate readout, averaged over the last 30 frames. */\nexport function FpsMeter() {\n const [fps, setFps] = useState(0);\n const samplesRef = useRef<number[]>([]);\n const lastTimeRef = useRef<number | null>(null);\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n const loop = useVisibleRaf(\n (time) => {\n const last = lastTimeRef.current;\n if (last !== null) {\n const delta = time - last;\n if (delta > 0) {\n const samples = samplesRef.current;\n samples.push(1000 / delta);\n if (samples.length > SAMPLE_WINDOW) samples.shift();\n setFps(rollingAverage(samples));\n }\n }\n lastTimeRef.current = time;\n loop.request();\n },\n {\n target: rootRef,\n // The gap spent suspended is not a frame that took an hour to draw.\n onResume: () => {\n lastTimeRef.current = null;\n },\n },\n );\n\n useEffect(() => {\n loop.request();\n return () => loop.cancel();\n }, [loop]);\n\n return (\n <div className=\"lk-fps-meter\" ref={rootRef}>\n <span className=\"lk-fps-meter-value\">FPS {fps}</span>\n </div>\n );\n}\n","import { useContext } from 'react';\nimport { CanvasStackContext } from '../canvas/CanvasStackContext';\n\n/** Props for `<ScaleIndicator>`. */\nexport interface ScaleIndicatorProps {\n /** Current view zoom factor. If omitted, reads from CanvasStackContext. Defaults to 1. */\n zoom?: number;\n /** World pixels per unit at zoom=1. Default: 1. */\n pixelsPerUnit?: number;\n /** Unit label. Default: 'px'. */\n unit?: string;\n /** Target bar width in display pixels. Default: 100. */\n targetWidth?: number;\n}\n\n// Rounds n to a \"nice\" number — 1, 2, 5, 10, 20, 50, ...\nfunction niceNumber(n: number): number {\n if (n <= 0) return 1;\n const exp = Math.floor(Math.log10(n));\n const fraction = n / 10 ** exp;\n let nice: number;\n if (fraction < 1.5) nice = 1;\n else if (fraction < 3.5) nice = 2;\n else if (fraction < 7.5) nice = 5;\n else nice = 10;\n return nice * 10 ** exp;\n}\n\n/** A scale bar: a labeled rule showing how far a round number of world units\n * is at the current zoom. Reads the zoom from the surrounding canvas stack\n * unless one is passed. */\nexport function ScaleIndicator({\n zoom,\n pixelsPerUnit = 1,\n unit = 'px',\n targetWidth = 100,\n}: ScaleIndicatorProps) {\n const ctxValue = useContext(CanvasStackContext);\n const effectiveZoom = zoom ?? ctxValue?.view.zoom ?? 1;\n const effectivePxPerUnit = pixelsPerUnit * effectiveZoom;\n const targetUnits = targetWidth / effectivePxPerUnit;\n const niceUnits = niceNumber(targetUnits);\n const barWidth = niceUnits * effectivePxPerUnit;\n return (\n <div className=\"lk-scale-indicator\" role=\"img\" aria-label={`Scale: ${niceUnits} ${unit}`}>\n <div className=\"lk-scale-indicator-bar\" style={{ width: `${barWidth}px` }} />\n <span className=\"lk-scale-indicator-label\">\n {niceUnits} {unit}\n </span>\n </div>\n );\n}\n","import { NumberField, Slider } from '../passthrough/weasel-ui';\n\n/** Props for `<ZoomControl>`. */\nexport interface ZoomControlProps {\n /** Current zoom as a scale factor: 1 is actual size. */\n zoom: number;\n /** Fired continuously while the slider drags. */\n onZoomChange: (zoom: number) => void;\n /** Fired when a drag settles or the field is committed. Defaults to\n * `onZoomChange`. */\n onZoomCommit?: (zoom: number) => void;\n /** Smallest selectable zoom. Defaults to 0.1 (10%). */\n min?: number;\n /** Largest selectable zoom. Defaults to 8 (800%). */\n max?: number;\n className?: string;\n}\n\n// Zoom is perceptually geometric — halving is as big a step as doubling — so\n// the slider runs on log2 and 100% lands mid-track for a symmetric range.\nconst toTrack = (zoom: number): number => Math.log2(zoom);\nconst fromTrack = (v: number): number => 2 ** v;\n\nconst clamp = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(hi, v));\n\n/** Zoom as a log-scaled slider with an editable percentage beside it. Pair it\n * with zoom-in / zoom-out buttons in a `<Toolbar.Group>`; this component owns\n * only the continuous part. */\nexport function ZoomControl({\n zoom,\n onZoomChange,\n onZoomCommit,\n min = 0.1,\n max = 8,\n className,\n}: ZoomControlProps) {\n const commit = onZoomCommit ?? onZoomChange;\n const emit = (track: number, done: boolean): void => {\n const next = clamp(fromTrack(track), min, max);\n (done ? commit : onZoomChange)(next);\n };\n\n return (\n <div className={`lk-zoom${className ? ` ${className}` : ''}`}>\n <Slider\n className=\"lk-zoom__slider\"\n ariaLabel=\"Zoom\"\n min={toTrack(min)}\n max={toTrack(max)}\n // Detents at the octaves plus actual size, so 25/50/100/200/400% are\n // reachable by drag rather than only by typing.\n stops={[-2, -1, 0, 1, 2]}\n thumbs={[{ value: clamp(toTrack(zoom), toTrack(min), toTrack(max)) }]}\n onInput={(next) => emit(next[0].value, false)}\n onChange={(next) => emit(next[0].value, true)}\n readoutPlacement=\"none\"\n />\n {/* `style: 'percent'` formats a fraction, so the field carries the scale\n factor itself and renders 0.5 as \"50%\". */}\n <NumberField\n className=\"lk-zoom__field\"\n aria-label=\"Zoom\"\n value={zoom}\n minValue={min}\n maxValue={max}\n step={0.01}\n hideSteppers\n formatOptions={{ style: 'percent', maximumFractionDigits: 0 }}\n onChange={(next) => {\n if (!Number.isFinite(next)) return;\n commit(clamp(next, min, max));\n }}\n />\n </div>\n );\n}\n","import type { ReactNode } from 'react';\n\n/** Props for `<StatusBar>`. */\nexport interface StatusBarProps {\n children: ReactNode;\n}\n\n/** A footer strip for readouts. Fill it with `<StatusBar.Section>`. */\nexport function StatusBar({ children }: StatusBarProps) {\n return <div className=\"lk-status-bar\">{children}</div>;\n}\n\n/** Props for `<StatusBar.Section>`. */\nexport interface StatusBarSectionProps {\n children: ReactNode;\n /** Pushes this section, and everything after it, to the far end. */\n end?: boolean;\n}\nfunction Section({ children, end }: StatusBarSectionProps) {\n return (\n <span className={`lk-status-bar-section${end ? ' lk-status-bar-section--end' : ''}`}>\n {children}\n </span>\n );\n}\n\n/** One readout within a status bar. */\nStatusBar.Section = Section;\n","import { type KeyboardEvent, useCallback, useEffect, useRef } from 'react';\n\n/** Where a key takes focus within `count` items, or null if it does not move it. */\nexport function nextIndex(current: number, key: string, count: number): number | null {\n if (count === 0) return null;\n switch (key) {\n case 'ArrowRight':\n return (current + 1) % count;\n case 'ArrowLeft':\n return (current - 1 + count) % count;\n case 'Home':\n return 0;\n case 'End':\n return count - 1;\n default:\n return null;\n }\n}\n\n/**\n * The APG roving-tabindex contract: exactly one item in the tab order, arrows\n * moving focus within. `tabIndex` is written onto the DOM nodes rather than\n * threaded through props, because the items are arbitrary children — the\n * container never sees them as a list it could index.\n */\nexport function useRovingTabIndex<T extends HTMLElement>() {\n const ref = useRef<T | null>(null);\n const stop = useRef(0);\n\n const items = useCallback(\n (): HTMLElement[] =>\n ref.current\n ? Array.from(\n ref.current.querySelectorAll<HTMLElement>('button:not([disabled]), [role=\"button\"]'),\n )\n : [],\n [],\n );\n\n const setTabStop = useCallback(\n (index: number) => {\n const found = items();\n const at = Math.min(Math.max(index, 0), Math.max(found.length - 1, 0));\n stop.current = at;\n for (const [i, el] of found.entries()) el.tabIndex = i === at ? 0 : -1;\n },\n [items],\n );\n\n // Re-establish the tab stop whenever the item set changes — a trial's toolbar\n // contributions are dynamic, and `disabled` moves a button in and out of the\n // set without touching the child list. The focused item keeps the stop.\n useEffect(() => {\n const resync = () => {\n const focused = items().indexOf(document.activeElement as HTMLElement);\n setTabStop(focused === -1 ? stop.current : focused);\n };\n resync();\n if (!ref.current) return;\n const mo = new MutationObserver(resync);\n mo.observe(ref.current, { childList: true, subtree: true, attributeFilter: ['disabled'] });\n return () => mo.disconnect();\n }, [items, setTabStop]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<T>) => {\n const found = items();\n const current = found.indexOf(document.activeElement as HTMLElement);\n const next = nextIndex(current === -1 ? 0 : current, e.key, found.length);\n if (next === null) return;\n e.preventDefault();\n found[next]?.focus();\n setTabStop(next);\n },\n [items, setTabStop],\n );\n\n return { ref, onKeyDown };\n}\n","import type { MouseEventHandler, ReactNode } from 'react';\nimport { useRovingTabIndex } from './useRovingTabIndex';\n\n/** Props for `<Toolbar>`. */\nexport interface ToolbarProps {\n children: ReactNode;\n /** Names the toolbar for assistive tech. Required by the APG pattern when a\n * view holds more than one. */\n 'aria-label'?: string;\n}\n\n/** A horizontal bar of controls. Fill it with `<Toolbar.Title>`,\n * `<Toolbar.Group>`, `<Toolbar.Button>` and `<Toolbar.Spacer>`. */\nexport function Toolbar({ children, 'aria-label': ariaLabel }: ToolbarProps) {\n const { ref, onKeyDown } = useRovingTabIndex<HTMLDivElement>();\n return (\n <div\n ref={ref}\n className=\"lk-toolbar\"\n role=\"toolbar\"\n aria-label={ariaLabel}\n onKeyDown={onKeyDown}\n >\n {children}\n </div>\n );\n}\n\ninterface TitleProps {\n children: ReactNode;\n}\nfunction Title({ children }: TitleProps) {\n return <span className=\"lk-toolbar-title\">{children}</span>;\n}\n\n/** Props for `<Toolbar.Group>`. */\nexport interface ToolbarGroupProps {\n children: ReactNode;\n /** Pushes this group, and everything after it, to the far end. */\n end?: boolean;\n 'aria-label'?: string;\n}\n\n/** Related controls, kept tight and ruled off from their neighbors. Group\n * rather than separate with repeated `<Toolbar.Spacer>`: consecutive spacers\n * share the free space, so every gap collapses to the same slack and the bar\n * reads as one undifferentiated run. */\nfunction Group({ children, end, 'aria-label': ariaLabel }: ToolbarGroupProps) {\n return (\n // biome-ignore lint/a11y/useSemanticElements: a group inside role=\"toolbar\" is the APG pattern; the rule is local and cannot see the parent. <fieldset> means form controls, needs a <legend> to be named, and its UA min-width breaks flex children.\n <div\n className={`lk-toolbar-group${end ? ' lk-toolbar-group--end' : ''}`}\n role=\"group\"\n aria-label={ariaLabel}\n >\n {children}\n </div>\n );\n}\n\n/** Props for `<Toolbar.Button>`. */\nexport interface ToolbarButtonProps {\n children: ReactNode;\n onClick: MouseEventHandler<HTMLButtonElement>;\n disabled?: boolean;\n title?: string;\n /** Required when `children` is an icon with no text of its own. */\n 'aria-label'?: string;\n /** `danger` reddens on hover — for actions that discard work. */\n variant?: 'default' | 'danger';\n /** Square, for a button whose whole content is one glyph. */\n iconOnly?: boolean;\n}\n\nfunction Button({\n children,\n onClick,\n disabled,\n title,\n 'aria-label': ariaLabel,\n variant = 'default',\n iconOnly,\n}: ToolbarButtonProps) {\n const cls = [\n 'lk-toolbar-button',\n variant === 'danger' && 'lk-toolbar-button--danger',\n iconOnly && 'lk-toolbar-button--icon',\n ]\n .filter(Boolean)\n .join(' ');\n return (\n <button\n type=\"button\"\n className={cls}\n onClick={onClick}\n disabled={disabled}\n title={title}\n aria-label={ariaLabel ?? title}\n >\n {children}\n </button>\n );\n}\n\nfunction Spacer() {\n return <span className=\"lk-toolbar-spacer\" aria-hidden=\"true\" />;\n}\n\n/** A label within a toolbar. */\nToolbar.Title = Title;\n/** A ruled-off run of related controls. */\nToolbar.Group = Group;\n/** A button within a toolbar. */\nToolbar.Button = Button;\n/** Flexible space that pushes what follows to the far end of the toolbar. */\nToolbar.Spacer = Spacer;\n"]}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { ZoomControl, ScaleIndicator, FpsMeter, StatusBar, Toolbar } from './chunk-O7NVSCLN.js';
|
|
2
|
+
import { ControlPanel } from './chunk-ZZAYVX4X.js';
|
|
3
|
+
import { St, Ct, wt, gt, Tt, Et, p2, ft, pt, mt, n3, o5 } from './chunk-TLGRYALP.js';
|
|
4
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
|
|
7
|
+
var ZOOM_STEP = 1.25;
|
|
8
|
+
function builtinContributions(instrument, ctx, controls) {
|
|
9
|
+
const out = [];
|
|
10
|
+
const zoom = ctx.zoom;
|
|
11
|
+
if (instrument.undo != null) {
|
|
12
|
+
out.push({
|
|
13
|
+
id: "undo",
|
|
14
|
+
region: "toolbar",
|
|
15
|
+
group: "history",
|
|
16
|
+
item: {
|
|
17
|
+
icon: St,
|
|
18
|
+
label: "Undo",
|
|
19
|
+
shortcut: "Mod+Z",
|
|
20
|
+
disabled: !ctx.canUndo,
|
|
21
|
+
onActivate: ctx.undo
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
out.push({
|
|
25
|
+
id: "redo",
|
|
26
|
+
region: "toolbar",
|
|
27
|
+
group: "history",
|
|
28
|
+
item: {
|
|
29
|
+
icon: Ct,
|
|
30
|
+
label: "Redo",
|
|
31
|
+
shortcut: "Mod+Shift+Z",
|
|
32
|
+
disabled: !ctx.canRedo,
|
|
33
|
+
onActivate: ctx.redo
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
for (const t of instrument.tools?.tools ?? []) {
|
|
38
|
+
out.push({
|
|
39
|
+
id: t.id,
|
|
40
|
+
region: "palette",
|
|
41
|
+
group: t.group,
|
|
42
|
+
item: { icon: t.icon, label: t.label, shortcut: t.shortcut }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (instrument.canvas != null && zoom !== null) {
|
|
46
|
+
out.push({
|
|
47
|
+
id: "zoom-out",
|
|
48
|
+
region: "viewport",
|
|
49
|
+
group: "zoom",
|
|
50
|
+
item: {
|
|
51
|
+
icon: wt,
|
|
52
|
+
label: "Zoom out",
|
|
53
|
+
onActivate: () => ctx.setZoom(zoom / ZOOM_STEP)
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
out.push({
|
|
57
|
+
id: "zoom-in",
|
|
58
|
+
region: "viewport",
|
|
59
|
+
group: "zoom",
|
|
60
|
+
item: {
|
|
61
|
+
icon: gt,
|
|
62
|
+
label: "Zoom in",
|
|
63
|
+
onActivate: () => ctx.setZoom(zoom * ZOOM_STEP)
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
out.push({
|
|
67
|
+
id: "actual-size",
|
|
68
|
+
region: "viewport",
|
|
69
|
+
group: "zoom",
|
|
70
|
+
item: { icon: Tt, label: "Actual size", onActivate: () => ctx.setZoom(1) }
|
|
71
|
+
});
|
|
72
|
+
out.push({
|
|
73
|
+
id: "zoom-control",
|
|
74
|
+
region: "viewport",
|
|
75
|
+
group: "zoom",
|
|
76
|
+
render: (c) => /* @__PURE__ */ jsx(ZoomControl, { zoom: c.zoom ?? 1, onZoomChange: c.setZoom })
|
|
77
|
+
});
|
|
78
|
+
out.push({
|
|
79
|
+
id: "scale",
|
|
80
|
+
region: "status",
|
|
81
|
+
group: "view",
|
|
82
|
+
render: () => /* @__PURE__ */ jsx(ScaleIndicator, {})
|
|
83
|
+
});
|
|
84
|
+
out.push({
|
|
85
|
+
id: "fps",
|
|
86
|
+
region: "status",
|
|
87
|
+
group: "view",
|
|
88
|
+
end: true,
|
|
89
|
+
render: () => /* @__PURE__ */ jsx(FpsMeter, {})
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (Object.keys(ctx.configSchema.group.children).length > 0) {
|
|
93
|
+
out.push({
|
|
94
|
+
id: "settings",
|
|
95
|
+
region: "sidebar",
|
|
96
|
+
item: {
|
|
97
|
+
title: "Settings",
|
|
98
|
+
body: /* @__PURE__ */ jsx(
|
|
99
|
+
ControlPanel,
|
|
100
|
+
{
|
|
101
|
+
schema: ctx.configSchema,
|
|
102
|
+
config: ctx.config,
|
|
103
|
+
setConfig: (key, value) => ctx.setConfig(String(key), value),
|
|
104
|
+
renderers: controls
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
out.push({
|
|
111
|
+
id: "snapshot",
|
|
112
|
+
region: "toolbar",
|
|
113
|
+
group: "history",
|
|
114
|
+
item: {
|
|
115
|
+
icon: Et,
|
|
116
|
+
label: "Save snapshot",
|
|
117
|
+
shortcut: "Mod+S",
|
|
118
|
+
onActivate: () => ctx.saveSnapshot()
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (ctx.savedSnapshots.length > 0) {
|
|
122
|
+
out.push({
|
|
123
|
+
id: "snapshot-load",
|
|
124
|
+
region: "toolbar",
|
|
125
|
+
group: "trial",
|
|
126
|
+
end: true,
|
|
127
|
+
render: (c) => /* @__PURE__ */ jsx(
|
|
128
|
+
p2,
|
|
129
|
+
{
|
|
130
|
+
className: "lk-toolbar__load-select",
|
|
131
|
+
"aria-label": "Load snapshot",
|
|
132
|
+
placeholder: "Load\u2026",
|
|
133
|
+
selectedKey: null,
|
|
134
|
+
options: c.savedSnapshots.map((sn) => ({ value: sn.id, label: sn.name })),
|
|
135
|
+
onSelectionChange: (id) => {
|
|
136
|
+
if (id != null) c.loadSnapshot(String(id));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
out.push({
|
|
143
|
+
id: "clone",
|
|
144
|
+
region: "toolbar",
|
|
145
|
+
group: "trial",
|
|
146
|
+
end: true,
|
|
147
|
+
item: { icon: ft, label: "Clone trial", onActivate: ctx.clone }
|
|
148
|
+
});
|
|
149
|
+
out.push({
|
|
150
|
+
id: "reset",
|
|
151
|
+
region: "toolbar",
|
|
152
|
+
group: "trial",
|
|
153
|
+
end: true,
|
|
154
|
+
item: { icon: pt, label: "Reset trial", onActivate: ctx.reset }
|
|
155
|
+
});
|
|
156
|
+
out.push({
|
|
157
|
+
id: "close",
|
|
158
|
+
region: "titlebar",
|
|
159
|
+
end: true,
|
|
160
|
+
item: {
|
|
161
|
+
icon: mt,
|
|
162
|
+
label: ctx.isLastTrial ? "Cannot close the last trial" : "Close trial",
|
|
163
|
+
danger: true,
|
|
164
|
+
disabled: ctx.isLastTrial,
|
|
165
|
+
onActivate: ctx.close
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/chrome/merge.ts
|
|
172
|
+
function mergeContributions(...bundles) {
|
|
173
|
+
const out = [];
|
|
174
|
+
const seen = /* @__PURE__ */ new Set();
|
|
175
|
+
for (const bundle of bundles) {
|
|
176
|
+
for (const entry of bundle) {
|
|
177
|
+
if (seen.has(entry.id)) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`[labkit] mergeContributions: duplicate contribution id "${entry.id}". Two bundles registered the same id; rename one, or suppress the built-in before adding yours.`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
seen.add(entry.id);
|
|
183
|
+
out.push(entry);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
function suppressContributions(bundle, ids) {
|
|
189
|
+
const present = new Set(bundle.map((c) => c.id));
|
|
190
|
+
for (const id of ids) {
|
|
191
|
+
if (!present.has(id)) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`[labkit] cannot suppress "${id}": no contribution with that id. Present ids: ${[...present].join(", ")}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const drop = new Set(ids);
|
|
198
|
+
return bundle.filter((c) => !drop.has(c.id));
|
|
199
|
+
}
|
|
200
|
+
function PaletteRegion({ contributions, ctx }) {
|
|
201
|
+
if (contributions.length === 0) return null;
|
|
202
|
+
return /* @__PURE__ */ jsx(
|
|
203
|
+
"div",
|
|
204
|
+
{
|
|
205
|
+
className: "lk-palette-region",
|
|
206
|
+
role: "toolbar",
|
|
207
|
+
"aria-label": "Tools",
|
|
208
|
+
"aria-orientation": "vertical",
|
|
209
|
+
children: /* @__PURE__ */ jsx(n3, { orientation: "vertical", children: contributions.map((c) => {
|
|
210
|
+
if (c.render) return /* @__PURE__ */ jsx("span", { children: c.render(ctx) }, c.id);
|
|
211
|
+
if (c.region !== "palette" || !c.item) return null;
|
|
212
|
+
const { icon: Icon, label, shortcut, disabled } = c.item;
|
|
213
|
+
return /* @__PURE__ */ jsx(
|
|
214
|
+
o5,
|
|
215
|
+
{
|
|
216
|
+
icon: /* @__PURE__ */ jsx(Icon, { size: 16 }),
|
|
217
|
+
label,
|
|
218
|
+
shortcut,
|
|
219
|
+
active: ctx.activeToolId === c.id,
|
|
220
|
+
disabled,
|
|
221
|
+
onClick: () => ctx.setActiveTool(c.id)
|
|
222
|
+
},
|
|
223
|
+
c.id
|
|
224
|
+
);
|
|
225
|
+
}) })
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
function Section({
|
|
230
|
+
title,
|
|
231
|
+
defaultCollapsed,
|
|
232
|
+
children
|
|
233
|
+
}) {
|
|
234
|
+
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
235
|
+
return /* @__PURE__ */ jsxs("section", { className: "lk-sidebar-section", children: [
|
|
236
|
+
/* @__PURE__ */ jsx(
|
|
237
|
+
"button",
|
|
238
|
+
{
|
|
239
|
+
type: "button",
|
|
240
|
+
className: "lk-sidebar-section__head",
|
|
241
|
+
"aria-expanded": !collapsed,
|
|
242
|
+
onClick: () => setCollapsed((c) => !c),
|
|
243
|
+
children: title
|
|
244
|
+
}
|
|
245
|
+
),
|
|
246
|
+
collapsed ? null : /* @__PURE__ */ jsx("div", { className: "lk-sidebar-section__body", children })
|
|
247
|
+
] });
|
|
248
|
+
}
|
|
249
|
+
function SidebarRegion({ contributions, ctx }) {
|
|
250
|
+
if (contributions.length === 0) return null;
|
|
251
|
+
return /* @__PURE__ */ jsx(Fragment, { children: contributions.map((c) => {
|
|
252
|
+
if (c.render) return /* @__PURE__ */ jsx("div", { children: c.render(ctx) }, c.id);
|
|
253
|
+
if (c.region !== "sidebar" || !c.item) return null;
|
|
254
|
+
return /* @__PURE__ */ jsx(
|
|
255
|
+
Section,
|
|
256
|
+
{
|
|
257
|
+
title: c.item.title,
|
|
258
|
+
defaultCollapsed: c.item.defaultCollapsed ?? false,
|
|
259
|
+
children: c.item.body
|
|
260
|
+
},
|
|
261
|
+
c.id
|
|
262
|
+
);
|
|
263
|
+
}) });
|
|
264
|
+
}
|
|
265
|
+
function StatusRegion({ contributions, ctx }) {
|
|
266
|
+
if (contributions.length === 0) return null;
|
|
267
|
+
return /* @__PURE__ */ jsx(StatusBar, { children: contributions.map((c) => {
|
|
268
|
+
if (c.render)
|
|
269
|
+
return /* @__PURE__ */ jsx(StatusBar.Section, { end: c.end, children: c.render(ctx) }, c.id);
|
|
270
|
+
if (c.region !== "status" || !c.item) return null;
|
|
271
|
+
return /* @__PURE__ */ jsx(StatusBar.Section, { end: c.end, children: /* @__PURE__ */ jsx("span", { title: c.item.title, children: c.item.text }) }, c.id);
|
|
272
|
+
}) });
|
|
273
|
+
}
|
|
274
|
+
function renderEntry(c, ctx) {
|
|
275
|
+
if (c.render) return /* @__PURE__ */ jsx("span", { children: c.render(ctx) }, c.id);
|
|
276
|
+
if (c.region !== "titlebar" || !c.item) return null;
|
|
277
|
+
const { icon: Icon, label, shortcut, disabled, danger } = c.item;
|
|
278
|
+
return /* @__PURE__ */ jsx(
|
|
279
|
+
"button",
|
|
280
|
+
{
|
|
281
|
+
type: "button",
|
|
282
|
+
className: `lk-titlebar-button${danger ? " lk-titlebar-button--danger" : ""}`,
|
|
283
|
+
disabled,
|
|
284
|
+
"aria-label": label,
|
|
285
|
+
title: shortcut ? `${label} (${shortcut})` : label,
|
|
286
|
+
onClick: c.item.onActivate,
|
|
287
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
288
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
289
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 14 })
|
|
290
|
+
},
|
|
291
|
+
c.id
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
function TitleBarRegion({ contributions, ctx }) {
|
|
295
|
+
if (contributions.length === 0) return null;
|
|
296
|
+
return /* @__PURE__ */ jsx("span", { className: "lk-trial__titlebar-actions", children: contributions.map((c) => renderEntry(c, ctx)) });
|
|
297
|
+
}
|
|
298
|
+
function groupsOf(contributions) {
|
|
299
|
+
const groups = [];
|
|
300
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
301
|
+
for (const c of contributions) {
|
|
302
|
+
if (c.group == null) {
|
|
303
|
+
groups.push({ key: c.id, end: c.end ?? false, entries: [c] });
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
let g = byKey.get(c.group);
|
|
307
|
+
if (!g) {
|
|
308
|
+
g = { key: c.group, end: c.end ?? false, entries: [] };
|
|
309
|
+
byKey.set(c.group, g);
|
|
310
|
+
groups.push(g);
|
|
311
|
+
}
|
|
312
|
+
g.entries.push(c);
|
|
313
|
+
}
|
|
314
|
+
return groups;
|
|
315
|
+
}
|
|
316
|
+
function renderEntry2(c, ctx) {
|
|
317
|
+
if (c.render) return /* @__PURE__ */ jsx("span", { children: c.render(ctx) }, c.id);
|
|
318
|
+
if (c.region !== "toolbar" || !c.item) return null;
|
|
319
|
+
const { icon: Icon, label, shortcut, disabled, danger, showLabel } = c.item;
|
|
320
|
+
return /* @__PURE__ */ jsxs(
|
|
321
|
+
Toolbar.Button,
|
|
322
|
+
{
|
|
323
|
+
iconOnly: !showLabel,
|
|
324
|
+
variant: danger ? "danger" : "default",
|
|
325
|
+
disabled,
|
|
326
|
+
"aria-label": label,
|
|
327
|
+
title: shortcut ? `${label} (${shortcut})` : label,
|
|
328
|
+
onClick: c.item.onActivate,
|
|
329
|
+
children: [
|
|
330
|
+
/* @__PURE__ */ jsx(Icon, { size: 16 }),
|
|
331
|
+
showLabel ? /* @__PURE__ */ jsx("span", { children: label }) : null
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
c.id
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
function ToolbarRegion({ contributions, ctx }) {
|
|
338
|
+
if (contributions.length === 0) return null;
|
|
339
|
+
const groups = groupsOf(contributions);
|
|
340
|
+
return /* @__PURE__ */ jsx(Toolbar, { "aria-label": "Trial actions", children: groups.map((g) => /* @__PURE__ */ jsx(Toolbar.Group, { end: g.end, "aria-label": g.key, children: g.entries.map((c) => renderEntry2(c, ctx)) }, g.key)) });
|
|
341
|
+
}
|
|
342
|
+
function ViewportRegion({ contributions, ctx }) {
|
|
343
|
+
if (contributions.length === 0) return null;
|
|
344
|
+
return /* @__PURE__ */ jsx("div", { className: "lk-viewport-controls", role: "toolbar", "aria-label": "View", children: contributions.map((c) => {
|
|
345
|
+
if (c.render) return /* @__PURE__ */ jsx("span", { children: c.render(ctx) }, c.id);
|
|
346
|
+
if (c.region !== "viewport" || !c.item) return null;
|
|
347
|
+
const { icon: Icon, label, disabled } = c.item;
|
|
348
|
+
return /* @__PURE__ */ jsx(
|
|
349
|
+
"button",
|
|
350
|
+
{
|
|
351
|
+
type: "button",
|
|
352
|
+
className: "lk-viewport-controls__button",
|
|
353
|
+
"aria-label": label,
|
|
354
|
+
title: label,
|
|
355
|
+
disabled,
|
|
356
|
+
onClick: c.item.onActivate,
|
|
357
|
+
children: /* @__PURE__ */ jsx(Icon, { size: 16 })
|
|
358
|
+
},
|
|
359
|
+
c.id
|
|
360
|
+
);
|
|
361
|
+
}) });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export { PaletteRegion, SidebarRegion, StatusRegion, TitleBarRegion, ToolbarRegion, ViewportRegion, builtinContributions, mergeContributions, suppressContributions };
|
|
365
|
+
//# sourceMappingURL=chunk-PO6L3NSH.js.map
|
|
366
|
+
//# sourceMappingURL=chunk-PO6L3NSH.js.map
|