@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,337 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ createContext,
5
+ forwardRef,
6
+ useCallback,
7
+ useContext,
8
+ useEffect,
9
+ useId,
10
+ useMemo,
11
+ useRef,
12
+ useState
13
+ } from "react";
14
+ import { cn } from "../../lib/utils";
15
+ const ChronoContext = createContext(null);
16
+ function useChronoContext() {
17
+ const ctx = useContext(ChronoContext);
18
+ if (!ctx) {
19
+ throw new Error("ChronoEvent used outside ChronologicalTimeline.");
20
+ }
21
+ return ctx;
22
+ }
23
+ function ImageMedia({ media }) {
24
+ return /* @__PURE__ */ jsxs("figure", { className: "overflow-hidden rounded-xl border bg-muted", children: [
25
+ /* @__PURE__ */ jsx(
26
+ "img",
27
+ {
28
+ alt: media.alt,
29
+ className: "aspect-video w-full object-cover",
30
+ loading: "lazy",
31
+ src: media.src
32
+ }
33
+ ),
34
+ media.caption || media.credit ? /* @__PURE__ */ jsxs("figcaption", { className: "border-t bg-background px-3 py-2 text-xs text-muted-foreground", children: [
35
+ media.caption ? /* @__PURE__ */ jsx("span", { className: "block", children: media.caption }) : null,
36
+ media.credit ? /* @__PURE__ */ jsx("span", { className: "block italic", children: media.credit }) : null
37
+ ] }) : null
38
+ ] });
39
+ }
40
+ function VideoMedia({ media }) {
41
+ return /* @__PURE__ */ jsxs("figure", { className: "overflow-hidden rounded-xl border bg-muted", children: [
42
+ /* @__PURE__ */ jsx("div", { className: "aspect-video w-full", children: /* @__PURE__ */ jsx(
43
+ "iframe",
44
+ {
45
+ allow: "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",
46
+ allowFullScreen: true,
47
+ className: "h-full w-full",
48
+ src: media.src,
49
+ title: media.title ?? "Embedded video"
50
+ }
51
+ ) }),
52
+ media.caption || media.credit ? /* @__PURE__ */ jsxs("figcaption", { className: "border-t bg-background px-3 py-2 text-xs text-muted-foreground", children: [
53
+ media.caption ? /* @__PURE__ */ jsx("span", { className: "block", children: media.caption }) : null,
54
+ media.credit ? /* @__PURE__ */ jsx("span", { className: "block italic", children: media.credit }) : null
55
+ ] }) : null
56
+ ] });
57
+ }
58
+ function AudioMedia({ media }) {
59
+ return /* @__PURE__ */ jsxs("figure", { className: "overflow-hidden rounded-xl border bg-muted px-3 py-3", children: [
60
+ /* @__PURE__ */ jsx(
61
+ "audio",
62
+ {
63
+ "aria-label": media.alt,
64
+ className: "w-full",
65
+ controls: true,
66
+ preload: "metadata",
67
+ src: media.src,
68
+ children: /* @__PURE__ */ jsx("track", { kind: "captions" })
69
+ }
70
+ ),
71
+ media.caption || media.credit ? /* @__PURE__ */ jsxs("figcaption", { className: "pt-2 text-xs text-muted-foreground", children: [
72
+ media.caption ? /* @__PURE__ */ jsx("span", { className: "block", children: media.caption }) : null,
73
+ media.credit ? /* @__PURE__ */ jsx("span", { className: "block italic", children: media.credit }) : null
74
+ ] }) : null
75
+ ] });
76
+ }
77
+ function Media({ media }) {
78
+ if (media.type === "image") return /* @__PURE__ */ jsx(ImageMedia, { media });
79
+ if (media.type === "video") return /* @__PURE__ */ jsx(VideoMedia, { media });
80
+ return /* @__PURE__ */ jsx(AudioMedia, { media });
81
+ }
82
+ function DateColumn({ date }) {
83
+ return /* @__PURE__ */ jsx("div", { className: "hidden items-start justify-end pr-4 text-right text-xs font-semibold uppercase tracking-wide text-muted-foreground md:flex md:group-[[data-side='right']]:order-3 md:group-[[data-side='right']]:justify-start md:group-[[data-side='right']]:pl-4 md:group-[[data-side='right']]:text-left", children: /* @__PURE__ */ jsx("time", { className: "pt-2", children: date }) });
84
+ }
85
+ function RailColumn({ featured }) {
86
+ return /* @__PURE__ */ jsxs(
87
+ "div",
88
+ {
89
+ "aria-hidden": "true",
90
+ className: "relative hidden md:flex md:w-6 md:items-start md:justify-center",
91
+ children: [
92
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-border" }),
93
+ /* @__PURE__ */ jsx(
94
+ "span",
95
+ {
96
+ className: cn(
97
+ "relative z-10 mt-3 block h-3 w-3 rounded-full border-2 border-background bg-primary",
98
+ featured ? "h-4 w-4" : ""
99
+ )
100
+ }
101
+ )
102
+ ]
103
+ }
104
+ );
105
+ }
106
+ function EventCard({
107
+ children,
108
+ date,
109
+ eventId,
110
+ featured,
111
+ media,
112
+ subtitle,
113
+ title
114
+ }) {
115
+ return /* @__PURE__ */ jsxs(
116
+ "div",
117
+ {
118
+ className: cn(
119
+ "rounded-2xl border bg-background p-5 shadow-sm md:group-[[data-side='right']]:order-1",
120
+ featured ? "ring-1 ring-primary/30" : ""
121
+ ),
122
+ children: [
123
+ /* @__PURE__ */ jsxs("header", { className: "mb-3 flex flex-col gap-1", children: [
124
+ /* @__PURE__ */ jsx("time", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground md:hidden", children: date }),
125
+ /* @__PURE__ */ jsx(
126
+ "h3",
127
+ {
128
+ className: cn(
129
+ "font-semibold text-foreground",
130
+ featured ? "text-xl" : "text-lg"
131
+ ),
132
+ id: `${eventId}-title`,
133
+ children: title
134
+ }
135
+ ),
136
+ subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : null
137
+ ] }),
138
+ media ? /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(Media, { media }) }) : null,
139
+ 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
140
+ ]
141
+ }
142
+ );
143
+ }
144
+ const ChronoEvent = forwardRef(
145
+ (props, forwardedRef) => {
146
+ const {
147
+ children,
148
+ className,
149
+ date,
150
+ featured = false,
151
+ id,
152
+ media,
153
+ subtitle,
154
+ title,
155
+ ...rest
156
+ } = props;
157
+ const generatedId = useId();
158
+ const eventId = id ?? generatedId;
159
+ const ref = useRef(null);
160
+ const { registerEvent, setActiveId } = useChronoContext();
161
+ const refCallback = useCallback(
162
+ (node) => {
163
+ ref.current = node;
164
+ registerEvent(eventId, node);
165
+ if (typeof forwardedRef === "function") forwardedRef(node);
166
+ else if (forwardedRef) forwardedRef.current = node;
167
+ },
168
+ [eventId, forwardedRef, registerEvent]
169
+ );
170
+ const handleFocus = useCallback(() => {
171
+ setActiveId(eventId);
172
+ }, [eventId, setActiveId]);
173
+ return /* @__PURE__ */ jsxs(
174
+ "article",
175
+ {
176
+ "aria-labelledby": `${eventId}-title`,
177
+ className: cn(
178
+ "group relative grid gap-4 py-6 md:grid-cols-[1fr_auto_1fr] md:gap-8",
179
+ className
180
+ ),
181
+ "data-event-id": eventId,
182
+ "data-featured": featured ? "true" : void 0,
183
+ id: eventId,
184
+ onFocus: handleFocus,
185
+ ref: refCallback,
186
+ ...rest,
187
+ children: [
188
+ /* @__PURE__ */ jsx(DateColumn, { date }),
189
+ /* @__PURE__ */ jsx(RailColumn, { featured }),
190
+ /* @__PURE__ */ jsx(
191
+ EventCard,
192
+ {
193
+ date,
194
+ eventId,
195
+ featured,
196
+ media,
197
+ subtitle,
198
+ title,
199
+ children
200
+ }
201
+ )
202
+ ]
203
+ }
204
+ );
205
+ }
206
+ );
207
+ ChronoEvent.displayName = "ChronoEvent";
208
+ function ProgressStrip({
209
+ activeId,
210
+ ids,
211
+ label
212
+ }) {
213
+ if (ids.length === 0) return null;
214
+ const activeIndex = activeId ? ids.indexOf(activeId) : -1;
215
+ const ratio = activeIndex < 0 ? 0 : (activeIndex + 1) / ids.length;
216
+ return /* @__PURE__ */ jsx(
217
+ "div",
218
+ {
219
+ "aria-label": label,
220
+ "aria-valuemax": 100,
221
+ "aria-valuemin": 0,
222
+ "aria-valuenow": Math.round(ratio * 100),
223
+ className: "sticky top-0 z-10 h-1 w-full bg-border",
224
+ role: "progressbar",
225
+ children: /* @__PURE__ */ jsx(
226
+ "span",
227
+ {
228
+ className: "block h-full bg-primary transition-[width] duration-200",
229
+ style: { width: `${(ratio * 100).toString()}%` }
230
+ }
231
+ )
232
+ }
233
+ );
234
+ }
235
+ function useChronoActiveTracker() {
236
+ const eventsRef = useRef(/* @__PURE__ */ new Map());
237
+ const [ids, setIds] = useState([]);
238
+ const [activeId, setActiveId] = useState();
239
+ const registerEvent = useCallback((id, node) => {
240
+ const map = eventsRef.current;
241
+ if (node) map.set(id, node);
242
+ else map.delete(id);
243
+ setIds([...map.keys()]);
244
+ }, []);
245
+ useEffect(() => {
246
+ if (typeof IntersectionObserver === "undefined") return;
247
+ const observer = new IntersectionObserver(
248
+ (entries) => {
249
+ const visible = entries.filter((entry) => entry.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
250
+ const first = visible[0];
251
+ if (first) {
252
+ const target = first.target;
253
+ if (target instanceof HTMLElement) {
254
+ const eventId = target.dataset.eventId;
255
+ if (eventId) setActiveId(eventId);
256
+ }
257
+ }
258
+ },
259
+ { rootMargin: "-30% 0px -50% 0px", threshold: 0.1 }
260
+ );
261
+ [...eventsRef.current.values()].forEach((node) => {
262
+ observer.observe(node);
263
+ });
264
+ return () => {
265
+ observer.disconnect();
266
+ };
267
+ }, [ids]);
268
+ return { activeId, ids, registerEvent, setActiveId };
269
+ }
270
+ function EventList({ activeId, children }) {
271
+ if (!Array.isArray(children)) {
272
+ return /* @__PURE__ */ jsx("ol", { className: "relative flex flex-col px-4 pb-6 md:px-6", children });
273
+ }
274
+ return /* @__PURE__ */ jsx("ol", { className: "relative flex flex-col px-4 pb-6 md:px-6", children: children.map((child, index) => /* @__PURE__ */ jsx(
275
+ "li",
276
+ {
277
+ className: "block list-none",
278
+ "data-active": isReactElementWithEventId(child, activeId) ? "true" : void 0,
279
+ "data-side": index % 2 === 0 ? "left" : "right",
280
+ children: child
281
+ },
282
+ getChildKey(child, index)
283
+ )) });
284
+ }
285
+ const ChronologicalTimeline = forwardRef((props, ref) => {
286
+ const {
287
+ children,
288
+ className,
289
+ progressLabel = "Timeline progress",
290
+ title,
291
+ ...rest
292
+ } = props;
293
+ const titleId = useId();
294
+ const { activeId, ids, registerEvent, setActiveId } = useChronoActiveTracker();
295
+ const ctx = useMemo(
296
+ () => ({ registerEvent, setActiveId, titleId }),
297
+ [registerEvent, setActiveId, titleId]
298
+ );
299
+ return /* @__PURE__ */ jsx(ChronoContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
300
+ "section",
301
+ {
302
+ "aria-labelledby": title ? titleId : void 0,
303
+ className: cn(
304
+ "relative mx-auto flex w-full max-w-4xl flex-col overflow-hidden rounded-2xl border bg-background text-foreground",
305
+ className
306
+ ),
307
+ ref,
308
+ ...rest,
309
+ children: [
310
+ /* @__PURE__ */ jsx(ProgressStrip, { activeId, ids, label: progressLabel }),
311
+ title ? /* @__PURE__ */ jsx("header", { className: "px-6 py-6", children: /* @__PURE__ */ jsx("h2", { className: "text-2xl font-semibold tracking-tight", id: titleId, children: title }) }) : null,
312
+ /* @__PURE__ */ jsx(EventList, { activeId, children })
313
+ ]
314
+ }
315
+ ) });
316
+ });
317
+ ChronologicalTimeline.displayName = "ChronologicalTimeline";
318
+ function isReactElementWithEventId(child, activeId) {
319
+ if (!activeId) return false;
320
+ if (typeof child !== "object" || child === null) return false;
321
+ if (!("props" in child)) return false;
322
+ const props = child.props;
323
+ if (typeof props !== "object" || props === null) return false;
324
+ const id = props.id;
325
+ return typeof id === "string" && id === activeId;
326
+ }
327
+ function getChildKey(child, fallback) {
328
+ if (typeof child !== "object" || child === null) return fallback;
329
+ if (!("key" in child)) return fallback;
330
+ const key = child.key;
331
+ if (typeof key === "string" || typeof key === "number") return key;
332
+ return fallback;
333
+ }
334
+ export {
335
+ ChronoEvent,
336
+ ChronologicalTimeline
337
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ ChronoEvent,
3
+ ChronologicalTimeline
4
+ } from "./chronological-timeline";
5
+ export {
6
+ ChronoEvent,
7
+ ChronologicalTimeline
8
+ };
@@ -0,0 +1,258 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ forwardRef
4
+ } from "react";
5
+ import { Globe } from "lucide-react";
6
+ import { cn } from "../../lib/utils";
7
+ import { Badge } from "../badge/badge";
8
+ const CIVILIZATION_COLOR_VARIANTS = {
9
+ amber: {
10
+ gradient: "from-amber-500/20 to-amber-700/40",
11
+ ring: "ring-amber-500/30"
12
+ },
13
+ blue: {
14
+ gradient: "from-blue-500/20 to-blue-700/40",
15
+ ring: "ring-blue-500/30"
16
+ },
17
+ emerald: {
18
+ gradient: "from-emerald-500/20 to-emerald-700/40",
19
+ ring: "ring-emerald-500/30"
20
+ },
21
+ neutral: {
22
+ gradient: "from-muted to-muted-foreground/10",
23
+ ring: "ring-border"
24
+ },
25
+ purple: {
26
+ gradient: "from-purple-500/20 to-purple-700/40",
27
+ ring: "ring-purple-500/30"
28
+ },
29
+ red: {
30
+ gradient: "from-red-500/20 to-red-700/40",
31
+ ring: "ring-red-500/30"
32
+ }
33
+ };
34
+ const DEFAULT_LABELS = {
35
+ achievements: "Achievements",
36
+ capital: "Capital",
37
+ duration: "Duration",
38
+ leaders: "Notable leaders",
39
+ peakPopulation: "Peak population",
40
+ timeline: "Era timeline"
41
+ };
42
+ function formatEraYear(year) {
43
+ if (year < 0) return `${Math.abs(year).toString()} BCE`;
44
+ return `${year.toString()} CE`;
45
+ }
46
+ function formatEra(era) {
47
+ const start = formatEraYear(era.start);
48
+ if (era.end === void 0) return `${start} \u2013 present`;
49
+ return `${start} \u2013 ${formatEraYear(era.end)}`;
50
+ }
51
+ function getDuration(era) {
52
+ if (!era) return void 0;
53
+ const end = era.end ?? (/* @__PURE__ */ new Date()).getFullYear();
54
+ const years = end - era.start;
55
+ if (years <= 0) return void 0;
56
+ return `${years.toString()} years`;
57
+ }
58
+ function CivilizationHero({ color, image, imageAlt }) {
59
+ const palette = CIVILIZATION_COLOR_VARIANTS[color];
60
+ return /* @__PURE__ */ jsx(
61
+ "div",
62
+ {
63
+ className: cn(
64
+ "relative h-32 w-full overflow-hidden rounded-t-2xl bg-gradient-to-br",
65
+ palette.gradient
66
+ ),
67
+ children: image ? /* @__PURE__ */ jsx(
68
+ "img",
69
+ {
70
+ alt: imageAlt ?? "",
71
+ className: "h-full w-full object-cover mix-blend-multiply",
72
+ src: image
73
+ }
74
+ ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-muted-foreground/50", children: /* @__PURE__ */ jsx(Globe, { "aria-hidden": "true", className: "h-12 w-12" }) })
75
+ }
76
+ );
77
+ }
78
+ function EraTimeline({ era, label }) {
79
+ const eraLabel = formatEra(era);
80
+ return /* @__PURE__ */ jsxs(
81
+ "div",
82
+ {
83
+ "aria-label": `${label}: ${eraLabel}`,
84
+ className: "flex flex-col gap-1",
85
+ role: "img",
86
+ children: [
87
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: eraLabel }),
88
+ /* @__PURE__ */ jsx("div", { className: "h-1.5 w-full rounded-full bg-muted", children: /* @__PURE__ */ jsx("span", { className: "block h-full w-2/3 rounded-full bg-primary" }) })
89
+ ]
90
+ }
91
+ );
92
+ }
93
+ function CivilizationStats({
94
+ capital,
95
+ capitalCaption,
96
+ durationCaption,
97
+ durationValue,
98
+ peakCaption,
99
+ peakPopulation
100
+ }) {
101
+ const items = [];
102
+ if (capital) items.push({ caption: capitalCaption, value: capital });
103
+ if (peakPopulation) {
104
+ items.push({ caption: peakCaption, value: peakPopulation });
105
+ }
106
+ if (durationValue) {
107
+ items.push({ caption: durationCaption, value: durationValue });
108
+ }
109
+ if (items.length === 0) return null;
110
+ return /* @__PURE__ */ jsx("dl", { className: "grid grid-cols-2 gap-x-3 gap-y-2 text-sm", children: items.map((item) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
111
+ /* @__PURE__ */ jsx("dt", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: item.caption }),
112
+ /* @__PURE__ */ jsx("dd", { className: "font-medium text-foreground", children: item.value })
113
+ ] }, item.caption)) });
114
+ }
115
+ function CivilizationListBlock({
116
+ heading,
117
+ items,
118
+ variant
119
+ }) {
120
+ if (items.length === 0) return null;
121
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
122
+ /* @__PURE__ */ jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: heading }),
123
+ variant === "badge" ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: items.map((item, index) => /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: item }, `${heading}-${index.toString()}`)) }) : /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1 text-sm text-foreground", children: items.map((item, index) => /* @__PURE__ */ jsx(
124
+ "li",
125
+ {
126
+ className: "leading-tight",
127
+ children: item
128
+ },
129
+ `${heading}-${index.toString()}`
130
+ )) })
131
+ ] });
132
+ }
133
+ function CivilizationBody({
134
+ achievements,
135
+ actionHref,
136
+ capital,
137
+ era,
138
+ labels,
139
+ leaders,
140
+ name,
141
+ peakPopulation,
142
+ region
143
+ }) {
144
+ const durationValue = getDuration(era);
145
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 p-5", children: [
146
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
147
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold leading-tight tracking-tight", children: name }),
148
+ region ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: region }) : null
149
+ ] }),
150
+ era ? /* @__PURE__ */ jsx(EraTimeline, { era, label: labels.timeline }) : null,
151
+ /* @__PURE__ */ jsx(
152
+ CivilizationStats,
153
+ {
154
+ capital,
155
+ capitalCaption: labels.capital,
156
+ durationCaption: labels.duration,
157
+ durationValue,
158
+ peakCaption: labels.peakPopulation,
159
+ peakPopulation
160
+ }
161
+ ),
162
+ achievements && achievements.length > 0 ? /* @__PURE__ */ jsx(
163
+ CivilizationListBlock,
164
+ {
165
+ heading: labels.achievements,
166
+ items: achievements,
167
+ variant: "badge"
168
+ }
169
+ ) : null,
170
+ leaders && leaders.length > 0 ? /* @__PURE__ */ jsx(
171
+ CivilizationListBlock,
172
+ {
173
+ heading: labels.leaders,
174
+ items: leaders,
175
+ variant: "list"
176
+ }
177
+ ) : null,
178
+ actionHref ? /* @__PURE__ */ jsx(
179
+ "a",
180
+ {
181
+ className: "text-sm font-medium text-primary underline-offset-4 hover:underline",
182
+ href: actionHref,
183
+ children: "Explore \u2192"
184
+ }
185
+ ) : null
186
+ ] });
187
+ }
188
+ const CivilizationCard = forwardRef(
189
+ (props, ref) => {
190
+ const {
191
+ achievements,
192
+ actionHref,
193
+ capital,
194
+ className,
195
+ color = "neutral",
196
+ era,
197
+ image,
198
+ labels,
199
+ leaders,
200
+ name,
201
+ peakPopulation,
202
+ region,
203
+ ...rest
204
+ } = props;
205
+ const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
206
+ const palette = CIVILIZATION_COLOR_VARIANTS[color];
207
+ const altName = typeof name === "string" ? name : void 0;
208
+ return /* @__PURE__ */ jsxs(
209
+ "article",
210
+ {
211
+ className: cn(
212
+ "flex flex-col overflow-hidden rounded-2xl border bg-background text-foreground shadow-sm ring-1",
213
+ palette.ring,
214
+ className
215
+ ),
216
+ ref,
217
+ ...rest,
218
+ children: [
219
+ /* @__PURE__ */ jsx(CivilizationHero, { color, image, imageAlt: altName }),
220
+ /* @__PURE__ */ jsx(
221
+ CivilizationBody,
222
+ {
223
+ achievements,
224
+ actionHref,
225
+ capital,
226
+ era,
227
+ labels: resolvedLabels,
228
+ leaders,
229
+ name,
230
+ peakPopulation,
231
+ region
232
+ }
233
+ )
234
+ ]
235
+ }
236
+ );
237
+ }
238
+ );
239
+ CivilizationCard.displayName = "CivilizationCard";
240
+ const CivilizationComparison = forwardRef(({ children, className, ...rest }, ref) => {
241
+ return /* @__PURE__ */ jsx(
242
+ "div",
243
+ {
244
+ className: cn(
245
+ "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",
246
+ className
247
+ ),
248
+ ref,
249
+ ...rest,
250
+ children
251
+ }
252
+ );
253
+ });
254
+ CivilizationComparison.displayName = "CivilizationComparison";
255
+ export {
256
+ CivilizationCard,
257
+ CivilizationComparison
258
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ CivilizationCard,
3
+ CivilizationComparison
4
+ } from "./civilization-card";
5
+ export {
6
+ CivilizationCard,
7
+ CivilizationComparison
8
+ };