@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.
Files changed (86) 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/checklist/checklist.js +7 -0
  20. package/dist/components/checklist/index.js +3 -1
  21. package/dist/components/comment-pin/comment-pin.js +104 -0
  22. package/dist/components/comment-pin/index.js +6 -0
  23. package/dist/components/connector-edge/connector-edge.js +66 -0
  24. package/dist/components/connector-edge/index.js +6 -0
  25. package/dist/components/conversation-thread/conversation-thread.js +348 -0
  26. package/dist/components/conversation-thread/index.js +20 -0
  27. package/dist/components/curriculum/curriculum.js +349 -0
  28. package/dist/components/curriculum/index.js +10 -0
  29. package/dist/components/data-list/data-list.js +1 -0
  30. package/dist/components/edge-label/edge-label.js +26 -0
  31. package/dist/components/edge-label/index.js +4 -0
  32. package/dist/components/form/form.js +432 -0
  33. package/dist/components/form/index.js +20 -0
  34. package/dist/components/glass-panel/glass-panel.js +21 -0
  35. package/dist/components/glass-panel/index.js +4 -0
  36. package/dist/components/group-hull/group-hull.js +29 -0
  37. package/dist/components/group-hull/index.js +4 -0
  38. package/dist/components/index.js +176 -0
  39. package/dist/components/infinite-plane/index.js +6 -0
  40. package/dist/components/infinite-plane/infinite-plane.js +75 -0
  41. package/dist/components/left-rail/index.js +4 -0
  42. package/dist/components/left-rail/left-rail.js +25 -0
  43. package/dist/components/live-cursor/index.js +6 -0
  44. package/dist/components/live-cursor/live-cursor.js +62 -0
  45. package/dist/components/mini-map-panel/index.js +6 -0
  46. package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
  47. package/dist/components/multi-select/index.js +6 -0
  48. package/dist/components/multi-select/multi-select.js +258 -0
  49. package/dist/components/object-card/index.js +6 -0
  50. package/dist/components/object-card/object-card.js +126 -0
  51. package/dist/components/object-handle/index.js +4 -0
  52. package/dist/components/object-handle/object-handle.js +38 -0
  53. package/dist/components/overview-board/index.js +8 -0
  54. package/dist/components/overview-board/overview-board.js +127 -0
  55. package/dist/components/presence-stack/index.js +6 -0
  56. package/dist/components/presence-stack/presence-stack.js +108 -0
  57. package/dist/components/presence-sync-indicator/index.js +6 -0
  58. package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
  59. package/dist/components/progress-tracker/index.js +20 -0
  60. package/dist/components/progress-tracker/progress-tracker.js +527 -0
  61. package/dist/components/right-dock/index.js +4 -0
  62. package/dist/components/right-dock/right-dock.js +28 -0
  63. package/dist/components/run-timeline/index.js +6 -0
  64. package/dist/components/run-timeline/run-timeline.js +221 -0
  65. package/dist/components/segmented-control/index.js +12 -0
  66. package/dist/components/segmented-control/segmented-control.js +61 -0
  67. package/dist/components/selection-presence/index.js +6 -0
  68. package/dist/components/selection-presence/selection-presence.js +50 -0
  69. package/dist/components/spinner/unicode-spinner.js +1 -0
  70. package/dist/components/tags-input/index.js +4 -0
  71. package/dist/components/tags-input/tags-input.js +178 -0
  72. package/dist/components/thread-bubble/index.js +6 -0
  73. package/dist/components/thread-bubble/thread-bubble.js +85 -0
  74. package/dist/components/top-bar/index.js +4 -0
  75. package/dist/components/top-bar/top-bar.js +31 -0
  76. package/dist/components/usage-breakdown/usage-breakdown.js +1 -0
  77. package/dist/components/viewport-bookmarks/index.js +6 -0
  78. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
  79. package/dist/components/workspace-switcher/index.js +6 -0
  80. package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
  81. package/dist/components/world-breadcrumbs/index.js +6 -0
  82. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
  83. package/dist/components/zoom-hud/index.js +4 -0
  84. package/dist/components/zoom-hud/zoom-hud.js +61 -0
  85. package/dist/index.d.ts +1468 -6
  86. package/package.json +7 -3
@@ -0,0 +1,221 @@
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 STATE_FILL = {
8
+ complete: "bg-emerald-500/70",
9
+ failed: "bg-red-500/70",
10
+ queued: "bg-amber-500/70",
11
+ running: "bg-blue-500/70",
12
+ stopped: "bg-muted-foreground/40"
13
+ };
14
+ const STATE_LABEL = {
15
+ complete: "Complete",
16
+ failed: "Failed",
17
+ queued: "Queued",
18
+ running: "Running",
19
+ stopped: "Stopped"
20
+ };
21
+ const DEFAULT_LABELS = {
22
+ empty: "No phases",
23
+ region: "Run timeline"
24
+ };
25
+ const clamp = (v, min, max) => {
26
+ if (v < min) {
27
+ return min;
28
+ }
29
+ if (v > max) {
30
+ return max;
31
+ }
32
+ return v;
33
+ };
34
+ const Endpoints = (props) => {
35
+ const fmt = props.format;
36
+ const showCursor = typeof props.cursor === "number";
37
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2 text-[11px] text-muted-foreground", children: [
38
+ /* @__PURE__ */ jsx("span", { "data-run-timeline-start": true, children: fmt ? fmt(props.start) : props.start }),
39
+ showCursor ? /* @__PURE__ */ jsx(
40
+ "span",
41
+ {
42
+ className: "font-semibold text-foreground",
43
+ "data-run-timeline-cursor-label": true,
44
+ children: fmt ? fmt(props.cursor ?? 0) : props.cursor
45
+ }
46
+ ) : null,
47
+ /* @__PURE__ */ jsx("span", { "data-run-timeline-end": true, children: fmt ? fmt(props.end) : props.end })
48
+ ] });
49
+ };
50
+ const PhaseBar = (props) => {
51
+ const { laneIndex, laneTotal, phase, span, start } = props;
52
+ const left = clamp((phase.start - start) / span, 0, 1) * 100;
53
+ const right = clamp((phase.end - start) / span, 0, 1) * 100;
54
+ const width = Math.max(right - left, 0.5);
55
+ const top = laneIndex / laneTotal * 100;
56
+ const height = 100 / laneTotal;
57
+ const state = phase.state ?? "running";
58
+ const sharedStyle = {
59
+ height: `${height}%`,
60
+ left: `${left}%`,
61
+ top: `${top}%`,
62
+ width: `${width}%`
63
+ };
64
+ const ariaLabel = `${STATE_LABEL[state]} ${phase.start} \u2192 ${phase.end}`;
65
+ if (phase.onActivate) {
66
+ const handleClick = () => {
67
+ phase.onActivate?.();
68
+ };
69
+ return /* @__PURE__ */ jsx(
70
+ "button",
71
+ {
72
+ "aria-label": ariaLabel,
73
+ className: cn(
74
+ "absolute flex items-center justify-start overflow-hidden truncate rounded-sm border border-border/50 px-1 text-left text-[10px] text-foreground transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
75
+ STATE_FILL[state]
76
+ ),
77
+ "data-run-phase": phase.id,
78
+ "data-run-phase-state": state,
79
+ onClick: handleClick,
80
+ style: sharedStyle,
81
+ type: "button",
82
+ children: phase.label
83
+ }
84
+ );
85
+ }
86
+ return /* @__PURE__ */ jsx(
87
+ "span",
88
+ {
89
+ "aria-label": ariaLabel,
90
+ className: cn(
91
+ "absolute flex items-center justify-start overflow-hidden truncate rounded-sm border border-border/50 px-1 text-[10px] text-foreground",
92
+ STATE_FILL[state]
93
+ ),
94
+ "data-run-phase": phase.id,
95
+ "data-run-phase-state": state,
96
+ role: "img",
97
+ style: sharedStyle,
98
+ children: phase.label
99
+ }
100
+ );
101
+ };
102
+ const TrackBody = (props) => {
103
+ const { cursor, end, lanes, phases, start } = props;
104
+ const span = end - start;
105
+ const cursorRatio = typeof cursor === "number" ? clamp((cursor - start) / span, 0, 1) : null;
106
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-stretch", children: [
107
+ /* @__PURE__ */ jsx("div", { className: "flex w-24 flex-col gap-px text-[10px] uppercase tracking-wide text-muted-foreground", children: lanes.map((lane) => /* @__PURE__ */ jsx(
108
+ "div",
109
+ {
110
+ className: "flex h-7 items-center px-2",
111
+ "data-run-timeline-lane": lane.id,
112
+ children: lane.label
113
+ },
114
+ lane.id
115
+ )) }),
116
+ /* @__PURE__ */ jsxs(
117
+ "div",
118
+ {
119
+ className: "relative flex-1 overflow-hidden rounded-md border border-border bg-muted/20",
120
+ "data-run-timeline-track": true,
121
+ style: { height: `${lanes.length * 28}px` },
122
+ children: [
123
+ phases.map((phase) => {
124
+ const index = lanes.findIndex(
125
+ (lane) => lane.id === (phase.laneId ?? "default")
126
+ );
127
+ if (index === -1) {
128
+ return null;
129
+ }
130
+ return /* @__PURE__ */ jsx(
131
+ PhaseBar,
132
+ {
133
+ laneIndex: index,
134
+ laneTotal: lanes.length,
135
+ phase,
136
+ span,
137
+ start
138
+ },
139
+ phase.id
140
+ );
141
+ }),
142
+ cursorRatio === null ? null : /* @__PURE__ */ jsx(
143
+ "span",
144
+ {
145
+ "aria-hidden": "true",
146
+ className: "absolute top-0 bottom-0 w-px bg-foreground",
147
+ "data-run-timeline-cursor": true,
148
+ style: { left: `${cursorRatio * 100}%` }
149
+ }
150
+ )
151
+ ]
152
+ }
153
+ )
154
+ ] });
155
+ };
156
+ const resolveLanes = (explicit) => {
157
+ if (explicit && explicit.length > 0) {
158
+ return explicit;
159
+ }
160
+ return [{ id: "default", label: "Run" }];
161
+ };
162
+ const RunTimeline = forwardRef(
163
+ (props, ref) => {
164
+ const {
165
+ className,
166
+ cursor,
167
+ end,
168
+ formatValue,
169
+ labels,
170
+ lanes,
171
+ phases,
172
+ start,
173
+ ...rest
174
+ } = props;
175
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
176
+ const safeEnd = end <= start ? start + 1 : end;
177
+ const resolvedLanes = resolveLanes(lanes);
178
+ return /* @__PURE__ */ jsxs(
179
+ "section",
180
+ {
181
+ "aria-label": resolvedLabels.region,
182
+ className: cn("flex w-full flex-col gap-2", className),
183
+ "data-run-timeline": true,
184
+ ref,
185
+ ...rest,
186
+ children: [
187
+ /* @__PURE__ */ jsx(
188
+ Endpoints,
189
+ {
190
+ cursor,
191
+ end: safeEnd,
192
+ format: formatValue,
193
+ start
194
+ }
195
+ ),
196
+ phases.length === 0 ? /* @__PURE__ */ jsx(
197
+ "p",
198
+ {
199
+ className: "rounded-md border border-border bg-muted/20 px-2 py-3 text-center text-[11px] text-muted-foreground",
200
+ "data-run-timeline-state": "empty",
201
+ children: resolvedLabels.empty
202
+ }
203
+ ) : /* @__PURE__ */ jsx(
204
+ TrackBody,
205
+ {
206
+ cursor,
207
+ end: safeEnd,
208
+ lanes: resolvedLanes,
209
+ phases,
210
+ start
211
+ }
212
+ )
213
+ ]
214
+ }
215
+ );
216
+ }
217
+ );
218
+ RunTimeline.displayName = "RunTimeline";
219
+ export {
220
+ RunTimeline
221
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ SegmentedControl,
3
+ SegmentedControlItem,
4
+ segmentedControlItemVariants,
5
+ segmentedControlVariants
6
+ } from "./segmented-control";
7
+ export {
8
+ SegmentedControl,
9
+ SegmentedControlItem,
10
+ segmentedControlItemVariants,
11
+ segmentedControlVariants
12
+ };
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
5
+ import { cva } from "class-variance-authority";
6
+ import { cn } from "../../lib/utils";
7
+ const segmentedControlVariants = cva(
8
+ "inline-flex w-full items-center rounded-lg bg-muted p-1 text-muted-foreground",
9
+ {
10
+ defaultVariants: {
11
+ size: "default"
12
+ },
13
+ variants: {
14
+ size: {
15
+ default: "min-h-10",
16
+ lg: "min-h-11",
17
+ sm: "min-h-9"
18
+ }
19
+ }
20
+ }
21
+ );
22
+ const segmentedControlItemVariants = cva(
23
+ "inline-flex flex-1 items-center justify-center rounded-md px-3 text-sm font-medium whitespace-nowrap transition-all outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-background data-[state=on]:text-foreground data-[state=on]:shadow-sm",
24
+ {
25
+ defaultVariants: {
26
+ size: "default"
27
+ },
28
+ variants: {
29
+ size: {
30
+ default: "min-h-8",
31
+ lg: "min-h-9 px-4",
32
+ sm: "min-h-7 px-2.5 text-xs"
33
+ }
34
+ }
35
+ }
36
+ );
37
+ const SegmentedControl = React.forwardRef(({ className, size, ...props }, ref) => /* @__PURE__ */ jsx(
38
+ ToggleGroupPrimitive.Root,
39
+ {
40
+ className: cn(segmentedControlVariants({ size }), className),
41
+ ref,
42
+ type: "single",
43
+ ...props
44
+ }
45
+ ));
46
+ SegmentedControl.displayName = "SegmentedControl";
47
+ const SegmentedControlItem = React.forwardRef(({ className, size, ...props }, ref) => /* @__PURE__ */ jsx(
48
+ ToggleGroupPrimitive.Item,
49
+ {
50
+ className: cn(segmentedControlItemVariants({ size }), className),
51
+ ref,
52
+ ...props
53
+ }
54
+ ));
55
+ SegmentedControlItem.displayName = "SegmentedControlItem";
56
+ export {
57
+ SegmentedControl,
58
+ SegmentedControlItem,
59
+ segmentedControlItemVariants,
60
+ segmentedControlVariants
61
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ SelectionPresence
3
+ } from "./selection-presence";
4
+ export {
5
+ SelectionPresence
6
+ };
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef
5
+ } from "react";
6
+ import { cn } from "../../lib/utils";
7
+ const DEFAULT_LABELS = {
8
+ region: "Selection presence"
9
+ };
10
+ const SelectionPresence = forwardRef((props, ref) => {
11
+ const { className, color, height, labels, name, width, x, y, ...rest } = props;
12
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
13
+ const accent = color ?? "var(--foreground)";
14
+ const ariaLabel = typeof name === "string" ? `${resolvedLabels.region}: ${name}` : resolvedLabels.region;
15
+ return /* @__PURE__ */ jsx(
16
+ "div",
17
+ {
18
+ "aria-label": ariaLabel,
19
+ className: cn(
20
+ "pointer-events-none absolute z-20 rounded-md border-2 border-dashed",
21
+ className
22
+ ),
23
+ "data-selection-presence": true,
24
+ ref,
25
+ role: "img",
26
+ style: {
27
+ backgroundColor: `color-mix(in srgb, ${accent} 10%, transparent)`,
28
+ borderColor: accent,
29
+ height,
30
+ left: x,
31
+ top: y,
32
+ width
33
+ },
34
+ ...rest,
35
+ children: name === null || name === void 0 ? null : /* @__PURE__ */ jsx(
36
+ "span",
37
+ {
38
+ className: "absolute -top-5 left-0 inline-flex items-center rounded-md px-1.5 py-0.5 text-[10px] font-medium text-white shadow-sm",
39
+ "data-selection-presence-chip": true,
40
+ style: { backgroundColor: accent },
41
+ children: name
42
+ }
43
+ )
44
+ }
45
+ );
46
+ });
47
+ SelectionPresence.displayName = "SelectionPresence";
48
+ export {
49
+ SelectionPresence
50
+ };
@@ -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,4 @@
1
+ import { TagsInput } from "./tags-input";
2
+ export {
3
+ TagsInput
4
+ };
@@ -0,0 +1,178 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { X } from "lucide-react";
5
+ import { cn } from "../../lib/utils";
6
+ function normalizeTag(tag) {
7
+ return tag.trim();
8
+ }
9
+ function getNormalizedTags(tags) {
10
+ return tags.map(normalizeTag).filter(
11
+ (tag, index, values) => tag.length > 0 && values.indexOf(tag) === index
12
+ );
13
+ }
14
+ function shouldAddTagFromKey(key) {
15
+ return key === "Enter" || key === ",";
16
+ }
17
+ function useTagsInputState({
18
+ defaultValue,
19
+ onValueChange,
20
+ value
21
+ }) {
22
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(
23
+ () => getNormalizedTags(defaultValue)
24
+ );
25
+ const isControlled = value !== void 0;
26
+ const tags = React.useMemo(
27
+ () => getNormalizedTags(value ?? uncontrolledValue),
28
+ [uncontrolledValue, value]
29
+ );
30
+ const updateTags = React.useCallback(
31
+ (nextTags) => {
32
+ const normalizedTags = getNormalizedTags(nextTags);
33
+ if (!isControlled) {
34
+ setUncontrolledValue(normalizedTags);
35
+ }
36
+ onValueChange?.(normalizedTags);
37
+ },
38
+ [isControlled, onValueChange]
39
+ );
40
+ return { tags, updateTags };
41
+ }
42
+ function useTagsInputHandlers({
43
+ disabled,
44
+ inputValue,
45
+ onKeyDown,
46
+ setInputValue,
47
+ tags,
48
+ updateTags
49
+ }) {
50
+ const removeTag = React.useCallback(
51
+ (tagToRemove) => {
52
+ updateTags(tags.filter((tag) => tag !== tagToRemove));
53
+ },
54
+ [tags, updateTags]
55
+ );
56
+ const commitTag = React.useCallback(() => {
57
+ const nextTag = normalizeTag(inputValue);
58
+ if (nextTag.length === 0 || tags.includes(nextTag)) {
59
+ setInputValue("");
60
+ return;
61
+ }
62
+ updateTags([...tags, nextTag]);
63
+ setInputValue("");
64
+ }, [inputValue, setInputValue, tags, updateTags]);
65
+ const handleKeyDown = React.useCallback(
66
+ (event) => {
67
+ onKeyDown?.(event);
68
+ if (event.defaultPrevented || disabled) {
69
+ return;
70
+ }
71
+ if (shouldAddTagFromKey(event.key)) {
72
+ event.preventDefault();
73
+ commitTag();
74
+ return;
75
+ }
76
+ if ((event.key === "Backspace" || event.key === "Delete") && inputValue.length === 0) {
77
+ const lastTag = tags.at(-1);
78
+ if (lastTag) {
79
+ event.preventDefault();
80
+ removeTag(lastTag);
81
+ }
82
+ }
83
+ },
84
+ [commitTag, disabled, inputValue.length, onKeyDown, removeTag, tags]
85
+ );
86
+ return { handleKeyDown, removeTag };
87
+ }
88
+ function TagList({ disabled, onRemove, tags }) {
89
+ return /* @__PURE__ */ jsx("ul", { className: "flex flex-wrap items-center gap-2", children: tags.map((tag) => /* @__PURE__ */ jsxs(
90
+ "li",
91
+ {
92
+ className: "flex items-center gap-1 rounded-md border bg-muted px-2 py-1 text-sm text-foreground",
93
+ children: [
94
+ /* @__PURE__ */ jsx("span", { children: tag }),
95
+ /* @__PURE__ */ jsx(
96
+ "button",
97
+ {
98
+ "aria-label": `Remove ${tag}`,
99
+ className: "rounded-sm text-muted-foreground outline-none ring-offset-background transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
100
+ disabled,
101
+ onClick: (event) => {
102
+ event.stopPropagation();
103
+ onRemove(tag);
104
+ },
105
+ type: "button",
106
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
107
+ }
108
+ )
109
+ ]
110
+ },
111
+ tag
112
+ )) });
113
+ }
114
+ const TagsInput = React.forwardRef(
115
+ ({
116
+ className,
117
+ defaultValue = [],
118
+ disabled = false,
119
+ onBlur,
120
+ onKeyDown,
121
+ onValueChange,
122
+ placeholder = "Add a tag",
123
+ value,
124
+ ...props
125
+ }, ref) => {
126
+ const [inputValue, setInputValue] = React.useState("");
127
+ const { tags, updateTags } = useTagsInputState({
128
+ defaultValue,
129
+ onValueChange,
130
+ value
131
+ });
132
+ const { handleKeyDown, removeTag } = useTagsInputHandlers({
133
+ disabled,
134
+ inputValue,
135
+ onKeyDown,
136
+ setInputValue,
137
+ tags,
138
+ updateTags
139
+ });
140
+ return /* @__PURE__ */ jsxs(
141
+ "div",
142
+ {
143
+ "aria-disabled": disabled || void 0,
144
+ className: cn(
145
+ "flex min-h-10 w-full flex-wrap items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background transition-colors focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
146
+ disabled && "cursor-not-allowed opacity-50",
147
+ className
148
+ ),
149
+ "data-disabled": disabled ? "true" : void 0,
150
+ role: "group",
151
+ children: [
152
+ /* @__PURE__ */ jsx(TagList, { disabled, onRemove: removeTag, tags }),
153
+ /* @__PURE__ */ jsx(
154
+ "input",
155
+ {
156
+ ...props,
157
+ className: "min-w-[8rem] flex-1 border-0 bg-transparent p-0 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed",
158
+ disabled,
159
+ onBlur,
160
+ onChange: (event) => {
161
+ setInputValue(event.target.value);
162
+ },
163
+ onKeyDown: handleKeyDown,
164
+ placeholder,
165
+ ref,
166
+ type: "text",
167
+ value: inputValue
168
+ }
169
+ )
170
+ ]
171
+ }
172
+ );
173
+ }
174
+ );
175
+ TagsInput.displayName = "TagsInput";
176
+ export {
177
+ TagsInput
178
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ ThreadBubble
3
+ } from "./thread-bubble";
4
+ export {
5
+ ThreadBubble
6
+ };
@@ -0,0 +1,85 @@
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 DEFAULT_LABELS = {
8
+ empty: "No replies yet",
9
+ region: "Comment thread"
10
+ };
11
+ const Message = (props) => {
12
+ const { message } = props;
13
+ return /* @__PURE__ */ jsxs("li", { className: "space-y-0.5", "data-thread-bubble-message": message.id, children: [
14
+ /* @__PURE__ */ jsxs("header", { className: "flex items-baseline justify-between gap-2 text-[10px]", children: [
15
+ /* @__PURE__ */ jsx(
16
+ "span",
17
+ {
18
+ className: "font-semibold",
19
+ "data-thread-bubble-author": true,
20
+ style: message.authorColor ? { color: message.authorColor } : void 0,
21
+ children: message.author
22
+ }
23
+ ),
24
+ message.ts ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", "data-thread-bubble-ts": true, children: message.ts }) : null
25
+ ] }),
26
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-foreground", "data-thread-bubble-body": true, children: message.body })
27
+ ] });
28
+ };
29
+ const ThreadBubble = forwardRef(
30
+ (props, ref) => {
31
+ const { className, footer, labels, messages, onResolve, title, ...rest } = props;
32
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
33
+ const handleResolve = () => {
34
+ onResolve?.();
35
+ };
36
+ return /* @__PURE__ */ jsxs(
37
+ "section",
38
+ {
39
+ "aria-label": resolvedLabels.region,
40
+ className: cn(
41
+ "flex w-72 flex-col gap-2 rounded-lg border border-border bg-background p-3 text-foreground shadow-md",
42
+ className
43
+ ),
44
+ "data-thread-bubble": true,
45
+ ref,
46
+ ...rest,
47
+ children: [
48
+ title || onResolve ? /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-2 text-[11px] uppercase tracking-wide text-muted-foreground", children: [
49
+ title ? /* @__PURE__ */ jsx("span", { className: "truncate font-semibold", "data-thread-bubble-title": true, children: title }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
50
+ onResolve ? /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ className: "rounded-full border border-border px-2 py-0.5 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
54
+ "data-thread-bubble-resolve": true,
55
+ onClick: handleResolve,
56
+ type: "button",
57
+ children: "Resolve"
58
+ }
59
+ ) : null
60
+ ] }) : null,
61
+ messages.length === 0 ? /* @__PURE__ */ jsx(
62
+ "p",
63
+ {
64
+ className: "px-1 py-2 text-center text-[11px] text-muted-foreground",
65
+ "data-thread-bubble-state": "empty",
66
+ children: resolvedLabels.empty
67
+ }
68
+ ) : /* @__PURE__ */ jsx("ul", { className: "space-y-2 overflow-y-auto", "data-thread-bubble-list": true, children: messages.map((message) => /* @__PURE__ */ jsx(Message, { message }, message.id)) }),
69
+ footer ? /* @__PURE__ */ jsx(
70
+ "footer",
71
+ {
72
+ className: "border-t border-border pt-2",
73
+ "data-thread-bubble-footer": true,
74
+ children: footer
75
+ }
76
+ ) : null
77
+ ]
78
+ }
79
+ );
80
+ }
81
+ );
82
+ ThreadBubble.displayName = "ThreadBubble";
83
+ export {
84
+ ThreadBubble
85
+ };
@@ -0,0 +1,4 @@
1
+ import { TopBar } from "./top-bar";
2
+ export {
3
+ TopBar
4
+ };
@@ -0,0 +1,31 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../../lib/utils";
4
+ const TopBar = forwardRef(
5
+ ({ children, className, leading, subtitle, title, trailing, ...props }, ref) => /* @__PURE__ */ jsxs(
6
+ "header",
7
+ {
8
+ className: cn(
9
+ "flex min-h-14 items-center justify-between gap-3 border-b border-border bg-background px-4 font-mono",
10
+ className
11
+ ),
12
+ ref,
13
+ ...props,
14
+ children: [
15
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
16
+ leading ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: leading }) : null,
17
+ title || subtitle ? /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
18
+ title ? /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium uppercase tracking-[0.18em] text-foreground", children: title }) : null,
19
+ subtitle ? /* @__PURE__ */ jsx("div", { className: "truncate text-[11px] uppercase tracking-[0.18em] text-muted-foreground", children: subtitle }) : null
20
+ ] }) : null
21
+ ] }),
22
+ children ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children }) : null,
23
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center justify-end gap-2", children: trailing })
24
+ ]
25
+ }
26
+ )
27
+ );
28
+ TopBar.displayName = "TopBar";
29
+ export {
30
+ TopBar
31
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import { forwardRef, useMemo } from "react";
3
4
  import { ArrowDownRight, ArrowUpRight } from "lucide-react";
@@ -0,0 +1,6 @@
1
+ import {
2
+ ViewportBookmarks
3
+ } from "./viewport-bookmarks";
4
+ export {
5
+ ViewportBookmarks
6
+ };