@weasel-js/labkit 1.2.0 → 1.3.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.
Files changed (227) hide show
  1. package/dist/_dts/{DrawCommand-C_XboUpz.d.ts → DrawCommand-DBB45NfN.d.ts} +196 -52
  2. package/dist/_dts/PrefsForm-BYa6cWnO.d.ts +201 -0
  3. package/dist/_dts/types-1Sdxy_Pv.d.ts +90 -0
  4. package/dist/_dts/types-BttTIent.d.ts +314 -0
  5. package/dist/_dts/types-DCGEcMO_.d.ts +162 -0
  6. package/dist/_dts/{useTrialState-BMNIx3Cy.d.ts → useTrialState-CsGMjhu9.d.ts} +5 -87
  7. package/dist/canvas/index.d.ts +7 -3
  8. package/dist/canvas/index.js +3 -1
  9. package/dist/chrome/index.d.ts +86 -0
  10. package/dist/chrome/index.js +9 -0
  11. package/dist/chrome/index.js.map +1 -0
  12. package/dist/chunk-3XXPU73K.js +138 -0
  13. package/dist/chunk-3XXPU73K.js.map +1 -0
  14. package/dist/{chunk-VUU5UXHE.js → chunk-4TMMVIDM.js} +8 -5
  15. package/dist/chunk-4TMMVIDM.js.map +1 -0
  16. package/dist/chunk-6X5RBC6G.js +366 -0
  17. package/dist/chunk-6X5RBC6G.js.map +1 -0
  18. package/dist/{chunk-574LJAV4.js → chunk-BDJXIBRZ.js} +33 -35
  19. package/dist/chunk-BDJXIBRZ.js.map +1 -0
  20. package/dist/{chunk-SFL7NFKN.js → chunk-BMW4TDP5.js} +15 -7
  21. package/dist/chunk-BMW4TDP5.js.map +1 -0
  22. package/dist/chunk-JDROYQM3.js +266 -0
  23. package/dist/chunk-JDROYQM3.js.map +1 -0
  24. package/dist/{chunk-THBG7FQZ.js → chunk-LGKRIUSW.js} +15 -12
  25. package/dist/chunk-LGKRIUSW.js.map +1 -0
  26. package/dist/{chunk-BOHF3PQO.js → chunk-QCICIHEO.js} +8129 -5196
  27. package/dist/chunk-QCICIHEO.js.map +1 -0
  28. package/dist/chunk-QRYPXSGP.js +212 -0
  29. package/dist/chunk-QRYPXSGP.js.map +1 -0
  30. package/dist/chunk-RGT4EJIB.js +6961 -0
  31. package/dist/chunk-RGT4EJIB.js.map +1 -0
  32. package/dist/chunk-SBXNQW4G.js +3 -0
  33. package/dist/chunk-SBXNQW4G.js.map +1 -0
  34. package/dist/chunk-T66HU3HX.js +17 -0
  35. package/dist/chunk-T66HU3HX.js.map +1 -0
  36. package/dist/chunk-WTI26YTM.js +266 -0
  37. package/dist/chunk-WTI26YTM.js.map +1 -0
  38. package/dist/controls/index.d.ts +19 -5
  39. package/dist/controls/index.js +3 -1
  40. package/dist/dragdrop/index.d.ts +46 -3
  41. package/dist/dragdrop/index.js +3 -1
  42. package/dist/index.d.ts +735 -565
  43. package/dist/index.js +661 -1019
  44. package/dist/index.js.map +1 -1
  45. package/dist/layers/index.d.ts +5 -3
  46. package/dist/layers/index.js +4 -1
  47. package/dist/passthrough/weasel-canvas.d.ts +69 -13
  48. package/dist/passthrough/weasel-canvas.js +1 -1
  49. package/dist/passthrough/weasel-ui.d.ts +3811 -3671
  50. package/dist/passthrough/weasel-ui.js +3 -2
  51. package/dist/primitives/index.d.ts +102 -8
  52. package/dist/primitives/index.js +5 -1
  53. package/dist/state/index.d.ts +3 -2
  54. package/dist/state/index.js +2 -2
  55. package/dist/styles.css +490 -803
  56. package/dist/surface/index.js +2 -1
  57. package/dist/ui/layers/index.d.ts +8 -2
  58. package/dist/ui/layers/index.js +2 -3
  59. package/dist/undo/index.d.ts +4 -2
  60. package/package.json +16 -7
  61. package/src/canvas/AGENTS.md +4 -0
  62. package/src/canvas/CanvasStack.tsx +6 -2
  63. package/src/canvas/useLayerScheduler.ts +22 -15
  64. package/src/canvas/usePanZoom.test.ts +80 -0
  65. package/src/canvas/usePanZoom.ts +16 -2
  66. package/src/chrome/ChromeRegions.stories.tsx +106 -0
  67. package/src/chrome/builtins.test.ts +153 -0
  68. package/src/chrome/builtins.tsx +205 -0
  69. package/src/chrome/index.ts +25 -0
  70. package/src/chrome/merge.test.ts +51 -0
  71. package/src/chrome/merge.ts +52 -0
  72. package/src/chrome/regions/PaletteRegion.less +14 -0
  73. package/src/chrome/regions/PaletteRegion.test.tsx +36 -0
  74. package/src/chrome/regions/PaletteRegion.tsx +41 -0
  75. package/src/chrome/regions/SidebarRegion.tsx +56 -0
  76. package/src/chrome/regions/StatusRegion.tsx +31 -0
  77. package/src/chrome/regions/TitleBarRegion.tsx +41 -0
  78. package/src/chrome/regions/ToolbarRegion.test.tsx +82 -0
  79. package/src/chrome/regions/ToolbarRegion.tsx +71 -0
  80. package/src/chrome/regions/ViewportRegion.less +42 -0
  81. package/src/chrome/regions/ViewportRegion.tsx +37 -0
  82. package/src/chrome/regions/regions.test.tsx +81 -0
  83. package/src/chrome/types.ts +127 -0
  84. package/src/config/builder.test.ts +93 -0
  85. package/src/config/builder.ts +220 -0
  86. package/src/config/fromConfigField.test.ts +93 -0
  87. package/src/config/fromConfigField.ts +74 -0
  88. package/src/config/index.ts +22 -0
  89. package/src/config/resolve.test.ts +119 -0
  90. package/src/config/resolve.ts +67 -0
  91. package/src/config/rules.ts +68 -0
  92. package/src/config/types.ts +108 -0
  93. package/src/config/useConfigSchema.ts +30 -0
  94. package/src/config/visible.ts +20 -0
  95. package/src/controls/ControlPanel.less +4 -58
  96. package/src/controls/ControlPanel.stories.tsx +125 -0
  97. package/src/controls/ControlPanel.test.tsx +245 -10
  98. package/src/controls/ControlPanel.tsx +236 -203
  99. package/src/dragdrop/DragDropRuntime.tsx +36 -18
  100. package/src/dragdrop/DragGhost.less +1 -1
  101. package/src/dragdrop/Palette.less +1 -1
  102. package/src/dragdrop/index.ts +11 -0
  103. package/src/index.ts +74 -5
  104. package/src/instrument/SineWave.smoke.test.tsx +8 -1
  105. package/src/instrument/defineInstrument.test.ts +38 -1
  106. package/src/instrument/defineInstrument.ts +31 -4
  107. package/src/instrument/index.ts +1 -0
  108. package/src/instrument/types.ts +27 -4
  109. package/src/instrument/validateConfigSchema.test.ts +13 -3
  110. package/src/instrument/validateConfigSchema.ts +7 -10
  111. package/src/lab/Lab.chrome.test.tsx +65 -0
  112. package/src/lab/Lab.tsx +58 -15
  113. package/src/lab/LabContext.ts +6 -0
  114. package/src/lab/LabFullChrome.stories.tsx +308 -0
  115. package/src/lab/LabHeader.test.tsx +42 -0
  116. package/src/lab/LabHeader.tsx +57 -0
  117. package/src/lab/LabPalette.test.tsx +35 -0
  118. package/src/lab/LabPalette.tsx +34 -0
  119. package/src/lab/LabShell.less +50 -3
  120. package/src/lab/Workspace.less +1 -30
  121. package/src/lab/Workspace.test.tsx +20 -10
  122. package/src/lab/Workspace.tsx +5 -8
  123. package/src/lab/index.ts +3 -1
  124. package/src/layers/AGENTS.md +1 -1
  125. package/src/layers/LayerList.less +31 -7
  126. package/src/layers/LayerList.tsx +17 -4
  127. package/src/passthrough/weasel-ui.ts +28 -0
  128. package/src/primitives/FloatingPanel.less +28 -0
  129. package/src/primitives/FloatingPanel.stories.tsx +46 -0
  130. package/src/primitives/FloatingPanel.test.tsx +240 -0
  131. package/src/primitives/FloatingPanel.tsx +190 -0
  132. package/src/primitives/FpsMeter.test.tsx +44 -0
  133. package/src/primitives/FpsMeter.tsx +20 -9
  134. package/src/primitives/JobProgress.less +69 -0
  135. package/src/primitives/JobProgress.stories.tsx +48 -0
  136. package/src/primitives/JobProgress.tsx +65 -0
  137. package/src/primitives/Legend.less +47 -0
  138. package/src/primitives/Legend.stories.tsx +32 -0
  139. package/src/primitives/Legend.test.tsx +57 -0
  140. package/src/primitives/Legend.tsx +40 -0
  141. package/src/primitives/Sidebar.less +32 -2
  142. package/src/primitives/StatusBar.less +7 -0
  143. package/src/primitives/StatusBar.tsx +10 -3
  144. package/src/primitives/Toolbar.less +50 -3
  145. package/src/primitives/Toolbar.test.tsx +67 -1
  146. package/src/primitives/Toolbar.tsx +72 -6
  147. package/src/primitives/ZoomControl.less +31 -0
  148. package/src/primitives/ZoomControl.tsx +76 -0
  149. package/src/primitives/floating.entry.test.tsx +14 -0
  150. package/src/primitives/index.ts +10 -2
  151. package/src/primitives/useRovingTabIndex.test.ts +27 -0
  152. package/src/primitives/useRovingTabIndex.ts +79 -0
  153. package/src/state/store.ts +11 -3
  154. package/src/state/toolSlot.test.ts +36 -0
  155. package/src/state/types.ts +4 -0
  156. package/src/styles.less +6 -4
  157. package/src/surface/useTiledSurface.ts +14 -10
  158. package/src/test-setup.ts +26 -0
  159. package/src/theme/Interstellar.stories.tsx +1 -1
  160. package/src/theme/base.less +19 -2
  161. package/src/theme/interstellar.tokens.json +6 -2
  162. package/src/tools/types.ts +24 -0
  163. package/src/trial/Trial.config.test.tsx +108 -0
  164. package/src/trial/Trial.job.test.tsx +13 -15
  165. package/src/trial/Trial.less +115 -10
  166. package/src/trial/Trial.stories.tsx +1 -0
  167. package/src/trial/Trial.test.tsx +67 -96
  168. package/src/trial/Trial.tsx +80 -39
  169. package/src/trial/TrialChrome.tsx +99 -80
  170. package/src/trial/TrialDragContext.ts +10 -0
  171. package/src/trial/TrialTitleBar.tsx +41 -0
  172. package/src/trial/index.ts +1 -15
  173. package/src/trial/trialChrome.borders.test.ts +38 -0
  174. package/src/ui/layers/index.ts +5 -1
  175. package/dist/_dts/index-JFAYj5Tv.d.ts +0 -144
  176. package/dist/_dts/types-x92Kfeme.d.ts +0 -62
  177. package/dist/chunk-574LJAV4.js.map +0 -1
  178. package/dist/chunk-5R2ATYPJ.js +0 -201
  179. package/dist/chunk-5R2ATYPJ.js.map +0 -1
  180. package/dist/chunk-73KA7WBO.js +0 -5992
  181. package/dist/chunk-73KA7WBO.js.map +0 -1
  182. package/dist/chunk-BOHF3PQO.js.map +0 -1
  183. package/dist/chunk-G5TJVQQT.js +0 -161
  184. package/dist/chunk-G5TJVQQT.js.map +0 -1
  185. package/dist/chunk-PMAU3SEE.js +0 -126
  186. package/dist/chunk-PMAU3SEE.js.map +0 -1
  187. package/dist/chunk-SFL7NFKN.js.map +0 -1
  188. package/dist/chunk-THBG7FQZ.js.map +0 -1
  189. package/dist/chunk-VUU5UXHE.js.map +0 -1
  190. package/src/instrument/capabilityDetector.test.ts +0 -64
  191. package/src/instrument/capabilityDetector.ts +0 -20
  192. package/src/trial/DefaultSidebar.tsx +0 -29
  193. package/src/trial/DefaultStatusBar.tsx +0 -19
  194. package/src/trial/DefaultToolbar.tsx +0 -92
  195. package/src/trial/slotTypes.ts +0 -54
  196. package/src/ui/format.test.ts +0 -33
  197. package/src/ui/format.ts +0 -30
  198. package/src/ui/layers/LayerStack.less +0 -143
  199. package/src/ui/layers/LayerStack.stories.tsx +0 -45
  200. package/src/ui/layers/LayerStack.test.tsx +0 -170
  201. package/src/ui/layers/LayerStack.tsx +0 -220
  202. package/src/ui/properties/CheckboxRow.stories.tsx +0 -27
  203. package/src/ui/properties/ColorRow.stories.tsx +0 -106
  204. package/src/ui/properties/CurveField.less +0 -48
  205. package/src/ui/properties/CurveField.stories.tsx +0 -20
  206. package/src/ui/properties/CurveField.test.tsx +0 -127
  207. package/src/ui/properties/CurveField.tsx +0 -184
  208. package/src/ui/properties/EffectCard.tsx +0 -324
  209. package/src/ui/properties/Gallery.stories.tsx +0 -178
  210. package/src/ui/properties/NumberRow.stories.tsx +0 -65
  211. package/src/ui/properties/PropertyGroup.less +0 -50
  212. package/src/ui/properties/PropertyGroup.stories.tsx +0 -34
  213. package/src/ui/properties/PropertyGroup.test.tsx +0 -33
  214. package/src/ui/properties/PropertyGroup.tsx +0 -45
  215. package/src/ui/properties/PropertyList.stories.tsx +0 -83
  216. package/src/ui/properties/PropertyPanel.less +0 -592
  217. package/src/ui/properties/PropertyPanel.stories.tsx +0 -190
  218. package/src/ui/properties/PropertyPanel.test.tsx +0 -193
  219. package/src/ui/properties/PropertyPanel.tsx +0 -469
  220. package/src/ui/properties/PropertyRow.stories.tsx +0 -53
  221. package/src/ui/properties/SelectRow.stories.tsx +0 -58
  222. package/src/ui/properties/SliderRow.stories.tsx +0 -74
  223. package/src/ui/properties/SpeechBalloonPanels.stories.tsx +0 -396
  224. package/src/ui/properties/TextRow.stories.tsx +0 -62
  225. package/src/ui/properties/ToggleRow.stories.tsx +0 -57
  226. package/src/ui/properties/index.ts +0 -39
  227. package/src/ui/properties/storyLayouts.tsx +0 -36
@@ -0,0 +1,212 @@
1
+ import { vn, yn } from './chunk-RGT4EJIB.js';
2
+ import { useRef, useMemo, useState, useEffect } from 'react';
3
+ import { floatingStrategy, DEFAULT_INSET, DEFAULT_ANCHOR, FLOATING_DRAG_PREFIX } from 'windease';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ var ITEM_ID = "panel";
7
+ function borderBoxOf(entry) {
8
+ const box = entry.borderBoxSize?.[0];
9
+ if (box) return { w: box.inlineSize, h: box.blockSize };
10
+ return { w: entry.contentRect.width, h: entry.contentRect.height };
11
+ }
12
+ function isInteractive(target) {
13
+ if (!(target instanceof Element)) return false;
14
+ return target.closest("input, button, a, select, textarea, [data-no-drag]") !== null;
15
+ }
16
+ function readStored(key) {
17
+ if (!key) return null;
18
+ try {
19
+ const raw = localStorage.getItem(key);
20
+ return raw ? JSON.parse(raw) : null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ function FloatingPanel({
26
+ children,
27
+ anchor = DEFAULT_ANCHOR,
28
+ snapCorners,
29
+ inset = DEFAULT_INSET,
30
+ storageKey,
31
+ className
32
+ }) {
33
+ const ref = useRef(null);
34
+ const strategy = useMemo(() => floatingStrategy(), []);
35
+ const options = useMemo(() => ({ defaultAnchor: anchor, inset }), [anchor, inset]);
36
+ const [container, setContainer] = useState({ w: 0, h: 0 });
37
+ const [size, setSize] = useState({ w: 0, h: 0 });
38
+ const [place, setPlace] = useState(
39
+ () => readStored(storageKey) ?? { x: 0, y: 0, anchor }
40
+ );
41
+ useEffect(() => {
42
+ if (!storageKey) return;
43
+ try {
44
+ localStorage.setItem(storageKey, JSON.stringify(place));
45
+ } catch {
46
+ }
47
+ }, [place, storageKey]);
48
+ const item = useMemo(
49
+ () => ({
50
+ id: ITEM_ID,
51
+ meta: { floating: true, ...snapCorners ? { snapCorners: [...snapCorners] } : {} },
52
+ natural: size
53
+ }),
54
+ [size, snapCorners]
55
+ );
56
+ useEffect(() => {
57
+ const el = ref.current;
58
+ const parent = el?.offsetParent ?? el?.parentElement;
59
+ if (!el || !parent) return;
60
+ const observer = new ResizeObserver((entries) => {
61
+ for (const entry of entries) {
62
+ if (entry.target === parent) {
63
+ setContainer({ w: entry.contentRect.width, h: entry.contentRect.height });
64
+ } else {
65
+ setSize(borderBoxOf(entry));
66
+ }
67
+ }
68
+ });
69
+ observer.observe(parent);
70
+ observer.observe(el);
71
+ return () => observer.disconnect();
72
+ }, []);
73
+ const rect = strategy.layout({
74
+ items: [item],
75
+ container,
76
+ state: { at: { [ITEM_ID]: place }, inner: void 0 },
77
+ options
78
+ }).placements.get(ITEM_ID);
79
+ const dragging = useRef(null);
80
+ const [isDragging, setDragging] = useState(false);
81
+ const onPointerDown = (e) => {
82
+ if (isInteractive(e.target)) return;
83
+ e.stopPropagation();
84
+ dragging.current = { x: e.clientX, y: e.clientY };
85
+ setDragging(true);
86
+ e.currentTarget.setPointerCapture?.(e.pointerId);
87
+ };
88
+ const onPointerMove = (e) => {
89
+ const from = dragging.current;
90
+ if (!from) return;
91
+ const dx = e.clientX - from.x;
92
+ const dy = e.clientY - from.y;
93
+ if (dx === 0 && dy === 0) return;
94
+ dragging.current = { x: e.clientX, y: e.clientY };
95
+ setPlace(
96
+ (prev) => strategy.reduce?.(
97
+ { at: { [ITEM_ID]: prev }, inner: void 0 },
98
+ {
99
+ affordanceId: `${FLOATING_DRAG_PREFIX}${ITEM_ID}`,
100
+ kind: "drag",
101
+ payload: { dx, dy }
102
+ },
103
+ { container, options, items: [item] }
104
+ ).at[ITEM_ID] ?? prev
105
+ );
106
+ };
107
+ const endDrag = (e) => {
108
+ if (!dragging.current) return;
109
+ dragging.current = null;
110
+ setDragging(false);
111
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
112
+ };
113
+ return /* @__PURE__ */ jsx(
114
+ "div",
115
+ {
116
+ className: className ? `lk-floating-panel ${className}` : "lk-floating-panel",
117
+ "data-dragging": isDragging ? "true" : void 0,
118
+ "data-placed": rect ? "true" : void 0,
119
+ onPointerCancel: endDrag,
120
+ onPointerDown,
121
+ onPointerMove,
122
+ onPointerUp: endDrag,
123
+ ref,
124
+ style: {
125
+ position: "absolute",
126
+ left: `${rect?.x ?? 0}px`,
127
+ top: `${rect?.y ?? 0}px`
128
+ },
129
+ children
130
+ }
131
+ );
132
+ }
133
+ function JobProgress({ job, className }) {
134
+ const { status, done, total, failures, error } = job;
135
+ if (status === "idle") return null;
136
+ const determinate = total !== null && total > 0;
137
+ const fraction = determinate ? Math.min(1, done / total) : 0;
138
+ const cls = ["lk-job", `lk-job--${status}`, className].filter(Boolean).join(" ");
139
+ return /* @__PURE__ */ jsxs("div", { className: cls, children: [
140
+ /* @__PURE__ */ jsx(
141
+ "div",
142
+ {
143
+ className: `lk-job__track${determinate ? "" : " lk-job__track--indeterminate"}`,
144
+ role: "progressbar",
145
+ "aria-label": "Job progress",
146
+ "aria-valuenow": determinate ? done : void 0,
147
+ "aria-valuemin": determinate ? 0 : void 0,
148
+ "aria-valuemax": determinate ? total : void 0,
149
+ children: /* @__PURE__ */ jsx(
150
+ "div",
151
+ {
152
+ className: "lk-job__fill",
153
+ style: { transform: `scaleX(${determinate ? fraction : 1})` }
154
+ }
155
+ )
156
+ }
157
+ ),
158
+ /* @__PURE__ */ jsxs("span", { className: "lk-job__count", children: [
159
+ done,
160
+ determinate ? ` / ${total}` : ""
161
+ ] }),
162
+ failures.length > 0 && /* @__PURE__ */ jsxs("span", { className: "lk-job__failures", title: failures.map((f) => f.error).join("\n"), children: [
163
+ /* @__PURE__ */ jsx(vn, { size: 14 }),
164
+ failures.length,
165
+ " failed"
166
+ ] }),
167
+ error && /* @__PURE__ */ jsxs("span", { className: "lk-job__error", title: error, children: [
168
+ /* @__PURE__ */ jsx(yn, { size: 14 }),
169
+ error
170
+ ] }),
171
+ status === "running" && /* @__PURE__ */ jsx("button", { type: "button", className: "lk-job__cancel", onClick: job.cancel, children: "Cancel" })
172
+ ] });
173
+ }
174
+ function Legend({ entries, className }) {
175
+ return /* @__PURE__ */ jsx("ul", { className: className ? `lk-legend ${className}` : "lk-legend", children: entries.map((entry) => {
176
+ const mark = entry.mark ?? "line";
177
+ return /* @__PURE__ */ jsxs("li", { className: "lk-legend__row", children: [
178
+ /* @__PURE__ */ jsx(
179
+ "span",
180
+ {
181
+ "aria-hidden": "true",
182
+ className: `lk-legend__swatch lk-legend__swatch--${mark}`,
183
+ style: { "--lk-legend-ink": entry.color }
184
+ }
185
+ ),
186
+ /* @__PURE__ */ jsx("span", { className: "lk-legend__label", children: entry.label })
187
+ ] }, entry.key);
188
+ }) });
189
+ }
190
+ function Sidebar({ children, title, collapsed = false, onToggle }) {
191
+ const className = `lk-sidebar${collapsed ? " lk-sidebar--collapsed" : ""}`;
192
+ return /* @__PURE__ */ jsxs("aside", { className, children: [
193
+ (title || onToggle) && /* @__PURE__ */ jsxs("div", { className: "lk-sidebar-header", children: [
194
+ title && /* @__PURE__ */ jsx("span", { className: "lk-sidebar-title", children: title }),
195
+ onToggle && /* @__PURE__ */ jsx(
196
+ "button",
197
+ {
198
+ type: "button",
199
+ className: "lk-sidebar-toggle",
200
+ onClick: onToggle,
201
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
202
+ children: collapsed ? "\u203A" : "\u2039"
203
+ }
204
+ )
205
+ ] }),
206
+ /* @__PURE__ */ jsx("div", { className: "lk-sidebar-body", children })
207
+ ] });
208
+ }
209
+
210
+ export { FloatingPanel, JobProgress, Legend, Sidebar };
211
+ //# sourceMappingURL=chunk-QRYPXSGP.js.map
212
+ //# sourceMappingURL=chunk-QRYPXSGP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/primitives/FloatingPanel.tsx","../src/primitives/JobProgress.tsx","../src/primitives/Legend.tsx","../src/primitives/Sidebar.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-QRYPXSGP.js","sourcesContent":["import type React from 'react';\nimport { type CSSProperties, type ReactNode, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n type Corner,\n DEFAULT_ANCHOR,\n DEFAULT_INSET,\n FLOATING_DRAG_PREFIX,\n type FloatingPlacement,\n floatingStrategy,\n} from 'windease';\n\n/** The strategy holds a map keyed by item id; a panel is always a lone item. */\nconst ITEM_ID = 'panel';\n\n/** Props for `<FloatingPanel>`. */\nexport interface FloatingPanelProps {\n children: ReactNode;\n /** Corner it rests in before it has ever been dragged. Default `'bottom-left'`. */\n anchor?: Corner;\n /** Corners that may capture it. Default: all four. */\n snapCorners?: readonly Corner[];\n /** Pixels in from a corner when snapped. Default 12. */\n inset?: number;\n /** localStorage key to remember its position under. Omit to forget on reload. */\n storageKey?: string;\n className?: string;\n}\n\n/**\n * The panel's footprint is its border box — `contentRect` excludes padding and\n * border, which would place a panel that then overflows its corner by exactly\n * that much.\n */\nfunction borderBoxOf(entry: ResizeObserverEntry): { w: number; h: number } {\n const box = entry.borderBoxSize?.[0];\n if (box) return { w: box.inlineSize, h: box.blockSize };\n return { w: entry.contentRect.width, h: entry.contentRect.height };\n}\n\n/** A pointerdown on one of these is the child's, not a drag. */\nfunction isInteractive(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false;\n return target.closest('input, button, a, select, textarea, [data-no-drag]') !== null;\n}\n\nfunction readStored(key: string | undefined): FloatingPlacement | null {\n if (!key) return null;\n try {\n const raw = localStorage.getItem(key);\n return raw ? (JSON.parse(raw) as FloatingPlacement) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * A draggable box that floats over its offset parent and snaps to its corners.\n *\n * Drives windease's `floatingStrategy` as a pure function rather than mounting a\n * windease container: a lab overlay has one item and no zone tree, so the node\n * model would be all cost. Pointer handling is therefore this component's, and\n * windease's own affordance rendering never participates.\n */\nexport function FloatingPanel({\n children,\n anchor = DEFAULT_ANCHOR,\n snapCorners,\n inset = DEFAULT_INSET,\n storageKey,\n className,\n}: FloatingPanelProps) {\n const ref = useRef<HTMLDivElement>(null);\n const strategy = useMemo(() => floatingStrategy(), []);\n const options = useMemo(() => ({ defaultAnchor: anchor, inset }), [anchor, inset]);\n\n const [container, setContainer] = useState({ w: 0, h: 0 });\n const [size, setSize] = useState({ w: 0, h: 0 });\n const [place, setPlace] = useState<FloatingPlacement>(\n () => readStored(storageKey) ?? { x: 0, y: 0, anchor },\n );\n\n useEffect(() => {\n if (!storageKey) return;\n try {\n localStorage.setItem(storageKey, JSON.stringify(place));\n } catch {\n // A full or disabled store is not worth failing a lab over.\n }\n }, [place, storageKey]);\n\n const item = useMemo(\n () => ({\n id: ITEM_ID,\n meta: { floating: true, ...(snapCorners ? { snapCorners: [...snapCorners] } : {}) },\n natural: size,\n }),\n [size, snapCorners],\n );\n\n useEffect(() => {\n const el = ref.current;\n const parent = el?.offsetParent ?? el?.parentElement;\n if (!el || !parent) return;\n // Sizes come off the entry, not the DOM: jsdom reports 0 for every\n // clientWidth/offsetWidth, and an unmeasured item is withheld from layout.\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === parent) {\n setContainer({ w: entry.contentRect.width, h: entry.contentRect.height });\n } else {\n setSize(borderBoxOf(entry));\n }\n }\n });\n observer.observe(parent);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n const rect = strategy\n .layout({\n items: [item],\n container,\n state: { at: { [ITEM_ID]: place }, inner: undefined },\n options,\n })\n .placements.get(ITEM_ID);\n\n const dragging = useRef<{ x: number; y: number } | null>(null);\n const [isDragging, setDragging] = useState(false);\n\n const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (isInteractive(e.target)) return;\n // The canvas stack underneath owns pan/zoom on the same pointer events.\n e.stopPropagation();\n dragging.current = { x: e.clientX, y: e.clientY };\n setDragging(true);\n e.currentTarget.setPointerCapture?.(e.pointerId);\n };\n\n const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const from = dragging.current;\n if (!from) return;\n const dx = e.clientX - from.x;\n const dy = e.clientY - from.y;\n if (dx === 0 && dy === 0) return;\n dragging.current = { x: e.clientX, y: e.clientY };\n setPlace(\n (prev) =>\n strategy.reduce?.(\n { at: { [ITEM_ID]: prev }, inner: undefined },\n {\n affordanceId: `${FLOATING_DRAG_PREFIX}${ITEM_ID}`,\n kind: 'drag',\n payload: { dx, dy },\n },\n { container, options, items: [item] },\n ).at[ITEM_ID] ?? prev,\n );\n };\n\n const endDrag = (e: React.PointerEvent<HTMLDivElement>) => {\n if (!dragging.current) return;\n dragging.current = null;\n setDragging(false);\n e.currentTarget.releasePointerCapture?.(e.pointerId);\n };\n\n return (\n <div\n className={className ? `lk-floating-panel ${className}` : 'lk-floating-panel'}\n data-dragging={isDragging ? 'true' : undefined}\n data-placed={rect ? 'true' : undefined}\n onPointerCancel={endDrag}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n ref={ref}\n style={\n {\n position: 'absolute',\n left: `${rect?.x ?? 0}px`,\n top: `${rect?.y ?? 0}px`,\n } as CSSProperties\n }\n >\n {children}\n </div>\n );\n}\n","import { ErrorIcon, WarningIcon } from '@weasel-js/ui';\nimport type { JobHandle } from '../job/types';\n\n/** Props for `<JobProgress>`. */\nexport interface JobProgressProps {\n job: JobHandle;\n className?: string;\n}\n\n/** A running job's progress, failures and outcome. Determinate once the job\n * reports a total; a job that cannot count up front never sends one, so the\n * bar stays indeterminate rather than inventing a denominator. */\nexport function JobProgress({ job, className }: JobProgressProps) {\n const { status, done, total, failures, error } = job;\n if (status === 'idle') return null;\n\n const determinate = total !== null && total > 0;\n const fraction = determinate ? Math.min(1, done / total) : 0;\n const cls = ['lk-job', `lk-job--${status}`, className].filter(Boolean).join(' ');\n\n return (\n <div className={cls}>\n <div\n className={`lk-job__track${determinate ? '' : ' lk-job__track--indeterminate'}`}\n role=\"progressbar\"\n aria-label=\"Job progress\"\n aria-valuenow={determinate ? done : undefined}\n aria-valuemin={determinate ? 0 : undefined}\n aria-valuemax={determinate ? total : undefined}\n >\n {/* Scaling a full-width bar keeps the fill off the layout path, so\n progress updates never reflow the status bar. */}\n <div\n className=\"lk-job__fill\"\n style={{ transform: `scaleX(${determinate ? fraction : 1})` }}\n />\n </div>\n\n <span className=\"lk-job__count\">\n {done}\n {determinate ? ` / ${total}` : ''}\n </span>\n\n {failures.length > 0 && (\n <span className=\"lk-job__failures\" title={failures.map((f) => f.error).join('\\n')}>\n <WarningIcon size={14} />\n {failures.length} failed\n </span>\n )}\n\n {error && (\n <span className=\"lk-job__error\" title={error}>\n <ErrorIcon size={14} />\n {error}\n </span>\n )}\n\n {status === 'running' && (\n <button type=\"button\" className=\"lk-job__cancel\" onClick={job.cancel}>\n Cancel\n </button>\n )}\n </div>\n );\n}\n","import type { CSSProperties } from 'react';\n\n/** How a legend entry's swatch is drawn, matching how the thing looks on canvas. */\nexport type LegendMark = 'line' | 'dash' | 'dot' | 'band';\n\n/** One row of a legend: a swatch and what it means. */\nexport interface LegendEntry {\n key: string;\n label: string;\n color: string;\n /** Defaults to `'line'`. */\n mark?: LegendMark;\n}\n\n/** Props for `<Legend>`. */\nexport interface LegendProps {\n entries: readonly LegendEntry[];\n className?: string;\n}\n\n/** A color key. Presentational only — no handlers, no state, no hover behavior. */\nexport function Legend({ entries, className }: LegendProps) {\n return (\n <ul className={className ? `lk-legend ${className}` : 'lk-legend'}>\n {entries.map((entry) => {\n const mark = entry.mark ?? 'line';\n return (\n <li className=\"lk-legend__row\" key={entry.key}>\n <span\n aria-hidden=\"true\"\n className={`lk-legend__swatch lk-legend__swatch--${mark}`}\n style={{ '--lk-legend-ink': entry.color } as CSSProperties}\n />\n <span className=\"lk-legend__label\">{entry.label}</span>\n </li>\n );\n })}\n </ul>\n );\n}\n","import type { ReactNode } from 'react';\n\n/** Props for `<Sidebar>`. */\nexport interface SidebarProps {\n children: ReactNode;\n title?: string;\n collapsed?: boolean;\n onToggle?: () => void;\n}\n\n/** A collapsible side panel with an optional title. Collapse state is the\n * caller's to hold. */\nexport function Sidebar({ children, title, collapsed = false, onToggle }: SidebarProps) {\n const className = `lk-sidebar${collapsed ? ' lk-sidebar--collapsed' : ''}`;\n return (\n <aside className={className}>\n {(title || onToggle) && (\n <div className=\"lk-sidebar-header\">\n {title && <span className=\"lk-sidebar-title\">{title}</span>}\n {onToggle && (\n <button\n type=\"button\"\n className=\"lk-sidebar-toggle\"\n onClick={onToggle}\n aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n >\n {collapsed ? '›' : '‹'}\n </button>\n )}\n </div>\n )}\n <div className=\"lk-sidebar-body\">{children}</div>\n </aside>\n );\n}\n"]}