@objectifthunes/whiteboard 0.1.1 → 0.2.1

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/index.js CHANGED
@@ -1,15 +1,11 @@
1
- // src/WhiteboardShell.tsx
2
- import { useRef as useRef2, useCallback, useEffect as useEffect2 } from "react";
3
-
4
- // src/ZoomBar.tsx
5
- import { useEffect } from "react";
6
-
7
- // src/icons.tsx
8
- import { jsx, jsxs } from "react/jsx-runtime";
9
- var defaults = (size = 14) => ({
1
+ import { jsx as n, jsxs as d, Fragment as re } from "react/jsx-runtime";
2
+ import { useEffect as X, useRef as S, useCallback as H, memo as Te, useId as Re, useState as Se, useLayoutEffect as We, Component as De, useMemo as ye, forwardRef as me, createElement as T } from "react";
3
+ import { create as Xe } from "zustand";
4
+ import { createPortal as Ye } from "react-dom";
5
+ const Y = (e = 14) => ({
10
6
  xmlns: "http://www.w3.org/2000/svg",
11
- width: size,
12
- height: size,
7
+ width: e,
8
+ height: e,
13
9
  viewBox: "0 0 24 24",
14
10
  fill: "none",
15
11
  stroke: "currentColor",
@@ -17,403 +13,308 @@ var defaults = (size = 14) => ({
17
13
  strokeLinecap: "round",
18
14
  strokeLinejoin: "round"
19
15
  });
20
- function Minus({ size, ...props }) {
21
- return /* @__PURE__ */ jsx("svg", { ...defaults(size), ...props, children: /* @__PURE__ */ jsx("path", { d: "M5 12h14" }) });
16
+ function Fe({ size: e, ...t }) {
17
+ return /* @__PURE__ */ n("svg", { ...Y(e), ...t, children: /* @__PURE__ */ n("path", { d: "M5 12h14" }) });
22
18
  }
23
- function Plus({ size, ...props }) {
24
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
25
- /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
26
- /* @__PURE__ */ jsx("path", { d: "M12 5v14" })
19
+ function Ge({ size: e, ...t }) {
20
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
21
+ /* @__PURE__ */ n("path", { d: "M5 12h14" }),
22
+ /* @__PURE__ */ n("path", { d: "M12 5v14" })
27
23
  ] });
28
24
  }
29
- function ScanSearch({ size, ...props }) {
30
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
31
- /* @__PURE__ */ jsx("path", { d: "M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z", fill: "none", stroke: "none" }),
32
- /* @__PURE__ */ jsx("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }),
33
- /* @__PURE__ */ jsx("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }),
34
- /* @__PURE__ */ jsx("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }),
35
- /* @__PURE__ */ jsx("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" }),
36
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
37
- /* @__PURE__ */ jsx("path", { d: "m16 16-1.9-1.9" })
25
+ function Ie({ size: e, ...t }) {
26
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
27
+ /* @__PURE__ */ n("path", { d: "M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z", fill: "none", stroke: "none" }),
28
+ /* @__PURE__ */ n("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }),
29
+ /* @__PURE__ */ n("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }),
30
+ /* @__PURE__ */ n("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }),
31
+ /* @__PURE__ */ n("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" }),
32
+ /* @__PURE__ */ n("circle", { cx: "12", cy: "12", r: "3" }),
33
+ /* @__PURE__ */ n("path", { d: "m16 16-1.9-1.9" })
38
34
  ] });
39
35
  }
40
- function RotateCcw({ size, ...props }) {
41
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
42
- /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
43
- /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" })
36
+ function Le({ size: e, ...t }) {
37
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
38
+ /* @__PURE__ */ n("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
39
+ /* @__PURE__ */ n("path", { d: "M3 3v5h5" })
44
40
  ] });
45
41
  }
46
- function Grid3x3({ size, ...props }) {
47
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
48
- /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
49
- /* @__PURE__ */ jsx("path", { d: "M3 9h18" }),
50
- /* @__PURE__ */ jsx("path", { d: "M3 15h18" }),
51
- /* @__PURE__ */ jsx("path", { d: "M9 3v18" }),
52
- /* @__PURE__ */ jsx("path", { d: "M15 3v18" })
42
+ function Ee({ size: e, ...t }) {
43
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
44
+ /* @__PURE__ */ n("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
45
+ /* @__PURE__ */ n("path", { d: "M3 9h18" }),
46
+ /* @__PURE__ */ n("path", { d: "M3 15h18" }),
47
+ /* @__PURE__ */ n("path", { d: "M9 3v18" }),
48
+ /* @__PURE__ */ n("path", { d: "M15 3v18" })
53
49
  ] });
54
50
  }
55
- function Maximize2({ size, ...props }) {
56
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
57
- /* @__PURE__ */ jsx("polyline", { points: "15 3 21 3 21 9" }),
58
- /* @__PURE__ */ jsx("polyline", { points: "9 21 3 21 3 15" }),
59
- /* @__PURE__ */ jsx("line", { x1: "21", x2: "14", y1: "3", y2: "10" }),
60
- /* @__PURE__ */ jsx("line", { x1: "3", x2: "10", y1: "21", y2: "14" })
51
+ function Ae({ size: e, ...t }) {
52
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
53
+ /* @__PURE__ */ n("polyline", { points: "15 3 21 3 21 9" }),
54
+ /* @__PURE__ */ n("polyline", { points: "9 21 3 21 3 15" }),
55
+ /* @__PURE__ */ n("line", { x1: "21", x2: "14", y1: "3", y2: "10" }),
56
+ /* @__PURE__ */ n("line", { x1: "3", x2: "10", y1: "21", y2: "14" })
61
57
  ] });
62
58
  }
63
- function Loader2({ size, ...props }) {
64
- return /* @__PURE__ */ jsx("svg", { ...defaults(size), ...props, children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
59
+ function fe({ size: e, ...t }) {
60
+ return /* @__PURE__ */ n("svg", { ...Y(e), ...t, children: /* @__PURE__ */ n("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
65
61
  }
66
- function AlertTriangle({ size, ...props }) {
67
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
68
- /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
69
- /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
70
- /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
62
+ function Oe({ size: e, ...t }) {
63
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
64
+ /* @__PURE__ */ n("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
65
+ /* @__PURE__ */ n("path", { d: "M12 9v4" }),
66
+ /* @__PURE__ */ n("path", { d: "M12 17h.01" })
71
67
  ] });
72
68
  }
73
- function Check({ size, ...props }) {
74
- return /* @__PURE__ */ jsx("svg", { ...defaults(size), ...props, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
69
+ function He({ size: e, ...t }) {
70
+ return /* @__PURE__ */ n("svg", { ...Y(e), ...t, children: /* @__PURE__ */ n("path", { d: "M20 6 9 17l-5-5" }) });
75
71
  }
76
- function X({ size, ...props }) {
77
- return /* @__PURE__ */ jsxs("svg", { ...defaults(size), ...props, children: [
78
- /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
79
- /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
72
+ function Me({ size: e, ...t }) {
73
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
74
+ /* @__PURE__ */ n("path", { d: "M18 6 6 18" }),
75
+ /* @__PURE__ */ n("path", { d: "m6 6 12 12" })
80
76
  ] });
81
77
  }
82
-
83
- // src/store.ts
84
- import { create } from "zustand";
85
-
86
- // src/grid.ts
87
- var WHITEBOARD_GRID = 20;
88
- function snapToWhiteboardGrid(value) {
89
- return Math.round(value / WHITEBOARD_GRID) * WHITEBOARD_GRID;
90
- }
91
-
92
- // src/store.ts
93
- function computeWhiteboardFit(panels, viewportSize, padding = 60) {
94
- if (panels.size === 0) return null;
95
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
96
- for (const r of panels.values()) {
97
- minX = Math.min(minX, r.x);
98
- minY = Math.min(minY, r.y);
99
- maxX = Math.max(maxX, r.x + r.width);
100
- maxY = Math.max(maxY, r.y + r.height);
101
- }
102
- const contentW = maxX - minX + padding * 2;
103
- const contentH = maxY - minY + padding * 2;
104
- const vpW = viewportSize.width || window.innerWidth;
105
- const vpH = viewportSize.height || window.innerHeight;
106
- const fitScale = Math.min(vpW / contentW, vpH / contentH, 1.5);
107
- const centerX = (minX + maxX) / 2;
108
- const centerY = (minY + maxY) / 2;
78
+ function Be({ size: e, ...t }) {
79
+ return /* @__PURE__ */ n("svg", { ...Y(e), ...t, children: /* @__PURE__ */ n("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }) });
80
+ }
81
+ function $e({ size: e, ...t }) {
82
+ return /* @__PURE__ */ d("svg", { ...Y(e), ...t, children: [
83
+ /* @__PURE__ */ n("circle", { cx: "12", cy: "12", r: "4" }),
84
+ /* @__PURE__ */ n("path", { d: "M12 2v2" }),
85
+ /* @__PURE__ */ n("path", { d: "M12 20v2" }),
86
+ /* @__PURE__ */ n("path", { d: "m4.93 4.93 1.41 1.41" }),
87
+ /* @__PURE__ */ n("path", { d: "m17.66 17.66 1.41 1.41" }),
88
+ /* @__PURE__ */ n("path", { d: "M2 12h2" }),
89
+ /* @__PURE__ */ n("path", { d: "M20 12h2" }),
90
+ /* @__PURE__ */ n("path", { d: "m6.34 17.66-1.41 1.41" }),
91
+ /* @__PURE__ */ n("path", { d: "m19.07 4.93-1.41 1.41" })
92
+ ] });
93
+ }
94
+ const he = 20;
95
+ function ue(e) {
96
+ return Math.round(e / he) * he;
97
+ }
98
+ function Ne(e, t, r = 60) {
99
+ if (e.size === 0) return null;
100
+ let s = 1 / 0, i = 1 / 0, u = -1 / 0, w = -1 / 0;
101
+ for (const v of e.values())
102
+ s = Math.min(s, v.x), i = Math.min(i, v.y), u = Math.max(u, v.x + v.width), w = Math.max(w, v.y + v.height);
103
+ const f = u - s + r * 2, m = w - i + r * 2, g = t.width || window.innerWidth, h = t.height || window.innerHeight, M = Math.min(g / f, h / m, 1.5), k = (s + u) / 2, _ = (i + w) / 2;
109
104
  return {
110
- scale: Math.min(3, Math.max(0.2, fitScale)),
105
+ scale: Math.min(3, Math.max(0.2, M)),
111
106
  offset: {
112
- x: vpW / 2 - centerX * fitScale,
113
- y: vpH / 2 - centerY * fitScale
107
+ x: g / 2 - k * M,
108
+ y: h / 2 - _ * M
114
109
  }
115
110
  };
116
111
  }
117
- function computeWhiteboardRectFocus(rect, viewportSize, padding = 40, maxScale = 1.5) {
118
- const vpW = viewportSize.width || window.innerWidth;
119
- const vpH = viewportSize.height || window.innerHeight;
120
- const safeWidth = Math.max(1, rect.width);
121
- const safeHeight = Math.max(1, rect.height);
122
- const fitScale = Math.min(
123
- (vpW - padding * 2) / safeWidth,
124
- (vpH - padding * 2) / safeHeight,
125
- maxScale
126
- );
127
- const nextScale = Math.min(3, Math.max(0.2, fitScale));
112
+ function Ve(e, t, r = 40, s = 1.5) {
113
+ const i = t.width || window.innerWidth, u = t.height || window.innerHeight, w = Math.max(1, e.width), f = Math.max(1, e.height), m = Math.min(
114
+ (i - r * 2) / w,
115
+ (u - r * 2) / f,
116
+ s
117
+ ), g = Math.min(3, Math.max(0.2, m));
128
118
  return {
129
- scale: nextScale,
119
+ scale: g,
130
120
  offset: {
131
- x: vpW / 2 - (rect.x + safeWidth / 2) * nextScale,
132
- y: vpH / 2 - (rect.y + safeHeight / 2) * nextScale
121
+ x: i / 2 - (e.x + w / 2) * g,
122
+ y: u / 2 - (e.y + f / 2) * g
133
123
  }
134
124
  };
135
125
  }
136
- var useWhiteboardStore = create((set, get) => ({
126
+ const p = Xe((e, t) => ({
137
127
  offset: { x: 0, y: 0 },
138
128
  scale: 1,
139
129
  viewportSize: { width: 0, height: 0 },
140
- snapToGrid: false,
141
- snapGridSize: WHITEBOARD_GRID,
130
+ snapToGrid: !1,
131
+ snapGridSize: he,
142
132
  panels: /* @__PURE__ */ new Map(),
143
133
  resetFns: /* @__PURE__ */ new Map(),
144
134
  registryVersion: 0,
145
- setOffset: (v) => set((s) => ({ offset: typeof v === "function" ? v(s.offset) : v })),
146
- setScale: (v) => set((s) => ({ scale: typeof v === "function" ? v(s.scale) : v })),
147
- setViewportSize: (v) => set({ viewportSize: v }),
148
- setSnapToGrid: (v) => set((s) => ({ snapToGrid: typeof v === "function" ? v(s.snapToGrid) : v })),
149
- register: (id, rect) => {
150
- get().panels.set(id, rect);
151
- set((s) => ({ registryVersion: s.registryVersion + 1 }));
135
+ setOffset: (r) => e((s) => ({ offset: typeof r == "function" ? r(s.offset) : r })),
136
+ setScale: (r) => e((s) => ({ scale: typeof r == "function" ? r(s.scale) : r })),
137
+ setViewportSize: (r) => e({ viewportSize: r }),
138
+ setSnapToGrid: (r) => e((s) => ({ snapToGrid: typeof r == "function" ? r(s.snapToGrid) : r })),
139
+ register: (r, s) => {
140
+ t().panels.set(r, s), e((i) => ({ registryVersion: i.registryVersion + 1 }));
152
141
  },
153
- unregister: (id) => {
154
- get().panels.delete(id);
155
- set((s) => ({ registryVersion: s.registryVersion + 1 }));
142
+ unregister: (r) => {
143
+ t().panels.delete(r), e((s) => ({ registryVersion: s.registryVersion + 1 }));
156
144
  },
157
- registerReset: (id, fn) => {
158
- get().resetFns.set(id, fn);
145
+ registerReset: (r, s) => {
146
+ t().resetFns.set(r, s);
159
147
  },
160
- unregisterReset: (id) => {
161
- get().resetFns.delete(id);
148
+ unregisterReset: (r) => {
149
+ t().resetFns.delete(r);
162
150
  },
163
151
  fitToContent: () => {
164
- const { panels, viewportSize } = get();
165
- const fit = computeWhiteboardFit(panels, viewportSize);
166
- if (fit) set({ scale: fit.scale, offset: fit.offset });
152
+ const { panels: r, viewportSize: s } = t(), i = Ne(r, s);
153
+ i && e({ scale: i.scale, offset: i.offset });
167
154
  },
168
- focusPanel: (rect, options) => {
169
- const { viewportSize } = get();
170
- const fit = computeWhiteboardRectFocus(
171
- rect,
172
- viewportSize,
173
- options?.padding ?? 40,
174
- options?.maxScale ?? 1.5
155
+ focusPanel: (r, s) => {
156
+ const { viewportSize: i } = t(), u = Ve(
157
+ r,
158
+ i,
159
+ (s == null ? void 0 : s.padding) ?? 40,
160
+ (s == null ? void 0 : s.maxScale) ?? 1.5
175
161
  );
176
- set({ scale: fit.scale, offset: fit.offset });
162
+ e({ scale: u.scale, offset: u.offset });
177
163
  },
178
164
  resetWidgets: () => {
179
- for (const fn of get().resetFns.values()) fn();
180
- const attempt = (tries = 0) => {
181
- const { panels, viewportSize } = get();
182
- const fit = computeWhiteboardFit(panels, viewportSize);
183
- if (fit) {
184
- set({ scale: fit.scale, offset: fit.offset });
165
+ for (const s of t().resetFns.values()) s();
166
+ const r = (s = 0) => {
167
+ const { panels: i, viewportSize: u } = t(), w = Ne(i, u);
168
+ if (w) {
169
+ e({ scale: w.scale, offset: w.offset });
185
170
  return;
186
171
  }
187
- if (tries < 6) requestAnimationFrame(() => attempt(tries + 1));
172
+ s < 6 && requestAnimationFrame(() => r(s + 1));
188
173
  };
189
- requestAnimationFrame(() => requestAnimationFrame(() => attempt()));
174
+ requestAnimationFrame(() => requestAnimationFrame(() => r()));
190
175
  },
191
- resetSession: () => set({
176
+ resetSession: () => e({
192
177
  offset: { x: 0, y: 0 },
193
178
  scale: 1,
194
179
  viewportSize: { width: 0, height: 0 },
195
- snapToGrid: false,
180
+ snapToGrid: !1,
196
181
  registryVersion: 0,
197
182
  panels: /* @__PURE__ */ new Map(),
198
183
  resetFns: /* @__PURE__ */ new Map()
199
184
  })
200
185
  }));
201
-
202
- // src/ZoomBar.tsx
203
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
204
- function ZoomBar({ extraActions }) {
205
- const scale = useWhiteboardStore((s) => s.scale);
206
- const viewportSize = useWhiteboardStore((s) => s.viewportSize);
207
- const snapToGrid = useWhiteboardStore((s) => s.snapToGrid);
208
- const setScale = useWhiteboardStore((s) => s.setScale);
209
- const setOffset = useWhiteboardStore((s) => s.setOffset);
210
- const setSnapToGrid = useWhiteboardStore((s) => s.setSnapToGrid);
211
- const fitToContent = useWhiteboardStore((s) => s.fitToContent);
212
- const resetWidgets = useWhiteboardStore((s) => s.resetWidgets);
213
- useEffect(() => {
214
- if (!snapToGrid) return;
215
- window.dispatchEvent(new Event("whiteboard-snap-now"));
216
- }, [snapToGrid]);
217
- const zoomTo = (nextScale) => {
218
- const clamped = Math.min(3, Math.max(0.2, nextScale));
219
- const ratio = clamped / scale;
220
- const cx = (viewportSize.width || window.innerWidth) / 2;
221
- const cy = (viewportSize.height || window.innerHeight) / 2;
222
- setOffset((prev) => ({
223
- x: cx - (cx - prev.x) * ratio,
224
- y: cy - (cy - prev.y) * ratio
225
- }));
226
- setScale(clamped);
186
+ function je({ extraActions: e }) {
187
+ const t = p((h) => h.scale), r = p((h) => h.viewportSize), s = p((h) => h.snapToGrid), i = p((h) => h.setScale), u = p((h) => h.setOffset), w = p((h) => h.setSnapToGrid), f = p((h) => h.fitToContent), m = p((h) => h.resetWidgets);
188
+ X(() => {
189
+ s && window.dispatchEvent(new Event("whiteboard-snap-now"));
190
+ }, [s]);
191
+ const g = (h) => {
192
+ const M = Math.min(3, Math.max(0.2, h)), k = M / t, _ = (r.width || window.innerWidth) / 2, v = (r.height || window.innerHeight) / 2;
193
+ u((P) => ({
194
+ x: _ - (_ - P.x) * k,
195
+ y: v - (v - P.y) * k
196
+ })), i(M);
227
197
  };
228
- return /* @__PURE__ */ jsxs2(
198
+ return /* @__PURE__ */ d(
229
199
  "div",
230
200
  {
231
201
  className: "zoom-bar",
232
- onPointerDown: (e) => e.stopPropagation(),
233
- onWheel: (e) => e.stopPropagation(),
202
+ onPointerDown: (h) => h.stopPropagation(),
203
+ onWheel: (h) => h.stopPropagation(),
234
204
  children: [
235
- /* @__PURE__ */ jsx2("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__icon", onClick: () => zoomTo(scale * 0.8), title: "Zoom out", "aria-label": "Zoom out", children: /* @__PURE__ */ jsx2(Minus, { size: 14 }) }),
236
- /* @__PURE__ */ jsxs2("span", { className: "zoom-bar__value", children: [
237
- Math.round(scale * 100),
205
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__icon", onClick: () => g(t * 0.8), title: "Zoom out", "aria-label": "Zoom out", children: /* @__PURE__ */ n(Fe, { size: 14 }) }),
206
+ /* @__PURE__ */ d("span", { className: "zoom-bar__value", children: [
207
+ Math.round(t * 100),
238
208
  "%"
239
209
  ] }),
240
- /* @__PURE__ */ jsx2("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__icon", onClick: () => zoomTo(scale * 1.2), title: "Zoom in", "aria-label": "Zoom in", children: /* @__PURE__ */ jsx2(Plus, { size: 14 }) }),
241
- /* @__PURE__ */ jsx2("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action", onClick: fitToContent, title: "Fit camera to all panels", "aria-label": "Fit camera to all panels", children: /* @__PURE__ */ jsx2(ScanSearch, { size: 14 }) }),
242
- /* @__PURE__ */ jsx2("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action", onClick: resetWidgets, title: "Reset panel positions", "aria-label": "Reset panel positions", children: /* @__PURE__ */ jsx2(RotateCcw, { size: 14 }) }),
243
- /* @__PURE__ */ jsx2(
210
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__icon", onClick: () => g(t * 1.2), title: "Zoom in", "aria-label": "Zoom in", children: /* @__PURE__ */ n(Ge, { size: 14 }) }),
211
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action", onClick: f, title: "Fit camera to all panels", "aria-label": "Fit camera to all panels", children: /* @__PURE__ */ n(Ie, { size: 14 }) }),
212
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action", onClick: m, title: "Reset panel positions", "aria-label": "Reset panel positions", children: /* @__PURE__ */ n(Le, { size: 14 }) }),
213
+ /* @__PURE__ */ n(
244
214
  "button",
245
215
  {
246
216
  type: "button",
247
- className: `wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action${snapToGrid ? " is-active" : ""}`,
248
- onClick: () => setSnapToGrid((prev) => !prev),
249
- title: snapToGrid ? "Disable snap to grid" : "Enable snap to grid",
250
- "aria-label": snapToGrid ? "Disable snap to grid" : "Enable snap to grid",
251
- children: /* @__PURE__ */ jsx2(Grid3x3, { size: 14 })
217
+ className: `wb-btn wb-btn--secondary wb-btn--icon-only zoom-bar__action${s ? " is-active" : ""}`,
218
+ onClick: () => w((h) => !h),
219
+ title: s ? "Disable snap to grid" : "Enable snap to grid",
220
+ "aria-label": s ? "Disable snap to grid" : "Enable snap to grid",
221
+ children: /* @__PURE__ */ n(Ee, { size: 14 })
252
222
  }
253
223
  ),
254
- extraActions
224
+ e
255
225
  ]
256
226
  }
257
227
  );
258
228
  }
259
-
260
- // src/Minimap.tsx
261
- import { useRef } from "react";
262
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
263
- var MAP_W = 200;
264
- var MAP_H = 150;
265
- var PADDING = 40;
266
- var MIN_WORLD_SIZE = 1;
267
- function Minimap({ loading = false }) {
268
- useWhiteboardStore((s) => s.registryVersion);
269
- const offset = useWhiteboardStore((s) => s.offset);
270
- const scale = useWhiteboardStore((s) => s.scale);
271
- const viewportSize = useWhiteboardStore((s) => s.viewportSize);
272
- const panels = useWhiteboardStore((s) => s.panels);
273
- const setOffset = useWhiteboardStore((s) => s.setOffset);
274
- const setScale = useWhiteboardStore((s) => s.setScale);
275
- const focusPanel = useWhiteboardStore((s) => s.focusPanel);
276
- const containerRef = useRef(null);
277
- const dragging = useRef(false);
278
- const lastPanelTapRef = useRef(null);
279
- const rectEntries = Array.from(panels.entries());
280
- const visibleRectEntries = rectEntries.filter(([, rect]) => Number.isFinite(rect.width) && Number.isFinite(rect.height));
281
- const rects = visibleRectEntries.map(([, rect]) => rect);
282
- if (loading || rects.length === 0) {
283
- return /* @__PURE__ */ jsx3("div", { className: "minimap minimap--loading", style: { width: MAP_W, height: MAP_H }, children: /* @__PURE__ */ jsx3("div", { className: "minimap__loading", children: /* @__PURE__ */ jsx3(Loader2, { size: 14, className: "icon-spin" }) }) });
284
- }
285
- const viewportWidth = viewportSize.width || window.innerWidth;
286
- const viewportHeight = viewportSize.height || window.innerHeight;
287
- const vpW = viewportWidth / scale;
288
- const vpH = viewportHeight / scale;
289
- const vpX = -offset.x / scale;
290
- const vpY = -offset.y / scale;
291
- let minX = Infinity;
292
- let minY = Infinity;
293
- let maxX = -Infinity;
294
- let maxY = -Infinity;
295
- for (const r of rects) {
296
- minX = Math.min(minX, r.x);
297
- minY = Math.min(minY, r.y);
298
- maxX = Math.max(maxX, r.x + r.width);
299
- maxY = Math.max(maxY, r.y + r.height);
300
- }
301
- minX -= PADDING;
302
- minY -= PADDING;
303
- maxX += PADDING;
304
- maxY += PADDING;
305
- const worldW = Math.max(MIN_WORLD_SIZE, maxX - minX);
306
- const worldH = Math.max(MIN_WORLD_SIZE, maxY - minY);
307
- const mapScale = Math.min(MAP_W / worldW, MAP_H / worldH);
308
- const contentW = worldW * mapScale;
309
- const contentH = worldH * mapScale;
310
- const mapOffsetX = (MAP_W - contentW) / 2;
311
- const mapOffsetY = (MAP_H - contentH) / 2;
312
- const toMapX = (wx) => mapOffsetX + (wx - minX) * mapScale;
313
- const toMapY = (wy) => mapOffsetY + (wy - minY) * mapScale;
314
- const clientToWorld = (clientX, clientY) => {
315
- if (!containerRef.current) return;
316
- const mapRect = containerRef.current.getBoundingClientRect();
317
- const localX = Math.min(MAP_W, Math.max(0, clientX - mapRect.left));
318
- const localY = Math.min(MAP_H, Math.max(0, clientY - mapRect.top));
319
- const boundedX = Math.min(contentW + mapOffsetX, Math.max(mapOffsetX, localX));
320
- const boundedY = Math.min(contentH + mapOffsetY, Math.max(mapOffsetY, localY));
321
- const worldX = (boundedX - mapOffsetX) / mapScale + minX;
322
- const worldY = (boundedY - mapOffsetY) / mapScale + minY;
323
- return { worldX, worldY };
324
- };
325
- const centerToWorld = (worldX, worldY, targetScale) => {
326
- const clampedScale = Math.min(3, Math.max(0.2, targetScale));
327
- setScale(clampedScale);
328
- setOffset({
329
- x: viewportWidth / 2 - worldX * clampedScale,
330
- y: viewportHeight / 2 - worldY * clampedScale
229
+ const ee = 200, te = 150, de = 40, xe = 1;
230
+ function Ue({ loading: e = !1 }) {
231
+ p((a) => a.registryVersion);
232
+ const t = p((a) => a.offset), r = p((a) => a.scale), s = p((a) => a.viewportSize), i = p((a) => a.panels), u = p((a) => a.setOffset), w = p((a) => a.setScale), f = p((a) => a.focusPanel), m = S(null), g = S(!1), h = S(null), k = Array.from(i.entries()).filter(([, a]) => Number.isFinite(a.width) && Number.isFinite(a.height)), _ = k.map(([, a]) => a);
233
+ if (e || _.length === 0)
234
+ return /* @__PURE__ */ n("div", { className: "minimap minimap--loading", style: { width: ee, height: te }, children: /* @__PURE__ */ n("div", { className: "minimap__loading", children: /* @__PURE__ */ n(fe, { size: 14, className: "icon-spin" }) }) });
235
+ const v = s.width || window.innerWidth, P = s.height || window.innerHeight, L = v / r, B = P / r, $ = -t.x / r, V = -t.y / r;
236
+ let F = 1 / 0, c = 1 / 0, R = -1 / 0, W = -1 / 0;
237
+ for (const a of _)
238
+ F = Math.min(F, a.x), c = Math.min(c, a.y), R = Math.max(R, a.x + a.width), W = Math.max(W, a.y + a.height);
239
+ F -= de, c -= de, R += de, W += de;
240
+ const C = Math.max(xe, R - F), O = Math.max(xe, W - c), z = Math.min(ee / C, te / O), E = C * z, U = O * z, Z = (ee - E) / 2, D = (te - U) / 2, q = (a) => Z + (a - F) * z, j = (a) => D + (a - c) * z, Q = (a, y) => {
241
+ if (!m.current) return;
242
+ const x = m.current.getBoundingClientRect(), G = Math.min(ee, Math.max(0, a - x.left)), A = Math.min(te, Math.max(0, y - x.top)), ae = Math.min(E + Z, Math.max(Z, G)), ie = Math.min(U + D, Math.max(D, A)), oe = (ae - Z) / z + F, ce = (ie - D) / z + c;
243
+ return { worldX: oe, worldY: ce };
244
+ }, K = (a, y, x) => {
245
+ const G = Math.min(3, Math.max(0.2, x));
246
+ w(G), u({
247
+ x: v / 2 - a * G,
248
+ y: P / 2 - y * G
331
249
  });
250
+ }, se = (a, y) => {
251
+ const x = Q(a, y);
252
+ x && K(x.worldX, x.worldY, r);
253
+ }, be = (a) => {
254
+ a.stopPropagation(), a.preventDefault(), g.current = !0, a.currentTarget.setPointerCapture(a.pointerId), se(a.clientX, a.clientY);
255
+ }, o = (a) => {
256
+ g.current && se(a.clientX, a.clientY);
257
+ }, b = () => {
258
+ g.current = !1;
332
259
  };
333
- const panTo = (clientX, clientY) => {
334
- const world = clientToWorld(clientX, clientY);
335
- if (!world) return;
336
- centerToWorld(world.worldX, world.worldY, scale);
337
- };
338
- const onDown = (e) => {
339
- e.stopPropagation();
340
- e.preventDefault();
341
- dragging.current = true;
342
- e.currentTarget.setPointerCapture(e.pointerId);
343
- panTo(e.clientX, e.clientY);
344
- };
345
- const onMove = (e) => {
346
- if (!dragging.current) return;
347
- panTo(e.clientX, e.clientY);
348
- };
349
- const onUp = () => {
350
- dragging.current = false;
351
- };
352
- const onWheel = (e) => {
353
- e.preventDefault();
354
- e.stopPropagation();
355
- const world = clientToWorld(e.clientX, e.clientY);
356
- if (!world) return;
357
- const factor = e.deltaY > 0 ? 0.9 : 1.1;
358
- centerToWorld(world.worldX, world.worldY, scale * factor);
359
- };
360
- return /* @__PURE__ */ jsxs3(
260
+ return /* @__PURE__ */ d(
361
261
  "div",
362
262
  {
363
- ref: containerRef,
364
- onPointerDown: onDown,
365
- onPointerMove: onMove,
366
- onPointerUp: onUp,
367
- onPointerCancel: onUp,
368
- onWheel,
263
+ ref: m,
264
+ onPointerDown: be,
265
+ onPointerMove: o,
266
+ onPointerUp: b,
267
+ onPointerCancel: b,
268
+ onWheel: (a) => {
269
+ a.preventDefault(), a.stopPropagation();
270
+ const y = Q(a.clientX, a.clientY);
271
+ if (!y) return;
272
+ const x = a.deltaY > 0 ? 0.9 : 1.1;
273
+ K(y.worldX, y.worldY, r * x);
274
+ },
369
275
  className: "minimap",
370
276
  style: {
371
- width: MAP_W,
372
- height: MAP_H
277
+ width: ee,
278
+ height: te
373
279
  },
374
280
  children: [
375
- visibleRectEntries.map(([id, r]) => /* @__PURE__ */ jsx3(
281
+ k.map(([a, y]) => /* @__PURE__ */ n(
376
282
  "div",
377
283
  {
378
284
  className: "minimap__panel",
379
- onPointerDown: (event) => {
380
- event.stopPropagation();
285
+ onPointerDown: (x) => {
286
+ x.stopPropagation();
381
287
  },
382
- onPointerUp: (event) => {
383
- event.stopPropagation();
384
- const now = Date.now();
385
- const last = lastPanelTapRef.current;
386
- if (last && last.id === id && now - last.time < 300) {
387
- event.preventDefault();
388
- focusPanel(r, { padding: r.focusPadding, maxScale: r.focusMaxScale });
389
- lastPanelTapRef.current = null;
288
+ onPointerUp: (x) => {
289
+ x.stopPropagation();
290
+ const G = Date.now(), A = h.current;
291
+ if (A && A.id === a && G - A.time < 300) {
292
+ x.preventDefault(), f(y, { padding: y.focusPadding, maxScale: y.focusMaxScale }), h.current = null;
390
293
  return;
391
294
  }
392
- lastPanelTapRef.current = { id, time: now };
295
+ h.current = { id: a, time: G };
393
296
  },
394
- onDoubleClick: (event) => {
395
- event.preventDefault();
396
- event.stopPropagation();
397
- focusPanel(r, { padding: r.focusPadding, maxScale: r.focusMaxScale });
297
+ onDoubleClick: (x) => {
298
+ x.preventDefault(), x.stopPropagation(), f(y, { padding: y.focusPadding, maxScale: y.focusMaxScale });
398
299
  },
399
300
  style: {
400
- left: toMapX(r.x),
401
- top: toMapY(r.y),
402
- width: Math.max(1, r.width * mapScale),
403
- height: Math.max(1, r.height * mapScale)
301
+ left: q(y.x),
302
+ top: j(y.y),
303
+ width: Math.max(1, y.width * z),
304
+ height: Math.max(1, y.height * z)
404
305
  }
405
306
  },
406
- id
307
+ a
407
308
  )),
408
- /* @__PURE__ */ jsx3(
309
+ /* @__PURE__ */ n(
409
310
  "div",
410
311
  {
411
312
  className: "minimap__viewport",
412
313
  style: {
413
- left: toMapX(vpX),
414
- top: toMapY(vpY),
415
- width: vpW * mapScale,
416
- height: vpH * mapScale
314
+ left: q($),
315
+ top: j(V),
316
+ width: L * z,
317
+ height: B * z
417
318
  }
418
319
  }
419
320
  )
@@ -421,384 +322,261 @@ function Minimap({ loading = false }) {
421
322
  }
422
323
  );
423
324
  }
424
-
425
- // src/WhiteboardShell.tsx
426
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
427
- function WhiteboardShell({ children, showMinimap = true, minimapLoading = false, extraActions }) {
428
- const offset = useWhiteboardStore((s) => s.offset);
429
- const scale = useWhiteboardStore((s) => s.scale);
430
- const registryVersion = useWhiteboardStore((s) => s.registryVersion);
431
- const viewportSize = useWhiteboardStore((s) => s.viewportSize);
432
- const setOffset = useWhiteboardStore((s) => s.setOffset);
433
- const setScale = useWhiteboardStore((s) => s.setScale);
434
- const setViewportSize = useWhiteboardStore((s) => s.setViewportSize);
435
- const shellRef = useRef2(null);
436
- const panning = useRef2(false);
437
- const last = useRef2({ x: 0, y: 0 });
438
- const activePointerId = useRef2(null);
439
- const scaleRef = useRef2(scale);
440
- const hasFitted = useRef2(false);
441
- useEffect2(() => {
442
- return () => {
443
- useWhiteboardStore.getState().resetSession();
444
- };
445
- }, []);
446
- useEffect2(() => {
447
- scaleRef.current = scale;
448
- }, [scale]);
449
- useEffect2(() => {
450
- const shell = shellRef.current;
451
- if (!shell) return;
452
- const updateViewport = () => {
453
- const rect = shell.getBoundingClientRect();
454
- setViewportSize({
455
- width: Math.max(0, rect.width),
456
- height: Math.max(0, rect.height)
325
+ function xt({ children: e, showMinimap: t = !0, minimapLoading: r = !1, extraActions: s }) {
326
+ const i = p((c) => c.offset), u = p((c) => c.scale), w = p((c) => c.registryVersion), f = p((c) => c.viewportSize), m = p((c) => c.setOffset), g = p((c) => c.setScale), h = p((c) => c.setViewportSize), M = S(null), k = S(!1), _ = S({ x: 0, y: 0 }), v = S(null), P = S(u), L = S(!1);
327
+ X(() => () => {
328
+ p.getState().resetSession();
329
+ }, []), X(() => {
330
+ P.current = u;
331
+ }, [u]), X(() => {
332
+ const c = M.current;
333
+ if (!c) return;
334
+ const R = () => {
335
+ const C = c.getBoundingClientRect();
336
+ h({
337
+ width: Math.max(0, C.width),
338
+ height: Math.max(0, C.height)
457
339
  });
458
340
  };
459
- updateViewport();
460
- if (typeof ResizeObserver === "undefined") return;
461
- const observer = new ResizeObserver(updateViewport);
462
- observer.observe(shell);
463
- return () => observer.disconnect();
464
- }, [setViewportSize]);
465
- useEffect2(() => {
466
- if (hasFitted.current) return;
467
- const { panels } = useWhiteboardStore.getState();
468
- if (panels.size === 0 || viewportSize.width <= 0 || viewportSize.height <= 0) return;
469
- hasFitted.current = true;
470
- requestAnimationFrame(() => {
471
- useWhiteboardStore.getState().fitToContent();
472
- });
473
- }, [registryVersion, viewportSize]);
474
- const onDown = useCallback((e) => {
475
- if (e.target !== e.currentTarget) return;
476
- if (e.button === 0 || e.button === 1) {
477
- panning.current = true;
478
- activePointerId.current = e.pointerId;
479
- last.current = { x: e.clientX, y: e.clientY };
480
- e.currentTarget.setPointerCapture(e.pointerId);
481
- e.preventDefault();
482
- }
483
- }, []);
484
- const onMove = useCallback((e) => {
485
- if (!panning.current || activePointerId.current !== e.pointerId) return;
486
- const dx = e.movementX ?? e.clientX - last.current.x;
487
- const dy = e.movementY ?? e.clientY - last.current.y;
488
- setOffset((p) => ({ x: p.x + dx, y: p.y + dy }));
489
- last.current = { x: e.clientX, y: e.clientY };
490
- }, [setOffset]);
491
- const stopPan = useCallback((e) => {
492
- if (activePointerId.current !== null) {
341
+ if (R(), typeof ResizeObserver > "u") return;
342
+ const W = new ResizeObserver(R);
343
+ return W.observe(c), () => W.disconnect();
344
+ }, [h]), X(() => {
345
+ if (L.current) return;
346
+ const { panels: c } = p.getState();
347
+ c.size === 0 || f.width <= 0 || f.height <= 0 || (L.current = !0, requestAnimationFrame(() => {
348
+ p.getState().fitToContent();
349
+ }));
350
+ }, [w, f]);
351
+ const B = H((c) => {
352
+ c.target === c.currentTarget && (c.button === 0 || c.button === 1) && (k.current = !0, v.current = c.pointerId, _.current = { x: c.clientX, y: c.clientY }, c.currentTarget.setPointerCapture(c.pointerId), c.preventDefault());
353
+ }, []), $ = H((c) => {
354
+ if (!k.current || v.current !== c.pointerId) return;
355
+ const R = c.movementX ?? c.clientX - _.current.x, W = c.movementY ?? c.clientY - _.current.y;
356
+ m((C) => ({ x: C.x + R, y: C.y + W })), _.current = { x: c.clientX, y: c.clientY };
357
+ }, [m]), V = H((c) => {
358
+ if (v.current !== null)
493
359
  try {
494
- e.currentTarget.releasePointerCapture(activePointerId.current);
360
+ c.currentTarget.releasePointerCapture(v.current);
495
361
  } catch {
496
362
  }
497
- }
498
- panning.current = false;
499
- activePointerId.current = null;
500
- }, []);
501
- const onWheel = useCallback((e) => {
502
- const s = scaleRef.current;
503
- const rect = e.currentTarget.getBoundingClientRect();
504
- const anchor = { x: e.clientX - rect.left, y: e.clientY - rect.top };
505
- const nextScale = Math.min(3, Math.max(0.2, s * (e.deltaY > 0 ? 0.9 : 1.1)));
506
- const ratio = nextScale / s;
507
- setOffset((prev) => ({
508
- x: anchor.x - (anchor.x - prev.x) * ratio,
509
- y: anchor.y - (anchor.y - prev.y) * ratio
510
- }));
511
- setScale(nextScale);
512
- }, [setOffset, setScale]);
513
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
514
- /* @__PURE__ */ jsx4(
363
+ k.current = !1, v.current = null;
364
+ }, []), F = H((c) => {
365
+ const R = P.current, W = c.currentTarget.getBoundingClientRect(), C = { x: c.clientX - W.left, y: c.clientY - W.top }, O = Math.min(3, Math.max(0.2, R * (c.deltaY > 0 ? 0.9 : 1.1))), z = O / R;
366
+ m((E) => ({
367
+ x: C.x - (C.x - E.x) * z,
368
+ y: C.y - (C.y - E.y) * z
369
+ })), g(O);
370
+ }, [m, g]);
371
+ return /* @__PURE__ */ d(re, { children: [
372
+ /* @__PURE__ */ n(
515
373
  "div",
516
374
  {
517
- ref: shellRef,
518
- onPointerDown: onDown,
519
- onPointerMove: onMove,
520
- onPointerUp: stopPan,
521
- onPointerCancel: stopPan,
522
- onWheel,
523
- onContextMenu: (e) => e.preventDefault(),
375
+ ref: M,
376
+ onPointerDown: B,
377
+ onPointerMove: $,
378
+ onPointerUp: V,
379
+ onPointerCancel: V,
380
+ onWheel: F,
381
+ onContextMenu: (c) => c.preventDefault(),
524
382
  className: "whiteboard-shell",
525
- children: /* @__PURE__ */ jsxs4(
383
+ children: /* @__PURE__ */ d(
526
384
  "div",
527
385
  {
528
386
  className: "whiteboard-canvas",
529
- style: { transform: `translate(${offset.x}px, ${offset.y}px) scale(${scale})` },
387
+ style: { transform: `translate(${i.x}px, ${i.y}px) scale(${u})` },
530
388
  children: [
531
- /* @__PURE__ */ jsx4("div", { className: "whiteboard-grid", "aria-hidden": true }),
532
- children
389
+ /* @__PURE__ */ n("div", { className: "whiteboard-grid", "aria-hidden": !0 }),
390
+ e
533
391
  ]
534
392
  }
535
393
  )
536
394
  }
537
395
  ),
538
- /* @__PURE__ */ jsx4(ZoomBar, { extraActions }),
539
- showMinimap ? /* @__PURE__ */ jsx4(Minimap, { loading: minimapLoading }) : null
396
+ /* @__PURE__ */ n(je, { extraActions: s }),
397
+ t ? /* @__PURE__ */ n(Ue, { loading: r }) : null
540
398
  ] });
541
399
  }
542
-
543
- // src/FloatingPanel.tsx
544
- import { useRef as useRef3, useId, useState, useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect, memo } from "react";
545
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
546
- var FloatingPanel = memo(function FloatingPanel2({
547
- title,
548
- defaultPosition,
549
- width = 300,
550
- className,
551
- trackRect: trackRectRef,
552
- focusable,
553
- focusPadding = 40,
554
- focusMaxScale = 1.5,
555
- headerActions,
556
- children
400
+ const St = Te(function({
401
+ title: t,
402
+ defaultPosition: r,
403
+ width: s = 300,
404
+ className: i,
405
+ trackRect: u,
406
+ focusable: w,
407
+ focusPadding: f = 40,
408
+ focusMaxScale: m = 1.5,
409
+ headerActions: g,
410
+ children: h
557
411
  }) {
558
- const panelId = useId();
559
- const [pos, setPos] = useState(defaultPosition);
560
- const dragging = useRef3(false);
561
- const posRef = useRef3(pos);
562
- const elRef = useRef3(null);
563
- const lastRegisteredRectRef = useRef3(null);
564
- const scale = useWhiteboardStore((s) => s.scale);
565
- const snapToGrid = useWhiteboardStore((s) => s.snapToGrid);
566
- const snapGridSize = useWhiteboardStore((s) => s.snapGridSize);
567
- const register = useWhiteboardStore((s) => s.register);
568
- const unregister = useWhiteboardStore((s) => s.unregister);
569
- const registerReset = useWhiteboardStore((s) => s.registerReset);
570
- const unregisterReset = useWhiteboardStore((s) => s.unregisterReset);
571
- const focusPanel = useWhiteboardStore((s) => s.focusPanel);
572
- const scaleRef = useRef3(scale);
573
- const snapToGridRef = useRef3(false);
574
- const snapGridSizeRef = useRef3(20);
575
- const defaultPosRef = useRef3(defaultPosition);
576
- const cleanupRef = useRef3(null);
577
- const lastTapRef = useRef3(null);
578
- useEffect3(() => {
579
- scaleRef.current = scale;
580
- }, [scale]);
581
- useEffect3(() => {
582
- snapToGridRef.current = snapToGrid;
583
- snapGridSizeRef.current = snapGridSize;
584
- }, [snapToGrid, snapGridSize]);
585
- useEffect3(() => {
586
- const onSnapNow = () => {
587
- const snapSize = snapGridSizeRef.current;
588
- setPos((current) => {
589
- const next = {
590
- x: Math.round(current.x / snapSize) * snapSize,
591
- y: Math.round(current.y / snapSize) * snapSize
412
+ const M = Re(), [k, _] = Se(r), v = S(!1), P = S(k), L = S(null), B = S(null), $ = p((o) => o.scale), V = p((o) => o.snapToGrid), F = p((o) => o.snapGridSize), c = p((o) => o.register), R = p((o) => o.unregister), W = p((o) => o.registerReset), C = p((o) => o.unregisterReset), O = p((o) => o.focusPanel), z = S($), E = S(!1), U = S(20), Z = S(r), D = S(null), q = S(null);
413
+ X(() => {
414
+ z.current = $;
415
+ }, [$]), X(() => {
416
+ E.current = V, U.current = F;
417
+ }, [V, F]), X(() => {
418
+ const o = () => {
419
+ const b = U.current;
420
+ _((N) => {
421
+ const a = {
422
+ x: Math.round(N.x / b) * b,
423
+ y: Math.round(N.y / b) * b
592
424
  };
593
- return next.x === current.x && next.y === current.y ? current : next;
425
+ return a.x === N.x && a.y === N.y ? N : a;
594
426
  });
595
427
  };
596
- window.addEventListener("whiteboard-snap-now", onSnapNow);
597
- return () => window.removeEventListener("whiteboard-snap-now", onSnapNow);
598
- }, []);
599
- useEffect3(() => {
600
- posRef.current = pos;
601
- }, [pos]);
602
- const registerRect = useCallback2(() => {
603
- const el = elRef.current;
604
- if (!el) return;
605
- const nextRect = {
606
- x: posRef.current.x,
607
- y: posRef.current.y,
608
- width: el.offsetWidth,
609
- height: el.offsetHeight,
610
- focusPadding,
611
- focusMaxScale
612
- };
613
- const prev = lastRegisteredRectRef.current;
614
- if (prev && prev.x === nextRect.x && prev.y === nextRect.y && prev.width === nextRect.width && prev.height === nextRect.height && prev.focusPadding === nextRect.focusPadding && prev.focusMaxScale === nextRect.focusMaxScale) {
615
- return;
616
- }
617
- lastRegisteredRectRef.current = nextRect;
618
- register(panelId, nextRect);
619
- if (trackRectRef) {
620
- trackRectRef.current = nextRect;
621
- }
622
- }, [focusMaxScale, focusPadding, panelId, register, trackRectRef]);
623
- const getCurrentRect = useCallback2(() => {
624
- const el = elRef.current;
625
- if (!el) return null;
626
- return {
627
- x: posRef.current.x,
628
- y: posRef.current.y,
629
- width: el.offsetWidth,
630
- height: el.offsetHeight,
631
- focusPadding,
632
- focusMaxScale
633
- };
634
- }, [focusMaxScale, focusPadding]);
635
- const handleFocus = useCallback2(() => {
636
- const rect = getCurrentRect();
637
- if (!rect) return;
638
- focusPanel(rect, { padding: focusPadding, maxScale: focusMaxScale });
639
- }, [focusPanel, focusPadding, focusMaxScale, getCurrentRect]);
640
- useEffect3(() => {
641
- registerReset(panelId, () => setPos(defaultPosRef.current));
642
- return () => {
643
- cleanupRef.current?.();
644
- unregister(panelId);
645
- unregisterReset(panelId);
646
- lastRegisteredRectRef.current = null;
647
- };
648
- }, [panelId, registerReset, unregister, unregisterReset]);
649
- useLayoutEffect(() => {
650
- registerRect();
651
- }, [pos.x, pos.y, width, registerRect]);
652
- useEffect3(() => {
653
- const el = elRef.current;
654
- if (!el) return;
655
- if (typeof ResizeObserver === "undefined") return;
656
- const obs = new ResizeObserver(() => {
657
- registerRect();
428
+ return window.addEventListener("whiteboard-snap-now", o), () => window.removeEventListener("whiteboard-snap-now", o);
429
+ }, []), X(() => {
430
+ P.current = k;
431
+ }, [k]);
432
+ const j = H(() => {
433
+ const o = L.current;
434
+ if (!o) return;
435
+ const b = {
436
+ x: P.current.x,
437
+ y: P.current.y,
438
+ width: o.offsetWidth,
439
+ height: o.offsetHeight,
440
+ focusPadding: f,
441
+ focusMaxScale: m
442
+ }, N = B.current;
443
+ N && N.x === b.x && N.y === b.y && N.width === b.width && N.height === b.height && N.focusPadding === b.focusPadding && N.focusMaxScale === b.focusMaxScale || (B.current = b, c(M, b), u && (u.current = b));
444
+ }, [m, f, M, c, u]), Q = H(() => {
445
+ const o = L.current;
446
+ return o ? {
447
+ x: P.current.x,
448
+ y: P.current.y,
449
+ width: o.offsetWidth,
450
+ height: o.offsetHeight,
451
+ focusPadding: f,
452
+ focusMaxScale: m
453
+ } : null;
454
+ }, [m, f]), K = H(() => {
455
+ const o = Q();
456
+ o && O(o, { padding: f, maxScale: m });
457
+ }, [O, f, m, Q]);
458
+ X(() => (W(M, () => _(Z.current)), () => {
459
+ var o;
460
+ (o = D.current) == null || o.call(D), R(M), C(M), B.current = null;
461
+ }), [M, W, R, C]), We(() => {
462
+ j();
463
+ }, [k.x, k.y, s, j]), X(() => {
464
+ const o = L.current;
465
+ if (!o || typeof ResizeObserver > "u") return;
466
+ const b = new ResizeObserver(() => {
467
+ j();
658
468
  });
659
- obs.observe(el);
660
- return () => obs.disconnect();
661
- }, [registerRect]);
662
- const onDown = useCallback2((e) => {
663
- if (e.button !== 0) return;
664
- dragging.current = true;
665
- const startX = e.clientX;
666
- const startY = e.clientY;
667
- const startPosX = posRef.current.x;
668
- const startPosY = posRef.current.y;
669
- const startScale = scaleRef.current;
670
- e.preventDefault();
671
- e.stopPropagation();
672
- e.target.setPointerCapture(e.pointerId);
673
- const move = (ev) => {
674
- if (!dragging.current) return;
675
- const rawX = startPosX + (ev.clientX - startX) / startScale;
676
- const rawY = startPosY + (ev.clientY - startY) / startScale;
677
- const snapSize = snapGridSizeRef.current;
678
- const nextX = snapToGridRef.current ? Math.round(rawX / snapSize) * snapSize : rawX;
679
- const nextY = snapToGridRef.current ? Math.round(rawY / snapSize) * snapSize : rawY;
680
- setPos({
681
- x: nextX,
682
- y: nextY
469
+ return b.observe(o), () => b.disconnect();
470
+ }, [j]);
471
+ const se = H((o) => {
472
+ var ae;
473
+ if (o.button !== 0) return;
474
+ v.current = !0;
475
+ const b = o.clientX, N = o.clientY, a = P.current.x, y = P.current.y, x = z.current;
476
+ o.preventDefault(), o.stopPropagation(), o.target.setPointerCapture(o.pointerId);
477
+ const G = (ie) => {
478
+ if (!v.current) return;
479
+ const oe = a + (ie.clientX - b) / x, ce = y + (ie.clientY - N) / x, le = U.current, Ce = E.current ? Math.round(oe / le) * le : oe, ze = E.current ? Math.round(ce / le) * le : ce;
480
+ _({
481
+ x: Ce,
482
+ y: ze
683
483
  });
484
+ }, A = () => {
485
+ v.current = !1, window.removeEventListener("pointermove", G), window.removeEventListener("pointerup", A), D.current = null;
684
486
  };
685
- const up = () => {
686
- dragging.current = false;
687
- window.removeEventListener("pointermove", move);
688
- window.removeEventListener("pointerup", up);
689
- cleanupRef.current = null;
690
- };
691
- cleanupRef.current?.();
692
- window.addEventListener("pointermove", move);
693
- window.addEventListener("pointerup", up);
694
- cleanupRef.current = up;
695
- }, []);
696
- const panelClassName = className ? `floating-panel ${className}` : "floating-panel";
697
- return /* @__PURE__ */ jsxs5(
487
+ (ae = D.current) == null || ae.call(D), window.addEventListener("pointermove", G), window.addEventListener("pointerup", A), D.current = A;
488
+ }, []), be = i ? `floating-panel ${i}` : "floating-panel";
489
+ return /* @__PURE__ */ d(
698
490
  "div",
699
491
  {
700
- ref: elRef,
701
- className: panelClassName,
702
- style: { left: pos.x, top: pos.y, width },
703
- onPointerDown: (e) => e.stopPropagation(),
704
- onPointerUp: (e) => {
705
- if (dragging.current) return;
706
- const now = Date.now();
707
- const last = lastTapRef.current;
708
- if (last && now - last.time < 300) {
709
- const dx = e.clientX - last.x;
710
- const dy = e.clientY - last.y;
711
- if (dx * dx + dy * dy < 100) {
712
- e.stopPropagation();
713
- handleFocus();
714
- lastTapRef.current = null;
492
+ ref: L,
493
+ className: be,
494
+ style: { left: k.x, top: k.y, width: s },
495
+ onPointerDown: (o) => o.stopPropagation(),
496
+ onPointerUp: (o) => {
497
+ if (v.current) return;
498
+ const b = Date.now(), N = q.current;
499
+ if (N && b - N.time < 300) {
500
+ const a = o.clientX - N.x, y = o.clientY - N.y;
501
+ if (a * a + y * y < 100) {
502
+ o.stopPropagation(), K(), q.current = null;
715
503
  return;
716
504
  }
717
505
  }
718
- lastTapRef.current = { time: now, x: e.clientX, y: e.clientY };
506
+ q.current = { time: b, x: o.clientX, y: o.clientY };
719
507
  },
720
- onWheel: (e) => e.stopPropagation(),
721
- onDoubleClick: (e) => {
722
- e.stopPropagation();
723
- handleFocus();
508
+ onWheel: (o) => o.stopPropagation(),
509
+ onDoubleClick: (o) => {
510
+ o.stopPropagation(), K();
724
511
  },
725
512
  children: [
726
- /* @__PURE__ */ jsxs5("div", { onPointerDown: onDown, className: "floating-panel__header", children: [
727
- /* @__PURE__ */ jsx5("strong", { className: "floating-panel__title", children: title }),
728
- headerActions,
729
- focusable && /* @__PURE__ */ jsx5(
513
+ /* @__PURE__ */ d("div", { onPointerDown: se, className: "floating-panel__header", children: [
514
+ /* @__PURE__ */ n("strong", { className: "floating-panel__title", children: t }),
515
+ g,
516
+ w && /* @__PURE__ */ n(
730
517
  "button",
731
518
  {
732
519
  type: "button",
733
520
  className: "wb-btn wb-btn--secondary wb-btn--icon-only floating-panel__focus",
734
- onClick: handleFocus,
735
- onPointerDown: (e) => e.stopPropagation(),
521
+ onClick: K,
522
+ onPointerDown: (o) => o.stopPropagation(),
736
523
  title: "Focus on this panel",
737
524
  "aria-label": "Focus on this panel",
738
- children: /* @__PURE__ */ jsx5(Maximize2, { size: 14 })
525
+ children: /* @__PURE__ */ n(Ae, { size: 14 })
739
526
  }
740
527
  )
741
528
  ] }),
742
- /* @__PURE__ */ jsx5("div", { className: "floating-panel__body", children })
529
+ /* @__PURE__ */ n("div", { className: "floating-panel__body", children: h })
743
530
  ]
744
531
  }
745
532
  );
746
533
  });
747
- function usePanelRect(initial) {
748
- const ref = useRef3({ ...initial, width: 0, height: 0 });
749
- return ref;
750
- }
751
- function belowPanel(rect, gap = WHITEBOARD_GRID) {
752
- return { x: rect.x, y: rect.y + rect.height + gap };
753
- }
754
-
755
- // src/ConfirmDialog.tsx
756
- import { useEffect as useEffect4 } from "react";
757
- import { createPortal } from "react-dom";
758
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
759
- function ConfirmDialog({
760
- open,
761
- title,
762
- message,
763
- onConfirm,
764
- onCancel,
765
- confirmLabel = "Confirm",
766
- loading,
767
- error
534
+ function Mt(e) {
535
+ return S({ ...e, width: 0, height: 0 });
536
+ }
537
+ function kt(e, t = he) {
538
+ return { x: e.x, y: e.y + e.height + t };
539
+ }
540
+ function _t({
541
+ open: e,
542
+ title: t,
543
+ message: r,
544
+ onConfirm: s,
545
+ onCancel: i,
546
+ confirmLabel: u = "Confirm",
547
+ loading: w,
548
+ error: f
768
549
  }) {
769
- useEffect4(() => {
770
- if (!open) return;
771
- const onKey = (e) => {
772
- if (e.key === "Escape") onCancel();
550
+ return X(() => {
551
+ if (!e) return;
552
+ const m = (g) => {
553
+ g.key === "Escape" && i();
773
554
  };
774
- window.addEventListener("keydown", onKey);
775
- return () => window.removeEventListener("keydown", onKey);
776
- }, [open, onCancel]);
777
- if (!open || typeof document === "undefined") return null;
778
- return createPortal(
779
- /* @__PURE__ */ jsx6("div", { className: "confirm-modal-overlay", onMouseDown: onCancel, children: /* @__PURE__ */ jsxs6(
555
+ return window.addEventListener("keydown", m), () => window.removeEventListener("keydown", m);
556
+ }, [e, i]), !e || typeof document > "u" ? null : Ye(
557
+ /* @__PURE__ */ n("div", { className: "confirm-modal-overlay", onMouseDown: i, children: /* @__PURE__ */ d(
780
558
  "div",
781
559
  {
782
560
  className: "confirm-modal",
783
561
  role: "dialog",
784
562
  "aria-modal": "true",
785
- "aria-label": title,
786
- onMouseDown: (e) => e.stopPropagation(),
563
+ "aria-label": t,
564
+ onMouseDown: (m) => m.stopPropagation(),
787
565
  children: [
788
- /* @__PURE__ */ jsxs6("div", { className: "confirm-modal__header", children: [
789
- /* @__PURE__ */ jsxs6("span", { className: "confirm-modal__title", children: [
790
- /* @__PURE__ */ jsx6(AlertTriangle, { size: 16 }),
791
- title
566
+ /* @__PURE__ */ d("div", { className: "confirm-modal__header", children: [
567
+ /* @__PURE__ */ d("span", { className: "confirm-modal__title", children: [
568
+ /* @__PURE__ */ n(Oe, { size: 16 }),
569
+ t
792
570
  ] }),
793
- /* @__PURE__ */ jsx6("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only", onClick: onCancel, "aria-label": "Close dialog", children: /* @__PURE__ */ jsx6(X, { size: 14 }) })
571
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--secondary wb-btn--icon-only", onClick: i, "aria-label": "Close dialog", children: /* @__PURE__ */ n(Me, { size: 14 }) })
794
572
  ] }),
795
- /* @__PURE__ */ jsx6("p", { className: "confirm-modal__message", children: message }),
796
- error && /* @__PURE__ */ jsx6("div", { className: "wb-alert wb-alert--error", children: error }),
797
- /* @__PURE__ */ jsxs6("div", { className: "wb-btn-row", children: [
798
- /* @__PURE__ */ jsx6("button", { type: "button", className: "wb-btn wb-btn--secondary", onClick: onCancel, children: "Cancel" }),
799
- /* @__PURE__ */ jsx6("button", { type: "button", className: "wb-btn wb-btn--danger", onClick: onConfirm, disabled: loading, children: loading ? "Deleting..." : /* @__PURE__ */ jsxs6(Fragment2, { children: [
800
- /* @__PURE__ */ jsx6(Check, { size: 14 }),
801
- confirmLabel
573
+ /* @__PURE__ */ n("p", { className: "confirm-modal__message", children: r }),
574
+ f && /* @__PURE__ */ n("div", { className: "wb-alert wb-alert--error", children: f }),
575
+ /* @__PURE__ */ d("div", { className: "wb-btn-row", children: [
576
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--secondary", onClick: i, children: "Cancel" }),
577
+ /* @__PURE__ */ n("button", { type: "button", className: "wb-btn wb-btn--danger", onClick: s, disabled: w, children: w ? "Deleting..." : /* @__PURE__ */ d(re, { children: [
578
+ /* @__PURE__ */ n(He, { size: 14 }),
579
+ u
802
580
  ] }) })
803
581
  ] })
804
582
  ]
@@ -807,92 +585,582 @@ function ConfirmDialog({
807
585
  document.body
808
586
  );
809
587
  }
810
-
811
- // src/PanelErrorBoundary.tsx
812
- import { Component } from "react";
813
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
814
- var PanelErrorBoundary = class extends Component {
588
+ class Pt extends De {
815
589
  constructor() {
816
- super(...arguments);
817
- this.state = { error: null };
590
+ super(...arguments), this.state = { error: null };
818
591
  }
819
- static getDerivedStateFromError(error) {
820
- return { error };
592
+ static getDerivedStateFromError(t) {
593
+ return { error: t };
821
594
  }
822
595
  render() {
823
- if (this.state.error) {
824
- return /* @__PURE__ */ jsxs7("div", { className: "wb-stack wb-stack--sm", children: [
825
- /* @__PURE__ */ jsx7("div", { className: "wb-alert wb-alert--error", children: this.props.fallbackMessage ?? "This panel crashed." }),
826
- /* @__PURE__ */ jsx7(
827
- "button",
828
- {
829
- type: "button",
830
- className: "wb-btn wb-btn--secondary",
831
- onClick: () => this.setState({ error: null }),
832
- children: "Retry"
833
- }
834
- )
835
- ] });
836
- }
837
- return this.props.children;
596
+ return this.state.error ? /* @__PURE__ */ d("div", { className: "wb-stack wb-stack--sm", children: [
597
+ /* @__PURE__ */ n("div", { className: "wb-alert wb-alert--error", children: this.props.fallbackMessage ?? "This panel crashed." }),
598
+ /* @__PURE__ */ n(
599
+ "button",
600
+ {
601
+ type: "button",
602
+ className: "wb-btn wb-btn--secondary",
603
+ onClick: () => this.setState({ error: null }),
604
+ children: "Retry"
605
+ }
606
+ )
607
+ ] }) : this.props.children;
838
608
  }
839
- };
840
-
841
- // src/useWhiteboardLayout.ts
842
- import { useMemo } from "react";
843
- function useWhiteboardLayout({
844
- widths,
845
- startX = 20,
846
- y = 40,
847
- gap = 20
609
+ }
610
+ function Ct({
611
+ widths: e,
612
+ startX: t = 20,
613
+ y: r = 40,
614
+ gap: s = 20
848
615
  }) {
849
- const panelWidth = useMemo(() => {
850
- const normalized = {};
851
- for (const [key, value] of Object.entries(widths)) {
852
- normalized[key] = snapToWhiteboardGrid(value);
853
- }
854
- return normalized;
855
- }, [widths]);
856
- const layout = useMemo(
616
+ const i = ye(() => {
617
+ const f = {};
618
+ for (const [m, g] of Object.entries(e))
619
+ f[m] = ue(g);
620
+ return f;
621
+ }, [e]), u = ye(
857
622
  () => ({
858
- startX: snapToWhiteboardGrid(startX),
859
- y: snapToWhiteboardGrid(y),
860
- gap: snapToWhiteboardGrid(gap)
623
+ startX: ue(t),
624
+ y: ue(r),
625
+ gap: ue(s)
861
626
  }),
862
- [startX, y, gap]
627
+ [t, r, s]
628
+ ), w = ye(() => {
629
+ const f = {};
630
+ let m = u.startX;
631
+ for (const [g, h] of Object.entries(i))
632
+ f[g] = { x: m, y: u.y }, m += h + u.gap;
633
+ return f;
634
+ }, [u.gap, u.startX, u.y, i]);
635
+ return { layout: u, panelWidth: i, positions: w };
636
+ }
637
+ function l(...e) {
638
+ return e.filter(Boolean).join(" ");
639
+ }
640
+ const Ze = {
641
+ error: "status-error",
642
+ muted: "text-sm text-muted",
643
+ info: "ui-alert ui-alert--info",
644
+ success: "ui-alert ui-alert--success"
645
+ };
646
+ function zt({ tone: e = "info", className: t, ...r }) {
647
+ return /* @__PURE__ */ n("p", { className: l(Ze[e], t), ...r });
648
+ }
649
+ const qe = {
650
+ primary: "",
651
+ secondary: "secondary-btn",
652
+ danger: "danger-btn"
653
+ }, Ke = me(function({
654
+ variant: t = "primary",
655
+ fullWidth: r = !1,
656
+ iconOnly: s = !1,
657
+ loading: i = !1,
658
+ disabled: u,
659
+ className: w,
660
+ children: f,
661
+ loadingText: m,
662
+ ...g
663
+ }, h) {
664
+ return /* @__PURE__ */ n(
665
+ "button",
666
+ {
667
+ ref: h,
668
+ className: l(qe[t], r && "full-width-btn", s && "icon-only-btn", w),
669
+ disabled: u || i,
670
+ ...g,
671
+ children: i ? /* @__PURE__ */ d(re, { children: [
672
+ /* @__PURE__ */ n(fe, { size: 14, className: "icon-spin" }),
673
+ m || f
674
+ ] }) : f
675
+ }
863
676
  );
864
- const positions = useMemo(() => {
865
- const next = {};
866
- let x = layout.startX;
867
- for (const [key, width] of Object.entries(panelWidth)) {
868
- ;
869
- next[key] = { x, y: layout.y };
870
- x += width + layout.gap;
677
+ });
678
+ function Je({ as: e = "div", className: t, ...r }) {
679
+ return T(e, {
680
+ className: l("button-row", t),
681
+ ...r
682
+ });
683
+ }
684
+ function Tt({ className: e, ...t }) {
685
+ return /* @__PURE__ */ n("span", { className: l("chip", e), ...t });
686
+ }
687
+ function Qe({ active: e = !1, className: t, ...r }) {
688
+ return /* @__PURE__ */ n(
689
+ "button",
690
+ {
691
+ type: "button",
692
+ className: l("choice-card", t),
693
+ "data-active": e,
694
+ ...r
871
695
  }
872
- return next;
873
- }, [layout.gap, layout.startX, layout.y, panelWidth]);
874
- return { layout, panelWidth, positions };
696
+ );
697
+ }
698
+ function pe({ as: e = "ul", reset: t = !0, className: r, ...s }) {
699
+ return T(e, {
700
+ className: l(t && "list-reset", r),
701
+ ...s
702
+ });
703
+ }
704
+ const et = {
705
+ sm: "",
706
+ md: "skeleton--md",
707
+ pill: "skeleton--pill"
708
+ };
709
+ function I({ as: e = "div", radius: t = "sm", className: r, ...s }) {
710
+ return T(e, {
711
+ className: l("skeleton", et[t], r),
712
+ "aria-hidden": !0,
713
+ ...s
714
+ });
715
+ }
716
+ function tt(e) {
717
+ const { className: t, ...r } = e;
718
+ return /* @__PURE__ */ n(I, { className: l("skeleton-input", t), ...r });
719
+ }
720
+ function ve(e) {
721
+ const { className: t, ...r } = e;
722
+ return /* @__PURE__ */ n(I, { className: l("skeleton-btn", t), ...r });
723
+ }
724
+ function Rt(e) {
725
+ const { className: t, ...r } = e;
726
+ return /* @__PURE__ */ n(I, { className: l("skeleton-icon-btn", t), ...r });
727
+ }
728
+ function nt(e) {
729
+ const { className: t, ...r } = e;
730
+ return /* @__PURE__ */ n(I, { className: l("skeleton-select", t), ...r });
731
+ }
732
+ function Wt(e) {
733
+ const { className: t, ...r } = e;
734
+ return /* @__PURE__ */ n(I, { className: l("skeleton-textarea", t), ...r });
735
+ }
736
+ function ke(e) {
737
+ const { className: t, ...r } = e;
738
+ return /* @__PURE__ */ n(I, { className: l("skeleton-thumb", t), ...r });
739
+ }
740
+ function ne(e) {
741
+ const { className: t, ...r } = e;
742
+ return /* @__PURE__ */ n(I, { radius: "pill", className: l("skeleton-chip", t), ...r });
743
+ }
744
+ function we(e) {
745
+ const { className: t, ...r } = e;
746
+ return /* @__PURE__ */ n(I, { className: l("skeleton-title", t), ...r });
747
+ }
748
+ function ge({ short: e = !1, className: t, ...r }) {
749
+ return /* @__PURE__ */ n(I, { className: l("skeleton-line", e && "skeleton-line--short", t), ...r });
750
+ }
751
+ function rt(e) {
752
+ const { className: t, ...r } = e;
753
+ return /* @__PURE__ */ n(I, { radius: "pill", className: l("skeleton-avatar", t), ...r });
754
+ }
755
+ function Dt(e) {
756
+ const { className: t, ...r } = e;
757
+ return /* @__PURE__ */ n(I, { className: l("skeleton-canvas", t), ...r });
758
+ }
759
+ function Xt({
760
+ options: e,
761
+ value: t,
762
+ onChange: r,
763
+ className: s
764
+ }) {
765
+ return /* @__PURE__ */ n(pe, { className: l("choice-list", s), children: e.map((i) => /* @__PURE__ */ n("li", { children: /* @__PURE__ */ n(
766
+ Qe,
767
+ {
768
+ active: t === i.value,
769
+ onClick: () => r(i.value),
770
+ disabled: i.disabled,
771
+ "data-has-description": i.description ? "true" : "false",
772
+ children: i.description ? /* @__PURE__ */ d("span", { className: "choice-card__row", children: [
773
+ /* @__PURE__ */ n("span", { className: "choice-card__label", children: i.label }),
774
+ /* @__PURE__ */ n("span", { className: "choice-card__description", children: i.description })
775
+ ] }) : /* @__PURE__ */ n("span", { className: "choice-card__label", children: i.label })
776
+ }
777
+ ) }, i.value)) });
778
+ }
779
+ function Yt({ count: e = 4, className: t, withDescription: r = !1 }) {
780
+ return /* @__PURE__ */ n(pe, { className: l("choice-list", t), "aria-hidden": !0, children: Array.from({ length: e }).map((s, i) => /* @__PURE__ */ n("li", { children: /* @__PURE__ */ d("div", { className: "choice-card", children: [
781
+ /* @__PURE__ */ n(we, { className: l("skeleton-title--sm", !r && "skeleton-choice-label") }),
782
+ r ? /* @__PURE__ */ n(ge, { short: !0 }) : null
783
+ ] }) }, `choice-skeleton-${i}`)) });
784
+ }
785
+ function Ft({ className: e, ...t }) {
786
+ return /* @__PURE__ */ n("div", { className: l("coord-grid", e), ...t });
787
+ }
788
+ function Gt({ axis: e, className: t, ...r }) {
789
+ return /* @__PURE__ */ d("label", { className: l("coord-input", t), children: [
790
+ e,
791
+ " ",
792
+ /* @__PURE__ */ n("input", { type: "number", step: "0.01", ...r })
793
+ ] });
794
+ }
795
+ function J({
796
+ as: e,
797
+ size: t = "md",
798
+ className: r,
799
+ ...s
800
+ }) {
801
+ return T(e ?? "div", {
802
+ className: l(t === "sm" ? "panel-stack-sm" : "panel-stack", r),
803
+ ...s
804
+ });
805
+ }
806
+ const st = {
807
+ xs: "text-xs",
808
+ sm: "text-sm",
809
+ md: ""
810
+ };
811
+ function at({ as: e = "p", clamp: t = !1, className: r, ...s }) {
812
+ return T(e, {
813
+ className: l("asset-title", t && "asset-title--clamp", r),
814
+ ...s
815
+ });
816
+ }
817
+ function It({ as: e = "h3", className: t, ...r }) {
818
+ return T(e, {
819
+ className: l("story-title", t),
820
+ ...r
821
+ });
822
+ }
823
+ function _e({ as: e = "p", size: t = "sm", className: r, ...s }) {
824
+ return T(e, {
825
+ className: l(st[t], "text-muted", r),
826
+ ...s
827
+ });
828
+ }
829
+ function Lt({ as: e = "h1", className: t, ...r }) {
830
+ return T(e, {
831
+ className: l("page-title", t),
832
+ ...r
833
+ });
834
+ }
835
+ function it({ as: e = "span", className: t, ...r }) {
836
+ return T(e, {
837
+ className: l("widget-section__title", t),
838
+ ...r
839
+ });
840
+ }
841
+ function ot({ as: e = "p", className: t, ...r }) {
842
+ return T(e, {
843
+ className: l("widget-section__description", t),
844
+ ...r
845
+ });
846
+ }
847
+ function Et({ title: e, description: t, action: r }) {
848
+ return /* @__PURE__ */ d(J, { size: "sm", children: [
849
+ /* @__PURE__ */ n(at, { children: e }),
850
+ t ? /* @__PURE__ */ n(_e, { children: t }) : null,
851
+ r
852
+ ] });
853
+ }
854
+ function ct({ className: e, ...t }) {
855
+ return /* @__PURE__ */ n("label", { className: l("widget-label", e), ...t });
856
+ }
857
+ function At({
858
+ as: e,
859
+ label: t,
860
+ htmlFor: r,
861
+ hint: s,
862
+ error: i,
863
+ layout: u = "stack",
864
+ className: w,
865
+ children: f,
866
+ ...m
867
+ }) {
868
+ return T(
869
+ e ?? "div",
870
+ {
871
+ className: l(u === "control" ? "widget-control" : "panel-stack-sm", w),
872
+ ...m
873
+ },
874
+ /* @__PURE__ */ d(re, { children: [
875
+ t ? /* @__PURE__ */ n(ct, { htmlFor: r, children: t }) : null,
876
+ f,
877
+ s ? /* @__PURE__ */ n(_e, { size: "xs", children: s }) : null,
878
+ i ? /* @__PURE__ */ n("p", { className: "field-error", children: i }) : null
879
+ ] })
880
+ );
881
+ }
882
+ function Ot({ isGenerating: e, children: t, message: r }) {
883
+ return /* @__PURE__ */ d("div", { className: "generating-overlay-wrap", children: [
884
+ t,
885
+ e && /* @__PURE__ */ d("div", { className: "generating-overlay", "aria-live": "polite", children: [
886
+ /* @__PURE__ */ n(fe, { size: 20, className: "icon-spin" }),
887
+ /* @__PURE__ */ n("span", { children: r ?? "Generating, please wait…" })
888
+ ] })
889
+ ] });
890
+ }
891
+ function Ht({ as: e = "span", icon: t, className: r, children: s, ...i }) {
892
+ return T(
893
+ e,
894
+ {
895
+ className: l("inline-row", r),
896
+ ...i
897
+ },
898
+ /* @__PURE__ */ d(re, { children: [
899
+ t,
900
+ s
901
+ ] })
902
+ );
903
+ }
904
+ function Bt({
905
+ src: e,
906
+ alt: t,
907
+ placeholder: r = "No image",
908
+ size: s = "md",
909
+ fit: i = "contain",
910
+ onImageError: u,
911
+ className: w,
912
+ ...f
913
+ }) {
914
+ const [m, g] = Se(null), h = !!(e && m === e), M = l("image-thumb", `image-thumb--${s}`, `image-thumb--fit-${i}`, w);
915
+ return /* @__PURE__ */ n("div", { className: M, ...f, children: e && !h ? /* @__PURE__ */ n(
916
+ "img",
917
+ {
918
+ src: e,
919
+ alt: t,
920
+ className: "image-thumb__img",
921
+ style: { objectFit: i, objectPosition: "center" },
922
+ onError: () => {
923
+ g(e), u == null || u();
924
+ }
925
+ }
926
+ ) : /* @__PURE__ */ n("span", { className: "image-thumb__placeholder", children: r }) });
927
+ }
928
+ function lt({
929
+ as: e,
930
+ justify: t = "start",
931
+ className: r,
932
+ ...s
933
+ }) {
934
+ return T(e ?? "div", {
935
+ className: l(t === "between" ? "space-between" : "inline-row", r),
936
+ ...s
937
+ });
938
+ }
939
+ const $t = me(function({ className: t, ...r }, s) {
940
+ return /* @__PURE__ */ n("input", { ref: s, className: l(t), ...r });
941
+ });
942
+ function Pe({ as: e = "div", className: t, ...r }) {
943
+ return T(e, {
944
+ className: l("item-card", t),
945
+ ...r
946
+ });
947
+ }
948
+ function ut({ as: e = "div", className: t, ...r }) {
949
+ return T(e, {
950
+ className: l("item-list", t),
951
+ ...r
952
+ });
953
+ }
954
+ function Vt({ label: e = "Loading...", className: t }) {
955
+ return /* @__PURE__ */ d("span", { className: l("status-inline", t), children: [
956
+ /* @__PURE__ */ n(fe, { size: 14, className: "icon-spin" }),
957
+ e
958
+ ] });
959
+ }
960
+ function jt({ children: e }) {
961
+ return /* @__PURE__ */ n("main", { className: "page-shell", children: e });
962
+ }
963
+ function Ut({ children: e }) {
964
+ return /* @__PURE__ */ n("div", { className: "page-card", children: e });
965
+ }
966
+ function Zt({ onClick: e, label: t = "Close" }) {
967
+ return /* @__PURE__ */ d(Ke, { variant: "secondary", className: "panel-close-btn", onClick: e, children: [
968
+ /* @__PURE__ */ n(Me, { size: 14 }),
969
+ t
970
+ ] });
971
+ }
972
+ function qt({ heading: e, description: t, actions: r, className: s, children: i, ...u }) {
973
+ return /* @__PURE__ */ d("section", { className: l("widget-section", s), ...u, children: [
974
+ e || r ? /* @__PURE__ */ d("header", { className: r ? "title-row" : void 0, children: [
975
+ e ? /* @__PURE__ */ n(it, { children: e }) : null,
976
+ r
977
+ ] }) : null,
978
+ t ? /* @__PURE__ */ n(ot, { children: t }) : null,
979
+ i
980
+ ] });
981
+ }
982
+ function Kt({ icon: e, label: t }) {
983
+ return /* @__PURE__ */ d("span", { className: "panel-title-with-icon", children: [
984
+ /* @__PURE__ */ n(e, { size: 13, className: "panel-title-icon" }),
985
+ /* @__PURE__ */ n("span", { children: t })
986
+ ] });
987
+ }
988
+ function dt({ as: e = "button", className: t, ...r }) {
989
+ return T(e, {
990
+ className: l("picker-card", t),
991
+ ...r
992
+ });
993
+ }
994
+ const ht = {
995
+ elements: "picker-grid--elements",
996
+ characters: "picker-grid--characters",
997
+ library: "picker-grid--library"
998
+ };
999
+ function Jt({ variant: e, className: t, ...r }) {
1000
+ return /* @__PURE__ */ n(pe, { className: l("picker-grid", ht[e], t), ...r });
1001
+ }
1002
+ const mt = {
1003
+ default: "",
1004
+ success: "success",
1005
+ warning: "warning",
1006
+ danger: "danger"
1007
+ };
1008
+ function Qt({ tone: e = "default", className: t, ...r }) {
1009
+ return /* @__PURE__ */ n("span", { className: l("pill", mt[e], t), ...r });
1010
+ }
1011
+ const en = me(function({ className: t, ...r }, s) {
1012
+ return /* @__PURE__ */ n("select", { ref: s, className: l(t), ...r });
1013
+ }), ft = {
1014
+ element: "split-layout--element",
1015
+ character: "split-layout--character",
1016
+ user: "split-layout--user"
1017
+ };
1018
+ function pt({ variant: e, className: t, ...r }) {
1019
+ return /* @__PURE__ */ n("div", { className: l("split-layout", ft[e], t), ...r });
1020
+ }
1021
+ function wt({ className: e, ...t }) {
1022
+ return /* @__PURE__ */ n("div", { className: l("tag-row", e), ...t });
1023
+ }
1024
+ const tn = me(function({ className: t, ...r }, s) {
1025
+ return /* @__PURE__ */ n("textarea", { ref: s, className: l(t), ...r });
1026
+ });
1027
+ function nn({ className: e, theme: t = "light", onToggle: r, lightIcon: s, darkIcon: i }) {
1028
+ return /* @__PURE__ */ n("button", { className: e, onClick: r, title: `Switch to ${t === "light" ? "dark" : "light"} mode`, "aria-label": "Toggle theme", children: t === "light" ? i ?? /* @__PURE__ */ n(Be, {}) : s ?? /* @__PURE__ */ n($e, {}) });
1029
+ }
1030
+ function rn({ className: e, ...t }) {
1031
+ return /* @__PURE__ */ n("div", { className: l("title-row", e), ...t });
1032
+ }
1033
+ function sn({ inputs: e = 1, showButton: t = !0, className: r, ...s }) {
1034
+ return /* @__PURE__ */ d(J, { className: r, ...s, children: [
1035
+ Array.from({ length: e }).map((i, u) => /* @__PURE__ */ n(tt, {}, `panel-form-input-${u}`)),
1036
+ t && /* @__PURE__ */ n(ve, {})
1037
+ ] });
1038
+ }
1039
+ function an() {
1040
+ return /* @__PURE__ */ d("article", { className: "story-card", children: [
1041
+ /* @__PURE__ */ n("div", { className: "story-cover", children: /* @__PURE__ */ n("div", { className: "story-cover__placeholder story-cover__placeholder--skeleton" }) }),
1042
+ /* @__PURE__ */ n("div", { className: "story-card__overlay story-card__overlay--skeleton", children: /* @__PURE__ */ d("div", { className: "story-card__overlay-text", children: [
1043
+ /* @__PURE__ */ d(lt, { as: "header", children: [
1044
+ /* @__PURE__ */ n(ne, {}),
1045
+ /* @__PURE__ */ n(ne, {})
1046
+ ] }),
1047
+ /* @__PURE__ */ n(we, {}),
1048
+ /* @__PURE__ */ n(ge, { short: !0 })
1049
+ ] }) })
1050
+ ] });
1051
+ }
1052
+ function gt() {
1053
+ return /* @__PURE__ */ n(Pe, { as: "li", children: /* @__PURE__ */ d(pt, { variant: "user", children: [
1054
+ /* @__PURE__ */ n(rt, {}),
1055
+ /* @__PURE__ */ d(J, { size: "sm", children: [
1056
+ /* @__PURE__ */ n(we, {}),
1057
+ /* @__PURE__ */ n(ge, { short: !0 })
1058
+ ] }),
1059
+ /* @__PURE__ */ d(J, { size: "sm", children: [
1060
+ /* @__PURE__ */ n(ne, {}),
1061
+ /* @__PURE__ */ n(nt, {})
1062
+ ] })
1063
+ ] }) });
1064
+ }
1065
+ function on({ count: e = 3 }) {
1066
+ return /* @__PURE__ */ n(ut, { as: "ul", children: Array.from({ length: e }).map((t, r) => /* @__PURE__ */ n(gt, {}, `user-skeleton-${r}`)) });
1067
+ }
1068
+ function cn() {
1069
+ return /* @__PURE__ */ d(Pe, { as: "li", children: [
1070
+ /* @__PURE__ */ d(J, { size: "sm", children: [
1071
+ /* @__PURE__ */ n(ke, { className: "asset-thumb" }),
1072
+ /* @__PURE__ */ n(we, {}),
1073
+ /* @__PURE__ */ d(wt, { children: [
1074
+ /* @__PURE__ */ n(ne, {}),
1075
+ /* @__PURE__ */ n(ne, {})
1076
+ ] })
1077
+ ] }),
1078
+ /* @__PURE__ */ d(Je, { children: [
1079
+ /* @__PURE__ */ n(ve, {}),
1080
+ /* @__PURE__ */ n(ve, {})
1081
+ ] })
1082
+ ] });
875
1083
  }
876
-
877
- // src/cn.ts
878
- function cn(...args) {
879
- return args.filter(Boolean).join(" ");
1084
+ function ln({ count: e = 8, gridClass: t }) {
1085
+ return /* @__PURE__ */ n(pe, { className: l("picker-grid", t), children: Array.from({ length: e }).map((r, s) => /* @__PURE__ */ n("li", { children: /* @__PURE__ */ n(dt, { as: "div", className: "picker-card--skeleton", children: /* @__PURE__ */ d(J, { size: "sm", children: [
1086
+ /* @__PURE__ */ n(ke, { className: "asset-thumb" }),
1087
+ /* @__PURE__ */ n(ge, { short: !0 })
1088
+ ] }) }) }, `picker-skeleton-${s}`)) });
880
1089
  }
881
1090
  export {
882
- ConfirmDialog,
883
- FloatingPanel,
884
- Minimap,
885
- PanelErrorBoundary,
886
- WHITEBOARD_GRID,
887
- WhiteboardShell,
888
- ZoomBar,
889
- belowPanel,
890
- cn,
891
- computeWhiteboardFit,
892
- computeWhiteboardRectFocus,
893
- snapToWhiteboardGrid,
894
- usePanelRect,
895
- useWhiteboardLayout,
896
- useWhiteboardStore
1091
+ zt as Alert,
1092
+ cn as AssetCardSkeleton,
1093
+ at as AssetTitle,
1094
+ rt as AvatarSkeleton,
1095
+ Ke as Button,
1096
+ Je as ButtonRow,
1097
+ ve as ButtonSkeleton,
1098
+ Dt as CanvasSkeleton,
1099
+ Tt as Chip,
1100
+ ne as ChipSkeleton,
1101
+ Qe as ChoiceCard,
1102
+ Xt as ChoiceGroup,
1103
+ Yt as ChoiceGroupSkeleton,
1104
+ _t as ConfirmDialog,
1105
+ Ft as CoordGrid,
1106
+ Gt as CoordInput,
1107
+ Et as EmptyState,
1108
+ At as Field,
1109
+ St as FloatingPanel,
1110
+ Ot as GeneratingOverlay,
1111
+ Rt as IconButtonSkeleton,
1112
+ Ht as IconText,
1113
+ Bt as ImageThumb,
1114
+ lt as Inline,
1115
+ $t as Input,
1116
+ tt as InputSkeleton,
1117
+ Pe as ItemCard,
1118
+ ut as ItemList,
1119
+ ct as Label,
1120
+ ge as LineSkeleton,
1121
+ pe as List,
1122
+ Vt as LoadingState,
1123
+ Ue as Minimap,
1124
+ _e as MutedText,
1125
+ Ut as PageCard,
1126
+ jt as PageShell,
1127
+ Lt as PageTitle,
1128
+ Zt as PanelCloseButton,
1129
+ Pt as PanelErrorBoundary,
1130
+ sn as PanelFormSkeleton,
1131
+ qt as PanelSection,
1132
+ Kt as PanelTitle,
1133
+ dt as PickerCard,
1134
+ Jt as PickerGrid,
1135
+ ln as PickerGridSkeleton,
1136
+ Qt as Pill,
1137
+ ot as SectionDescription,
1138
+ it as SectionTitle,
1139
+ en as Select,
1140
+ nt as SelectSkeleton,
1141
+ I as Skeleton,
1142
+ pt as SplitLayout,
1143
+ J as Stack,
1144
+ an as StoryCardSkeleton,
1145
+ It as StoryTitle,
1146
+ wt as TagRow,
1147
+ tn as Textarea,
1148
+ Wt as TextareaSkeleton,
1149
+ nn as ThemeToggle,
1150
+ ke as ThumbSkeleton,
1151
+ rn as TitleRow,
1152
+ we as TitleSkeleton,
1153
+ gt as UserCardSkeleton,
1154
+ on as UserListSkeleton,
1155
+ he as WHITEBOARD_GRID,
1156
+ xt as WhiteboardShell,
1157
+ je as ZoomBar,
1158
+ kt as belowPanel,
1159
+ l as cn,
1160
+ Ne as computeWhiteboardFit,
1161
+ Ve as computeWhiteboardRectFocus,
1162
+ ue as snapToWhiteboardGrid,
1163
+ Mt as usePanelRect,
1164
+ Ct as useWhiteboardLayout,
1165
+ p as useWhiteboardStore
897
1166
  };
898
- //# sourceMappingURL=index.js.map