@vllnt/ui 0.2.1-canary.0aaaad2 → 0.2.1-canary.2994ef2

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 (75) hide show
  1. package/CHANGELOG.md +2 -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/auto-reload/auto-reload.js +1 -1
  6. package/dist/components/blog-card/blog-card.js +3 -1
  7. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +2 -2
  8. package/dist/components/candlestick-chart/candlestick-chart.js +2 -1
  9. package/dist/components/carousel/carousel.js +14 -6
  10. package/dist/components/checklist/checklist.js +35 -7
  11. package/dist/components/checklist/index.js +8 -2
  12. package/dist/components/chronological-timeline/chronological-timeline.js +2 -2
  13. package/dist/components/code-playground/code-playground.js +2 -1
  14. package/dist/components/comment-pin/comment-pin.js +2 -2
  15. package/dist/components/comparison/comparison.js +2 -1
  16. package/dist/components/completion-dialog/completion-dialog.js +18 -10
  17. package/dist/components/content-intro/content-intro.js +17 -14
  18. package/dist/components/conversation-thread/conversation-thread.js +3 -3
  19. package/dist/components/exercise/exercise.js +10 -2
  20. package/dist/components/faq/faq.js +6 -2
  21. package/dist/components/filter-bar/filter-bar.js +6 -6
  22. package/dist/components/floating-toolbar/floating-toolbar.js +2 -2
  23. package/dist/components/gantt-chart/gantt-chart.js +8 -7
  24. package/dist/components/globe-3d/globe-3d.js +16 -4
  25. package/dist/components/horizontal-scroll-row/horizontal-scroll-row.js +2 -1
  26. package/dist/components/interactive-timeline/interactive-timeline.js +8 -5
  27. package/dist/components/jarvis-dock/jarvis-dock.js +2 -2
  28. package/dist/components/key-concept/key-concept.js +2 -1
  29. package/dist/components/keyboard-shortcuts-help/keyboard-shortcuts-help.js +2 -1
  30. package/dist/components/knowledge-check/knowledge-check.js +2 -2
  31. package/dist/components/learning-objectives/learning-objectives.js +2 -1
  32. package/dist/components/live-feed/live-feed.js +1 -1
  33. package/dist/components/map-2d/map-2d.js +1 -1
  34. package/dist/components/map-timeline/map-timeline.js +5 -5
  35. package/dist/components/market-treemap/market-treemap.js +2 -2
  36. package/dist/components/mdx-content/mdx-content.js +6 -6
  37. package/dist/components/navbar-saas/navbar-saas.js +1 -0
  38. package/dist/components/number-ticker/number-ticker.js +1 -1
  39. package/dist/components/order-book/order-book.js +2 -2
  40. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +2 -2
  41. package/dist/components/profile-section/profile-section.js +2 -1
  42. package/dist/components/progress-tracker/progress-tracker.js +12 -6
  43. package/dist/components/prompt-templates/prompt-templates.js +2 -2
  44. package/dist/components/quiz/quiz.js +3 -2
  45. package/dist/components/relationship-inspector/relationship-inspector.js +2 -2
  46. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +2 -2
  47. package/dist/components/run-timeline/run-timeline.js +2 -2
  48. package/dist/components/share-dialog/share-dialog.js +2 -2
  49. package/dist/components/share-section/share-section.js +2 -1
  50. package/dist/components/sidebar/sidebar.js +19 -18
  51. package/dist/components/slideshow/slideshow.js +2 -1
  52. package/dist/components/social-fab/social-fab.js +10 -10
  53. package/dist/components/spinner/spinner.js +1 -1
  54. package/dist/components/stat-card/stat-card.js +1 -1
  55. package/dist/components/status-board/status-board.js +12 -5
  56. package/dist/components/step-by-step/step-by-step.js +2 -1
  57. package/dist/components/table-of-contents/table-of-contents.js +5 -2
  58. package/dist/components/table-of-contents-panel/table-of-contents-panel.js +3 -1
  59. package/dist/components/tags-input/tags-input.js +11 -3
  60. package/dist/components/terminal/terminal.js +10 -2
  61. package/dist/components/theme-toggle/theme-toggle.js +2 -2
  62. package/dist/components/thinking-block/thinking-block.js +2 -2
  63. package/dist/components/timeline-scrubber/timeline-scrubber.js +2 -2
  64. package/dist/components/tldr-section/tldr-section.js +9 -7
  65. package/dist/components/transaction-list/transaction-list.js +2 -2
  66. package/dist/components/tutorial-complete/tutorial-complete.js +7 -5
  67. package/dist/components/tutorial-filters/tutorial-filters.js +1 -1
  68. package/dist/components/tutorial-intro-content/tutorial-intro-content.js +5 -5
  69. package/dist/components/tutorial-mdx/tutorial-mdx.js +5 -5
  70. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +2 -2
  71. package/dist/components/watchlist/watchlist.js +2 -1
  72. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +2 -2
  73. package/dist/components/world-clock-bar/world-clock-bar.js +4 -3
  74. package/dist/index.d.ts +95 -24
  75. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { memo, useCallback, useState } from "react";
3
+ import { memo, useCallback, useTransition } from "react";
4
4
  import { cn } from "../../lib/utils";
5
5
  import { Badge } from "../badge";
6
6
  function SearchInput({
@@ -160,15 +160,15 @@ function FilterBarImpl({
160
160
  searchQuery,
161
161
  tags
162
162
  }) {
163
- const [isPending, setIsPending] = useState(false);
163
+ const [isPending, startTransition] = useTransition();
164
164
  const mergedLabels = { ...DEFAULT_LABELS, ...labels };
165
165
  const handleDifficultyChange = useCallback(
166
166
  (difficulty) => {
167
- setIsPending(true);
168
- onFiltersChange({ difficulty });
169
- setIsPending(false);
167
+ startTransition(() => {
168
+ onFiltersChange({ difficulty });
169
+ });
170
170
  },
171
- [onFiltersChange]
171
+ [onFiltersChange, startTransition]
172
172
  );
173
173
  const handleSearchChange = useCallback(
174
174
  (search) => {
@@ -31,7 +31,7 @@ const FloatingToolbar = forwardRef(
31
31
  ...rest,
32
32
  children: actions.map((action) => {
33
33
  const variant = action.variant ?? "ghost";
34
- const handleClick = () => {
34
+ const handleActivateToolbarAction = () => {
35
35
  action.onActivate();
36
36
  };
37
37
  return /* @__PURE__ */ jsxs(
@@ -46,7 +46,7 @@ const FloatingToolbar = forwardRef(
46
46
  "data-action-id": action.id,
47
47
  "data-variant": variant,
48
48
  disabled: action.disabled,
49
- onClick: handleClick,
49
+ onClick: handleActivateToolbarAction,
50
50
  type: "button",
51
51
  children: [
52
52
  action.glyph ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex size-3", children: action.glyph }) : null,
@@ -56,7 +56,7 @@ function getTickDateTimeFormatter(locale, scale) {
56
56
  let formatter = TICK_FORMATTER_CACHE.get(key);
57
57
  if (!formatter) {
58
58
  const options = scale === "month" ? { month: "short", year: "numeric" } : { day: "2-digit", month: "short" };
59
- formatter = new Intl.DateTimeFormat(locale, options);
59
+ formatter = Intl.DateTimeFormat(locale, options);
60
60
  TICK_FORMATTER_CACHE.set(key, formatter);
61
61
  }
62
62
  return formatter;
@@ -93,13 +93,14 @@ function buildTicks(input) {
93
93
  const formatter = buildTickFormatter(scale, locale);
94
94
  const stepDays = getTickStep(scale);
95
95
  const tickCount = Math.floor(totalDays / stepDays);
96
- return Array.from({ length: tickCount + 1 }).map((_, index) => {
96
+ return Array.from({ length: tickCount + 1 }).reduce((ticks, _, index) => {
97
97
  const day = index * stepDays;
98
- return {
99
- date: new Date(start.getTime() + day * MS_PER_DAY),
100
- offset: day
101
- };
102
- }).filter((tick) => tick.date.getTime() <= end.getTime()).map((tick) => ({ label: formatter(tick.date), offset: tick.offset }));
98
+ const date = new Date(start.getTime() + day * MS_PER_DAY);
99
+ if (date.getTime() <= end.getTime()) {
100
+ ticks.push({ label: formatter(date), offset: day });
101
+ }
102
+ return ticks;
103
+ }, []);
103
104
  }
104
105
  function useChartGeometry(options) {
105
106
  const { endDate, locale, scale, startDate } = options;
@@ -161,9 +161,12 @@ const GlobeArc = forwardRef(
161
161
  GlobeArc.displayName = "GlobeArc";
162
162
  function buildLine(arguments_) {
163
163
  const { points, rotationLat, rotationLng } = arguments_;
164
- return points.map((coord) => project(coord, rotationLng, rotationLat)).reduce(
165
- (state, projected) => {
166
- if (!projected.visible) return { path: state.path, pen: "up" };
164
+ return points.reduce(
165
+ (state, coord) => {
166
+ const projected = project(coord, rotationLng, rotationLat);
167
+ if (!projected.visible) {
168
+ return { path: state.path, pen: "up" };
169
+ }
167
170
  const head = state.pen === "up" ? "M" : "L";
168
171
  const separator = state.path.length > 0 ? " " : "";
169
172
  return {
@@ -185,7 +188,16 @@ function Graticule({ rotationLat, rotationLng }) {
185
188
  const meridians = range(-150, 180, 30).map(
186
189
  (lng) => range(-85, 85, 5).map((lat) => ({ lat, lng }))
187
190
  );
188
- const lines = [...parallels, ...meridians].map((points) => buildLine({ points, rotationLat, rotationLng })).filter((path) => path.length > 0);
191
+ const lines = [...parallels, ...meridians].reduce(
192
+ (paths, points) => {
193
+ const path = buildLine({ points, rotationLat, rotationLng });
194
+ if (path.length > 0) {
195
+ paths.push(path);
196
+ }
197
+ return paths;
198
+ },
199
+ []
200
+ );
189
201
  return /* @__PURE__ */ jsx(
190
202
  "g",
191
203
  {
@@ -6,6 +6,7 @@ import { useHorizontalScroll } from "../../lib/use-horizontal-scroll";
6
6
  import { cn } from "../../lib/utils";
7
7
  import { Button } from "../button/button";
8
8
  const HorizontalScrollRow = memo(function HorizontalScrollRow2({
9
+ as: Heading = "h3",
9
10
  children,
10
11
  className,
11
12
  description,
@@ -16,7 +17,7 @@ const HorizontalScrollRow = memo(function HorizontalScrollRow2({
16
17
  return /* @__PURE__ */ jsxs("section", { className: cn("space-y-4", className), children: [
17
18
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
18
19
  /* @__PURE__ */ jsxs("div", { children: [
19
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: title }),
20
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold", children: title }),
20
21
  description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
21
22
  ] }),
22
23
  showControls ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
@@ -219,7 +219,7 @@ function EventNode({
219
219
  if (!visible) return null;
220
220
  const titleText = typeof event.title === "string" ? event.title : "Event";
221
221
  const tooltipId = `${event.id}-tooltip`;
222
- const handleClick = (mouseEvent) => {
222
+ const handleSelectTimelineEvent = (mouseEvent) => {
223
223
  mouseEvent.stopPropagation();
224
224
  onSelect(event);
225
225
  };
@@ -236,7 +236,7 @@ function EventNode({
236
236
  "data-event-id": event.id,
237
237
  "data-event-track": event.track ?? "",
238
238
  "data-selected": active ? "true" : void 0,
239
- onClick: handleClick,
239
+ onClick: handleSelectTimelineEvent,
240
240
  style: {
241
241
  left: `${(left * 100).toString()}%`,
242
242
  width: isDuration ? `${(width * 100).toString()}%` : void 0
@@ -607,9 +607,12 @@ function useTimelineContextValue(arguments_) {
607
607
  function useTimelineFilter(categories, events) {
608
608
  const [hidden, setHidden] = useState(() => /* @__PURE__ */ new Set());
609
609
  const visibleCategories = useMemo(
610
- () => new Set(
611
- categories.filter((category) => !hidden.has(category.id)).map((category) => category.id)
612
- ),
610
+ () => categories.reduce((visible, category) => {
611
+ if (!hidden.has(category.id)) {
612
+ visible.add(category.id);
613
+ }
614
+ return visible;
615
+ }, /* @__PURE__ */ new Set()),
613
616
  [categories, hidden]
614
617
  );
615
618
  const toggleCategory = useCallback((id) => {
@@ -17,7 +17,7 @@ const DEFAULT_LABELS = {
17
17
  const ActionButton = (props) => {
18
18
  const { action } = props;
19
19
  const tone = action.tone ?? "neutral";
20
- const handleClick = () => {
20
+ const handleActivateAction = () => {
21
21
  action.onActivate();
22
22
  };
23
23
  return /* @__PURE__ */ jsxs(
@@ -26,7 +26,7 @@ const ActionButton = (props) => {
26
26
  className: "group relative flex size-12 flex-col items-center justify-center gap-0.5 rounded-md border border-transparent text-[10px] uppercase tracking-wide text-muted-foreground transition-colors hover:border-border hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
27
27
  "data-jarvis-action": action.id,
28
28
  "data-jarvis-tone": tone,
29
- onClick: handleClick,
29
+ onClick: handleActivateAction,
30
30
  type: "button",
31
31
  children: [
32
32
  /* @__PURE__ */ jsx(
@@ -43,13 +43,14 @@ function KeyConcept({
43
43
  );
44
44
  }
45
45
  function Glossary({
46
+ as: Heading = "h4",
46
47
  children,
47
48
  className,
48
49
  icon,
49
50
  title = "Key Terms"
50
51
  }) {
51
52
  return /* @__PURE__ */ jsxs("div", { className: cn("my-6", className), children: [
52
- /* @__PURE__ */ jsxs("h4", { className: "font-semibold mb-3 flex items-center gap-2", children: [
53
+ /* @__PURE__ */ jsxs(Heading, { className: "font-semibold mb-3 flex items-center gap-2", children: [
53
54
  icon ? /* @__PURE__ */ jsx("span", { className: "size-4", children: icon }) : /* @__PURE__ */ jsx(
54
55
  "svg",
55
56
  {
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { memo, useEffect, useRef } from "react";
4
4
  import { cn } from "../../lib/utils";
5
5
  function KeyboardShortcutsHelpImpl({
6
+ as: Heading = "h2",
6
7
  className,
7
8
  closeIcon,
8
9
  footer,
@@ -58,7 +59,7 @@ function KeyboardShortcutsHelpImpl({
58
59
  ),
59
60
  children: [
60
61
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
61
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", id: "shortcuts-title", children: title }),
62
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold", id: "shortcuts-title", children: title }),
62
63
  /* @__PURE__ */ jsx(
63
64
  "button",
64
65
  {
@@ -238,13 +238,13 @@ function FillBlankField({
238
238
  onChange,
239
239
  value
240
240
  }) {
241
- const handleChange = useCallback(
241
+ const handleBlankValueChange = useCallback(
242
242
  (event) => {
243
243
  onChange(event.target.value);
244
244
  },
245
245
  [onChange]
246
246
  );
247
- return /* @__PURE__ */ jsx(Input, { id: inputId, onChange: handleChange, value });
247
+ return /* @__PURE__ */ jsx(Input, { id: inputId, onChange: handleBlankValueChange, value });
248
248
  }
249
249
  function QuestionField({
250
250
  groupName,
@@ -2,6 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { CheckCircle2, Clock, GraduationCap, Target } from "lucide-react";
4
4
  function LearningObjectives({
5
+ as: Heading = "h4",
5
6
  estimatedTime,
6
7
  objectives,
7
8
  title = "What you'll learn"
@@ -10,7 +11,7 @@ function LearningObjectives({
10
11
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
11
12
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
12
13
  /* @__PURE__ */ jsx(Target, { className: "size-5 text-primary" }),
13
- /* @__PURE__ */ jsx("h4", { className: "font-semibold text-foreground", children: title })
14
+ /* @__PURE__ */ jsx(Heading, { className: "font-semibold text-foreground", children: title })
14
15
  ] }),
15
16
  estimatedTime ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
16
17
  /* @__PURE__ */ jsx(Clock, { className: "size-4" }),
@@ -131,7 +131,7 @@ const LiveFeed = React.forwardRef(
131
131
  [events, maxItems]
132
132
  );
133
133
  return /* @__PURE__ */ jsxs(Card, { className: cn("shadow-sm", className), ref, ...props, children: [
134
- /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3 space-y-0 pb-3", children: [
134
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3 gap-y-0 pb-3", children: [
135
135
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
136
136
  /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: title }),
137
137
  description ? /* @__PURE__ */ jsx(CardDescription, { children: description }) : null
@@ -53,7 +53,7 @@ function useMapState(arguments_) {
53
53
  }, [center]);
54
54
  const [pan, setPan] = useState(initialPan);
55
55
  const [zoom, setZoom] = useState(
56
- clamp(initialZoom, MIN_ZOOM, MAX_ZOOM)
56
+ () => clamp(initialZoom, MIN_ZOOM, MAX_ZOOM)
57
57
  );
58
58
  const zoomIn = useCallback(() => {
59
59
  setZoom((current) => clamp(current * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM));
@@ -214,7 +214,7 @@ MapTimelineControls.displayName = "MapTimelineControls";
214
214
  const MapTimelineSlider = forwardRef(({ className, ...rest }, ref) => {
215
215
  const { endYear, labels, setYear, startYear, year } = useTimelineContext();
216
216
  const sliderId = useId();
217
- const handleChange = (event) => {
217
+ const handleYearChange = (event) => {
218
218
  setYear(Number.parseInt(event.target.value, 10));
219
219
  };
220
220
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2 text-xs font-medium text-muted-foreground", children: [
@@ -230,7 +230,7 @@ const MapTimelineSlider = forwardRef(({ className, ...rest }, ref) => {
230
230
  id: sliderId,
231
231
  max: endYear,
232
232
  min: startYear,
233
- onChange: handleChange,
233
+ onChange: handleYearChange,
234
234
  ref,
235
235
  type: "range",
236
236
  value: year,
@@ -251,7 +251,7 @@ const MapTimelineSlider = forwardRef(({ className, ...rest }, ref) => {
251
251
  MapTimelineSlider.displayName = "MapTimelineSlider";
252
252
  const MapTimelinePlayButton = forwardRef(({ className, ...rest }, ref) => {
253
253
  const { isPlaying, labels, setIsPlaying } = useTimelineContext();
254
- const handleClick = () => {
254
+ const handleTogglePlayback = () => {
255
255
  setIsPlaying(!isPlaying);
256
256
  };
257
257
  return /* @__PURE__ */ jsx(
@@ -264,7 +264,7 @@ const MapTimelinePlayButton = forwardRef(({ className, ...rest }, ref) => {
264
264
  className
265
265
  ),
266
266
  "data-playing": isPlaying ? "true" : void 0,
267
- onClick: handleClick,
267
+ onClick: handleTogglePlayback,
268
268
  ref,
269
269
  type: "button",
270
270
  ...rest,
@@ -301,7 +301,7 @@ function useTimelineCtx(arguments_) {
301
301
  function useTimelineState(arguments_) {
302
302
  const { endYear, initialYear, onYearChange, startYear } = arguments_;
303
303
  const [year, setYear] = useState(
304
- clamp(initialYear ?? startYear, startYear, endYear)
304
+ () => clamp(initialYear ?? startYear, startYear, endYear)
305
305
  );
306
306
  const [isPlaying, setIsPlaying] = useState(false);
307
307
  const updateYear = useCallback(
@@ -67,7 +67,7 @@ function MarketTreemapTile({
67
67
  }
68
68
  );
69
69
  }
70
- const MarketTreemap = React.forwardRef(({ className, items, ...props }, reference) => {
70
+ const MarketTreemap = React.forwardRef(({ as: Heading = "h2", className, items, ...props }, reference) => {
71
71
  if (items.length === 0) {
72
72
  return null;
73
73
  }
@@ -85,7 +85,7 @@ const MarketTreemap = React.forwardRef(({ className, items, ...props }, referenc
85
85
  /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-3", children: [
86
86
  /* @__PURE__ */ jsxs("div", { children: [
87
87
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground", children: "Sector heatmap" }),
88
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Market treemap" })
88
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold text-foreground", children: "Market treemap" })
89
89
  ] }),
90
90
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Tile size maps market cap proxy; color reflects session change." })
91
91
  ] }),
@@ -16,7 +16,7 @@ const MDXComponents = {
16
16
  blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx(
17
17
  "blockquote",
18
18
  {
19
- className: "border-l-4 border-primary pl-4 italic text-muted-foreground my-6 py-2 text-sm",
19
+ className: "border-l border-primary pl-4 italic text-muted-foreground my-6 py-2 text-sm",
20
20
  ...props,
21
21
  children
22
22
  }
@@ -37,9 +37,9 @@ const MDXComponents = {
37
37
  );
38
38
  },
39
39
  em: ({ children, ...props }) => /* @__PURE__ */ jsx("em", { className: "italic", ...props, children }),
40
- h1: ({ children, ...props }) => /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold mt-8 mb-4", ...props, children }),
41
- h2: ({ children, ...props }) => /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold mt-6 mb-3", ...props, children }),
42
- h3: ({ children, ...props }) => /* @__PURE__ */ jsx("h3", { className: "text-lg font-bold mt-4 mb-2", ...props, children }),
40
+ h1: ({ children, ...props }) => /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold mt-8 mb-4", ...props, children }),
41
+ h2: ({ children, ...props }) => /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold mt-6 mb-3", ...props, children }),
42
+ h3: ({ children, ...props }) => /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mt-4 mb-2", ...props, children }),
43
43
  hr: ({ ...props }) => /* @__PURE__ */ jsx("hr", { className: "my-8 border-border", ...props }),
44
44
  li: ({ children, ...props }) => /* @__PURE__ */ jsx(
45
45
  "li",
@@ -89,8 +89,8 @@ const proseClasses = [
89
89
  "prose-strong:font-semibold prose-em:italic",
90
90
  "prose-a:text-primary prose-a:underline prose-a:underline-offset-4 hover:prose-a:text-primary/80",
91
91
  "prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-code:font-mono",
92
- "prose-pre:my-6 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:bg-black prose-pre:py-4 prose-pre:font-mono prose-pre:text-sm prose-pre:text-white prose-pre:shadow-lg dark:prose-pre:bg-zinc-900",
93
- "prose-blockquote:border-l-4 prose-blockquote:border-primary prose-blockquote:pl-4 prose-blockquote:italic prose-blockquote:text-muted-foreground prose-blockquote:my-6 prose-blockquote:py-2",
92
+ "prose-pre:my-6 prose-pre:overflow-x-auto prose-pre:rounded-lg prose-pre:border prose-pre:bg-zinc-950 prose-pre:py-4 prose-pre:font-mono prose-pre:text-sm prose-pre:text-white prose-pre:shadow-lg dark:prose-pre:bg-zinc-900",
93
+ "prose-blockquote:border-l prose-blockquote:border-primary prose-blockquote:pl-4 prose-blockquote:italic prose-blockquote:text-muted-foreground prose-blockquote:my-6 prose-blockquote:py-2",
94
94
  "prose-hr:my-8 prose-hr:border-border",
95
95
  "prose-table:w-full prose-table:border-collapse prose-table:border prose-table:border-border",
96
96
  "prose-th:border prose-th:border-border prose-th:bg-muted prose-th:p-2 prose-th:text-left prose-th:font-medium",
@@ -24,6 +24,7 @@ function NavbarSaas({
24
24
  Button,
25
25
  {
26
26
  className: "lg:hidden",
27
+ "data-testid": "navbar-saas-mobile-trigger",
27
28
  onClick: () => {
28
29
  setOpen(!open);
29
30
  },
@@ -7,7 +7,7 @@ function getNumberTickerFormatter(locale, formatOptions) {
7
7
  const key = `${locale ?? ""}|${formatOptions ? JSON.stringify(formatOptions) : ""}`;
8
8
  let formatter = NUMBER_FORMATTER_CACHE.get(key);
9
9
  if (!formatter) {
10
- formatter = new Intl.NumberFormat(locale, formatOptions);
10
+ formatter = Intl.NumberFormat(locale, formatOptions);
11
11
  NUMBER_FORMATTER_CACHE.set(key, formatter);
12
12
  }
13
13
  return formatter;
@@ -68,7 +68,7 @@ function BookSide({
68
68
  ] });
69
69
  }
70
70
  const OrderBook = React.forwardRef(
71
- ({ asks, bids, className, precision = 2, ...props }, reference) => {
71
+ ({ as: Heading = "h2", asks, bids, className, precision = 2, ...props }, reference) => {
72
72
  if (asks.length === 0 && bids.length === 0) {
73
73
  return null;
74
74
  }
@@ -90,7 +90,7 @@ const OrderBook = React.forwardRef(
90
90
  /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-wrap items-start justify-between gap-3", children: [
91
91
  /* @__PURE__ */ jsxs("div", { children: [
92
92
  /* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground", children: "Level II" }),
93
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Order book" })
93
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold text-foreground", children: "Order book" })
94
94
  ] }),
95
95
  /* @__PURE__ */ jsxs("div", { className: "rounded-full border border-border bg-background/70 px-3 py-1 text-sm text-muted-foreground tabular-nums", children: [
96
96
  "Spread ",
@@ -40,7 +40,7 @@ const RowBody = (props) => {
40
40
  const Row = (props) => {
41
41
  const { policy } = props;
42
42
  if (policy.onToggle) {
43
- const handleClick = () => {
43
+ const handleTogglePolicy = () => {
44
44
  policy.onToggle?.();
45
45
  };
46
46
  return /* @__PURE__ */ jsx(
@@ -49,7 +49,7 @@ const Row = (props) => {
49
49
  className: "flex w-full items-center gap-2 rounded-md border border-transparent px-2 py-1.5 text-left transition-colors hover:border-border hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
50
50
  "data-policy-row": policy.id,
51
51
  "data-policy-status": policy.status,
52
- onClick: handleClick,
52
+ onClick: handleTogglePolicy,
53
53
  type: "button",
54
54
  children: /* @__PURE__ */ jsx(RowBody, { policy })
55
55
  }
@@ -3,6 +3,7 @@ import Image from "next/image";
3
3
  import Link from "next/link";
4
4
  import { Button } from "../button/button";
5
5
  function ProfileSection({
6
+ as: Heading = "h3",
6
7
  compact = false,
7
8
  dict,
8
9
  imageAlt,
@@ -25,7 +26,7 @@ function ProfileSection({
25
26
  }
26
27
  ) }) : null,
27
28
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
28
- /* @__PURE__ */ jsx("h3", { className: `font-semibold ${compact ? "text-lg" : "text-xl"}`, children: dict.profile.name }),
29
+ /* @__PURE__ */ jsx(Heading, { className: `font-semibold ${compact ? "text-lg" : "text-xl"}`, children: dict.profile.name }),
29
30
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: compact ? dict.profile.tagline : `> ${dict.profile.tagline}` })
30
31
  ] }),
31
32
  showSocialLinks ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center items-center gap-2", children: socialLinks?.map((link) => /* @__PURE__ */ jsx(
@@ -10,7 +10,10 @@ import {
10
10
  CardHeader,
11
11
  CardTitle
12
12
  } from "../card";
13
- import { CHECKLIST_PROGRESS_EVENT } from "../checklist";
13
+ import {
14
+ CHECKLIST_PROGRESS_EVENT,
15
+ parseChecklistStorageValue
16
+ } from "../checklist";
14
17
  import { ProgressBar } from "../progress-bar";
15
18
  const ProgressTrackerContext = React.createContext(null);
16
19
  function clampPercentage(value) {
@@ -41,15 +44,13 @@ function readPersistedChecklistItems(persistKey) {
41
44
  try {
42
45
  const saved = localStorage.getItem(`checklist:${persistKey}`);
43
46
  if (!saved) return [];
44
- const parsed = JSON.parse(saved);
45
- return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
47
+ return parseChecklistStorageValue(saved);
46
48
  } catch {
47
49
  return [];
48
50
  }
49
51
  }
50
52
  function areStringArraysEqual(left, right) {
51
- if (left.length !== right.length) return false;
52
- return left.every((value, index) => value === right[index]);
53
+ return left.length === right.length && left.every((value, index) => value === right[index]);
53
54
  }
54
55
  function getChecklistPersistKey(event) {
55
56
  if (!(event instanceof CustomEvent)) return null;
@@ -162,7 +163,12 @@ function ProgressTrackerOverview({
162
163
  }) {
163
164
  const { modules, overallProgress, streak, title } = useProgressTrackerContext();
164
165
  const trackedPersistKeys = React.useMemo(
165
- () => modules.map((module) => module.persistKey).filter(Boolean),
166
+ () => modules.reduce((keys, module) => {
167
+ if (module.persistKey) {
168
+ keys.push(module.persistKey);
169
+ }
170
+ return keys;
171
+ }, []),
166
172
  [modules]
167
173
  );
168
174
  const [, forceChecklistRefresh] = React.useState(0);
@@ -243,7 +243,7 @@ function VariableField({
243
243
  onValueChange,
244
244
  value
245
245
  }) {
246
- const handleChange = useCallback(
246
+ const handleVariableValueChange = useCallback(
247
247
  (event) => {
248
248
  onValueChange(name, event.target.value);
249
249
  },
@@ -255,7 +255,7 @@ function VariableField({
255
255
  Input,
256
256
  {
257
257
  id: fieldId,
258
- onChange: handleChange,
258
+ onChange: handleVariableValueChange,
259
259
  placeholder: `Value for ${name}`,
260
260
  value
261
261
  }
@@ -118,6 +118,7 @@ function QuizResult({
118
118
  );
119
119
  }
120
120
  function Quiz({
121
+ as: Heading = "h4",
121
122
  className,
122
123
  explanation,
123
124
  hint,
@@ -159,7 +160,7 @@ function Quiz({
159
160
  )
160
161
  }
161
162
  ),
162
- /* @__PURE__ */ jsx("h4", { className: "font-semibold text-foreground", children: question })
163
+ /* @__PURE__ */ jsx(Heading, { className: "font-semibold text-foreground", children: question })
163
164
  ] }),
164
165
  /* @__PURE__ */ jsx("div", { className: "space-y-2 mb-4", children: options.map((opt, index) => /* @__PURE__ */ jsx(
165
166
  QuizOptionButton,
@@ -172,7 +173,7 @@ function Quiz({
172
173
  selectedIndex,
173
174
  submitted
174
175
  },
175
- typeof opt === "string" ? opt : `quiz-option-${String(opt)}`
176
+ `quiz-option-${index}-${opt.label}`
176
177
  )) }),
177
178
  hint && !submitted ? /* @__PURE__ */ jsx(
178
179
  QuizHint,
@@ -28,7 +28,7 @@ const RowBody = (props) => {
28
28
  const Row = (props) => {
29
29
  const { edge } = props;
30
30
  if (edge.onActivate) {
31
- const handleClick = () => {
31
+ const handleActivateRelationship = () => {
32
32
  edge.onActivate?.();
33
33
  };
34
34
  return /* @__PURE__ */ jsx(
@@ -37,7 +37,7 @@ const Row = (props) => {
37
37
  className: "flex w-full items-center gap-2 rounded-md border border-transparent px-2 py-1.5 text-left transition-colors hover:border-border hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
38
38
  "data-relationship-direction": edge.direction,
39
39
  "data-relationship-row": true,
40
- onClick: handleClick,
40
+ onClick: handleActivateRelationship,
41
41
  type: "button",
42
42
  children: /* @__PURE__ */ jsx(RowBody, { edge })
43
43
  }
@@ -63,7 +63,7 @@ const RowBody = (props) => {
63
63
  const Row = (props) => {
64
64
  const { assignment } = props;
65
65
  if (assignment.onActivate) {
66
- const handleClick = () => {
66
+ const handleActivateAssignment = () => {
67
67
  assignment.onActivate?.();
68
68
  };
69
69
  return /* @__PURE__ */ jsx(
@@ -72,7 +72,7 @@ const Row = (props) => {
72
72
  className: "flex w-full items-center gap-2 rounded-md border border-transparent px-2 py-1.5 text-left transition-colors hover:border-border hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
73
73
  "data-routing-assignment": assignment.id,
74
74
  "data-routing-role": assignment.role,
75
- onClick: handleClick,
75
+ onClick: handleActivateAssignment,
76
76
  type: "button",
77
77
  children: /* @__PURE__ */ jsx(RowBody, { assignment })
78
78
  }
@@ -63,7 +63,7 @@ const PhaseBar = (props) => {
63
63
  };
64
64
  const ariaLabel = `${STATE_LABEL[state]} ${phase.start} \u2192 ${phase.end}`;
65
65
  if (phase.onActivate) {
66
- const handleClick = () => {
66
+ const handleActivatePhase = () => {
67
67
  phase.onActivate?.();
68
68
  };
69
69
  return /* @__PURE__ */ jsx(
@@ -76,7 +76,7 @@ const PhaseBar = (props) => {
76
76
  ),
77
77
  "data-run-phase": phase.id,
78
78
  "data-run-phase-state": state,
79
- onClick: handleClick,
79
+ onClick: handleActivatePhase,
80
80
  style: sharedStyle,
81
81
  type: "button",
82
82
  children: phase.label
@@ -13,7 +13,7 @@ function PlatformButton({
13
13
  onClick,
14
14
  platform
15
15
  }) {
16
- const handleClick = () => {
16
+ const handleSelectPlatform = () => {
17
17
  onClick(platform.key);
18
18
  };
19
19
  return /* @__PURE__ */ jsx(
@@ -26,7 +26,7 @@ function PlatformButton({
26
26
  "hover:bg-accent hover:text-accent-foreground",
27
27
  "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
28
28
  ),
29
- onClick: handleClick,
29
+ onClick: handleSelectPlatform,
30
30
  type: "button",
31
31
  children: platform.label
32
32
  }
@@ -26,6 +26,7 @@ function buildShareUrl(platform, url, title) {
26
26
  }
27
27
  }
28
28
  function ShareSection({
29
+ as: Heading = "h3",
29
30
  buildUrl: buildUrlFunction = buildShareUrl,
30
31
  platforms = defaultPlatforms,
31
32
  shareOn,
@@ -34,7 +35,7 @@ function ShareSection({
34
35
  url
35
36
  }) {
36
37
  return /* @__PURE__ */ jsxs("div", { className: "border-t border-border pt-6 mt-8", children: [
37
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-4", children: shareTitle }),
38
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold mb-4", children: shareTitle }),
38
39
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3", children: platforms.map((platform) => /* @__PURE__ */ jsxs(
39
40
  "a",
40
41
  {