@vllnt/ui 0.2.0 → 0.2.1-canary.0aaaad2

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 (296) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/README.md +27 -12
  3. package/dist/components/accordion/accordion.js +2 -2
  4. package/dist/components/activity-heatmap/activity-heatmap.js +15 -13
  5. package/dist/components/activity-log/activity-log.js +6 -5
  6. package/dist/components/agent-activity/agent-activity.js +311 -0
  7. package/dist/components/agent-activity/index.js +18 -0
  8. package/dist/components/ai-artifact/ai-artifact.js +422 -0
  9. package/dist/components/ai-artifact/index.js +24 -0
  10. package/dist/components/ai-chat-input/ai-chat-input.js +2 -2
  11. package/dist/components/ai-message-bubble/ai-message-bubble.js +1 -1
  12. package/dist/components/ai-sidebar/ai-sidebar.js +254 -0
  13. package/dist/components/ai-sidebar/index.js +22 -0
  14. package/dist/components/ai-source-citation/ai-source-citation.js +2 -2
  15. package/dist/components/ai-tool-call-display/ai-tool-call-display.js +4 -4
  16. package/dist/components/alert-pulse/alert-pulse.js +93 -0
  17. package/dist/components/alert-pulse/index.js +6 -0
  18. package/dist/components/anchor-port/anchor-port.js +51 -0
  19. package/dist/components/anchor-port/index.js +4 -0
  20. package/dist/components/animated-text/animated-text.js +2 -4
  21. package/dist/components/auto-reload/auto-reload.js +377 -0
  22. package/dist/components/auto-reload/index.js +6 -0
  23. package/dist/components/avatar/avatar.js +1 -1
  24. package/dist/components/avatar-group/avatar-group.js +4 -4
  25. package/dist/components/banner/banner.js +155 -0
  26. package/dist/components/banner/index.js +10 -0
  27. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +91 -0
  28. package/dist/components/bottom-activity-strip/index.js +6 -0
  29. package/dist/components/bottom-bar/bottom-bar.js +25 -0
  30. package/dist/components/bottom-bar/index.js +4 -0
  31. package/dist/components/breadcrumb/breadcrumb.js +33 -26
  32. package/dist/components/button/button.js +1 -1
  33. package/dist/components/calendar/calendar.js +6 -6
  34. package/dist/components/callout/callout.js +1 -1
  35. package/dist/components/candlestick-chart/candlestick-chart.js +1 -1
  36. package/dist/components/canvas-shell/canvas-foundation-demo.js +183 -0
  37. package/dist/components/canvas-shell/canvas-shell-route-config.js +0 -0
  38. package/dist/components/canvas-shell/canvas-shell.js +261 -0
  39. package/dist/components/canvas-shell/index.js +4 -0
  40. package/dist/components/canvas-view/canvas-view.js +461 -0
  41. package/dist/components/canvas-view/index.js +6 -0
  42. package/dist/components/carousel/carousel.js +4 -4
  43. package/dist/components/chart/area-chart.js +1 -0
  44. package/dist/components/chart/line-chart.js +1 -0
  45. package/dist/components/chat-dock-section/chat-dock-section.js +56 -0
  46. package/dist/components/chat-dock-section/index.js +6 -0
  47. package/dist/components/checkbox/checkbox.js +2 -2
  48. package/dist/components/checklist/checklist.js +10 -3
  49. package/dist/components/checklist/index.js +3 -1
  50. package/dist/components/choropleth-map/choropleth-map.js +373 -0
  51. package/dist/components/choropleth-map/index.js +10 -0
  52. package/dist/components/chronological-timeline/chronological-timeline.js +338 -0
  53. package/dist/components/chronological-timeline/index.js +8 -0
  54. package/dist/components/civilization-card/civilization-card.js +258 -0
  55. package/dist/components/civilization-card/index.js +8 -0
  56. package/dist/components/code-block/code-block.js +2 -2
  57. package/dist/components/code-playground/code-playground.js +8 -7
  58. package/dist/components/combobox/combobox.js +46 -22
  59. package/dist/components/command/command.js +1 -1
  60. package/dist/components/comment-pin/comment-pin.js +104 -0
  61. package/dist/components/comment-pin/index.js +6 -0
  62. package/dist/components/comparison/comparison.js +9 -9
  63. package/dist/components/completion-dialog/completion-dialog.js +1 -1
  64. package/dist/components/connector-edge/connector-edge.js +66 -0
  65. package/dist/components/connector-edge/index.js +6 -0
  66. package/dist/components/content-intro/content-intro.js +7 -6
  67. package/dist/components/context-lens/context-lens.js +98 -0
  68. package/dist/components/context-lens/index.js +6 -0
  69. package/dist/components/context-menu/context-menu.js +4 -4
  70. package/dist/components/conversation-thread/conversation-thread.js +348 -0
  71. package/dist/components/conversation-thread/index.js +20 -0
  72. package/dist/components/cookie-consent/cookie-consent.js +1 -1
  73. package/dist/components/copy-button/copy-button.js +189 -0
  74. package/dist/components/copy-button/index.js +8 -0
  75. package/dist/components/countdown-timer/countdown-timer.js +8 -7
  76. package/dist/components/credit-badge/credit-badge.js +1 -7
  77. package/dist/components/curriculum/curriculum.js +349 -0
  78. package/dist/components/curriculum/index.js +10 -0
  79. package/dist/components/data-list/data-list.js +2 -1
  80. package/dist/components/data-table/data-table.js +5 -4
  81. package/dist/components/date-picker/date-picker.js +1 -1
  82. package/dist/components/dialog/dialog.js +1 -1
  83. package/dist/components/document-sibling-nav/document-sibling-nav.js +111 -0
  84. package/dist/components/document-sibling-nav/index.js +8 -0
  85. package/dist/components/dropdown-menu/dropdown-menu.js +3 -3
  86. package/dist/components/edge-label/edge-label.js +26 -0
  87. package/dist/components/edge-label/index.js +4 -0
  88. package/dist/components/empty-state/empty-state.js +93 -0
  89. package/dist/components/empty-state/index.js +8 -0
  90. package/dist/components/era-comparison/era-comparison.js +198 -0
  91. package/dist/components/era-comparison/index.js +16 -0
  92. package/dist/components/exercise/exercise.js +4 -4
  93. package/dist/components/faq/faq.js +2 -2
  94. package/dist/components/file-upload/file-upload.js +3 -3
  95. package/dist/components/filter-bar/filter-bar.js +2 -1
  96. package/dist/components/flashcard/flashcard.js +1 -1
  97. package/dist/components/floating-action-button/floating-action-button.js +1 -1
  98. package/dist/components/floating-toolbar/floating-toolbar.js +66 -0
  99. package/dist/components/floating-toolbar/index.js +6 -0
  100. package/dist/components/flow-diagram/flow-controls.js +9 -9
  101. package/dist/components/flow-diagram/flow-error-boundary.js +2 -2
  102. package/dist/components/flow-diagram/flow-fullscreen.js +2 -2
  103. package/dist/components/follow-mode/follow-mode.js +89 -0
  104. package/dist/components/follow-mode/index.js +6 -0
  105. package/dist/components/form/form.js +432 -0
  106. package/dist/components/form/index.js +20 -0
  107. package/dist/components/gantt-chart/gantt-chart.js +333 -0
  108. package/dist/components/gantt-chart/index.js +6 -0
  109. package/dist/components/geography-quiz-map/geography-quiz-map.js +343 -0
  110. package/dist/components/geography-quiz-map/index.js +12 -0
  111. package/dist/components/glass-panel/glass-panel.js +21 -0
  112. package/dist/components/glass-panel/index.js +4 -0
  113. package/dist/components/globe-3d/globe-3d.js +417 -0
  114. package/dist/components/globe-3d/index.js +10 -0
  115. package/dist/components/group-hull/group-hull.js +29 -0
  116. package/dist/components/group-hull/index.js +4 -0
  117. package/dist/components/handoff-beacon/handoff-beacon.js +78 -0
  118. package/dist/components/handoff-beacon/index.js +6 -0
  119. package/dist/components/heat-map-overlay/heat-map-overlay.js +215 -0
  120. package/dist/components/heat-map-overlay/index.js +6 -0
  121. package/dist/components/heat-overlay/heat-overlay.js +92 -0
  122. package/dist/components/heat-overlay/index.js +6 -0
  123. package/dist/components/historic-timeline/historic-timeline.js +342 -0
  124. package/dist/components/historic-timeline/index.js +6 -0
  125. package/dist/components/historical-figure-card/historical-figure-card.js +270 -0
  126. package/dist/components/historical-figure-card/index.js +6 -0
  127. package/dist/components/horizontal-scroll-row/horizontal-scroll-row.js +4 -4
  128. package/dist/components/index.js +568 -1
  129. package/dist/components/infinite-plane/index.js +6 -0
  130. package/dist/components/infinite-plane/infinite-plane.js +75 -0
  131. package/dist/components/input-otp/input-otp.js +1 -1
  132. package/dist/components/interactive-timeline/index.js +16 -0
  133. package/dist/components/interactive-timeline/interactive-timeline.js +708 -0
  134. package/dist/components/jarvis-dock/index.js +6 -0
  135. package/dist/components/jarvis-dock/jarvis-dock.js +98 -0
  136. package/dist/components/kbd/index.js +5 -0
  137. package/dist/components/kbd/kbd.js +117 -0
  138. package/dist/components/key-concept/key-concept.js +4 -4
  139. package/dist/components/keyboard-shortcuts-help/keyboard-shortcuts-help.js +2 -2
  140. package/dist/components/knowledge-check/index.js +6 -0
  141. package/dist/components/knowledge-check/knowledge-check.js +448 -0
  142. package/dist/components/learning-objectives/learning-objectives.js +12 -12
  143. package/dist/components/left-rail/index.js +4 -0
  144. package/dist/components/left-rail/left-rail.js +25 -0
  145. package/dist/components/live-cursor/index.js +6 -0
  146. package/dist/components/live-cursor/live-cursor.js +62 -0
  147. package/dist/components/live-feed/live-feed.js +15 -13
  148. package/dist/components/map-2d/index.js +20 -0
  149. package/dist/components/map-2d/map-2d.js +455 -0
  150. package/dist/components/map-timeline/index.js +16 -0
  151. package/dist/components/map-timeline/map-timeline.js +506 -0
  152. package/dist/components/market-treemap/market-treemap.js +1 -1
  153. package/dist/components/mdx-content/mdx-content.js +2 -1
  154. package/dist/components/menubar/menubar.js +4 -4
  155. package/dist/components/metric-cluster/index.js +6 -0
  156. package/dist/components/metric-cluster/metric-cluster.js +96 -0
  157. package/dist/components/metric-gauge/metric-gauge.js +1 -1
  158. package/dist/components/mini-map-panel/index.js +6 -0
  159. package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
  160. package/dist/components/model-comparison/index.js +12 -0
  161. package/dist/components/model-comparison/model-comparison.js +211 -0
  162. package/dist/components/model-selector/model-selector.js +2 -2
  163. package/dist/components/multi-select/index.js +6 -0
  164. package/dist/components/multi-select/multi-select.js +258 -0
  165. package/dist/components/multi-select-lasso/index.js +6 -0
  166. package/dist/components/multi-select-lasso/multi-select-lasso.js +76 -0
  167. package/dist/components/navbar-saas/navbar-saas.js +3 -2
  168. package/dist/components/navigation-menu/navigation-menu.js +2 -2
  169. package/dist/components/newsletter-signup/index.js +8 -0
  170. package/dist/components/newsletter-signup/newsletter-signup.js +269 -0
  171. package/dist/components/number-input/number-input.js +1 -1
  172. package/dist/components/number-ticker/number-ticker.js +11 -4
  173. package/dist/components/object-card/index.js +6 -0
  174. package/dist/components/object-card/object-card.js +126 -0
  175. package/dist/components/object-handle/index.js +4 -0
  176. package/dist/components/object-handle/object-handle.js +38 -0
  177. package/dist/components/object-inspector/index.js +6 -0
  178. package/dist/components/object-inspector/object-inspector.js +136 -0
  179. package/dist/components/overview-board/index.js +8 -0
  180. package/dist/components/overview-board/overview-board.js +127 -0
  181. package/dist/components/parallel-timeline/index.js +6 -0
  182. package/dist/components/parallel-timeline/parallel-timeline.js +251 -0
  183. package/dist/components/password-input/password-input.js +1 -1
  184. package/dist/components/plan-badge/plan-badge.js +1 -7
  185. package/dist/components/playback-ghost/index.js +6 -0
  186. package/dist/components/playback-ghost/playback-ghost.js +83 -0
  187. package/dist/components/policy-delivery-panel/index.js +6 -0
  188. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +99 -0
  189. package/dist/components/presence-stack/index.js +6 -0
  190. package/dist/components/presence-stack/presence-stack.js +108 -0
  191. package/dist/components/presence-sync-indicator/index.js +6 -0
  192. package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
  193. package/dist/components/pricing-table/index.js +8 -0
  194. package/dist/components/pricing-table/pricing-table.js +247 -0
  195. package/dist/components/primary-source-viewer/index.js +26 -0
  196. package/dist/components/primary-source-viewer/primary-source-viewer.js +439 -0
  197. package/dist/components/pro-tip/pro-tip.js +6 -6
  198. package/dist/components/profile-section/profile-section.js +1 -1
  199. package/dist/components/progress-card/progress-card.js +4 -3
  200. package/dist/components/progress-tracker/index.js +20 -0
  201. package/dist/components/progress-tracker/progress-tracker.js +529 -0
  202. package/dist/components/prompt-templates/index.js +6 -0
  203. package/dist/components/prompt-templates/prompt-templates.js +403 -0
  204. package/dist/components/property-section/index.js +6 -0
  205. package/dist/components/property-section/property-section.js +101 -0
  206. package/dist/components/quiz/quiz.js +5 -5
  207. package/dist/components/radio-group/radio-group.js +2 -2
  208. package/dist/components/rating/rating.js +3 -3
  209. package/dist/components/relationship-inspector/index.js +6 -0
  210. package/dist/components/relationship-inspector/relationship-inspector.js +102 -0
  211. package/dist/components/resizable/resizable.js +1 -1
  212. package/dist/components/right-dock/index.js +4 -0
  213. package/dist/components/right-dock/right-dock.js +28 -0
  214. package/dist/components/route-map/index.js +6 -0
  215. package/dist/components/route-map/route-map.js +339 -0
  216. package/dist/components/routing-assignment-panel/index.js +6 -0
  217. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +122 -0
  218. package/dist/components/run-timeline/index.js +6 -0
  219. package/dist/components/run-timeline/run-timeline.js +221 -0
  220. package/dist/components/runtime-overview-panel/index.js +6 -0
  221. package/dist/components/runtime-overview-panel/runtime-overview-panel.js +89 -0
  222. package/dist/components/scope-selector/scope-selector.js +7 -7
  223. package/dist/components/search-bar/search-bar.js +24 -2
  224. package/dist/components/search-dialog/search-dialog.js +1 -1
  225. package/dist/components/segmented-control/index.js +12 -0
  226. package/dist/components/segmented-control/segmented-control.js +61 -0
  227. package/dist/components/select/select.js +5 -5
  228. package/dist/components/selection-halo/index.js +6 -0
  229. package/dist/components/selection-halo/selection-halo.js +72 -0
  230. package/dist/components/selection-presence/index.js +6 -0
  231. package/dist/components/selection-presence/selection-presence.js +50 -0
  232. package/dist/components/severity-badge/severity-badge.js +2 -2
  233. package/dist/components/sheet/sheet.js +1 -1
  234. package/dist/components/sidebar/sidebar.js +1 -1
  235. package/dist/components/sidebar-toggle/sidebar-toggle.js +2 -2
  236. package/dist/components/slider/slider.js +1 -1
  237. package/dist/components/slideshow/slideshow.js +12 -11
  238. package/dist/components/snap-guides/index.js +6 -0
  239. package/dist/components/snap-guides/snap-guides.js +45 -0
  240. package/dist/components/social-fab/social-fab.js +2 -1
  241. package/dist/components/sparkline-grid/sparkline-grid.js +1 -1
  242. package/dist/components/spinner/spinner.js +3 -3
  243. package/dist/components/spinner/unicode-spinner.js +1 -0
  244. package/dist/components/stat-card/stat-card.js +3 -3
  245. package/dist/components/state-badge-overlay/index.js +6 -0
  246. package/dist/components/state-badge-overlay/state-badge-overlay.js +90 -0
  247. package/dist/components/status-board/status-board.js +1 -1
  248. package/dist/components/status-indicator/status-indicator.js +3 -3
  249. package/dist/components/step-by-step/step-by-step.js +8 -7
  250. package/dist/components/step-navigation/step-navigation.js +2 -2
  251. package/dist/components/stepper/stepper.js +3 -3
  252. package/dist/components/sticky-metric/index.js +6 -0
  253. package/dist/components/sticky-metric/sticky-metric.js +83 -0
  254. package/dist/components/story-map/index.js +8 -0
  255. package/dist/components/story-map/story-map.js +414 -0
  256. package/dist/components/subscription-card/subscription-card.js +1 -1
  257. package/dist/components/switch/switch.js +1 -1
  258. package/dist/components/table-of-contents-panel/table-of-contents-panel.js +5 -5
  259. package/dist/components/tags-input/index.js +4 -0
  260. package/dist/components/tags-input/tags-input.js +178 -0
  261. package/dist/components/terminal/terminal.js +48 -34
  262. package/dist/components/thinking-block/thinking-block.js +1 -1
  263. package/dist/components/thread-bubble/index.js +6 -0
  264. package/dist/components/thread-bubble/thread-bubble.js +85 -0
  265. package/dist/components/threshold-ring/index.js +6 -0
  266. package/dist/components/threshold-ring/threshold-ring.js +160 -0
  267. package/dist/components/ticker-tape/ticker-tape.js +3 -3
  268. package/dist/components/timeline/index.js +12 -0
  269. package/dist/components/timeline/timeline.js +239 -0
  270. package/dist/components/timeline-scrubber/index.js +6 -0
  271. package/dist/components/timeline-scrubber/timeline-scrubber.js +179 -0
  272. package/dist/components/tldr-section/tldr-section.js +2 -2
  273. package/dist/components/top-bar/index.js +4 -0
  274. package/dist/components/top-bar/top-bar.js +31 -0
  275. package/dist/components/tour/tour.js +1 -1
  276. package/dist/components/transaction-list/index.js +14 -0
  277. package/dist/components/transaction-list/transaction-list.js +247 -0
  278. package/dist/components/tree-view/index.js +6 -0
  279. package/dist/components/tree-view/tree-view.js +298 -0
  280. package/dist/components/tutorial-complete/tutorial-complete.js +8 -8
  281. package/dist/components/usage-breakdown/usage-breakdown.js +10 -5
  282. package/dist/components/video-embed/video-embed.js +2 -2
  283. package/dist/components/viewport-bookmarks/index.js +6 -0
  284. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
  285. package/dist/components/wallet-card/wallet-card.js +1 -1
  286. package/dist/components/watchlist/watchlist.js +5 -5
  287. package/dist/components/workspace-switcher/index.js +6 -0
  288. package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
  289. package/dist/components/world-breadcrumbs/index.js +6 -0
  290. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
  291. package/dist/components/world-clock-bar/world-clock-bar.js +32 -12
  292. package/dist/components/zoom-hud/index.js +4 -0
  293. package/dist/components/zoom-hud/zoom-hud.js +61 -0
  294. package/dist/index.d.ts +7907 -226
  295. package/dist/index.js +3 -1
  296. package/package.json +9 -5
@@ -0,0 +1,708 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ createContext,
5
+ forwardRef,
6
+ useCallback,
7
+ useContext,
8
+ useId,
9
+ useMemo,
10
+ useRef,
11
+ useState,
12
+ useSyncExternalStore
13
+ } from "react";
14
+ import { cn } from "../../lib/utils";
15
+ const MIN_ZOOM = 1;
16
+ const MAX_ZOOM = 32;
17
+ const ZOOM_STEP = 2;
18
+ const MS_PER_DAY = 864e5;
19
+ const TICK_TARGET_PX = 120;
20
+ const COLOR_PALETTE = {
21
+ amber: {
22
+ bar: "bg-amber-500/80",
23
+ chip: "border-amber-400 text-amber-700 dark:text-amber-300",
24
+ chipActive: "bg-amber-500 text-white border-amber-500",
25
+ dot: "border-amber-500 bg-amber-500"
26
+ },
27
+ blue: {
28
+ bar: "bg-blue-500/80",
29
+ chip: "border-blue-400 text-blue-700 dark:text-blue-300",
30
+ chipActive: "bg-blue-500 text-white border-blue-500",
31
+ dot: "border-blue-500 bg-blue-500"
32
+ },
33
+ emerald: {
34
+ bar: "bg-emerald-500/80",
35
+ chip: "border-emerald-400 text-emerald-700 dark:text-emerald-300",
36
+ chipActive: "bg-emerald-500 text-white border-emerald-500",
37
+ dot: "border-emerald-500 bg-emerald-500"
38
+ },
39
+ neutral: {
40
+ bar: "bg-muted-foreground/70",
41
+ chip: "border-border text-muted-foreground",
42
+ chipActive: "bg-foreground text-background border-foreground",
43
+ dot: "border-muted-foreground bg-muted-foreground"
44
+ },
45
+ purple: {
46
+ bar: "bg-purple-500/80",
47
+ chip: "border-purple-400 text-purple-700 dark:text-purple-300",
48
+ chipActive: "bg-purple-500 text-white border-purple-500",
49
+ dot: "border-purple-500 bg-purple-500"
50
+ },
51
+ red: {
52
+ bar: "bg-red-500/80",
53
+ chip: "border-red-400 text-red-700 dark:text-red-300",
54
+ chipActive: "bg-red-500 text-white border-red-500",
55
+ dot: "border-red-500 bg-red-500"
56
+ },
57
+ rose: {
58
+ bar: "bg-rose-500/80",
59
+ chip: "border-rose-400 text-rose-700 dark:text-rose-300",
60
+ chipActive: "bg-rose-500 text-white border-rose-500",
61
+ dot: "border-rose-500 bg-rose-500"
62
+ }
63
+ };
64
+ const DEFAULT_LABELS = {
65
+ region: "Interactive timeline",
66
+ today: "Jump to today",
67
+ zoomIn: "Zoom in",
68
+ zoomOut: "Zoom out"
69
+ };
70
+ const TimelineContext = createContext(null);
71
+ function useTimelineContext() {
72
+ const ctx = useContext(TimelineContext);
73
+ if (!ctx) {
74
+ throw new Error("InteractiveTimeline subcomponent used outside its root.");
75
+ }
76
+ return ctx;
77
+ }
78
+ function clamp(value, min, max) {
79
+ return Math.min(Math.max(value, min), max);
80
+ }
81
+ function dateToOffset(date, start, span) {
82
+ if (span <= 0) return 0;
83
+ return (date.getTime() - start) / span;
84
+ }
85
+ function formatDate(date) {
86
+ return date.toLocaleDateString(void 0, {
87
+ day: "2-digit",
88
+ month: "short",
89
+ year: "numeric"
90
+ });
91
+ }
92
+ function dayCount(start, end) {
93
+ return Math.max(1, Math.round((end - start) / MS_PER_DAY));
94
+ }
95
+ function buildTicks(start, end, containerWidth) {
96
+ if (containerWidth <= 0) return [];
97
+ const targetCount = Math.max(2, Math.floor(containerWidth / TICK_TARGET_PX));
98
+ const span = end - start;
99
+ if (span <= 0) return [];
100
+ const totalDays = dayCount(start, end);
101
+ const stepDays = Math.max(1, Math.round(totalDays / (targetCount - 1)));
102
+ const stepMs = stepDays * MS_PER_DAY;
103
+ return Array.from({ length: targetCount }).map((_, index) => {
104
+ const time = start + stepMs * index;
105
+ const clamped = Math.min(time, end);
106
+ return {
107
+ date: new Date(clamped),
108
+ offset: (clamped - start) / span
109
+ };
110
+ });
111
+ }
112
+ function resolveEventColor(event, tracks, categories) {
113
+ if (event.color) return event.color;
114
+ if (event.category) {
115
+ const cat = categories.find((c) => c.id === event.category);
116
+ if (cat?.color) return cat.color;
117
+ }
118
+ if (event.track) {
119
+ const track = tracks.find((t) => t.id === event.track);
120
+ if (track?.color) return track.color;
121
+ }
122
+ return "neutral";
123
+ }
124
+ function Axis({ endTime, startTime, ticks }) {
125
+ return /* @__PURE__ */ jsx(
126
+ "div",
127
+ {
128
+ "aria-hidden": "true",
129
+ className: "relative h-7 border-b border-border text-[10px] font-medium uppercase tracking-wide text-muted-foreground",
130
+ "data-end": endTime,
131
+ "data-start": startTime,
132
+ children: ticks.map((tick) => /* @__PURE__ */ jsx(
133
+ "span",
134
+ {
135
+ className: "absolute top-1 -translate-x-1/2 whitespace-nowrap",
136
+ style: { left: `${(tick.offset * 100).toString()}%` },
137
+ children: formatDate(tick.date)
138
+ },
139
+ tick.date.getTime()
140
+ ))
141
+ }
142
+ );
143
+ }
144
+ function getNow() {
145
+ return Date.now();
146
+ }
147
+ function noopUnsubscribe() {
148
+ return;
149
+ }
150
+ function emptySubscribe() {
151
+ return noopUnsubscribe;
152
+ }
153
+ function useNow() {
154
+ return useSyncExternalStore(emptySubscribe, getNow, getNow);
155
+ }
156
+ function TodayMarker({ endTime, startTime }) {
157
+ const now = useNow();
158
+ if (now < startTime || now > endTime) return null;
159
+ const offset = (now - startTime) / (endTime - startTime);
160
+ return /* @__PURE__ */ jsx(
161
+ "div",
162
+ {
163
+ "aria-hidden": "true",
164
+ className: "pointer-events-none absolute inset-y-0 z-20 w-px bg-primary",
165
+ "data-testid": "today-marker",
166
+ style: { left: `${(offset * 100).toString()}%` },
167
+ children: /* @__PURE__ */ jsx("span", { className: "absolute -top-2 -translate-x-1/2 rounded bg-primary px-1 text-[10px] font-medium text-primary-foreground", children: "Today" })
168
+ }
169
+ );
170
+ }
171
+ function eventGeometry(event, startTime, endTime) {
172
+ const span = endTime - startTime;
173
+ const startOffset = dateToOffset(event.startDate, startTime, span);
174
+ const endOffset = event.endDate ? dateToOffset(event.endDate, startTime, span) : startOffset;
175
+ const visible = endOffset >= 0 && startOffset <= 1 && event.startDate.getTime() <= endTime;
176
+ const left = clamp(startOffset, 0, 1);
177
+ const width = Math.max(0, Math.min(1, endOffset) - left);
178
+ return {
179
+ isDuration: width > 0 && Boolean(event.endDate),
180
+ left,
181
+ visible,
182
+ width
183
+ };
184
+ }
185
+ function EventTooltip({ event, tooltipId }) {
186
+ return /* @__PURE__ */ jsxs(
187
+ "span",
188
+ {
189
+ className: "pointer-events-none absolute left-1/2 top-5 hidden -translate-x-1/2 whitespace-nowrap rounded border bg-popover px-2 py-1 text-[11px] font-medium text-popover-foreground shadow-md group-hover:block group-focus-visible:block",
190
+ id: tooltipId,
191
+ role: "tooltip",
192
+ children: [
193
+ /* @__PURE__ */ jsx("span", { className: "block", children: event.title }),
194
+ /* @__PURE__ */ jsxs("span", { className: "block text-[10px] text-muted-foreground", children: [
195
+ formatDate(event.startDate),
196
+ event.endDate ? ` \u2013 ${formatDate(event.endDate)}` : null
197
+ ] }),
198
+ event.description ? /* @__PURE__ */ jsx("span", { className: "block text-[10px] text-muted-foreground", children: event.description }) : null
199
+ ]
200
+ }
201
+ );
202
+ }
203
+ function EventNode({
204
+ active,
205
+ categories,
206
+ endTime,
207
+ event,
208
+ onSelect,
209
+ startTime,
210
+ tracks
211
+ }) {
212
+ const color = resolveEventColor(event, tracks, categories);
213
+ const palette = COLOR_PALETTE[color];
214
+ const { isDuration, left, visible, width } = eventGeometry(
215
+ event,
216
+ startTime,
217
+ endTime
218
+ );
219
+ if (!visible) return null;
220
+ const titleText = typeof event.title === "string" ? event.title : "Event";
221
+ const tooltipId = `${event.id}-tooltip`;
222
+ const handleClick = (mouseEvent) => {
223
+ mouseEvent.stopPropagation();
224
+ onSelect(event);
225
+ };
226
+ return /* @__PURE__ */ jsxs(
227
+ "button",
228
+ {
229
+ "aria-describedby": event.description ? tooltipId : void 0,
230
+ "aria-label": titleText,
231
+ "aria-pressed": active,
232
+ className: cn(
233
+ "group absolute top-1/2 z-10 -translate-y-1/2 cursor-pointer rounded-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
234
+ isDuration ? "" : "-translate-x-1/2"
235
+ ),
236
+ "data-event-id": event.id,
237
+ "data-event-track": event.track ?? "",
238
+ "data-selected": active ? "true" : void 0,
239
+ onClick: handleClick,
240
+ style: {
241
+ left: `${(left * 100).toString()}%`,
242
+ width: isDuration ? `${(width * 100).toString()}%` : void 0
243
+ },
244
+ type: "button",
245
+ children: [
246
+ isDuration ? /* @__PURE__ */ jsx(
247
+ "span",
248
+ {
249
+ className: cn(
250
+ "block h-3 rounded-sm shadow-sm ring-2 ring-background",
251
+ palette.bar,
252
+ active ? "ring-primary" : ""
253
+ )
254
+ }
255
+ ) : /* @__PURE__ */ jsx(
256
+ "span",
257
+ {
258
+ className: cn(
259
+ "block size-3 rounded-full border-2 ring-2 ring-background",
260
+ palette.dot,
261
+ active ? "ring-primary" : ""
262
+ )
263
+ }
264
+ ),
265
+ /* @__PURE__ */ jsx(EventTooltip, { event, tooltipId })
266
+ ]
267
+ }
268
+ );
269
+ }
270
+ function TrackRow({
271
+ categories,
272
+ endTime,
273
+ events,
274
+ onSelect,
275
+ selectedId,
276
+ startTime,
277
+ track
278
+ }) {
279
+ const palette = COLOR_PALETTE[track.color ?? "neutral"];
280
+ return /* @__PURE__ */ jsxs(
281
+ "div",
282
+ {
283
+ className: "relative flex h-12 items-center border-t border-border/60",
284
+ "data-track-id": track.id,
285
+ children: [
286
+ /* @__PURE__ */ jsxs("div", { className: "absolute left-0 z-30 flex h-full w-32 shrink-0 items-center gap-2 border-r border-border bg-background px-3 text-xs font-medium", children: [
287
+ /* @__PURE__ */ jsx(
288
+ "span",
289
+ {
290
+ "aria-hidden": "true",
291
+ className: cn("size-2 rounded-full", palette.dot)
292
+ }
293
+ ),
294
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: track.label })
295
+ ] }),
296
+ /* @__PURE__ */ jsxs("div", { className: "relative ml-32 h-full flex-1", children: [
297
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 top-1/2 h-px -translate-y-1/2 bg-border" }),
298
+ events.map((event) => /* @__PURE__ */ jsx(
299
+ EventNode,
300
+ {
301
+ active: selectedId === event.id,
302
+ categories,
303
+ endTime,
304
+ event,
305
+ onSelect,
306
+ startTime,
307
+ tracks: [track]
308
+ },
309
+ event.id
310
+ ))
311
+ ] })
312
+ ]
313
+ }
314
+ );
315
+ }
316
+ function useScrollDrag(ref) {
317
+ const dragRef = useRef(
318
+ null
319
+ );
320
+ const onPointerDown = useCallback(
321
+ (event) => {
322
+ const node = ref.current;
323
+ if (!node) return;
324
+ dragRef.current = {
325
+ originScroll: node.scrollLeft,
326
+ originX: event.clientX
327
+ };
328
+ node.setPointerCapture(event.pointerId);
329
+ },
330
+ [ref]
331
+ );
332
+ const onPointerMove = useCallback(
333
+ (event) => {
334
+ const node = ref.current;
335
+ const drag = dragRef.current;
336
+ if (!node || !drag) return;
337
+ node.scrollLeft = drag.originScroll - (event.clientX - drag.originX);
338
+ },
339
+ [ref]
340
+ );
341
+ const onPointerEnd = useCallback(
342
+ (event) => {
343
+ const node = ref.current;
344
+ if (!node) return;
345
+ if (node.hasPointerCapture(event.pointerId)) {
346
+ node.releasePointerCapture(event.pointerId);
347
+ }
348
+ dragRef.current = null;
349
+ },
350
+ [ref]
351
+ );
352
+ return {
353
+ onPointerCancel: onPointerEnd,
354
+ onPointerDown,
355
+ onPointerMove,
356
+ onPointerUp: onPointerEnd
357
+ };
358
+ }
359
+ function ScrollArea({
360
+ categories,
361
+ endTime,
362
+ events,
363
+ onSelect,
364
+ selectedId,
365
+ startTime,
366
+ tracks,
367
+ zoom
368
+ }) {
369
+ const ref = useRef(null);
370
+ const [containerWidth, setContainerWidth] = useState(800);
371
+ const dragHandlers = useScrollDrag(ref);
372
+ const handleRef = useCallback((node) => {
373
+ ref.current = node;
374
+ if (node) setContainerWidth(node.clientWidth);
375
+ }, []);
376
+ const innerWidth = `${(zoom * 100).toString()}%`;
377
+ const ticks = useMemo(
378
+ () => buildTicks(startTime, endTime, containerWidth * zoom),
379
+ [containerWidth, endTime, startTime, zoom]
380
+ );
381
+ return /* @__PURE__ */ jsx(
382
+ "div",
383
+ {
384
+ className: "relative w-full cursor-grab overflow-x-auto active:cursor-grabbing",
385
+ "data-zoom": zoom,
386
+ ref: handleRef,
387
+ ...dragHandlers,
388
+ children: /* @__PURE__ */ jsxs("div", { className: "relative", style: { width: innerWidth }, children: [
389
+ /* @__PURE__ */ jsx(Axis, { endTime, startTime, ticks }),
390
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
391
+ /* @__PURE__ */ jsx(TodayMarker, { endTime, startTime }),
392
+ tracks.map((track) => {
393
+ const trackEvents = events.filter(
394
+ (event) => (event.track ?? tracks[0]?.id) === track.id
395
+ );
396
+ return /* @__PURE__ */ jsx(
397
+ TrackRow,
398
+ {
399
+ categories,
400
+ endTime,
401
+ events: trackEvents,
402
+ onSelect,
403
+ selectedId,
404
+ startTime,
405
+ track
406
+ },
407
+ track.id
408
+ );
409
+ })
410
+ ] })
411
+ ] })
412
+ }
413
+ );
414
+ }
415
+ const InteractiveTimelineToolbar = forwardRef(({ children, className, ...rest }, ref) => /* @__PURE__ */ jsx(
416
+ "div",
417
+ {
418
+ className: cn(
419
+ "flex flex-wrap items-center gap-2 border-b border-border bg-muted/30 px-3 py-2",
420
+ className
421
+ ),
422
+ ref,
423
+ role: "toolbar",
424
+ ...rest,
425
+ children
426
+ }
427
+ ));
428
+ InteractiveTimelineToolbar.displayName = "InteractiveTimelineToolbar";
429
+ const InteractiveTimelineZoomIn = forwardRef(({ className, ...rest }, ref) => {
430
+ const { labels, zoomIn } = useTimelineContext();
431
+ return /* @__PURE__ */ jsx(
432
+ "button",
433
+ {
434
+ "aria-label": labels.zoomIn,
435
+ className: cn(
436
+ "inline-flex size-8 items-center justify-center rounded-md border border-border bg-background text-sm font-medium hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
437
+ className
438
+ ),
439
+ onClick: zoomIn,
440
+ ref,
441
+ type: "button",
442
+ ...rest,
443
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "+" })
444
+ }
445
+ );
446
+ });
447
+ InteractiveTimelineZoomIn.displayName = "InteractiveTimelineZoomIn";
448
+ const InteractiveTimelineZoomOut = forwardRef(({ className, ...rest }, ref) => {
449
+ const { labels, zoomOut } = useTimelineContext();
450
+ return /* @__PURE__ */ jsx(
451
+ "button",
452
+ {
453
+ "aria-label": labels.zoomOut,
454
+ className: cn(
455
+ "inline-flex size-8 items-center justify-center rounded-md border border-border bg-background text-sm font-medium hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
456
+ className
457
+ ),
458
+ onClick: zoomOut,
459
+ ref,
460
+ type: "button",
461
+ ...rest,
462
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2212" })
463
+ }
464
+ );
465
+ });
466
+ InteractiveTimelineZoomOut.displayName = "InteractiveTimelineZoomOut";
467
+ const InteractiveTimelineToday = forwardRef(({ children, className, ...rest }, ref) => {
468
+ const { centerToday, labels } = useTimelineContext();
469
+ return /* @__PURE__ */ jsx(
470
+ "button",
471
+ {
472
+ "aria-label": labels.today,
473
+ className: cn(
474
+ "inline-flex h-8 items-center rounded-md border border-border bg-background px-3 text-xs font-medium hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
475
+ className
476
+ ),
477
+ onClick: centerToday,
478
+ ref,
479
+ type: "button",
480
+ ...rest,
481
+ children: children ?? "Today"
482
+ }
483
+ );
484
+ });
485
+ InteractiveTimelineToday.displayName = "InteractiveTimelineToday";
486
+ const InteractiveTimelineFilter = forwardRef(({ categories, className, ...rest }, ref) => {
487
+ const { toggleCategory, visibleCategories } = useTimelineContext();
488
+ return /* @__PURE__ */ jsx(
489
+ "div",
490
+ {
491
+ className: cn("flex flex-wrap items-center gap-1.5", className),
492
+ ref,
493
+ role: "group",
494
+ ...rest,
495
+ children: categories.map((category) => {
496
+ const active = visibleCategories.has(category.id);
497
+ const palette = COLOR_PALETTE[category.color ?? "neutral"];
498
+ return /* @__PURE__ */ jsx(
499
+ "button",
500
+ {
501
+ "aria-pressed": active,
502
+ className: cn(
503
+ "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium",
504
+ active ? palette.chipActive : palette.chip
505
+ ),
506
+ "data-category-id": category.id,
507
+ onClick: () => {
508
+ toggleCategory(category.id);
509
+ },
510
+ type: "button",
511
+ children: category.label
512
+ },
513
+ category.id
514
+ );
515
+ })
516
+ }
517
+ );
518
+ });
519
+ InteractiveTimelineFilter.displayName = "InteractiveTimelineFilter";
520
+ const FALLBACK_TRACK = {
521
+ color: "neutral",
522
+ id: "default",
523
+ label: "Events"
524
+ };
525
+ function noop() {
526
+ return;
527
+ }
528
+ function useToolbarHandlers(arguments_) {
529
+ const { endTime, scrollerId, setZoom, startTime } = arguments_;
530
+ const zoomIn = useCallback(() => {
531
+ setZoom((current) => clamp(current * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM));
532
+ }, [setZoom]);
533
+ const zoomOut = useCallback(() => {
534
+ setZoom((current) => clamp(current / ZOOM_STEP, MIN_ZOOM, MAX_ZOOM));
535
+ }, [setZoom]);
536
+ const centerToday = useCallback(() => {
537
+ if (typeof document === "undefined") return;
538
+ const node = document.querySelector(
539
+ `[data-scroller-id="${scrollerId}"]`
540
+ );
541
+ if (!node) return;
542
+ const span = endTime - startTime;
543
+ if (span <= 0) return;
544
+ const now = Date.now();
545
+ const offset = clamp((now - startTime) / span, 0, 1);
546
+ const targetX = offset * node.scrollWidth - node.clientWidth / 2;
547
+ node.scrollLeft = clamp(targetX, 0, node.scrollWidth);
548
+ }, [endTime, scrollerId, startTime]);
549
+ return { centerToday, zoomIn, zoomOut };
550
+ }
551
+ function useTimelineFrame(arguments_) {
552
+ const { endDate, labels, startDate, trackProperty } = arguments_;
553
+ const tracks = trackProperty && trackProperty.length > 0 ? trackProperty : [FALLBACK_TRACK];
554
+ const resolvedLabels = useMemo(
555
+ () => ({ ...DEFAULT_LABELS, ...labels }),
556
+ [labels]
557
+ );
558
+ return {
559
+ endTime: endDate.getTime(),
560
+ resolvedLabels,
561
+ startTime: startDate.getTime(),
562
+ tracks
563
+ };
564
+ }
565
+ function useEventSelection(onEventClick) {
566
+ const [selectedId, setSelectedId] = useState();
567
+ const handleSelect = useCallback(
568
+ (event) => {
569
+ setSelectedId(event.id);
570
+ onEventClick(event);
571
+ },
572
+ [onEventClick]
573
+ );
574
+ return { handleSelect, selectedId };
575
+ }
576
+ function useTimelineContextValue(arguments_) {
577
+ const {
578
+ centerToday,
579
+ labels,
580
+ toggleCategory,
581
+ visibleCategories,
582
+ zoom,
583
+ zoomIn,
584
+ zoomOut
585
+ } = arguments_;
586
+ return useMemo(
587
+ () => ({
588
+ centerToday,
589
+ labels,
590
+ toggleCategory,
591
+ visibleCategories,
592
+ zoom,
593
+ zoomIn,
594
+ zoomOut
595
+ }),
596
+ [
597
+ centerToday,
598
+ labels,
599
+ toggleCategory,
600
+ visibleCategories,
601
+ zoom,
602
+ zoomIn,
603
+ zoomOut
604
+ ]
605
+ );
606
+ }
607
+ function useTimelineFilter(categories, events) {
608
+ const [hidden, setHidden] = useState(() => /* @__PURE__ */ new Set());
609
+ const visibleCategories = useMemo(
610
+ () => new Set(
611
+ categories.filter((category) => !hidden.has(category.id)).map((category) => category.id)
612
+ ),
613
+ [categories, hidden]
614
+ );
615
+ const toggleCategory = useCallback((id) => {
616
+ setHidden((current) => {
617
+ const next = new Set(current);
618
+ if (next.has(id)) next.delete(id);
619
+ else next.add(id);
620
+ return next;
621
+ });
622
+ }, []);
623
+ const filteredEvents = useMemo(() => {
624
+ if (categories.length === 0) return events;
625
+ return events.filter((event) => {
626
+ if (!event.category) return true;
627
+ return !hidden.has(event.category);
628
+ });
629
+ }, [categories.length, events, hidden]);
630
+ return { filteredEvents, toggleCategory, visibleCategories };
631
+ }
632
+ const InteractiveTimeline = forwardRef((props, ref) => {
633
+ const {
634
+ categories = [],
635
+ children,
636
+ className,
637
+ endDate,
638
+ events = [],
639
+ labels,
640
+ onEventClick = noop,
641
+ startDate,
642
+ tracks: trackProperty,
643
+ ...rest
644
+ } = props;
645
+ const { endTime, resolvedLabels, startTime, tracks } = useTimelineFrame({
646
+ endDate,
647
+ labels,
648
+ startDate,
649
+ trackProperty
650
+ });
651
+ const scrollerId = useId();
652
+ const [zoom, setZoom] = useState(1);
653
+ const { filteredEvents, toggleCategory, visibleCategories } = useTimelineFilter(categories, events);
654
+ const { centerToday, zoomIn, zoomOut } = useToolbarHandlers({
655
+ endTime,
656
+ scrollerId,
657
+ setZoom,
658
+ startTime,
659
+ zoom
660
+ });
661
+ const { handleSelect, selectedId } = useEventSelection(onEventClick);
662
+ const ctx = useTimelineContextValue({
663
+ centerToday,
664
+ labels: resolvedLabels,
665
+ toggleCategory,
666
+ visibleCategories,
667
+ zoom,
668
+ zoomIn,
669
+ zoomOut
670
+ });
671
+ return /* @__PURE__ */ jsx(TimelineContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
672
+ "section",
673
+ {
674
+ "aria-label": resolvedLabels.region,
675
+ className: cn(
676
+ "flex w-full flex-col overflow-hidden rounded-2xl border bg-background text-foreground",
677
+ className
678
+ ),
679
+ ref,
680
+ ...rest,
681
+ children: [
682
+ children,
683
+ /* @__PURE__ */ jsx("div", { "data-scroller-id": scrollerId, id: scrollerId, children: /* @__PURE__ */ jsx(
684
+ ScrollArea,
685
+ {
686
+ categories,
687
+ endTime,
688
+ events: filteredEvents,
689
+ onSelect: handleSelect,
690
+ selectedId,
691
+ startTime,
692
+ tracks,
693
+ zoom
694
+ }
695
+ ) })
696
+ ]
697
+ }
698
+ ) });
699
+ });
700
+ InteractiveTimeline.displayName = "InteractiveTimeline";
701
+ export {
702
+ InteractiveTimeline,
703
+ InteractiveTimelineFilter,
704
+ InteractiveTimelineToday,
705
+ InteractiveTimelineToolbar,
706
+ InteractiveTimelineZoomIn,
707
+ InteractiveTimelineZoomOut
708
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ JarvisDock
3
+ } from "./jarvis-dock";
4
+ export {
5
+ JarvisDock
6
+ };