@vllnt/ui 0.2.0 → 0.2.1-canary.9c473e0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +27 -12
  3. package/dist/components/activity-log/activity-log.js +1 -0
  4. package/dist/components/anchor-port/anchor-port.js +51 -0
  5. package/dist/components/anchor-port/index.js +4 -0
  6. package/dist/components/animated-text/animated-text.js +1 -0
  7. package/dist/components/bottom-bar/bottom-bar.js +25 -0
  8. package/dist/components/bottom-bar/index.js +4 -0
  9. package/dist/components/canvas-shell/canvas-foundation-demo.js +183 -0
  10. package/dist/components/canvas-shell/canvas-shell-route-config.js +0 -0
  11. package/dist/components/canvas-shell/canvas-shell.js +261 -0
  12. package/dist/components/canvas-shell/index.js +4 -0
  13. package/dist/components/canvas-view/canvas-view.js +461 -0
  14. package/dist/components/canvas-view/index.js +6 -0
  15. package/dist/components/chart/area-chart.js +1 -0
  16. package/dist/components/chart/line-chart.js +1 -0
  17. package/dist/components/chat-dock-section/chat-dock-section.js +56 -0
  18. package/dist/components/chat-dock-section/index.js +6 -0
  19. package/dist/components/connector-edge/connector-edge.js +66 -0
  20. package/dist/components/connector-edge/index.js +6 -0
  21. package/dist/components/data-list/data-list.js +1 -0
  22. package/dist/components/edge-label/edge-label.js +26 -0
  23. package/dist/components/edge-label/index.js +4 -0
  24. package/dist/components/form/form.js +263 -0
  25. package/dist/components/form/index.js +16 -0
  26. package/dist/components/glass-panel/glass-panel.js +21 -0
  27. package/dist/components/glass-panel/index.js +4 -0
  28. package/dist/components/group-hull/group-hull.js +29 -0
  29. package/dist/components/group-hull/index.js +4 -0
  30. package/dist/components/index.js +88 -0
  31. package/dist/components/left-rail/index.js +4 -0
  32. package/dist/components/left-rail/left-rail.js +25 -0
  33. package/dist/components/mini-map-panel/index.js +6 -0
  34. package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
  35. package/dist/components/multi-select/index.js +6 -0
  36. package/dist/components/multi-select/multi-select.js +258 -0
  37. package/dist/components/object-card/index.js +6 -0
  38. package/dist/components/object-card/object-card.js +126 -0
  39. package/dist/components/object-handle/index.js +4 -0
  40. package/dist/components/object-handle/object-handle.js +38 -0
  41. package/dist/components/overview-board/index.js +8 -0
  42. package/dist/components/overview-board/overview-board.js +127 -0
  43. package/dist/components/right-dock/index.js +4 -0
  44. package/dist/components/right-dock/right-dock.js +28 -0
  45. package/dist/components/segmented-control/index.js +12 -0
  46. package/dist/components/segmented-control/segmented-control.js +61 -0
  47. package/dist/components/spinner/unicode-spinner.js +1 -0
  48. package/dist/components/tags-input/index.js +4 -0
  49. package/dist/components/tags-input/tags-input.js +178 -0
  50. package/dist/components/top-bar/index.js +4 -0
  51. package/dist/components/top-bar/top-bar.js +31 -0
  52. package/dist/components/usage-breakdown/usage-breakdown.js +1 -0
  53. package/dist/components/workspace-switcher/index.js +6 -0
  54. package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
  55. package/dist/components/zoom-hud/index.js +4 -0
  56. package/dist/components/zoom-hud/zoom-hud.js +61 -0
  57. package/dist/index.d.ts +455 -5
  58. package/package.json +1 -1
@@ -0,0 +1,4 @@
1
+ import { CanvasShell } from "./canvas-shell";
2
+ export {
3
+ CanvasShell
4
+ };
@@ -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,6 @@
1
+ import {
2
+ CanvasView
3
+ } from "./canvas-view";
4
+ export {
5
+ CanvasView
6
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import * as React from "react";
3
4
  import { cn } from "../../lib/utils";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import * as React from "react";
3
4
  import { cn } from "../../lib/utils";
@@ -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
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ ChatDockSection
3
+ } from "./chat-dock-section";
4
+ export {
5
+ ChatDockSection
6
+ };
@@ -0,0 +1,66 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../../lib/utils";
4
+ import { EdgeLabel } from "../edge-label";
5
+ const strokeClasses = {
6
+ active: "stroke-sky-500",
7
+ blocked: "stroke-amber-500",
8
+ idle: "stroke-muted-foreground/60"
9
+ };
10
+ const ConnectorEdge = forwardRef(
11
+ ({ className, end, label, start, state = "idle", ...props }, ref) => {
12
+ const width = Math.max(Math.abs(end.x - start.x), 32);
13
+ const height = Math.max(Math.abs(end.y - start.y), 32);
14
+ const midX = width / 2;
15
+ const startX = start.x <= end.x ? 4 : width - 4;
16
+ const endX = start.x <= end.x ? width - 4 : 4;
17
+ const startY = start.y <= end.y ? 4 : height - 4;
18
+ const endY = start.y <= end.y ? height - 4 : 4;
19
+ const path = `M ${startX} ${startY} C ${midX} ${startY}, ${midX} ${endY}, ${endX} ${endY}`;
20
+ const style = {
21
+ height,
22
+ width
23
+ };
24
+ return /* @__PURE__ */ jsxs(
25
+ "div",
26
+ {
27
+ className: cn("relative inline-flex", className),
28
+ ref,
29
+ style,
30
+ ...props,
31
+ children: [
32
+ /* @__PURE__ */ jsx(
33
+ "svg",
34
+ {
35
+ className: "overflow-visible",
36
+ height,
37
+ viewBox: `0 0 ${width} ${height}`,
38
+ width,
39
+ children: /* @__PURE__ */ jsx(
40
+ "path",
41
+ {
42
+ className: cn("fill-none stroke-[2.5]", strokeClasses[state]),
43
+ d: path,
44
+ strokeDasharray: state === "blocked" ? "4 4" : void 0,
45
+ strokeLinecap: "round"
46
+ }
47
+ )
48
+ }
49
+ ),
50
+ label ? /* @__PURE__ */ jsx(
51
+ EdgeLabel,
52
+ {
53
+ className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2",
54
+ emphasis: state === "active" ? "active" : "subtle",
55
+ children: label
56
+ }
57
+ ) : null
58
+ ]
59
+ }
60
+ );
61
+ }
62
+ );
63
+ ConnectorEdge.displayName = "ConnectorEdge";
64
+ export {
65
+ ConnectorEdge
66
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ ConnectorEdge
3
+ } from "./connector-edge";
4
+ export {
5
+ ConnectorEdge
6
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx } from "react/jsx-runtime";
2
3
  import * as React from "react";
3
4
  import { cva } from "class-variance-authority";
@@ -0,0 +1,26 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../../lib/utils";
4
+ const emphasisClasses = {
5
+ active: "border-sky-500/30 bg-sky-500/10 text-sky-700 dark:text-sky-300",
6
+ subtle: "border-border/60 bg-background/90 text-muted-foreground"
7
+ };
8
+ const EdgeLabel = forwardRef(
9
+ ({ className, emphasis = "subtle", ...props }, ref) => /* @__PURE__ */ jsx(
10
+ "span",
11
+ {
12
+ className: cn(
13
+ "inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-medium uppercase tracking-[0.18em] shadow-sm",
14
+ emphasisClasses[emphasis],
15
+ className
16
+ ),
17
+ "data-emphasis": emphasis,
18
+ ref,
19
+ ...props
20
+ }
21
+ )
22
+ );
23
+ EdgeLabel.displayName = "EdgeLabel";
24
+ export {
25
+ EdgeLabel
26
+ };
@@ -0,0 +1,4 @@
1
+ import { EdgeLabel } from "./edge-label";
2
+ export {
3
+ EdgeLabel
4
+ };