@vllnt/ui 0.2.1-canary.ab2c69a → 0.2.1-canary.bb791f1

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 (57) hide show
  1. package/dist/components/alert-pulse/alert-pulse.js +93 -0
  2. package/dist/components/alert-pulse/index.js +6 -0
  3. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +91 -0
  4. package/dist/components/bottom-activity-strip/index.js +6 -0
  5. package/dist/components/comment-pin/comment-pin.js +104 -0
  6. package/dist/components/comment-pin/index.js +6 -0
  7. package/dist/components/context-lens/context-lens.js +98 -0
  8. package/dist/components/context-lens/index.js +6 -0
  9. package/dist/components/floating-toolbar/floating-toolbar.js +66 -0
  10. package/dist/components/floating-toolbar/index.js +6 -0
  11. package/dist/components/follow-mode/follow-mode.js +89 -0
  12. package/dist/components/follow-mode/index.js +6 -0
  13. package/dist/components/handoff-beacon/handoff-beacon.js +78 -0
  14. package/dist/components/handoff-beacon/index.js +6 -0
  15. package/dist/components/heat-overlay/heat-overlay.js +92 -0
  16. package/dist/components/heat-overlay/index.js +6 -0
  17. package/dist/components/index.js +108 -0
  18. package/dist/components/jarvis-dock/index.js +6 -0
  19. package/dist/components/jarvis-dock/jarvis-dock.js +98 -0
  20. package/dist/components/live-cursor/index.js +6 -0
  21. package/dist/components/live-cursor/live-cursor.js +62 -0
  22. package/dist/components/metric-cluster/index.js +6 -0
  23. package/dist/components/metric-cluster/metric-cluster.js +96 -0
  24. package/dist/components/multi-select-lasso/index.js +6 -0
  25. package/dist/components/multi-select-lasso/multi-select-lasso.js +76 -0
  26. package/dist/components/object-inspector/index.js +6 -0
  27. package/dist/components/object-inspector/object-inspector.js +136 -0
  28. package/dist/components/playback-ghost/index.js +6 -0
  29. package/dist/components/playback-ghost/playback-ghost.js +83 -0
  30. package/dist/components/policy-delivery-panel/index.js +6 -0
  31. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +99 -0
  32. package/dist/components/presence-sync-indicator/index.js +6 -0
  33. package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
  34. package/dist/components/property-section/index.js +6 -0
  35. package/dist/components/property-section/property-section.js +101 -0
  36. package/dist/components/relationship-inspector/index.js +6 -0
  37. package/dist/components/relationship-inspector/relationship-inspector.js +102 -0
  38. package/dist/components/routing-assignment-panel/index.js +6 -0
  39. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +122 -0
  40. package/dist/components/run-timeline/index.js +6 -0
  41. package/dist/components/run-timeline/run-timeline.js +221 -0
  42. package/dist/components/runtime-overview-panel/index.js +6 -0
  43. package/dist/components/runtime-overview-panel/runtime-overview-panel.js +89 -0
  44. package/dist/components/selection-halo/index.js +6 -0
  45. package/dist/components/selection-halo/selection-halo.js +72 -0
  46. package/dist/components/snap-guides/index.js +6 -0
  47. package/dist/components/snap-guides/snap-guides.js +45 -0
  48. package/dist/components/state-badge-overlay/index.js +6 -0
  49. package/dist/components/state-badge-overlay/state-badge-overlay.js +90 -0
  50. package/dist/components/sticky-metric/index.js +6 -0
  51. package/dist/components/sticky-metric/sticky-metric.js +83 -0
  52. package/dist/components/threshold-ring/index.js +6 -0
  53. package/dist/components/threshold-ring/threshold-ring.js +160 -0
  54. package/dist/components/timeline-scrubber/index.js +6 -0
  55. package/dist/components/timeline-scrubber/timeline-scrubber.js +179 -0
  56. package/dist/index.d.ts +2168 -107
  57. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ import { cn } from "../../lib/utils";
5
+ const SEVERITY_STROKE = {
6
+ error: "stroke-red-500",
7
+ info: "stroke-blue-500",
8
+ warn: "stroke-amber-500"
9
+ };
10
+ const SEVERITY_FILL = {
11
+ error: "fill-red-500/20",
12
+ info: "fill-blue-500/20",
13
+ warn: "fill-amber-500/20"
14
+ };
15
+ const SEVERITY_LABEL = {
16
+ error: "Error",
17
+ info: "Info",
18
+ warn: "Warning"
19
+ };
20
+ const safeRadius = (value) => value < 6 ? 6 : value;
21
+ const AlertPulse = forwardRef(
22
+ (props, ref) => {
23
+ const {
24
+ className,
25
+ cx,
26
+ cy,
27
+ labels,
28
+ radius = 36,
29
+ reducedMotion = false,
30
+ severity = "warn",
31
+ ...rest
32
+ } = props;
33
+ const r = safeRadius(radius);
34
+ const ariaLabel = labels?.region ?? SEVERITY_LABEL[severity];
35
+ const size = r * 2 + 24;
36
+ return /* @__PURE__ */ jsxs(
37
+ "svg",
38
+ {
39
+ "aria-label": ariaLabel,
40
+ className: cn(
41
+ "pointer-events-none absolute z-20 overflow-visible",
42
+ className
43
+ ),
44
+ "data-alert-pulse": true,
45
+ "data-alert-severity": severity,
46
+ height: size,
47
+ ref,
48
+ role: "img",
49
+ style: {
50
+ left: cx - size / 2,
51
+ top: cy - size / 2
52
+ },
53
+ width: size,
54
+ ...rest,
55
+ children: [
56
+ /* @__PURE__ */ jsx(
57
+ "circle",
58
+ {
59
+ className: cn("origin-center", SEVERITY_STROKE[severity]),
60
+ cx: size / 2,
61
+ cy: size / 2,
62
+ fill: "none",
63
+ r,
64
+ strokeOpacity: 0.7,
65
+ strokeWidth: 2
66
+ }
67
+ ),
68
+ /* @__PURE__ */ jsx(
69
+ "circle",
70
+ {
71
+ className: cn(
72
+ "origin-center",
73
+ SEVERITY_STROKE[severity],
74
+ SEVERITY_FILL[severity],
75
+ reducedMotion ? null : "animate-ping"
76
+ ),
77
+ cx: size / 2,
78
+ cy: size / 2,
79
+ "data-alert-pulse-ring": true,
80
+ r,
81
+ strokeOpacity: 0.4,
82
+ strokeWidth: 2
83
+ }
84
+ )
85
+ ]
86
+ }
87
+ );
88
+ }
89
+ );
90
+ AlertPulse.displayName = "AlertPulse";
91
+ export {
92
+ AlertPulse
93
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ AlertPulse
3
+ } from "./alert-pulse";
4
+ export {
5
+ AlertPulse
6
+ };
@@ -0,0 +1,91 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const TONE_DOT = {
8
+ danger: "bg-red-500",
9
+ info: "bg-blue-500",
10
+ neutral: "bg-muted-foreground",
11
+ success: "bg-emerald-500",
12
+ warn: "bg-amber-500"
13
+ };
14
+ const DEFAULT_LABELS = {
15
+ empty: "No recent activity",
16
+ region: "Recent activity"
17
+ };
18
+ const ChipBody = (props) => {
19
+ const { event } = props;
20
+ const tone = event.tone ?? "neutral";
21
+ return /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 whitespace-nowrap", children: [
22
+ /* @__PURE__ */ jsx(
23
+ "span",
24
+ {
25
+ "aria-hidden": "true",
26
+ className: cn("h-1.5 w-1.5 rounded-full", TONE_DOT[tone])
27
+ }
28
+ ),
29
+ /* @__PURE__ */ jsx("span", { className: "text-foreground", children: event.label }),
30
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", "data-strip-event-ts": true, children: event.ts })
31
+ ] });
32
+ };
33
+ const Chip = (props) => {
34
+ const { event } = props;
35
+ const tone = event.tone ?? "neutral";
36
+ if (event.onActivate) {
37
+ const handleClick = () => {
38
+ event.onActivate?.();
39
+ };
40
+ return /* @__PURE__ */ jsx(
41
+ "button",
42
+ {
43
+ className: "flex items-center rounded-full border border-border bg-background px-2 py-1 text-[11px] transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
44
+ "data-strip-event": event.id,
45
+ "data-strip-event-tone": tone,
46
+ onClick: handleClick,
47
+ type: "button",
48
+ children: /* @__PURE__ */ jsx(ChipBody, { event })
49
+ }
50
+ );
51
+ }
52
+ return /* @__PURE__ */ jsx(
53
+ "span",
54
+ {
55
+ className: "flex items-center rounded-full border border-border bg-background px-2 py-1 text-[11px]",
56
+ "data-strip-event": event.id,
57
+ "data-strip-event-tone": tone,
58
+ children: /* @__PURE__ */ jsx(ChipBody, { event })
59
+ }
60
+ );
61
+ };
62
+ const BottomActivityStrip = forwardRef((props, ref) => {
63
+ const { className, events, labels, maxEvents, ...rest } = props;
64
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
65
+ const visible = maxEvents === void 0 || maxEvents >= events.length ? events : events.slice(0, maxEvents);
66
+ return /* @__PURE__ */ jsx(
67
+ "section",
68
+ {
69
+ "aria-label": resolvedLabels.region,
70
+ className: cn(
71
+ "flex w-full items-center gap-2 overflow-x-auto rounded-md border border-border bg-background/90 px-2 py-1 text-foreground",
72
+ className
73
+ ),
74
+ "data-bottom-activity-strip": true,
75
+ ref,
76
+ ...rest,
77
+ children: visible.length === 0 ? /* @__PURE__ */ jsx(
78
+ "span",
79
+ {
80
+ className: "px-2 text-[11px] text-muted-foreground",
81
+ "data-strip-state": "empty",
82
+ children: resolvedLabels.empty
83
+ }
84
+ ) : visible.map((event) => /* @__PURE__ */ jsx(Chip, { event }, event.id))
85
+ }
86
+ );
87
+ });
88
+ BottomActivityStrip.displayName = "BottomActivityStrip";
89
+ export {
90
+ BottomActivityStrip
91
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ BottomActivityStrip
3
+ } from "./bottom-activity-strip";
4
+ export {
5
+ BottomActivityStrip
6
+ };
@@ -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
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ CommentPin
3
+ } from "./comment-pin";
4
+ export {
5
+ CommentPin
6
+ };
@@ -0,0 +1,98 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useId } from "react";
4
+ import { cn } from "../../lib/utils";
5
+ const DEFAULT_LABELS = {
6
+ region: "Context lens"
7
+ };
8
+ const clamp01 = (value) => {
9
+ if (value < 0) {
10
+ return 0;
11
+ }
12
+ if (value > 1) {
13
+ return 1;
14
+ }
15
+ return value;
16
+ };
17
+ const safeRadius = (value) => value < 0 ? 0 : value;
18
+ const ContextLens = forwardRef(
19
+ (props, ref) => {
20
+ const { className, focus, labels, opacity = 0.55, ...rest } = props;
21
+ const maskId = useId();
22
+ if (!focus) {
23
+ return null;
24
+ }
25
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
26
+ const inner = safeRadius(focus.inner);
27
+ const outer = safeRadius(Math.max(focus.outer, inner));
28
+ const fillOpacity = clamp01(opacity);
29
+ return /* @__PURE__ */ jsxs(
30
+ "svg",
31
+ {
32
+ "aria-hidden": "true",
33
+ "aria-label": resolvedLabels.region,
34
+ className: cn(
35
+ "pointer-events-none absolute inset-0 z-20 h-full w-full",
36
+ className
37
+ ),
38
+ "data-context-lens": true,
39
+ ref,
40
+ ...rest,
41
+ children: [
42
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
43
+ "radialGradient",
44
+ {
45
+ cx: focus.cx,
46
+ cy: focus.cy,
47
+ "data-context-lens-gradient": true,
48
+ gradientUnits: "userSpaceOnUse",
49
+ id: maskId,
50
+ r: outer === 0 ? 1 : outer,
51
+ children: [
52
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "black", stopOpacity: 0 }),
53
+ /* @__PURE__ */ jsx(
54
+ "stop",
55
+ {
56
+ offset: outer === 0 ? "100%" : `${inner / outer * 100}%`,
57
+ stopColor: "black",
58
+ stopOpacity: 0
59
+ }
60
+ ),
61
+ /* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "black", stopOpacity: 1 })
62
+ ]
63
+ }
64
+ ) }),
65
+ /* @__PURE__ */ jsx(
66
+ "rect",
67
+ {
68
+ "data-context-lens-dim": true,
69
+ fill: "black",
70
+ fillOpacity,
71
+ height: "100%",
72
+ mask: `url(#${maskId}-mask)`,
73
+ width: "100%",
74
+ x: 0,
75
+ y: 0
76
+ }
77
+ ),
78
+ /* @__PURE__ */ jsxs("mask", { id: `${maskId}-mask`, children: [
79
+ /* @__PURE__ */ jsx("rect", { fill: "white", height: "100%", width: "100%", x: 0, y: 0 }),
80
+ /* @__PURE__ */ jsx(
81
+ "circle",
82
+ {
83
+ cx: focus.cx,
84
+ cy: focus.cy,
85
+ fill: `url(#${maskId})`,
86
+ r: outer
87
+ }
88
+ )
89
+ ] })
90
+ ]
91
+ }
92
+ );
93
+ }
94
+ );
95
+ ContextLens.displayName = "ContextLens";
96
+ export {
97
+ ContextLens
98
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ ContextLens
3
+ } from "./context-lens";
4
+ export {
5
+ ContextLens
6
+ };
@@ -0,0 +1,66 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const VARIANT_CLASSES = {
8
+ destructive: "border-red-300 bg-red-500/10 text-red-700 hover:bg-red-500/20 dark:text-red-300",
9
+ ghost: "border-border bg-background text-foreground hover:bg-accent",
10
+ primary: "border-primary bg-primary text-primary-foreground hover:bg-primary/90"
11
+ };
12
+ const DEFAULT_LABELS = {
13
+ region: "Selection actions"
14
+ };
15
+ const FloatingToolbar = forwardRef(
16
+ (props, ref) => {
17
+ const { actions, className, labels, x, y, ...rest } = props;
18
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
19
+ return /* @__PURE__ */ jsx(
20
+ "div",
21
+ {
22
+ "aria-label": resolvedLabels.region,
23
+ className: cn(
24
+ "absolute z-30 flex -translate-y-full items-center gap-1 rounded-md border border-border bg-background/95 p-1 shadow-md backdrop-blur",
25
+ className
26
+ ),
27
+ "data-floating-toolbar": true,
28
+ ref,
29
+ role: "toolbar",
30
+ style: { left: `${x.toString()}px`, top: `${y.toString()}px` },
31
+ ...rest,
32
+ children: actions.map((action) => {
33
+ const variant = action.variant ?? "ghost";
34
+ const handleClick = () => {
35
+ action.onActivate();
36
+ };
37
+ return /* @__PURE__ */ jsxs(
38
+ "button",
39
+ {
40
+ "aria-label": action.ariaLabel ?? void 0,
41
+ className: cn(
42
+ "inline-flex h-7 items-center gap-1 rounded-md border px-2 text-xs font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
43
+ VARIANT_CLASSES[variant],
44
+ action.disabled ? "cursor-not-allowed opacity-50" : ""
45
+ ),
46
+ "data-action-id": action.id,
47
+ "data-variant": variant,
48
+ disabled: action.disabled,
49
+ onClick: handleClick,
50
+ type: "button",
51
+ children: [
52
+ action.glyph ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex h-3 w-3", children: action.glyph }) : null,
53
+ /* @__PURE__ */ jsx("span", { children: action.label })
54
+ ]
55
+ },
56
+ action.id
57
+ );
58
+ })
59
+ }
60
+ );
61
+ }
62
+ );
63
+ FloatingToolbar.displayName = "FloatingToolbar";
64
+ export {
65
+ FloatingToolbar
66
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ FloatingToolbar
3
+ } from "./floating-toolbar";
4
+ export {
5
+ FloatingToolbar
6
+ };
@@ -0,0 +1,89 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const PALETTE = {
8
+ amber: { chip: "bg-amber-500 text-white", ring: "ring-amber-500" },
9
+ blue: { chip: "bg-blue-500 text-white", ring: "ring-blue-500" },
10
+ emerald: { chip: "bg-emerald-500 text-white", ring: "ring-emerald-500" },
11
+ purple: { chip: "bg-purple-500 text-white", ring: "ring-purple-500" },
12
+ red: { chip: "bg-red-500 text-white", ring: "ring-red-500" },
13
+ rose: { chip: "bg-rose-500 text-white", ring: "ring-rose-500" }
14
+ };
15
+ const DEFAULT_LABELS = {
16
+ region: "Follow mode",
17
+ stop: "Stop"
18
+ };
19
+ const FollowMode = forwardRef(
20
+ (props, ref) => {
21
+ const {
22
+ children,
23
+ className,
24
+ color = "blue",
25
+ labels,
26
+ name,
27
+ onStop,
28
+ ...rest
29
+ } = props;
30
+ const palette = PALETTE[color];
31
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
32
+ return /* @__PURE__ */ jsxs(
33
+ "div",
34
+ {
35
+ "aria-label": resolvedLabels.region,
36
+ className: cn(
37
+ "relative h-full w-full rounded-2xl ring-2 ring-inset",
38
+ palette.ring,
39
+ className
40
+ ),
41
+ "data-follow-color": color,
42
+ ref,
43
+ ...rest,
44
+ children: [
45
+ /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ className: "pointer-events-auto absolute left-1/2 top-2 z-30 flex -translate-x-1/2 items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-semibold shadow-sm",
49
+ "data-follow-chip": true,
50
+ children: [
51
+ /* @__PURE__ */ jsxs(
52
+ "span",
53
+ {
54
+ className: cn(
55
+ "inline-flex items-center gap-1 rounded-full px-1.5 py-0.5",
56
+ palette.chip
57
+ ),
58
+ children: [
59
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u25B8" }),
60
+ /* @__PURE__ */ jsxs("span", { children: [
61
+ "Following ",
62
+ name
63
+ ] })
64
+ ]
65
+ }
66
+ ),
67
+ onStop ? /* @__PURE__ */ jsx(
68
+ "button",
69
+ {
70
+ "aria-label": resolvedLabels.stop,
71
+ className: "inline-flex h-5 items-center rounded-full border border-border bg-background px-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
72
+ onClick: onStop,
73
+ type: "button",
74
+ children: resolvedLabels.stop
75
+ }
76
+ ) : null
77
+ ]
78
+ }
79
+ ),
80
+ children
81
+ ]
82
+ }
83
+ );
84
+ }
85
+ );
86
+ FollowMode.displayName = "FollowMode";
87
+ export {
88
+ FollowMode
89
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ FollowMode
3
+ } from "./follow-mode";
4
+ export {
5
+ FollowMode
6
+ };
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const LEVEL_RING = {
8
+ info: "ring-blue-500",
9
+ request: "ring-amber-500",
10
+ urgent: "ring-red-500 animate-pulse"
11
+ };
12
+ const LEVEL_DOT = {
13
+ info: "bg-blue-500",
14
+ request: "bg-amber-500",
15
+ urgent: "bg-red-500"
16
+ };
17
+ const DEFAULT_LABELS = {
18
+ region: "Attention"
19
+ };
20
+ const HandoffBeacon = forwardRef(
21
+ (props, ref) => {
22
+ const {
23
+ className,
24
+ labels,
25
+ level = "info",
26
+ message,
27
+ source,
28
+ x,
29
+ y,
30
+ ...rest
31
+ } = props;
32
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
33
+ return /* @__PURE__ */ jsxs(
34
+ "div",
35
+ {
36
+ "aria-label": resolvedLabels.region,
37
+ className: cn(
38
+ "pointer-events-none absolute z-30 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-1",
39
+ className
40
+ ),
41
+ "data-handoff-level": level,
42
+ ref,
43
+ role: "status",
44
+ style: { left: `${x.toString()}px`, top: `${y.toString()}px` },
45
+ ...rest,
46
+ children: [
47
+ /* @__PURE__ */ jsx(
48
+ "span",
49
+ {
50
+ "aria-hidden": "true",
51
+ className: cn(
52
+ "block h-3 w-3 rounded-full ring-2 ring-offset-2 ring-offset-background",
53
+ LEVEL_DOT[level],
54
+ LEVEL_RING[level]
55
+ )
56
+ }
57
+ ),
58
+ source || message ? /* @__PURE__ */ jsx(
59
+ "div",
60
+ {
61
+ className: "pointer-events-auto rounded-md border border-border bg-popover px-2 py-1 text-center text-[11px] font-medium shadow-md",
62
+ "data-handoff-card": true,
63
+ children: source ? /* @__PURE__ */ jsxs("p", { className: "text-foreground", children: [
64
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: source }),
65
+ message ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: " \xB7 " }) : null,
66
+ message ? /* @__PURE__ */ jsx("span", { children: message }) : null
67
+ ] }) : message ? /* @__PURE__ */ jsx("p", { className: "text-foreground", children: message }) : null
68
+ }
69
+ ) : null
70
+ ]
71
+ }
72
+ );
73
+ }
74
+ );
75
+ HandoffBeacon.displayName = "HandoffBeacon";
76
+ export {
77
+ HandoffBeacon
78
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ HandoffBeacon
3
+ } from "./handoff-beacon";
4
+ export {
5
+ HandoffBeacon
6
+ };