@vllnt/ui 0.2.0 → 0.2.1-canary.06f0e84

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 (202) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/README.md +27 -12
  3. package/dist/components/activity-log/activity-log.js +1 -0
  4. package/dist/components/agent-activity/agent-activity.js +311 -0
  5. package/dist/components/agent-activity/index.js +18 -0
  6. package/dist/components/ai-artifact/ai-artifact.js +422 -0
  7. package/dist/components/ai-artifact/index.js +24 -0
  8. package/dist/components/ai-sidebar/ai-sidebar.js +254 -0
  9. package/dist/components/ai-sidebar/index.js +22 -0
  10. package/dist/components/alert-pulse/alert-pulse.js +93 -0
  11. package/dist/components/alert-pulse/index.js +6 -0
  12. package/dist/components/anchor-port/anchor-port.js +51 -0
  13. package/dist/components/anchor-port/index.js +4 -0
  14. package/dist/components/animated-text/animated-text.js +1 -0
  15. package/dist/components/auto-reload/auto-reload.js +367 -0
  16. package/dist/components/auto-reload/index.js +6 -0
  17. package/dist/components/banner/banner.js +155 -0
  18. package/dist/components/banner/index.js +10 -0
  19. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +91 -0
  20. package/dist/components/bottom-activity-strip/index.js +6 -0
  21. package/dist/components/bottom-bar/bottom-bar.js +25 -0
  22. package/dist/components/bottom-bar/index.js +4 -0
  23. package/dist/components/canvas-shell/canvas-foundation-demo.js +183 -0
  24. package/dist/components/canvas-shell/canvas-shell-route-config.js +0 -0
  25. package/dist/components/canvas-shell/canvas-shell.js +261 -0
  26. package/dist/components/canvas-shell/index.js +4 -0
  27. package/dist/components/canvas-view/canvas-view.js +461 -0
  28. package/dist/components/canvas-view/index.js +6 -0
  29. package/dist/components/chart/area-chart.js +1 -0
  30. package/dist/components/chart/line-chart.js +1 -0
  31. package/dist/components/chat-dock-section/chat-dock-section.js +56 -0
  32. package/dist/components/chat-dock-section/index.js +6 -0
  33. package/dist/components/checklist/checklist.js +7 -0
  34. package/dist/components/checklist/index.js +3 -1
  35. package/dist/components/choropleth-map/choropleth-map.js +373 -0
  36. package/dist/components/choropleth-map/index.js +10 -0
  37. package/dist/components/chronological-timeline/chronological-timeline.js +337 -0
  38. package/dist/components/chronological-timeline/index.js +8 -0
  39. package/dist/components/civilization-card/civilization-card.js +258 -0
  40. package/dist/components/civilization-card/index.js +8 -0
  41. package/dist/components/combobox/combobox.js +44 -20
  42. package/dist/components/comment-pin/comment-pin.js +104 -0
  43. package/dist/components/comment-pin/index.js +6 -0
  44. package/dist/components/connector-edge/connector-edge.js +66 -0
  45. package/dist/components/connector-edge/index.js +6 -0
  46. package/dist/components/context-lens/context-lens.js +98 -0
  47. package/dist/components/context-lens/index.js +6 -0
  48. package/dist/components/conversation-thread/conversation-thread.js +348 -0
  49. package/dist/components/conversation-thread/index.js +20 -0
  50. package/dist/components/copy-button/copy-button.js +189 -0
  51. package/dist/components/copy-button/index.js +8 -0
  52. package/dist/components/curriculum/curriculum.js +349 -0
  53. package/dist/components/curriculum/index.js +10 -0
  54. package/dist/components/data-list/data-list.js +1 -0
  55. package/dist/components/document-sibling-nav/document-sibling-nav.js +111 -0
  56. package/dist/components/document-sibling-nav/index.js +8 -0
  57. package/dist/components/edge-label/edge-label.js +26 -0
  58. package/dist/components/edge-label/index.js +4 -0
  59. package/dist/components/empty-state/empty-state.js +93 -0
  60. package/dist/components/empty-state/index.js +8 -0
  61. package/dist/components/era-comparison/era-comparison.js +198 -0
  62. package/dist/components/era-comparison/index.js +16 -0
  63. package/dist/components/floating-toolbar/floating-toolbar.js +66 -0
  64. package/dist/components/floating-toolbar/index.js +6 -0
  65. package/dist/components/follow-mode/follow-mode.js +89 -0
  66. package/dist/components/follow-mode/index.js +6 -0
  67. package/dist/components/form/form.js +432 -0
  68. package/dist/components/form/index.js +20 -0
  69. package/dist/components/gantt-chart/gantt-chart.js +331 -0
  70. package/dist/components/gantt-chart/index.js +6 -0
  71. package/dist/components/geography-quiz-map/geography-quiz-map.js +343 -0
  72. package/dist/components/geography-quiz-map/index.js +12 -0
  73. package/dist/components/glass-panel/glass-panel.js +21 -0
  74. package/dist/components/glass-panel/index.js +4 -0
  75. package/dist/components/globe-3d/globe-3d.js +417 -0
  76. package/dist/components/globe-3d/index.js +10 -0
  77. package/dist/components/group-hull/group-hull.js +29 -0
  78. package/dist/components/group-hull/index.js +4 -0
  79. package/dist/components/handoff-beacon/handoff-beacon.js +78 -0
  80. package/dist/components/handoff-beacon/index.js +6 -0
  81. package/dist/components/heat-map-overlay/heat-map-overlay.js +215 -0
  82. package/dist/components/heat-map-overlay/index.js +6 -0
  83. package/dist/components/heat-overlay/heat-overlay.js +92 -0
  84. package/dist/components/heat-overlay/index.js +6 -0
  85. package/dist/components/historic-timeline/historic-timeline.js +342 -0
  86. package/dist/components/historic-timeline/index.js +6 -0
  87. package/dist/components/historical-figure-card/historical-figure-card.js +273 -0
  88. package/dist/components/historical-figure-card/index.js +6 -0
  89. package/dist/components/index.js +568 -1
  90. package/dist/components/infinite-plane/index.js +6 -0
  91. package/dist/components/infinite-plane/infinite-plane.js +75 -0
  92. package/dist/components/interactive-timeline/index.js +16 -0
  93. package/dist/components/interactive-timeline/interactive-timeline.js +708 -0
  94. package/dist/components/jarvis-dock/index.js +6 -0
  95. package/dist/components/jarvis-dock/jarvis-dock.js +98 -0
  96. package/dist/components/kbd/index.js +5 -0
  97. package/dist/components/kbd/kbd.js +117 -0
  98. package/dist/components/knowledge-check/index.js +6 -0
  99. package/dist/components/knowledge-check/knowledge-check.js +448 -0
  100. package/dist/components/left-rail/index.js +4 -0
  101. package/dist/components/left-rail/left-rail.js +25 -0
  102. package/dist/components/live-cursor/index.js +6 -0
  103. package/dist/components/live-cursor/live-cursor.js +62 -0
  104. package/dist/components/map-2d/index.js +20 -0
  105. package/dist/components/map-2d/map-2d.js +455 -0
  106. package/dist/components/map-timeline/index.js +16 -0
  107. package/dist/components/map-timeline/map-timeline.js +506 -0
  108. package/dist/components/metric-cluster/index.js +6 -0
  109. package/dist/components/metric-cluster/metric-cluster.js +96 -0
  110. package/dist/components/mini-map-panel/index.js +6 -0
  111. package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
  112. package/dist/components/model-comparison/index.js +12 -0
  113. package/dist/components/model-comparison/model-comparison.js +211 -0
  114. package/dist/components/multi-select/index.js +6 -0
  115. package/dist/components/multi-select/multi-select.js +258 -0
  116. package/dist/components/multi-select-lasso/index.js +6 -0
  117. package/dist/components/multi-select-lasso/multi-select-lasso.js +76 -0
  118. package/dist/components/newsletter-signup/index.js +8 -0
  119. package/dist/components/newsletter-signup/newsletter-signup.js +269 -0
  120. package/dist/components/object-card/index.js +6 -0
  121. package/dist/components/object-card/object-card.js +126 -0
  122. package/dist/components/object-handle/index.js +4 -0
  123. package/dist/components/object-handle/object-handle.js +38 -0
  124. package/dist/components/object-inspector/index.js +6 -0
  125. package/dist/components/object-inspector/object-inspector.js +136 -0
  126. package/dist/components/overview-board/index.js +8 -0
  127. package/dist/components/overview-board/overview-board.js +127 -0
  128. package/dist/components/parallel-timeline/index.js +6 -0
  129. package/dist/components/parallel-timeline/parallel-timeline.js +251 -0
  130. package/dist/components/playback-ghost/index.js +6 -0
  131. package/dist/components/playback-ghost/playback-ghost.js +83 -0
  132. package/dist/components/policy-delivery-panel/index.js +6 -0
  133. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +99 -0
  134. package/dist/components/presence-stack/index.js +6 -0
  135. package/dist/components/presence-stack/presence-stack.js +108 -0
  136. package/dist/components/presence-sync-indicator/index.js +6 -0
  137. package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
  138. package/dist/components/pricing-table/index.js +8 -0
  139. package/dist/components/pricing-table/pricing-table.js +247 -0
  140. package/dist/components/primary-source-viewer/index.js +26 -0
  141. package/dist/components/primary-source-viewer/primary-source-viewer.js +439 -0
  142. package/dist/components/progress-tracker/index.js +20 -0
  143. package/dist/components/progress-tracker/progress-tracker.js +527 -0
  144. package/dist/components/prompt-templates/index.js +6 -0
  145. package/dist/components/prompt-templates/prompt-templates.js +403 -0
  146. package/dist/components/property-section/index.js +6 -0
  147. package/dist/components/property-section/property-section.js +101 -0
  148. package/dist/components/relationship-inspector/index.js +6 -0
  149. package/dist/components/relationship-inspector/relationship-inspector.js +102 -0
  150. package/dist/components/right-dock/index.js +4 -0
  151. package/dist/components/right-dock/right-dock.js +28 -0
  152. package/dist/components/route-map/index.js +6 -0
  153. package/dist/components/route-map/route-map.js +339 -0
  154. package/dist/components/routing-assignment-panel/index.js +6 -0
  155. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +122 -0
  156. package/dist/components/run-timeline/index.js +6 -0
  157. package/dist/components/run-timeline/run-timeline.js +221 -0
  158. package/dist/components/runtime-overview-panel/index.js +6 -0
  159. package/dist/components/runtime-overview-panel/runtime-overview-panel.js +89 -0
  160. package/dist/components/segmented-control/index.js +12 -0
  161. package/dist/components/segmented-control/segmented-control.js +61 -0
  162. package/dist/components/selection-halo/index.js +6 -0
  163. package/dist/components/selection-halo/selection-halo.js +72 -0
  164. package/dist/components/selection-presence/index.js +6 -0
  165. package/dist/components/selection-presence/selection-presence.js +50 -0
  166. package/dist/components/snap-guides/index.js +6 -0
  167. package/dist/components/snap-guides/snap-guides.js +45 -0
  168. package/dist/components/spinner/unicode-spinner.js +1 -0
  169. package/dist/components/state-badge-overlay/index.js +6 -0
  170. package/dist/components/state-badge-overlay/state-badge-overlay.js +90 -0
  171. package/dist/components/sticky-metric/index.js +6 -0
  172. package/dist/components/sticky-metric/sticky-metric.js +83 -0
  173. package/dist/components/story-map/index.js +8 -0
  174. package/dist/components/story-map/story-map.js +414 -0
  175. package/dist/components/tags-input/index.js +4 -0
  176. package/dist/components/tags-input/tags-input.js +178 -0
  177. package/dist/components/thread-bubble/index.js +6 -0
  178. package/dist/components/thread-bubble/thread-bubble.js +85 -0
  179. package/dist/components/threshold-ring/index.js +6 -0
  180. package/dist/components/threshold-ring/threshold-ring.js +160 -0
  181. package/dist/components/timeline/index.js +12 -0
  182. package/dist/components/timeline/timeline.js +239 -0
  183. package/dist/components/timeline-scrubber/index.js +6 -0
  184. package/dist/components/timeline-scrubber/timeline-scrubber.js +179 -0
  185. package/dist/components/top-bar/index.js +4 -0
  186. package/dist/components/top-bar/top-bar.js +31 -0
  187. package/dist/components/transaction-list/index.js +14 -0
  188. package/dist/components/transaction-list/transaction-list.js +226 -0
  189. package/dist/components/tree-view/index.js +6 -0
  190. package/dist/components/tree-view/tree-view.js +298 -0
  191. package/dist/components/usage-breakdown/usage-breakdown.js +1 -0
  192. package/dist/components/viewport-bookmarks/index.js +6 -0
  193. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
  194. package/dist/components/workspace-switcher/index.js +6 -0
  195. package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
  196. package/dist/components/world-breadcrumbs/index.js +6 -0
  197. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
  198. package/dist/components/zoom-hud/index.js +4 -0
  199. package/dist/components/zoom-hud/zoom-hud.js +61 -0
  200. package/dist/index.d.ts +7906 -225
  201. package/dist/index.js +3 -1
  202. package/package.json +9 -5
@@ -0,0 +1,414 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ Children,
5
+ createContext,
6
+ forwardRef,
7
+ isValidElement,
8
+ useCallback,
9
+ useContext,
10
+ useEffect,
11
+ useId,
12
+ useMemo,
13
+ useRef,
14
+ useState
15
+ } from "react";
16
+ import { cn } from "../../lib/utils";
17
+ const VIEWBOX_WIDTH = 1e3;
18
+ const VIEWBOX_HEIGHT = 500;
19
+ const PALETTE = {
20
+ amber: "fill-amber-500",
21
+ blue: "fill-blue-500",
22
+ emerald: "fill-emerald-500",
23
+ purple: "fill-purple-500",
24
+ red: "fill-red-500",
25
+ rose: "fill-rose-500"
26
+ };
27
+ const DEFAULT_LABELS = {
28
+ narrative: "Narrative",
29
+ progress: "Story progress",
30
+ region: "Story map"
31
+ };
32
+ const StoryMapContext = createContext(null);
33
+ function useStoryMapContext() {
34
+ const ctx = useContext(StoryMapContext);
35
+ if (!ctx) {
36
+ throw new Error("StoryMap subcomponent used outside its root.");
37
+ }
38
+ return ctx;
39
+ }
40
+ function projectEquirectangular(position) {
41
+ const [lng, lat] = position;
42
+ return {
43
+ x: (lng + 180) / 360 * VIEWBOX_WIDTH,
44
+ y: (90 - lat) / 180 * VIEWBOX_HEIGHT
45
+ };
46
+ }
47
+ function ChapterMedia({ media }) {
48
+ return /* @__PURE__ */ jsxs("figure", { className: "overflow-hidden rounded-xl border bg-muted", children: [
49
+ /* @__PURE__ */ jsx(
50
+ "img",
51
+ {
52
+ alt: media.alt,
53
+ className: "aspect-video w-full object-cover",
54
+ loading: "lazy",
55
+ src: media.src
56
+ }
57
+ ),
58
+ media.caption ? /* @__PURE__ */ jsx("figcaption", { className: "border-t bg-background px-3 py-2 text-xs text-muted-foreground", children: media.caption }) : null
59
+ ] });
60
+ }
61
+ function ChapterHeader({ subtitle, title }) {
62
+ return /* @__PURE__ */ jsxs("header", { className: "space-y-1", children: [
63
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold tracking-tight text-foreground", children: title }),
64
+ subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : null
65
+ ] });
66
+ }
67
+ const StoryMapChapter = forwardRef(
68
+ (props, forwardedRef) => {
69
+ const {
70
+ center,
71
+ children,
72
+ className,
73
+ color = "red",
74
+ id,
75
+ media,
76
+ subtitle,
77
+ title,
78
+ zoom = 2,
79
+ ...rest
80
+ } = props;
81
+ const generatedId = useId();
82
+ const chapterId = id ?? generatedId;
83
+ const localRef = useRef(null);
84
+ const { registerChapter, registerMarker, unregisterMarker } = useStoryMapContext();
85
+ useEffect(() => {
86
+ registerMarker({ center, color, id: chapterId, zoom });
87
+ return () => {
88
+ unregisterMarker(chapterId);
89
+ };
90
+ }, [center, chapterId, color, registerMarker, unregisterMarker, zoom]);
91
+ const refCallback = useCallback(
92
+ (node) => {
93
+ localRef.current = node;
94
+ registerChapter(chapterId, node);
95
+ if (typeof forwardedRef === "function") forwardedRef(node);
96
+ else if (forwardedRef) forwardedRef.current = node;
97
+ },
98
+ [chapterId, forwardedRef, registerChapter]
99
+ );
100
+ return /* @__PURE__ */ jsxs(
101
+ "article",
102
+ {
103
+ className: cn(
104
+ "flex min-h-screen flex-col justify-center gap-3 py-12",
105
+ className
106
+ ),
107
+ "data-chapter-id": chapterId,
108
+ id: chapterId,
109
+ ref: refCallback,
110
+ ...rest,
111
+ children: [
112
+ /* @__PURE__ */ jsx(ChapterHeader, { subtitle, title }),
113
+ media ? /* @__PURE__ */ jsx(ChapterMedia, { media }) : null,
114
+ children ? /* @__PURE__ */ jsx("div", { className: "space-y-2 text-sm leading-relaxed text-foreground [&_blockquote]:my-3 [&_blockquote]:border-l-2 [&_blockquote]:border-primary [&_blockquote]:pl-3 [&_blockquote]:italic [&_blockquote]:text-muted-foreground", children }) : null
115
+ ]
116
+ }
117
+ );
118
+ }
119
+ );
120
+ StoryMapChapter.displayName = "StoryMapChapter";
121
+ function Stage({
122
+ activeId,
123
+ backdrop,
124
+ backdropAlt,
125
+ markers
126
+ }) {
127
+ const active = markers.find((marker) => marker.id === activeId);
128
+ const zoom = active?.zoom ?? 1;
129
+ const innerWidth = VIEWBOX_WIDTH / zoom;
130
+ const innerHeight = VIEWBOX_HEIGHT / zoom;
131
+ const center = active ? projectEquirectangular(active.center) : { x: VIEWBOX_WIDTH / 2, y: VIEWBOX_HEIGHT / 2 };
132
+ const viewX = center.x - innerWidth / 2;
133
+ const viewY = center.y - innerHeight / 2;
134
+ return /* @__PURE__ */ jsxs(
135
+ "svg",
136
+ {
137
+ "aria-hidden": "true",
138
+ className: "block h-full w-full transition-[viewBox] duration-500",
139
+ "data-active-chapter-id": activeId ?? "",
140
+ "data-active-zoom": zoom,
141
+ preserveAspectRatio: "xMidYMid slice",
142
+ viewBox: `${viewX.toString()} ${viewY.toString()} ${innerWidth.toString()} ${innerHeight.toString()}`,
143
+ children: [
144
+ /* @__PURE__ */ jsx(
145
+ "rect",
146
+ {
147
+ className: "fill-muted",
148
+ height: VIEWBOX_HEIGHT,
149
+ width: VIEWBOX_WIDTH,
150
+ x: "0",
151
+ y: "0"
152
+ }
153
+ ),
154
+ backdrop ? /* @__PURE__ */ jsx(
155
+ "image",
156
+ {
157
+ "aria-label": backdropAlt,
158
+ height: VIEWBOX_HEIGHT,
159
+ href: backdrop,
160
+ preserveAspectRatio: "xMidYMid slice",
161
+ width: VIEWBOX_WIDTH,
162
+ x: "0",
163
+ y: "0"
164
+ }
165
+ ) : null,
166
+ markers.map((marker) => {
167
+ const point = projectEquirectangular(marker.center);
168
+ const isActive = marker.id === activeId;
169
+ return /* @__PURE__ */ jsxs(
170
+ "g",
171
+ {
172
+ "data-marker-active": isActive ? "true" : void 0,
173
+ "data-marker-id": marker.id,
174
+ transform: `translate(${point.x.toString()}, ${point.y.toString()})`,
175
+ children: [
176
+ /* @__PURE__ */ jsx(
177
+ "circle",
178
+ {
179
+ className: cn("stroke-background", PALETTE[marker.color]),
180
+ r: isActive ? 12 : 6,
181
+ strokeWidth: 2
182
+ }
183
+ ),
184
+ isActive ? /* @__PURE__ */ jsx(
185
+ "circle",
186
+ {
187
+ className: cn("opacity-30", PALETTE[marker.color]),
188
+ r: 24
189
+ }
190
+ ) : null
191
+ ]
192
+ },
193
+ marker.id
194
+ );
195
+ })
196
+ ]
197
+ }
198
+ );
199
+ }
200
+ function chapterIdsFromChildren(children) {
201
+ const ids = [];
202
+ Children.forEach(children, (child) => {
203
+ if (!isValidElement(child)) return;
204
+ const element = child;
205
+ if (typeof element.props.id === "string") ids.push(element.props.id);
206
+ });
207
+ return ids;
208
+ }
209
+ function ProgressStrip({
210
+ activeId,
211
+ ids,
212
+ label
213
+ }) {
214
+ if (ids.length === 0) return null;
215
+ const activeIndex = activeId ? ids.indexOf(activeId) : -1;
216
+ const ratio = activeIndex < 0 ? 0 : (activeIndex + 1) / ids.length;
217
+ return /* @__PURE__ */ jsx(
218
+ "div",
219
+ {
220
+ "aria-label": label,
221
+ "aria-valuemax": 100,
222
+ "aria-valuemin": 0,
223
+ "aria-valuenow": Math.round(ratio * 100),
224
+ className: "sticky top-0 z-20 h-1 w-full bg-border",
225
+ role: "progressbar",
226
+ children: /* @__PURE__ */ jsx(
227
+ "span",
228
+ {
229
+ className: "block h-full bg-primary transition-[width] duration-200",
230
+ style: { width: `${(ratio * 100).toString()}%` }
231
+ }
232
+ )
233
+ }
234
+ );
235
+ }
236
+ function useChapterRegistry() {
237
+ const chapterMapRef = useRef(/* @__PURE__ */ new Map());
238
+ const markersRef = useRef(/* @__PURE__ */ new Map());
239
+ const [chapterIds, setChapterIds] = useState([]);
240
+ const [markerEntries, setMarkerEntries] = useState([]);
241
+ const [activeId, setActiveId] = useState();
242
+ const registerChapter = useCallback(
243
+ (id, node) => {
244
+ const map = chapterMapRef.current;
245
+ if (node) map.set(id, node);
246
+ else map.delete(id);
247
+ setChapterIds([...map.keys()]);
248
+ },
249
+ []
250
+ );
251
+ const registerMarker = useCallback((entry) => {
252
+ markersRef.current.set(entry.id, entry);
253
+ setMarkerEntries([...markersRef.current.values()]);
254
+ }, []);
255
+ const unregisterMarker = useCallback((id) => {
256
+ markersRef.current.delete(id);
257
+ setMarkerEntries([...markersRef.current.values()]);
258
+ }, []);
259
+ useEffect(() => {
260
+ if (typeof IntersectionObserver === "undefined") return;
261
+ const observer = new IntersectionObserver(
262
+ (entries) => {
263
+ const visible = entries.filter((entry) => entry.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
264
+ const first = visible[0];
265
+ if (first?.target instanceof HTMLElement) {
266
+ const id = first.target.dataset.chapterId;
267
+ if (id) setActiveId(id);
268
+ }
269
+ },
270
+ { rootMargin: "-30% 0px -50% 0px", threshold: 0.1 }
271
+ );
272
+ [...chapterMapRef.current.values()].forEach((node) => {
273
+ observer.observe(node);
274
+ });
275
+ return () => {
276
+ observer.disconnect();
277
+ };
278
+ }, [chapterIds]);
279
+ return {
280
+ activeId,
281
+ markerEntries,
282
+ registerChapter,
283
+ registerMarker,
284
+ setActiveId,
285
+ unregisterMarker
286
+ };
287
+ }
288
+ const Shell = forwardRef(function Shell2(props, ref) {
289
+ const {
290
+ activeId,
291
+ backdrop,
292
+ backdropAlt,
293
+ children,
294
+ className,
295
+ labels,
296
+ markers,
297
+ orderedIds,
298
+ titleId
299
+ } = props;
300
+ return /* @__PURE__ */ jsxs(
301
+ "section",
302
+ {
303
+ "aria-labelledby": titleId,
304
+ className: cn(
305
+ "relative flex w-full flex-col overflow-hidden rounded-2xl border bg-background text-foreground md:flex-row",
306
+ className
307
+ ),
308
+ ref,
309
+ children: [
310
+ /* @__PURE__ */ jsx("span", { className: "sr-only", id: titleId, children: labels.region }),
311
+ /* @__PURE__ */ jsx(
312
+ ProgressStrip,
313
+ {
314
+ activeId,
315
+ ids: orderedIds,
316
+ label: labels.progress
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsx(
320
+ "div",
321
+ {
322
+ className: "sticky top-0 hidden h-[80vh] flex-1 self-start md:block",
323
+ "data-story-map-stage": true,
324
+ children: /* @__PURE__ */ jsx(
325
+ Stage,
326
+ {
327
+ activeId,
328
+ backdrop,
329
+ backdropAlt,
330
+ markers
331
+ }
332
+ )
333
+ }
334
+ ),
335
+ /* @__PURE__ */ jsx(
336
+ "div",
337
+ {
338
+ "aria-label": labels.narrative,
339
+ className: "flex-1 px-6 md:max-w-xl",
340
+ role: "region",
341
+ children
342
+ }
343
+ ),
344
+ /* @__PURE__ */ jsx(
345
+ "div",
346
+ {
347
+ className: "block aspect-[2/1] w-full border-t border-border md:hidden",
348
+ "data-story-map-stage-mobile": true,
349
+ children: /* @__PURE__ */ jsx(
350
+ Stage,
351
+ {
352
+ activeId,
353
+ backdrop,
354
+ backdropAlt,
355
+ markers
356
+ }
357
+ )
358
+ }
359
+ )
360
+ ]
361
+ }
362
+ );
363
+ });
364
+ const StoryMap = forwardRef((props, ref) => {
365
+ const { backdrop, backdropAlt, children, className, labels, ...rest } = props;
366
+ const titleId = useId();
367
+ const resolvedLabels = useMemo(
368
+ () => ({ ...DEFAULT_LABELS, ...labels }),
369
+ [labels]
370
+ );
371
+ const {
372
+ activeId,
373
+ markerEntries,
374
+ registerChapter,
375
+ registerMarker,
376
+ setActiveId,
377
+ unregisterMarker
378
+ } = useChapterRegistry();
379
+ const ctx = useMemo(
380
+ () => ({
381
+ activeId,
382
+ registerChapter,
383
+ registerMarker,
384
+ setActiveId,
385
+ unregisterMarker
386
+ }),
387
+ [activeId, registerChapter, registerMarker, setActiveId, unregisterMarker]
388
+ );
389
+ const orderedIds = useMemo(
390
+ () => chapterIdsFromChildren(children),
391
+ [children]
392
+ );
393
+ return /* @__PURE__ */ jsx(StoryMapContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
394
+ Shell,
395
+ {
396
+ activeId,
397
+ backdrop,
398
+ backdropAlt,
399
+ className,
400
+ labels: resolvedLabels,
401
+ markers: markerEntries,
402
+ orderedIds,
403
+ ref,
404
+ titleId,
405
+ ...rest,
406
+ children
407
+ }
408
+ ) });
409
+ });
410
+ StoryMap.displayName = "StoryMap";
411
+ export {
412
+ StoryMap,
413
+ StoryMapChapter
414
+ };
@@ -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,6 @@
1
+ import {
2
+ ThresholdRing
3
+ } from "./threshold-ring";
4
+ export {
5
+ ThresholdRing
6
+ };