@vllnt/ui 0.2.1-canary.cacf57f → 0.2.1-canary.d519db2

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 (83) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/dist/components/activity-heatmap/activity-heatmap.js +2 -1
  3. package/dist/components/ai-artifact/ai-artifact.js +8 -8
  4. package/dist/components/ai-sidebar/ai-sidebar.js +4 -4
  5. package/dist/components/animated-text/animated-text.js +6 -3
  6. package/dist/components/auto-reload/auto-reload.js +1 -1
  7. package/dist/components/blog-card/blog-card.js +3 -1
  8. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +2 -2
  9. package/dist/components/candlestick-chart/candlestick-chart.js +2 -1
  10. package/dist/components/carousel/carousel.js +14 -6
  11. package/dist/components/checklist/checklist.js +35 -7
  12. package/dist/components/checklist/index.js +8 -2
  13. package/dist/components/chronological-timeline/chronological-timeline.js +2 -2
  14. package/dist/components/code-playground/code-playground.js +2 -1
  15. package/dist/components/combobox/combobox.js +0 -5
  16. package/dist/components/comment-pin/comment-pin.js +2 -2
  17. package/dist/components/comparison/comparison.js +2 -1
  18. package/dist/components/completion-dialog/completion-dialog.js +18 -10
  19. package/dist/components/content-intro/content-intro.js +17 -14
  20. package/dist/components/conversation-thread/conversation-thread.js +3 -3
  21. package/dist/components/date-picker/date-picker.js +0 -5
  22. package/dist/components/exercise/exercise.js +10 -2
  23. package/dist/components/faq/faq.js +6 -2
  24. package/dist/components/file-upload/file-upload.js +0 -5
  25. package/dist/components/filter-bar/filter-bar.js +6 -6
  26. package/dist/components/floating-toolbar/floating-toolbar.js +2 -2
  27. package/dist/components/gantt-chart/gantt-chart.js +8 -7
  28. package/dist/components/globe-3d/globe-3d.js +16 -4
  29. package/dist/components/horizontal-scroll-row/horizontal-scroll-row.js +2 -1
  30. package/dist/components/interactive-timeline/interactive-timeline.js +8 -5
  31. package/dist/components/jarvis-dock/jarvis-dock.js +2 -2
  32. package/dist/components/key-concept/key-concept.js +2 -1
  33. package/dist/components/keyboard-shortcuts-help/keyboard-shortcuts-help.js +2 -1
  34. package/dist/components/knowledge-check/knowledge-check.js +2 -2
  35. package/dist/components/lang-provider/lang-provider.js +3 -3
  36. package/dist/components/learning-objectives/learning-objectives.js +2 -1
  37. package/dist/components/live-feed/live-feed.js +1 -1
  38. package/dist/components/map-2d/map-2d.js +1 -1
  39. package/dist/components/map-timeline/map-timeline.js +5 -5
  40. package/dist/components/market-treemap/market-treemap.js +2 -2
  41. package/dist/components/mdx-content/mdx-content.js +14 -5
  42. package/dist/components/navbar-saas/navbar-saas.js +1 -0
  43. package/dist/components/number-ticker/number-ticker.js +1 -1
  44. package/dist/components/order-book/order-book.js +2 -2
  45. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +2 -2
  46. package/dist/components/profile-section/profile-section.js +2 -1
  47. package/dist/components/progress-tracker/progress-tracker.js +16 -8
  48. package/dist/components/prompt-templates/prompt-templates.js +2 -2
  49. package/dist/components/quiz/quiz.js +3 -2
  50. package/dist/components/relationship-inspector/relationship-inspector.js +2 -2
  51. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +2 -2
  52. package/dist/components/run-timeline/run-timeline.js +2 -2
  53. package/dist/components/search-dialog/search-dialog.js +483 -50
  54. package/dist/components/share-dialog/share-dialog.js +2 -2
  55. package/dist/components/share-section/share-section.js +2 -1
  56. package/dist/components/sidebar/sidebar.js +20 -19
  57. package/dist/components/slideshow/slideshow.js +2 -1
  58. package/dist/components/social-fab/social-fab.js +10 -10
  59. package/dist/components/spinner/spinner.js +1 -1
  60. package/dist/components/spinner/unicode-spinner.js +4 -2
  61. package/dist/components/stat-card/stat-card.js +1 -1
  62. package/dist/components/status-board/status-board.js +12 -5
  63. package/dist/components/step-by-step/step-by-step.js +2 -1
  64. package/dist/components/table-of-contents/table-of-contents.js +5 -2
  65. package/dist/components/table-of-contents-panel/table-of-contents-panel.js +3 -1
  66. package/dist/components/tabs/tabs.js +26 -8
  67. package/dist/components/tags-input/tags-input.js +11 -3
  68. package/dist/components/terminal/terminal.js +10 -2
  69. package/dist/components/theme-toggle/theme-toggle.js +2 -2
  70. package/dist/components/thinking-block/thinking-block.js +2 -2
  71. package/dist/components/timeline-scrubber/timeline-scrubber.js +2 -2
  72. package/dist/components/tldr-section/tldr-section.js +9 -7
  73. package/dist/components/transaction-list/transaction-list.js +2 -2
  74. package/dist/components/tutorial-complete/tutorial-complete.js +7 -5
  75. package/dist/components/tutorial-filters/tutorial-filters.js +1 -1
  76. package/dist/components/tutorial-intro-content/tutorial-intro-content.js +1 -1
  77. package/dist/components/tutorial-mdx/tutorial-mdx.js +1 -1
  78. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +2 -2
  79. package/dist/components/watchlist/watchlist.js +2 -1
  80. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +2 -2
  81. package/dist/components/world-clock-bar/world-clock-bar.js +4 -3
  82. package/dist/index.d.ts +113 -29
  83. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
10
10
 
11
11
  ### Added
12
12
 
13
+ - **Release intelligence surface** — `/changelog`, `/releases`, `/rss.xml`, and `/atom.xml` expose one changelog source through HTML, GitHub release cards, and feed readers. `/docs/changelog` redirects to `/changelog`.
13
14
  - **Hooks + utility primitives** — `CopyButton` (+ `useCopyToClipboard` hook), `Banner` + `BannerAction`, `Kbd`, `EmptyState`, `DocumentSiblingNav`.
14
15
  - **Pricing + identity cards** — `PricingTable` + `PricingPlan`, `HistoricalFigureCard`, `CivilizationCard` (+ `CivilizationComparison`).
15
16
  - **Newsletter** — `NewsletterSignup` (state-machine driven submit flow).
@@ -29,6 +30,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
29
30
  - **Time navigation** — `TimelineScrubber`, `PlaybackGhost`, `BottomActivityStrip`, `RunTimeline`.
30
31
  - Total component count: **225** (up from 140).
31
32
 
33
+ - **A11y heading-level override** — every title-bearing component (`ProfileSection`, `FAQ`, `Slideshow`, `WorldClockBar`, `TableOfContentsPanel`, `TableOfContents`, `KeyboardShortcutsHelp`, `Watchlist`, `OrderBook`, `HorizontalScrollRow`, `MarketTreemap`, `ActivityHeatmap`, `Glossary`, `StatusBoard`, `CodePlayground`, `Comparison`, `Quiz`, `Exercise`, `ShareSection`, `CompletionDialog`, `Checklist`, `LearningObjectives`, `CandlestickChart`, `StepByStep`) accepts an `as` prop (`"h1"`–`"h6"`); multi-title components (`ContentIntro`, `TutorialComplete`) expose `titleAs` + `tocLabelAs` / `sectionLabelAs`. Defaults preserve existing heading tags. `HeadingTag` is re-exported from `@vllnt/ui`.
34
+
32
35
  ### Changed
33
36
 
34
37
  - Registry installs use a **hybrid CLI strategy** — leaf component source is inlined, sibling registry items resolve via `@vllnt/ui` to keep installs minimal and dedupe shared primitives.
@@ -145,6 +145,7 @@ function HeatmapGrid({
145
145
  }
146
146
  const ActivityHeatmap = React.forwardRef(
147
147
  ({
148
+ as: Heading = "h2",
148
149
  className,
149
150
  data,
150
151
  description,
@@ -157,7 +158,7 @@ const ActivityHeatmap = React.forwardRef(
157
158
  const gridData = getGridData(data, normalizedEndDate, weeks);
158
159
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), ref, ...props, children: [
159
160
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
160
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold tracking-tight", children: title }),
161
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold tracking-tight", children: title }),
161
162
  description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
162
163
  ] }),
163
164
  /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border bg-card p-4 shadow-sm", children: /* @__PURE__ */ jsx(HeatmapGrid, { gridData, weeks }) })
@@ -259,7 +259,7 @@ const AIArtifactToolbar = forwardRef(({ className, ...rest }, ref) => /* @__PURE
259
259
  AIArtifactToolbar.displayName = "AIArtifactToolbar";
260
260
  const AIArtifactCopyButton = forwardRef(({ className, onClick, ...rest }, ref) => {
261
261
  const { copied, copy, labels } = useAIArtifact();
262
- const handleClick = useCallback(
262
+ const handleCopyArtifact = useCallback(
263
263
  (event) => {
264
264
  onClick?.(event);
265
265
  if (event.defaultPrevented) return;
@@ -272,7 +272,7 @@ const AIArtifactCopyButton = forwardRef(({ className, onClick, ...rest }, ref) =
272
272
  {
273
273
  "aria-label": copied ? labels.copied : labels.copy,
274
274
  className: cn("size-8", className),
275
- onClick: handleClick,
275
+ onClick: handleCopyArtifact,
276
276
  ref,
277
277
  size: "icon",
278
278
  type: "button",
@@ -285,7 +285,7 @@ const AIArtifactCopyButton = forwardRef(({ className, onClick, ...rest }, ref) =
285
285
  AIArtifactCopyButton.displayName = "AIArtifactCopyButton";
286
286
  const AIArtifactEditButton = forwardRef(({ className, onClick, ...rest }, ref) => {
287
287
  const { hasOnEdit, labels, onEdit } = useAIArtifact();
288
- const handleClick = useCallback(
288
+ const handleEditArtifact = useCallback(
289
289
  (event) => {
290
290
  onClick?.(event);
291
291
  if (event.defaultPrevented) return;
@@ -299,7 +299,7 @@ const AIArtifactEditButton = forwardRef(({ className, onClick, ...rest }, ref) =
299
299
  {
300
300
  "aria-label": labels.edit,
301
301
  className: cn("size-8", className),
302
- onClick: handleClick,
302
+ onClick: handleEditArtifact,
303
303
  ref,
304
304
  size: "icon",
305
305
  type: "button",
@@ -312,7 +312,7 @@ const AIArtifactEditButton = forwardRef(({ className, onClick, ...rest }, ref) =
312
312
  AIArtifactEditButton.displayName = "AIArtifactEditButton";
313
313
  const AIArtifactDownloadButton = forwardRef(({ className, onClick, ...rest }, ref) => {
314
314
  const { download, labels } = useAIArtifact();
315
- const handleClick = useCallback(
315
+ const handleDownloadArtifact = useCallback(
316
316
  (event) => {
317
317
  onClick?.(event);
318
318
  if (event.defaultPrevented) return;
@@ -325,7 +325,7 @@ const AIArtifactDownloadButton = forwardRef(({ className, onClick, ...rest }, re
325
325
  {
326
326
  "aria-label": labels.download,
327
327
  className: cn("size-8", className),
328
- onClick: handleClick,
328
+ onClick: handleDownloadArtifact,
329
329
  ref,
330
330
  size: "icon",
331
331
  type: "button",
@@ -338,7 +338,7 @@ const AIArtifactDownloadButton = forwardRef(({ className, onClick, ...rest }, re
338
338
  AIArtifactDownloadButton.displayName = "AIArtifactDownloadButton";
339
339
  const AIArtifactFullscreenButton = forwardRef(({ className, onClick, ...rest }, ref) => {
340
340
  const { fullscreen, labels, toggleFullscreen } = useAIArtifact();
341
- const handleClick = useCallback(
341
+ const handleToggleArtifactFullscreen = useCallback(
342
342
  (event) => {
343
343
  onClick?.(event);
344
344
  if (event.defaultPrevented) return;
@@ -352,7 +352,7 @@ const AIArtifactFullscreenButton = forwardRef(({ className, onClick, ...rest },
352
352
  "aria-label": fullscreen ? labels.exitFullscreen : labels.enterFullscreen,
353
353
  "aria-pressed": fullscreen,
354
354
  className: cn("size-8", className),
355
- onClick: handleClick,
355
+ onClick: handleToggleArtifactFullscreen,
356
356
  ref,
357
357
  size: "icon",
358
358
  type: "button",
@@ -169,7 +169,7 @@ const AISidebarTitle = forwardRef(({ children, className, ...rest }, ref) => {
169
169
  AISidebarTitle.displayName = "AISidebarTitle";
170
170
  const AISidebarClose = forwardRef(({ className, onClick, ...rest }, ref) => {
171
171
  const { close, labels } = useAISidebar();
172
- const handleClick = useCallback(
172
+ const handleCloseSidebar = useCallback(
173
173
  (event) => {
174
174
  onClick?.(event);
175
175
  if (event.defaultPrevented) return;
@@ -182,7 +182,7 @@ const AISidebarClose = forwardRef(({ className, onClick, ...rest }, ref) => {
182
182
  {
183
183
  "aria-label": labels.close,
184
184
  className: cn("size-8", className),
185
- onClick: handleClick,
185
+ onClick: handleCloseSidebar,
186
186
  ref,
187
187
  size: "icon",
188
188
  type: "button",
@@ -215,7 +215,7 @@ const AISidebarFooter = forwardRef(({ children, className, ...rest }, ref) => /*
215
215
  AISidebarFooter.displayName = "AISidebarFooter";
216
216
  const AISidebarTrigger = forwardRef(({ children, className, onClick, ...rest }, ref) => {
217
217
  const { labels, openState, toggle } = useAISidebar();
218
- const handleClick = useCallback(
218
+ const handleToggleSidebar = useCallback(
219
219
  (event) => {
220
220
  onClick?.(event);
221
221
  if (event.defaultPrevented) return;
@@ -230,7 +230,7 @@ const AISidebarTrigger = forwardRef(({ children, className, onClick, ...rest },
230
230
  "aria-label": children ? void 0 : labels.open,
231
231
  className: cn(className),
232
232
  "data-state": openState ? "open" : "closed",
233
- onClick: handleClick,
233
+ onClick: handleToggleSidebar,
234
234
  ref,
235
235
  size: children ? "sm" : "icon",
236
236
  type: "button",
@@ -87,13 +87,16 @@ function buildRevealPlan(direction, length, randomness) {
87
87
  return revealPlan;
88
88
  }
89
89
  function useRevealProgress(active, length, stagger) {
90
- const [progress, setProgress] = React.useState(0);
90
+ const [progress, setProgress] = React.useState(() => active ? 0 : length);
91
+ const [revealKey, setRevealKey] = React.useState({ active, length, stagger });
92
+ if (revealKey.active !== active || revealKey.length !== length || revealKey.stagger !== stagger) {
93
+ setRevealKey({ active, length, stagger });
94
+ setProgress(active ? 0 : length);
95
+ }
91
96
  React.useEffect(() => {
92
97
  if (!active) {
93
- setProgress(length);
94
98
  return;
95
99
  }
96
- setProgress(0);
97
100
  const revealInterval = window.setInterval(
98
101
  () => {
99
102
  setProgress((current) => {
@@ -39,7 +39,7 @@ function getCurrencyFormatter(locale, currency) {
39
39
  const key = `${locale}|${currency}`;
40
40
  let formatter = CURRENCY_FORMATTER_CACHE.get(key);
41
41
  if (!formatter) {
42
- formatter = new Intl.NumberFormat(locale, {
42
+ formatter = Intl.NumberFormat(locale, {
43
43
  currency,
44
44
  style: "currency"
45
45
  });
@@ -43,7 +43,9 @@ function ContentCard({
43
43
  ] }) }) : null
44
44
  ] }) });
45
45
  }
46
- const BlogCard = ContentCard;
46
+ function BlogCard(props) {
47
+ return /* @__PURE__ */ jsx(ContentCard, { ...props });
48
+ }
47
49
  export {
48
50
  BlogCard,
49
51
  ContentCard
@@ -34,7 +34,7 @@ const Chip = (props) => {
34
34
  const { event } = props;
35
35
  const tone = event.tone ?? "neutral";
36
36
  if (event.onActivate) {
37
- const handleClick = () => {
37
+ const handleActivateEvent = () => {
38
38
  event.onActivate?.();
39
39
  };
40
40
  return /* @__PURE__ */ jsx(
@@ -43,7 +43,7 @@ const Chip = (props) => {
43
43
  className: "flex items-center rounded-full border border-border bg-background px-2 py-1 text-[11px] transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
44
44
  "data-strip-event": event.id,
45
45
  "data-strip-event-tone": tone,
46
- onClick: handleClick,
46
+ onClick: handleActivateEvent,
47
47
  type: "button",
48
48
  children: /* @__PURE__ */ jsx(ChipBody, { event })
49
49
  }
@@ -158,6 +158,7 @@ function SessionPill({ sessionChange }) {
158
158
  }
159
159
  const CandlestickChart = React.forwardRef(
160
160
  ({
161
+ as: Heading = "h3",
161
162
  className,
162
163
  data,
163
164
  height = DEFAULT_HEIGHT,
@@ -185,7 +186,7 @@ const CandlestickChart = React.forwardRef(
185
186
  /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-wrap items-start justify-between gap-3", children: [
186
187
  /* @__PURE__ */ jsxs("div", { children: [
187
188
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground", children: "OHLC session" }),
188
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-foreground", children: "Candlestick chart" })
189
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold text-foreground", children: "Candlestick chart" })
189
190
  ] }),
190
191
  /* @__PURE__ */ jsx(SessionPill, { sessionChange })
191
192
  ] }),
@@ -7,6 +7,7 @@ import {
7
7
  useContext,
8
8
  useEffect,
9
9
  useMemo,
10
+ useRef,
10
11
  useState
11
12
  } from "react";
12
13
  import useEmblaCarousel from "embla-carousel-react";
@@ -43,6 +44,10 @@ function useCarouselLogic({
43
44
  setCanScrollPrevious(api2.canScrollPrev());
44
45
  setCanScrollNext(api2.canScrollNext());
45
46
  }, []);
47
+ const onSelectReference = useRef(onSelect);
48
+ useEffect(() => {
49
+ onSelectReference.current = onSelect;
50
+ }, [onSelect]);
46
51
  const scrollPrevious = useCallback(() => {
47
52
  api?.scrollPrev();
48
53
  }, [api]);
@@ -71,17 +76,20 @@ function useCarouselLogic({
71
76
  if (!api) {
72
77
  return;
73
78
  }
74
- api.on("reInit", onSelect);
75
- api.on("select", onSelect);
79
+ const notifySelection = (selectedApi) => {
80
+ onSelectReference.current(selectedApi);
81
+ };
82
+ api.on("reInit", notifySelection);
83
+ api.on("select", notifySelection);
76
84
  const rafId = requestAnimationFrame(() => {
77
- onSelect(api);
85
+ notifySelection(api);
78
86
  });
79
87
  return () => {
80
- api?.off("select", onSelect);
81
- api?.off("reInit", onSelect);
88
+ api?.off("select", notifySelection);
89
+ api?.off("reInit", notifySelection);
82
90
  cancelAnimationFrame(rafId);
83
91
  };
84
- }, [api, onSelect]);
92
+ }, [api]);
85
93
  return {
86
94
  api,
87
95
  canScrollNext,
@@ -3,6 +3,31 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { cn } from "../../lib/utils";
5
5
  const CHECKLIST_PROGRESS_EVENT = "vllnt:checklist-progress-change";
6
+ const CHECKLIST_STORAGE_VERSION = 1;
7
+ function stringItemsFromUnknown(value) {
8
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
9
+ }
10
+ function parseChecklistStorageValue(saved) {
11
+ try {
12
+ const parsed = JSON.parse(saved);
13
+ if (Array.isArray(parsed)) {
14
+ return stringItemsFromUnknown(parsed);
15
+ }
16
+ if (typeof parsed === "object" && parsed !== null && "version" in parsed && "checked" in parsed && parsed.version === CHECKLIST_STORAGE_VERSION) {
17
+ return stringItemsFromUnknown(parsed.checked);
18
+ }
19
+ } catch {
20
+ return [];
21
+ }
22
+ return [];
23
+ }
24
+ function createChecklistStorageValue(ids) {
25
+ const payload = {
26
+ checked: [...ids],
27
+ version: CHECKLIST_STORAGE_VERSION
28
+ };
29
+ return JSON.stringify(payload);
30
+ }
6
31
  function ChecklistItemRow({
7
32
  isChecked,
8
33
  item,
@@ -67,13 +92,14 @@ function ChecklistItemRow({
67
92
  }
68
93
  function ChecklistHeader({
69
94
  checked,
95
+ Heading,
70
96
  progress,
71
97
  title,
72
98
  total
73
99
  }) {
74
100
  if (!title) return null;
75
101
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
76
- /* @__PURE__ */ jsxs("h4", { className: "font-semibold flex items-center gap-2", children: [
102
+ /* @__PURE__ */ jsxs(Heading, { className: "font-semibold flex items-center gap-2", children: [
77
103
  /* @__PURE__ */ jsx(
78
104
  "svg",
79
105
  {
@@ -105,6 +131,7 @@ function ChecklistHeader({
105
131
  ] });
106
132
  }
107
133
  function Checklist({
134
+ as: Heading = "h4",
108
135
  className,
109
136
  items,
110
137
  onComplete,
@@ -115,10 +142,7 @@ function Checklist({
115
142
  if (typeof window !== "undefined" && persistKey) {
116
143
  const saved = localStorage.getItem(`checklist:${persistKey}`);
117
144
  if (saved) {
118
- try {
119
- return new Set(JSON.parse(saved));
120
- } catch {
121
- }
145
+ return new Set(parseChecklistStorageValue(saved));
122
146
  }
123
147
  }
124
148
  return /* @__PURE__ */ new Set();
@@ -132,7 +156,7 @@ function Checklist({
132
156
  try {
133
157
  localStorage.setItem(
134
158
  `checklist:${persistKey}`,
135
- JSON.stringify([...newChecked])
159
+ createChecklistStorageValue(newChecked)
136
160
  );
137
161
  window.dispatchEvent(
138
162
  new CustomEvent(CHECKLIST_PROGRESS_EVENT, {
@@ -153,6 +177,7 @@ function Checklist({
153
177
  ChecklistHeader,
154
178
  {
155
179
  checked: checked.size,
180
+ Heading,
156
181
  progress,
157
182
  title,
158
183
  total: items.length
@@ -184,5 +209,8 @@ function Checklist({
184
209
  }
185
210
  export {
186
211
  CHECKLIST_PROGRESS_EVENT,
187
- Checklist
212
+ CHECKLIST_STORAGE_VERSION,
213
+ Checklist,
214
+ createChecklistStorageValue,
215
+ parseChecklistStorageValue
188
216
  };
@@ -1,8 +1,14 @@
1
1
  import {
2
2
  Checklist,
3
- CHECKLIST_PROGRESS_EVENT
3
+ CHECKLIST_PROGRESS_EVENT,
4
+ CHECKLIST_STORAGE_VERSION,
5
+ createChecklistStorageValue,
6
+ parseChecklistStorageValue
4
7
  } from "./checklist";
5
8
  export {
6
9
  CHECKLIST_PROGRESS_EVENT,
7
- Checklist
10
+ CHECKLIST_STORAGE_VERSION,
11
+ Checklist,
12
+ createChecklistStorageValue,
13
+ parseChecklistStorageValue
8
14
  };
@@ -168,7 +168,7 @@ const ChronoEvent = forwardRef(
168
168
  },
169
169
  [eventId, forwardedRef, registerEvent]
170
170
  );
171
- const handleFocus = useCallback(() => {
171
+ const handleFocusEvent = useCallback(() => {
172
172
  setActiveId(eventId);
173
173
  }, [eventId, setActiveId]);
174
174
  return /* @__PURE__ */ jsxs(
@@ -182,7 +182,7 @@ const ChronoEvent = forwardRef(
182
182
  "data-event-id": eventId,
183
183
  "data-featured": featured ? "true" : void 0,
184
184
  id: eventId,
185
- onFocus: handleFocus,
185
+ onFocus: handleFocusEvent,
186
186
  ref: refCallback,
187
187
  ...rest,
188
188
  children: [
@@ -13,6 +13,7 @@ function CodeLine({ highlightLines, line, lineNumber }) {
13
13
  }
14
14
  const EMPTY_HIGHLIGHT_LINES = [];
15
15
  function CodePlayground({
16
+ as: Heading = "h4",
16
17
  children,
17
18
  description,
18
19
  filename,
@@ -36,7 +37,7 @@ function CodePlayground({
36
37
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
37
38
  /* @__PURE__ */ jsx("div", { className: "flex size-8 items-center justify-center rounded bg-primary/10", children: /* @__PURE__ */ jsx(Code, { className: "size-4 text-primary" }) }),
38
39
  /* @__PURE__ */ jsxs("div", { children: [
39
- /* @__PURE__ */ jsx("h4", { className: "font-semibold text-sm", children: title }),
40
+ /* @__PURE__ */ jsx(Heading, { className: "font-semibold text-sm", children: title }),
40
41
  description ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: description }) : null
41
42
  ] })
42
43
  ] }),
@@ -15,11 +15,6 @@ import {
15
15
  import { Popover, PopoverContent, PopoverTrigger } from "../popover";
16
16
  function useComboboxValue(value, onValueChange) {
17
17
  const [internalValue, setInternalValue] = React.useState(value ?? "");
18
- React.useEffect(() => {
19
- if (value !== void 0) {
20
- setInternalValue(value);
21
- }
22
- }, [value]);
23
18
  const resolvedValue = value ?? internalValue;
24
19
  const setResolvedValue = (nextValue) => {
25
20
  if (value === void 0) {
@@ -57,7 +57,7 @@ const CommentPin = forwardRef(
57
57
  const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
58
58
  const showBadge = typeof unread === "number" && unread > 0;
59
59
  const ariaLabel = showBadge ? `${resolvedLabels.region}, ${unread} ${resolvedLabels.unreadSuffix}` : resolvedLabels.region;
60
- const handleClick = () => {
60
+ const handleActivateComment = () => {
61
61
  onActivate?.();
62
62
  };
63
63
  const body = /* @__PURE__ */ jsx(
@@ -89,7 +89,7 @@ const CommentPin = forwardRef(
89
89
  "aria-label": ariaLabel,
90
90
  className: "relative inline-flex rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
91
91
  "data-comment-pin-trigger": true,
92
- onClick: handleClick,
92
+ onClick: handleActivateComment,
93
93
  type: "button",
94
94
  children: body
95
95
  }
@@ -24,6 +24,7 @@ const variantConfig = {
24
24
  };
25
25
  function Comparison({
26
26
  after,
27
+ as: Heading = "h4",
27
28
  before,
28
29
  title,
29
30
  ...rest
@@ -40,7 +41,7 @@ function Comparison({
40
41
  const BeforeIcon = beforeConfig.icon;
41
42
  const AfterIcon = afterConfig.icon;
42
43
  return /* @__PURE__ */ jsxs("div", { className: "my-6", children: [
43
- title ? /* @__PURE__ */ jsx("h4", { className: "font-semibold mb-3", children: title }) : null,
44
+ title ? /* @__PURE__ */ jsx(Heading, { className: "font-semibold mb-3", children: title }) : null,
44
45
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
45
46
  /* @__PURE__ */ jsxs("div", { className: cn("rounded-lg border", beforeConfig.className), children: [
46
47
  /* @__PURE__ */ jsx(
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { memo, useCallback, useEffect, useRef } from "react";
3
+ import { memo, useEffect, useRef } from "react";
4
4
  import { cn } from "../../lib/utils";
5
5
  import { Button } from "../button";
6
6
  function DialogContent({
7
+ as: Heading = "h2",
7
8
  cancelLabel = "Skip",
8
9
  cancelShortcut = "S",
9
10
  className,
@@ -57,7 +58,7 @@ function DialogContent({
57
58
  }
58
59
  ),
59
60
  /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
60
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", id: "completion-dialog-title", children: title }),
61
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold", id: "completion-dialog-title", children: title }),
61
62
  description ? /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground mt-1.5", children: description }) : null
62
63
  ] }),
63
64
  /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-2", children: [
@@ -83,6 +84,7 @@ function DialogContent({
83
84
  );
84
85
  }
85
86
  function CompletionDialogImpl({
87
+ as,
86
88
  cancelLabel,
87
89
  cancelShortcut = "S",
88
90
  className,
@@ -96,8 +98,11 @@ function CompletionDialogImpl({
96
98
  onConfirm,
97
99
  title
98
100
  }) {
99
- const handleKeyDown = useCallback(
100
- (event) => {
101
+ const keyDownHandlerRef = useRef(() => {
102
+ return;
103
+ });
104
+ useEffect(() => {
105
+ keyDownHandlerRef.current = (event) => {
101
106
  if (!isOpen) return;
102
107
  if (event.key === "Escape") {
103
108
  event.preventDefault();
@@ -116,16 +121,18 @@ function CompletionDialogImpl({
116
121
  event.stopPropagation();
117
122
  onCancel();
118
123
  }
119
- },
120
- [isOpen, onClose, onConfirm, onCancel, confirmShortcut, cancelShortcut]
121
- );
124
+ };
125
+ }, [cancelShortcut, confirmShortcut, isOpen, onCancel, onClose, onConfirm]);
122
126
  useEffect(() => {
123
127
  if (!isOpen) return;
124
- document.addEventListener("keydown", handleKeyDown, true);
128
+ const onDocumentKeyDown = (event) => {
129
+ keyDownHandlerRef.current(event);
130
+ };
131
+ document.addEventListener("keydown", onDocumentKeyDown, true);
125
132
  return () => {
126
- document.removeEventListener("keydown", handleKeyDown, true);
133
+ document.removeEventListener("keydown", onDocumentKeyDown, true);
127
134
  };
128
- }, [isOpen, handleKeyDown]);
135
+ }, [isOpen]);
129
136
  if (!isOpen) return null;
130
137
  return /* @__PURE__ */ jsxs(
131
138
  "div",
@@ -149,6 +156,7 @@ function CompletionDialogImpl({
149
156
  /* @__PURE__ */ jsx(
150
157
  DialogContent,
151
158
  {
159
+ as,
152
160
  cancelLabel,
153
161
  cancelShortcut,
154
162
  className,
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { memo, useCallback, useEffect } from "react";
3
+ import { memo, useEffect, useRef } from "react";
4
4
  import { cn } from "../../lib/utils";
5
5
  import { Button } from "../button";
6
6
  const DEFAULT_LABELS = {
@@ -19,33 +19,36 @@ function ContentIntroImpl({
19
19
  onStart,
20
20
  renderIntroContent,
21
21
  sections,
22
- title
22
+ title,
23
+ titleAs: TitleHeading = "h2",
24
+ tocLabelAs: TocHeading = "h3"
23
25
  }) {
24
26
  const mergedLabels = { ...DEFAULT_LABELS, ...labels };
25
27
  const hasProgress = completedSections.size > 0;
26
- const handleKeyDown = useCallback(
27
- (event) => {
28
+ const onStartRef = useRef(onStart);
29
+ useEffect(() => {
30
+ onStartRef.current = onStart;
31
+ }, [onStart]);
32
+ useEffect(() => {
33
+ const onDocumentKeyDown = (event) => {
28
34
  if (event.key === "Enter") {
29
35
  event.preventDefault();
30
- onStart();
36
+ onStartRef.current();
31
37
  }
32
- },
33
- [onStart]
34
- );
35
- useEffect(() => {
36
- document.addEventListener("keydown", handleKeyDown);
38
+ };
39
+ document.addEventListener("keydown", onDocumentKeyDown);
37
40
  return () => {
38
- document.removeEventListener("keydown", handleKeyDown);
41
+ document.removeEventListener("keydown", onDocumentKeyDown);
39
42
  };
40
- }, [handleKeyDown]);
43
+ }, []);
41
44
  return /* @__PURE__ */ jsxs(Fragment, { children: [
42
45
  /* @__PURE__ */ jsxs("div", { className: "animate-in fade-in-0 duration-500 pb-24", children: [
43
46
  /* @__PURE__ */ jsxs("section", { className: "py-6", children: [
44
- /* @__PURE__ */ jsx("h2", { className: "text-2xl md:text-3xl font-semibold mb-6", children: title }),
47
+ /* @__PURE__ */ jsx(TitleHeading, { className: "text-2xl md:text-3xl font-semibold mb-6", children: title }),
45
48
  /* @__PURE__ */ jsx("div", { className: cn("max-w-none", "[&_h2:first-of-type]:hidden"), children: renderIntroContent() })
46
49
  ] }),
47
50
  /* @__PURE__ */ jsxs("section", { className: "mt-8 py-6 border-t border-border", children: [
48
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-4", children: mergedLabels.tableOfContentsLabel }),
51
+ /* @__PURE__ */ jsx(TocHeading, { className: "text-lg font-semibold mb-4", children: mergedLabels.tableOfContentsLabel }),
49
52
  /* @__PURE__ */ jsx("ol", { className: "space-y-2", children: sections.map((section, index) => {
50
53
  const isCompleted = !isLoading && completedSections.has(section.id);
51
54
  return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
@@ -319,18 +319,18 @@ const ConversationLoading = forwardRef(({ className }, reference) => {
319
319
  /* @__PURE__ */ jsx(
320
320
  "span",
321
321
  {
322
- className: "size-2 animate-bounce rounded-full bg-muted-foreground",
322
+ className: "size-2 animate-pulse rounded-full bg-muted-foreground",
323
323
  style: { animationDelay: "-0.3s" }
324
324
  }
325
325
  ),
326
326
  /* @__PURE__ */ jsx(
327
327
  "span",
328
328
  {
329
- className: "size-2 animate-bounce rounded-full bg-muted-foreground",
329
+ className: "size-2 animate-pulse rounded-full bg-muted-foreground",
330
330
  style: { animationDelay: "-0.15s" }
331
331
  }
332
332
  ),
333
- /* @__PURE__ */ jsx("span", { className: "size-2 animate-bounce rounded-full bg-muted-foreground" })
333
+ /* @__PURE__ */ jsx("span", { className: "size-2 animate-pulse rounded-full bg-muted-foreground" })
334
334
  ]
335
335
  }
336
336
  );
@@ -25,11 +25,6 @@ const DatePicker = React.forwardRef(
25
25
  value
26
26
  );
27
27
  const selectedDate = value ?? internalValue;
28
- React.useEffect(() => {
29
- if (value !== void 0) {
30
- setInternalValue(value);
31
- }
32
- }, [value]);
33
28
  const handleSelect = (nextDate) => {
34
29
  if (value === void 0) {
35
30
  setInternalValue(nextDate);