@vllnt/ui 0.2.0 → 0.2.1-canary.73a93ee
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/CHANGELOG.md +12 -1
- package/README.md +27 -12
- package/dist/components/activity-log/activity-log.js +1 -0
- package/dist/components/anchor-port/anchor-port.js +51 -0
- package/dist/components/anchor-port/index.js +4 -0
- package/dist/components/animated-text/animated-text.js +1 -0
- package/dist/components/bottom-bar/bottom-bar.js +25 -0
- package/dist/components/bottom-bar/index.js +4 -0
- package/dist/components/canvas-shell/canvas-foundation-demo.js +183 -0
- package/dist/components/canvas-shell/canvas-shell-route-config.js +0 -0
- package/dist/components/canvas-shell/canvas-shell.js +261 -0
- package/dist/components/canvas-shell/index.js +4 -0
- package/dist/components/canvas-view/canvas-view.js +461 -0
- package/dist/components/canvas-view/index.js +6 -0
- package/dist/components/chart/area-chart.js +1 -0
- package/dist/components/chart/line-chart.js +1 -0
- package/dist/components/chat-dock-section/chat-dock-section.js +56 -0
- package/dist/components/chat-dock-section/index.js +6 -0
- package/dist/components/checklist/checklist.js +7 -0
- package/dist/components/checklist/index.js +3 -1
- package/dist/components/comment-pin/comment-pin.js +104 -0
- package/dist/components/comment-pin/index.js +6 -0
- package/dist/components/connector-edge/connector-edge.js +66 -0
- package/dist/components/connector-edge/index.js +6 -0
- package/dist/components/conversation-thread/conversation-thread.js +348 -0
- package/dist/components/conversation-thread/index.js +20 -0
- package/dist/components/curriculum/curriculum.js +349 -0
- package/dist/components/curriculum/index.js +10 -0
- package/dist/components/data-list/data-list.js +1 -0
- package/dist/components/edge-label/edge-label.js +26 -0
- package/dist/components/edge-label/index.js +4 -0
- package/dist/components/form/form.js +432 -0
- package/dist/components/form/index.js +20 -0
- package/dist/components/glass-panel/glass-panel.js +21 -0
- package/dist/components/glass-panel/index.js +4 -0
- package/dist/components/group-hull/group-hull.js +29 -0
- package/dist/components/group-hull/index.js +4 -0
- package/dist/components/index.js +176 -0
- package/dist/components/infinite-plane/index.js +6 -0
- package/dist/components/infinite-plane/infinite-plane.js +75 -0
- package/dist/components/left-rail/index.js +4 -0
- package/dist/components/left-rail/left-rail.js +25 -0
- package/dist/components/live-cursor/index.js +6 -0
- package/dist/components/live-cursor/live-cursor.js +62 -0
- package/dist/components/mini-map-panel/index.js +6 -0
- package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
- package/dist/components/multi-select/index.js +6 -0
- package/dist/components/multi-select/multi-select.js +258 -0
- package/dist/components/object-card/index.js +6 -0
- package/dist/components/object-card/object-card.js +126 -0
- package/dist/components/object-handle/index.js +4 -0
- package/dist/components/object-handle/object-handle.js +38 -0
- package/dist/components/overview-board/index.js +8 -0
- package/dist/components/overview-board/overview-board.js +127 -0
- package/dist/components/presence-stack/index.js +6 -0
- package/dist/components/presence-stack/presence-stack.js +108 -0
- package/dist/components/presence-sync-indicator/index.js +6 -0
- package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
- package/dist/components/progress-tracker/index.js +20 -0
- package/dist/components/progress-tracker/progress-tracker.js +527 -0
- package/dist/components/right-dock/index.js +4 -0
- package/dist/components/right-dock/right-dock.js +28 -0
- package/dist/components/run-timeline/index.js +6 -0
- package/dist/components/run-timeline/run-timeline.js +221 -0
- package/dist/components/segmented-control/index.js +12 -0
- package/dist/components/segmented-control/segmented-control.js +61 -0
- package/dist/components/selection-presence/index.js +6 -0
- package/dist/components/selection-presence/selection-presence.js +50 -0
- package/dist/components/spinner/unicode-spinner.js +1 -0
- package/dist/components/tags-input/index.js +4 -0
- package/dist/components/tags-input/tags-input.js +178 -0
- package/dist/components/thread-bubble/index.js +6 -0
- package/dist/components/thread-bubble/thread-bubble.js +85 -0
- package/dist/components/top-bar/index.js +4 -0
- package/dist/components/top-bar/top-bar.js +31 -0
- package/dist/components/usage-breakdown/usage-breakdown.js +1 -0
- package/dist/components/viewport-bookmarks/index.js +6 -0
- package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
- package/dist/components/workspace-switcher/index.js +6 -0
- package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
- package/dist/components/world-breadcrumbs/index.js +6 -0
- package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
- package/dist/components/zoom-hud/index.js +4 -0
- package/dist/components/zoom-hud/zoom-hud.js +61 -0
- package/dist/index.d.ts +1468 -6
- package/package.json +7 -3
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useId,
|
|
8
|
+
useImperativeHandle,
|
|
9
|
+
useRef,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
12
|
+
import { cn } from "../../lib/utils";
|
|
13
|
+
const DEFAULT_VIEWPORT = { x: 0, y: 0, zoom: 1 };
|
|
14
|
+
const INTERACTIVE_ELEMENT_SELECTOR = [
|
|
15
|
+
"a[href]",
|
|
16
|
+
"button",
|
|
17
|
+
'input:not([type="hidden"])',
|
|
18
|
+
"select",
|
|
19
|
+
"textarea",
|
|
20
|
+
"summary",
|
|
21
|
+
'[contenteditable=""]',
|
|
22
|
+
'[contenteditable="true"]',
|
|
23
|
+
'[role="button"]',
|
|
24
|
+
'[role="checkbox"]',
|
|
25
|
+
'[role="link"]',
|
|
26
|
+
'[role="menuitem"]',
|
|
27
|
+
'[role="option"]',
|
|
28
|
+
'[role="radio"]',
|
|
29
|
+
'[role="slider"]',
|
|
30
|
+
'[role="spinbutton"]',
|
|
31
|
+
'[role="switch"]',
|
|
32
|
+
'[role="tab"]',
|
|
33
|
+
'[role="textbox"]'
|
|
34
|
+
].join(", ");
|
|
35
|
+
function clampZoom(value, minZoom, maxZoom) {
|
|
36
|
+
return Math.min(maxZoom, Math.max(minZoom, Number(value.toFixed(2))));
|
|
37
|
+
}
|
|
38
|
+
function isHtmlElement(target) {
|
|
39
|
+
return target instanceof HTMLElement;
|
|
40
|
+
}
|
|
41
|
+
function isInteractiveDescendant(element, container) {
|
|
42
|
+
const interactiveAncestor = element.closest(INTERACTIVE_ELEMENT_SELECTOR);
|
|
43
|
+
return interactiveAncestor !== null && container.contains(interactiveAncestor);
|
|
44
|
+
}
|
|
45
|
+
function supportsScrollableOverflow(value) {
|
|
46
|
+
return value === "auto" || value === "overlay" || value === "scroll";
|
|
47
|
+
}
|
|
48
|
+
function hasScrollableAxis(element, axis) {
|
|
49
|
+
const style = window.getComputedStyle(element);
|
|
50
|
+
if (axis === "x") {
|
|
51
|
+
return supportsScrollableOverflow(style.overflowX) && element.scrollWidth > element.clientWidth;
|
|
52
|
+
}
|
|
53
|
+
return supportsScrollableOverflow(style.overflowY) && element.scrollHeight > element.clientHeight;
|
|
54
|
+
}
|
|
55
|
+
function hasScrollableAncestor(element, container, delta) {
|
|
56
|
+
if (!container.contains(element) || element === container) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (delta.x !== 0 && hasScrollableAxis(element, "x") || delta.y !== 0 && hasScrollableAxis(element, "y")) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return element.parentElement === null ? false : hasScrollableAncestor(element.parentElement, container, delta);
|
|
63
|
+
}
|
|
64
|
+
function shouldHandleCanvasKeyboardEvent(event) {
|
|
65
|
+
if (isHtmlElement(event.target) && event.target !== event.currentTarget && isInteractiveDescendant(event.target, event.currentTarget)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
function shouldHandleCanvasWheelEvent(event) {
|
|
71
|
+
if (isHtmlElement(event.target) && hasScrollableAncestor(event.target, event.currentTarget, {
|
|
72
|
+
x: event.deltaX,
|
|
73
|
+
y: event.deltaY
|
|
74
|
+
})) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
function isPanGesture(event, isSpacePressed) {
|
|
80
|
+
return event.button === 1 || event.button === 0 && isSpacePressed;
|
|
81
|
+
}
|
|
82
|
+
function createViewportKeyHandler({
|
|
83
|
+
nudgeViewport,
|
|
84
|
+
resetViewport,
|
|
85
|
+
setViewport,
|
|
86
|
+
viewportRef,
|
|
87
|
+
zoomStep
|
|
88
|
+
}) {
|
|
89
|
+
return (event) => {
|
|
90
|
+
if (event.key === "+" || event.key === "=") {
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
setViewport({
|
|
93
|
+
...viewportRef.current,
|
|
94
|
+
zoom: viewportRef.current.zoom + zoomStep
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (event.key === "-") {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
setViewport({
|
|
101
|
+
...viewportRef.current,
|
|
102
|
+
zoom: viewportRef.current.zoom - zoomStep
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (event.key === "0") {
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
resetViewport();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (event.key === "ArrowLeft") {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
nudgeViewport(40, 0);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (event.key === "ArrowRight") {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
nudgeViewport(-40, 0);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (event.key === "ArrowUp") {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
nudgeViewport(0, 40);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (event.key === "ArrowDown") {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
nudgeViewport(0, -40);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function useViewportState({
|
|
133
|
+
defaultViewport,
|
|
134
|
+
maxZoom,
|
|
135
|
+
minZoom,
|
|
136
|
+
onViewportChange
|
|
137
|
+
}) {
|
|
138
|
+
const defaultViewportRef = useRef(defaultViewport);
|
|
139
|
+
const viewportRef = useRef(defaultViewport);
|
|
140
|
+
const [viewport, setViewport] = useState(defaultViewport);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
defaultViewportRef.current = defaultViewport;
|
|
143
|
+
}, [defaultViewport]);
|
|
144
|
+
const applyViewport = useCallback(
|
|
145
|
+
(nextViewport) => {
|
|
146
|
+
const resolvedViewport = {
|
|
147
|
+
x: Math.round(nextViewport.x),
|
|
148
|
+
y: Math.round(nextViewport.y),
|
|
149
|
+
zoom: clampZoom(nextViewport.zoom, minZoom, maxZoom)
|
|
150
|
+
};
|
|
151
|
+
viewportRef.current = resolvedViewport;
|
|
152
|
+
setViewport(resolvedViewport);
|
|
153
|
+
onViewportChange?.(resolvedViewport);
|
|
154
|
+
},
|
|
155
|
+
[maxZoom, minZoom, onViewportChange]
|
|
156
|
+
);
|
|
157
|
+
const resetViewport = useCallback(() => {
|
|
158
|
+
applyViewport(defaultViewportRef.current);
|
|
159
|
+
}, [applyViewport]);
|
|
160
|
+
const nudgeViewport = useCallback(
|
|
161
|
+
(deltaX, deltaY) => {
|
|
162
|
+
const currentViewport = viewportRef.current;
|
|
163
|
+
applyViewport({
|
|
164
|
+
x: currentViewport.x + deltaX,
|
|
165
|
+
y: currentViewport.y + deltaY,
|
|
166
|
+
zoom: currentViewport.zoom
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
[applyViewport]
|
|
170
|
+
);
|
|
171
|
+
return {
|
|
172
|
+
nudgeViewport,
|
|
173
|
+
resetViewport,
|
|
174
|
+
setViewport: applyViewport,
|
|
175
|
+
viewport,
|
|
176
|
+
viewportRef
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function useCanvasKeyboardInteractions({
|
|
180
|
+
nudgeViewport,
|
|
181
|
+
resetViewport,
|
|
182
|
+
setViewport,
|
|
183
|
+
viewportRef,
|
|
184
|
+
zoomStep
|
|
185
|
+
}) {
|
|
186
|
+
const [isSpacePressed, setIsSpacePressed] = useState(false);
|
|
187
|
+
const handleWheel = useCallback(
|
|
188
|
+
(event) => {
|
|
189
|
+
if (event.ctrlKey || event.metaKey) {
|
|
190
|
+
event.preventDefault();
|
|
191
|
+
setViewport({
|
|
192
|
+
...viewportRef.current,
|
|
193
|
+
zoom: viewportRef.current.zoom + (event.deltaY > 0 ? -zoomStep : zoomStep)
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (!shouldHandleCanvasWheelEvent(event)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
nudgeViewport(-event.deltaX, -event.deltaY);
|
|
202
|
+
},
|
|
203
|
+
[nudgeViewport, setViewport, viewportRef, zoomStep]
|
|
204
|
+
);
|
|
205
|
+
const handleKeyDown = useCallback(
|
|
206
|
+
(event) => {
|
|
207
|
+
if (!shouldHandleCanvasKeyboardEvent(event)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (event.key === " ") {
|
|
211
|
+
event.preventDefault();
|
|
212
|
+
setIsSpacePressed(true);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
createViewportKeyHandler({
|
|
216
|
+
nudgeViewport,
|
|
217
|
+
resetViewport,
|
|
218
|
+
setViewport,
|
|
219
|
+
viewportRef,
|
|
220
|
+
zoomStep
|
|
221
|
+
})(event);
|
|
222
|
+
},
|
|
223
|
+
[nudgeViewport, resetViewport, setViewport, viewportRef, zoomStep]
|
|
224
|
+
);
|
|
225
|
+
const handleKeyUp = useCallback(
|
|
226
|
+
(event) => {
|
|
227
|
+
if (!shouldHandleCanvasKeyboardEvent(event)) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (event.key === " ") {
|
|
231
|
+
setIsSpacePressed(false);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
[]
|
|
235
|
+
);
|
|
236
|
+
return { handleKeyDown, handleKeyUp, handleWheel, isSpacePressed };
|
|
237
|
+
}
|
|
238
|
+
function endCanvasDrag(event, dragOriginRef, setIsDragging) {
|
|
239
|
+
dragOriginRef.current = null;
|
|
240
|
+
setIsDragging(false);
|
|
241
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
242
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function useCanvasPointerInteractions({
|
|
246
|
+
isSpacePressed,
|
|
247
|
+
setViewport,
|
|
248
|
+
viewportRef
|
|
249
|
+
}) {
|
|
250
|
+
const dragOriginRef = useRef(null);
|
|
251
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
252
|
+
const handlePointerDown = useCallback(
|
|
253
|
+
(event) => {
|
|
254
|
+
if (!isPanGesture(event, isSpacePressed)) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (event.button === 1) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
}
|
|
260
|
+
dragOriginRef.current = {
|
|
261
|
+
pointerX: event.clientX,
|
|
262
|
+
pointerY: event.clientY,
|
|
263
|
+
viewport: viewportRef.current
|
|
264
|
+
};
|
|
265
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
266
|
+
setIsDragging(true);
|
|
267
|
+
},
|
|
268
|
+
[isSpacePressed, viewportRef]
|
|
269
|
+
);
|
|
270
|
+
const handlePointerMove = useCallback(
|
|
271
|
+
(event) => {
|
|
272
|
+
const dragOrigin = dragOriginRef.current;
|
|
273
|
+
if (!dragOrigin) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
setViewport({
|
|
277
|
+
x: dragOrigin.viewport.x + (event.clientX - dragOrigin.pointerX),
|
|
278
|
+
y: dragOrigin.viewport.y + (event.clientY - dragOrigin.pointerY),
|
|
279
|
+
zoom: dragOrigin.viewport.zoom
|
|
280
|
+
});
|
|
281
|
+
},
|
|
282
|
+
[setViewport]
|
|
283
|
+
);
|
|
284
|
+
const handlePointerCancel = useCallback(
|
|
285
|
+
(event) => {
|
|
286
|
+
endCanvasDrag(event, dragOriginRef, setIsDragging);
|
|
287
|
+
},
|
|
288
|
+
[]
|
|
289
|
+
);
|
|
290
|
+
const handlePointerUp = useCallback(
|
|
291
|
+
(event) => {
|
|
292
|
+
endCanvasDrag(event, dragOriginRef, setIsDragging);
|
|
293
|
+
},
|
|
294
|
+
[]
|
|
295
|
+
);
|
|
296
|
+
return {
|
|
297
|
+
handlePointerCancel,
|
|
298
|
+
handlePointerDown,
|
|
299
|
+
handlePointerMove,
|
|
300
|
+
handlePointerUp,
|
|
301
|
+
isDragging
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function usePreventBodySelection(disabled) {
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
if (typeof document === "undefined") {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const { body } = document;
|
|
310
|
+
const previousUserSelect = body.style.userSelect;
|
|
311
|
+
if (disabled) {
|
|
312
|
+
body.style.userSelect = "none";
|
|
313
|
+
}
|
|
314
|
+
return () => {
|
|
315
|
+
body.style.userSelect = previousUserSelect;
|
|
316
|
+
};
|
|
317
|
+
}, [disabled]);
|
|
318
|
+
}
|
|
319
|
+
function useCanvasViewHandle(ref, viewportState) {
|
|
320
|
+
useImperativeHandle(
|
|
321
|
+
ref,
|
|
322
|
+
() => ({
|
|
323
|
+
resetViewport: viewportState.resetViewport,
|
|
324
|
+
setViewport: viewportState.setViewport
|
|
325
|
+
}),
|
|
326
|
+
[viewportState.resetViewport, viewportState.setViewport]
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
function CanvasInteractionLayer({
|
|
330
|
+
children,
|
|
331
|
+
instructionsId,
|
|
332
|
+
isDragging,
|
|
333
|
+
isSpacePressed,
|
|
334
|
+
onKeyDown,
|
|
335
|
+
onKeyUp,
|
|
336
|
+
onPointerCancel,
|
|
337
|
+
onPointerDown,
|
|
338
|
+
onPointerMove,
|
|
339
|
+
onPointerUp,
|
|
340
|
+
onWheel,
|
|
341
|
+
viewport
|
|
342
|
+
}) {
|
|
343
|
+
return /* @__PURE__ */ jsxs(
|
|
344
|
+
"div",
|
|
345
|
+
{
|
|
346
|
+
"aria-describedby": instructionsId,
|
|
347
|
+
"aria-label": "Canvas workspace",
|
|
348
|
+
"aria-roledescription": "canvas",
|
|
349
|
+
className: cn(
|
|
350
|
+
"relative h-full w-full select-none touch-none outline-none",
|
|
351
|
+
isDragging || isSpacePressed ? "cursor-grab active:cursor-grabbing" : "cursor-default"
|
|
352
|
+
),
|
|
353
|
+
"data-viewport": JSON.stringify(viewport),
|
|
354
|
+
onKeyDown,
|
|
355
|
+
onKeyUp,
|
|
356
|
+
onPointerCancel,
|
|
357
|
+
onPointerDown,
|
|
358
|
+
onPointerMove,
|
|
359
|
+
onPointerUp,
|
|
360
|
+
onWheel,
|
|
361
|
+
role: "button",
|
|
362
|
+
tabIndex: 0,
|
|
363
|
+
children: [
|
|
364
|
+
/* @__PURE__ */ jsx("div", { className: "sr-only", id: instructionsId, children: "Hold space and drag or use the middle mouse button to pan. Use plus, minus, or control wheel to zoom. Press zero to reset the viewport." }),
|
|
365
|
+
children
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
function CanvasContentLayer({
|
|
371
|
+
children,
|
|
372
|
+
overlay,
|
|
373
|
+
viewport
|
|
374
|
+
}) {
|
|
375
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
376
|
+
/* @__PURE__ */ jsx(
|
|
377
|
+
"div",
|
|
378
|
+
{
|
|
379
|
+
className: "absolute inset-0 origin-top-left transition-transform duration-150 ease-out",
|
|
380
|
+
style: {
|
|
381
|
+
transform: `translate3d(${viewport.x}px, ${viewport.y}px, 0) scale(${viewport.zoom})`
|
|
382
|
+
},
|
|
383
|
+
children
|
|
384
|
+
}
|
|
385
|
+
),
|
|
386
|
+
overlay ? /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-20", children: overlay }) : null
|
|
387
|
+
] });
|
|
388
|
+
}
|
|
389
|
+
const CanvasView = forwardRef(
|
|
390
|
+
({
|
|
391
|
+
children,
|
|
392
|
+
className,
|
|
393
|
+
defaultViewport = DEFAULT_VIEWPORT,
|
|
394
|
+
maxZoom = 2,
|
|
395
|
+
minZoom = 0.5,
|
|
396
|
+
onViewportChange,
|
|
397
|
+
overlay,
|
|
398
|
+
zoomStep = 0.1,
|
|
399
|
+
...props
|
|
400
|
+
}, ref) => {
|
|
401
|
+
const instructionsId = useId();
|
|
402
|
+
const viewportState = useViewportState({
|
|
403
|
+
defaultViewport,
|
|
404
|
+
maxZoom,
|
|
405
|
+
minZoom,
|
|
406
|
+
onViewportChange
|
|
407
|
+
});
|
|
408
|
+
const keyboard = useCanvasKeyboardInteractions({
|
|
409
|
+
nudgeViewport: viewportState.nudgeViewport,
|
|
410
|
+
resetViewport: viewportState.resetViewport,
|
|
411
|
+
setViewport: viewportState.setViewport,
|
|
412
|
+
viewportRef: viewportState.viewportRef,
|
|
413
|
+
zoomStep
|
|
414
|
+
});
|
|
415
|
+
const pointer = useCanvasPointerInteractions({
|
|
416
|
+
isSpacePressed: keyboard.isSpacePressed,
|
|
417
|
+
setViewport: viewportState.setViewport,
|
|
418
|
+
viewportRef: viewportState.viewportRef
|
|
419
|
+
});
|
|
420
|
+
usePreventBodySelection(pointer.isDragging);
|
|
421
|
+
useCanvasViewHandle(ref, viewportState);
|
|
422
|
+
return /* @__PURE__ */ jsx(
|
|
423
|
+
"div",
|
|
424
|
+
{
|
|
425
|
+
className: cn(
|
|
426
|
+
"relative h-full min-h-[32rem] overflow-hidden rounded-sm border border-border bg-background",
|
|
427
|
+
className
|
|
428
|
+
),
|
|
429
|
+
...props,
|
|
430
|
+
children: /* @__PURE__ */ jsx(
|
|
431
|
+
CanvasInteractionLayer,
|
|
432
|
+
{
|
|
433
|
+
instructionsId,
|
|
434
|
+
isDragging: pointer.isDragging,
|
|
435
|
+
isSpacePressed: keyboard.isSpacePressed,
|
|
436
|
+
onKeyDown: keyboard.handleKeyDown,
|
|
437
|
+
onKeyUp: keyboard.handleKeyUp,
|
|
438
|
+
onPointerCancel: pointer.handlePointerCancel,
|
|
439
|
+
onPointerDown: pointer.handlePointerDown,
|
|
440
|
+
onPointerMove: pointer.handlePointerMove,
|
|
441
|
+
onPointerUp: pointer.handlePointerUp,
|
|
442
|
+
onWheel: keyboard.handleWheel,
|
|
443
|
+
viewport: viewportState.viewport,
|
|
444
|
+
children: /* @__PURE__ */ jsx(
|
|
445
|
+
CanvasContentLayer,
|
|
446
|
+
{
|
|
447
|
+
overlay,
|
|
448
|
+
viewport: viewportState.viewport,
|
|
449
|
+
children
|
|
450
|
+
}
|
|
451
|
+
)
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
}
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
CanvasView.displayName = "CanvasView";
|
|
459
|
+
export {
|
|
460
|
+
CanvasView
|
|
461
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { ArrowUpRight, MessageSquareText } from "lucide-react";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
import { Button } from "../button";
|
|
6
|
+
const ChatDockSection = forwardRef(
|
|
7
|
+
({
|
|
8
|
+
className,
|
|
9
|
+
composerPlaceholder = "Ask about runs, errors, or pending work\u2026",
|
|
10
|
+
contextLabel,
|
|
11
|
+
messages,
|
|
12
|
+
title = "Assistant",
|
|
13
|
+
...props
|
|
14
|
+
}, ref) => /* @__PURE__ */ jsxs(
|
|
15
|
+
"section",
|
|
16
|
+
{
|
|
17
|
+
className: cn(
|
|
18
|
+
"flex flex-col gap-4 rounded-2xl border border-border/70 bg-background/75 p-4 shadow-[0_10px_35px_hsl(var(--foreground)/0.06)] backdrop-blur-xl",
|
|
19
|
+
className
|
|
20
|
+
),
|
|
21
|
+
ref,
|
|
22
|
+
...props,
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
25
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
26
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
27
|
+
/* @__PURE__ */ jsx(MessageSquareText, { className: "size-4 text-primary" }),
|
|
28
|
+
title
|
|
29
|
+
] }),
|
|
30
|
+
contextLabel ? /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-[0.24em] text-muted-foreground", children: contextLabel }) : null
|
|
31
|
+
] }),
|
|
32
|
+
/* @__PURE__ */ jsxs(Button, { size: "sm", type: "button", variant: "ghost", children: [
|
|
33
|
+
"Open thread",
|
|
34
|
+
/* @__PURE__ */ jsx(ArrowUpRight, { className: "size-4" })
|
|
35
|
+
] })
|
|
36
|
+
] }),
|
|
37
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: messages.map((message) => /* @__PURE__ */ jsxs(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
className: "rounded-xl border border-border/60 bg-background/85 px-3 py-2",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx("div", { className: "text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground", children: message.speaker }),
|
|
43
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 text-sm leading-6 text-foreground", children: message.body })
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
message.id
|
|
47
|
+
)) }),
|
|
48
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-xl border border-dashed border-border/80 bg-background/70 px-3 py-3 text-sm text-muted-foreground", children: composerPlaceholder })
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
ChatDockSection.displayName = "ChatDockSection";
|
|
54
|
+
export {
|
|
55
|
+
ChatDockSection
|
|
56
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { cn } from "../../lib/utils";
|
|
5
|
+
const CHECKLIST_PROGRESS_EVENT = "vllnt:checklist-progress-change";
|
|
5
6
|
function ChecklistItemRow({
|
|
6
7
|
isChecked,
|
|
7
8
|
item,
|
|
@@ -133,6 +134,11 @@ function Checklist({
|
|
|
133
134
|
`checklist:${persistKey}`,
|
|
134
135
|
JSON.stringify([...newChecked])
|
|
135
136
|
);
|
|
137
|
+
window.dispatchEvent(
|
|
138
|
+
new CustomEvent(CHECKLIST_PROGRESS_EVENT, {
|
|
139
|
+
detail: { persistKey }
|
|
140
|
+
})
|
|
141
|
+
);
|
|
136
142
|
} catch {
|
|
137
143
|
}
|
|
138
144
|
}
|
|
@@ -177,5 +183,6 @@ function Checklist({
|
|
|
177
183
|
] });
|
|
178
184
|
}
|
|
179
185
|
export {
|
|
186
|
+
CHECKLIST_PROGRESS_EVENT,
|
|
180
187
|
Checklist
|
|
181
188
|
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef
|
|
5
|
+
} from "react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
const DEFAULT_LABELS = {
|
|
8
|
+
region: "Comment",
|
|
9
|
+
unreadSuffix: "unread"
|
|
10
|
+
};
|
|
11
|
+
const STATE_FILL = {
|
|
12
|
+
open: "bg-foreground text-background",
|
|
13
|
+
resolved: "bg-muted text-muted-foreground"
|
|
14
|
+
};
|
|
15
|
+
const PinBody = (props) => {
|
|
16
|
+
const showBadge = typeof props.unread === "number" && props.unread > 0;
|
|
17
|
+
const useAccent = props.accent && props.state === "open";
|
|
18
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19
|
+
/* @__PURE__ */ jsx(
|
|
20
|
+
"span",
|
|
21
|
+
{
|
|
22
|
+
"aria-hidden": "true",
|
|
23
|
+
className: cn(
|
|
24
|
+
"flex h-7 w-7 items-center justify-center rounded-full border border-background text-[11px] font-semibold shadow-sm",
|
|
25
|
+
STATE_FILL[props.state]
|
|
26
|
+
),
|
|
27
|
+
"data-comment-pin-body": true,
|
|
28
|
+
style: useAccent ? { backgroundColor: props.accent, color: "white" } : void 0,
|
|
29
|
+
children: props.authorInitial ?? "\u2022"
|
|
30
|
+
}
|
|
31
|
+
),
|
|
32
|
+
showBadge ? /* @__PURE__ */ jsx(
|
|
33
|
+
"span",
|
|
34
|
+
{
|
|
35
|
+
"aria-hidden": "true",
|
|
36
|
+
className: "absolute -right-1 -top-1 inline-flex min-h-[14px] min-w-[14px] items-center justify-center rounded-full bg-red-500 px-1 text-[9px] font-medium text-white",
|
|
37
|
+
"data-comment-pin-unread": true,
|
|
38
|
+
children: props.unread
|
|
39
|
+
}
|
|
40
|
+
) : null
|
|
41
|
+
] });
|
|
42
|
+
};
|
|
43
|
+
const CommentPin = forwardRef(
|
|
44
|
+
(props, ref) => {
|
|
45
|
+
const {
|
|
46
|
+
authorInitial,
|
|
47
|
+
className,
|
|
48
|
+
color,
|
|
49
|
+
labels,
|
|
50
|
+
onActivate,
|
|
51
|
+
state = "open",
|
|
52
|
+
unread,
|
|
53
|
+
x,
|
|
54
|
+
y,
|
|
55
|
+
...rest
|
|
56
|
+
} = props;
|
|
57
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
58
|
+
const showBadge = typeof unread === "number" && unread > 0;
|
|
59
|
+
const ariaLabel = showBadge ? `${resolvedLabels.region}, ${unread} ${resolvedLabels.unreadSuffix}` : resolvedLabels.region;
|
|
60
|
+
const handleClick = () => {
|
|
61
|
+
onActivate?.();
|
|
62
|
+
};
|
|
63
|
+
const body = /* @__PURE__ */ jsx(
|
|
64
|
+
PinBody,
|
|
65
|
+
{
|
|
66
|
+
accent: color,
|
|
67
|
+
authorInitial,
|
|
68
|
+
state,
|
|
69
|
+
unread
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
"aria-label": ariaLabel,
|
|
76
|
+
className: cn(
|
|
77
|
+
"absolute z-30 inline-flex -translate-x-1/2 -translate-y-1/2",
|
|
78
|
+
className
|
|
79
|
+
),
|
|
80
|
+
"data-comment-pin": true,
|
|
81
|
+
"data-comment-pin-state": state,
|
|
82
|
+
ref,
|
|
83
|
+
role: "img",
|
|
84
|
+
style: { left: x, top: y },
|
|
85
|
+
...rest,
|
|
86
|
+
children: onActivate ? /* @__PURE__ */ jsx(
|
|
87
|
+
"button",
|
|
88
|
+
{
|
|
89
|
+
"aria-label": ariaLabel,
|
|
90
|
+
className: "relative inline-flex rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
91
|
+
"data-comment-pin-trigger": true,
|
|
92
|
+
onClick: handleClick,
|
|
93
|
+
type: "button",
|
|
94
|
+
children: body
|
|
95
|
+
}
|
|
96
|
+
) : /* @__PURE__ */ jsx("span", { className: "relative inline-flex", children: body })
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
CommentPin.displayName = "CommentPin";
|
|
102
|
+
export {
|
|
103
|
+
CommentPin
|
|
104
|
+
};
|