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

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 (112) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/dist/components/activity-heatmap/activity-heatmap.js +2 -1
  3. package/dist/components/activity-log/activity-log.js +1 -1
  4. package/dist/components/ai-artifact/ai-artifact.js +8 -8
  5. package/dist/components/ai-chat-input/ai-chat-input.js +1 -1
  6. package/dist/components/ai-message-bubble/ai-message-bubble.js +1 -1
  7. package/dist/components/ai-sidebar/ai-sidebar.js +4 -4
  8. package/dist/components/ai-tool-call-display/ai-tool-call-display.js +1 -1
  9. package/dist/components/animated-text/animated-text.js +6 -3
  10. package/dist/components/annotation/annotation.js +1 -1
  11. package/dist/components/auto-reload/auto-reload.js +1 -1
  12. package/dist/components/avatar-group/avatar-group.js +1 -1
  13. package/dist/components/blog-card/blog-card.js +4 -2
  14. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +2 -2
  15. package/dist/components/candlestick-chart/candlestick-chart.js +2 -1
  16. package/dist/components/canvas-shell/canvas-foundation-demo.js +1 -1
  17. package/dist/components/carousel/carousel.js +14 -6
  18. package/dist/components/category-filter/category-filter.js +1 -1
  19. package/dist/components/chat-dock-section/chat-dock-section.js +1 -1
  20. package/dist/components/checklist/checklist.js +35 -7
  21. package/dist/components/checklist/index.js +8 -2
  22. package/dist/components/chronological-timeline/chronological-timeline.js +2 -2
  23. package/dist/components/code-playground/code-playground.js +3 -2
  24. package/dist/components/combobox/combobox.js +1 -6
  25. package/dist/components/comment-pin/comment-pin.js +2 -2
  26. package/dist/components/comparison/comparison.js +2 -1
  27. package/dist/components/completion-dialog/completion-dialog.js +19 -11
  28. package/dist/components/connector-edge/connector-edge.js +1 -1
  29. package/dist/components/content-intro/content-intro.js +18 -15
  30. package/dist/components/conversation-thread/conversation-thread.js +4 -4
  31. package/dist/components/countdown-timer/countdown-timer.js +1 -1
  32. package/dist/components/data-table/data-table.js +1 -1
  33. package/dist/components/date-picker/date-picker.js +1 -6
  34. package/dist/components/exercise/exercise.js +11 -3
  35. package/dist/components/faq/faq.js +6 -2
  36. package/dist/components/file-upload/file-upload.js +1 -6
  37. package/dist/components/filter-bar/filter-bar.js +7 -7
  38. package/dist/components/flashcard/flashcard.js +1 -1
  39. package/dist/components/floating-toolbar/floating-toolbar.js +2 -2
  40. package/dist/components/form/form.js +1 -1
  41. package/dist/components/gantt-chart/gantt-chart.js +8 -7
  42. package/dist/components/globe-3d/globe-3d.js +16 -4
  43. package/dist/components/horizontal-scroll-row/horizontal-scroll-row.js +2 -1
  44. package/dist/components/inline-input/inline-input.js +1 -1
  45. package/dist/components/interactive-timeline/interactive-timeline.js +8 -5
  46. package/dist/components/jarvis-dock/jarvis-dock.js +2 -2
  47. package/dist/components/key-concept/key-concept.js +2 -1
  48. package/dist/components/keyboard-shortcuts-help/keyboard-shortcuts-help.js +2 -1
  49. package/dist/components/knowledge-check/knowledge-check.js +2 -2
  50. package/dist/components/lang-provider/lang-provider.js +3 -3
  51. package/dist/components/learning-objectives/learning-objectives.js +2 -1
  52. package/dist/components/live-feed/live-feed.js +2 -2
  53. package/dist/components/map-2d/map-2d.js +1 -1
  54. package/dist/components/map-timeline/map-timeline.js +5 -5
  55. package/dist/components/market-treemap/market-treemap.js +2 -2
  56. package/dist/components/mdx-content/mdx-content.js +17 -8
  57. package/dist/components/metric-gauge/metric-gauge.js +1 -1
  58. package/dist/components/model-selector/model-selector.js +1 -1
  59. package/dist/components/multi-select/multi-select.js +1 -1
  60. package/dist/components/navbar-saas/navbar-saas.js +5 -5
  61. package/dist/components/number-input/number-input.js +1 -1
  62. package/dist/components/number-ticker/number-ticker.js +1 -1
  63. package/dist/components/object-card/object-card.js +1 -1
  64. package/dist/components/order-book/order-book.js +2 -2
  65. package/dist/components/overview-board/overview-board.js +1 -1
  66. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +2 -2
  67. package/dist/components/profile-section/profile-section.js +2 -1
  68. package/dist/components/progress-card/progress-card.js +1 -1
  69. package/dist/components/progress-tracker/progress-tracker.js +17 -9
  70. package/dist/components/prompt-templates/prompt-templates.js +2 -2
  71. package/dist/components/quiz/quiz.js +3 -2
  72. package/dist/components/relationship-inspector/relationship-inspector.js +2 -2
  73. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +2 -2
  74. package/dist/components/run-timeline/run-timeline.js +2 -2
  75. package/dist/components/scope-selector/scope-selector.js +1 -1
  76. package/dist/components/search-dialog/search-dialog.js +484 -51
  77. package/dist/components/share-dialog/share-dialog.js +2 -2
  78. package/dist/components/share-section/share-section.js +2 -1
  79. package/dist/components/sidebar/sidebar.js +30 -25
  80. package/dist/components/sidebar-toggle/sidebar-toggle.js +1 -1
  81. package/dist/components/slideshow/slideshow.js +3 -2
  82. package/dist/components/social-fab/social-fab.js +10 -10
  83. package/dist/components/spinner/spinner.js +1 -1
  84. package/dist/components/spinner/unicode-spinner.js +4 -2
  85. package/dist/components/stat-card/stat-card.js +2 -2
  86. package/dist/components/status-board/status-board.js +13 -6
  87. package/dist/components/step-by-step/step-by-step.js +2 -1
  88. package/dist/components/table-of-contents/table-of-contents.js +5 -2
  89. package/dist/components/table-of-contents-panel/table-of-contents-panel.js +3 -1
  90. package/dist/components/tabs/tabs.js +26 -8
  91. package/dist/components/tags-input/tags-input.js +11 -3
  92. package/dist/components/terminal/terminal.js +11 -3
  93. package/dist/components/theme-toggle/theme-toggle.js +2 -2
  94. package/dist/components/thinking-block/thinking-block.js +2 -2
  95. package/dist/components/ticker-tape/ticker-tape.js +1 -1
  96. package/dist/components/timeline-scrubber/timeline-scrubber.js +2 -2
  97. package/dist/components/tldr-section/tldr-section.js +9 -7
  98. package/dist/components/tour/tour.js +1 -1
  99. package/dist/components/transaction-list/transaction-list.js +2 -2
  100. package/dist/components/tutorial-card/tutorial-card.js +1 -1
  101. package/dist/components/tutorial-complete/tutorial-complete.js +8 -6
  102. package/dist/components/tutorial-filters/tutorial-filters.js +2 -2
  103. package/dist/components/tutorial-intro-content/tutorial-intro-content.js +5 -5
  104. package/dist/components/tutorial-mdx/tutorial-mdx.js +6 -6
  105. package/dist/components/usage-breakdown/usage-breakdown.js +1 -1
  106. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +2 -2
  107. package/dist/components/watchlist/watchlist.js +2 -1
  108. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +2 -2
  109. package/dist/components/world-clock-bar/world-clock-bar.js +5 -4
  110. package/dist/components/zoom-hud/zoom-hud.js +1 -1
  111. package/dist/index.d.ts +113 -29
  112. package/package.json +3 -3
@@ -1,29 +1,29 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useEffect, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { ChevronDown } from "lucide-react";
5
5
  import Link from "next/link";
6
6
  import { usePathname } from "next/navigation";
7
+ import { useMounted } from "../../lib/use-mounted";
7
8
  import { cn } from "../../lib/utils";
8
- import { useSidebar } from "../sidebar-provider";
9
+ import { useSidebar } from "../sidebar-provider/sidebar-provider";
10
+ const getMobileSnapshot = () => typeof window === "undefined" ? false : window.innerWidth < 1024;
11
+ const getServerMobileSnapshot = () => false;
12
+ const subscribeToViewportResize = (onStoreChange) => {
13
+ window.addEventListener("resize", onStoreChange);
14
+ return () => {
15
+ window.removeEventListener("resize", onStoreChange);
16
+ };
17
+ };
9
18
  function useMobile(setOpen) {
10
- const [isMobile, setIsMobile] = useState(false);
19
+ const isMobile = useSyncExternalStore(
20
+ subscribeToViewportResize,
21
+ getMobileSnapshot,
22
+ getServerMobileSnapshot
23
+ );
11
24
  useEffect(() => {
12
- const checkMobile = () => {
13
- const mobile = window.innerWidth < 1024;
14
- setIsMobile(mobile);
15
- if (mobile) {
16
- setOpen(false);
17
- } else {
18
- setOpen(true);
19
- }
20
- };
21
- checkMobile();
22
- window.addEventListener("resize", checkMobile);
23
- return () => {
24
- window.removeEventListener("resize", checkMobile);
25
- };
26
- }, [setOpen]);
25
+ setOpen(!isMobile);
26
+ }, [isMobile, setOpen]);
27
27
  return isMobile;
28
28
  }
29
29
  function useScrollFade(containerReference) {
@@ -38,7 +38,7 @@ function useScrollFade(containerReference) {
38
38
  setShowBottomFade(scrollTop < scrollHeight - clientHeight - 1);
39
39
  };
40
40
  checkScroll();
41
- container.addEventListener("scroll", checkScroll);
41
+ container.addEventListener("scroll", checkScroll, { passive: true });
42
42
  return () => {
43
43
  container.removeEventListener("scroll", checkScroll);
44
44
  };
@@ -88,6 +88,7 @@ function CollapsibleSection({
88
88
  "grid transition-all duration-200 ease-in-out",
89
89
  isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
90
90
  ),
91
+ hidden: !isOpen,
91
92
  children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children })
92
93
  }
93
94
  )
@@ -97,15 +98,18 @@ function Sidebar({ sections }) {
97
98
  const pathname = usePathname();
98
99
  const { open, setOpen } = useSidebar();
99
100
  const isMobile = useMobile(setOpen);
101
+ const mounted = useMounted();
100
102
  const scrollContainerReference = useRef(null);
101
103
  const { showBottomFade, showTopFade } = useScrollFade(
102
104
  scrollContainerReference
103
105
  );
106
+ const collapsed = mounted && !isMobile && !open;
104
107
  return /* @__PURE__ */ jsxs(Fragment, { children: [
105
108
  isMobile && open ? /* @__PURE__ */ jsx(
106
109
  "div",
107
110
  {
108
111
  className: "fixed inset-0 bg-black/50 z-40 lg:hidden",
112
+ "data-testid": "sidebar-overlay",
109
113
  onClick: () => {
110
114
  setOpen(false);
111
115
  },
@@ -122,15 +126,16 @@ function Sidebar({ sections }) {
122
126
  "aside",
123
127
  {
124
128
  className: cn(
125
- "fixed lg:relative top-16 lg:top-0 bottom-0 lg:bottom-auto left-0 z-40 lg:h-full border-r bg-background transition-transform duration-300 ease-in-out",
129
+ "fixed lg:relative top-16 lg:top-0 bottom-0 lg:bottom-auto left-0 z-40 lg:h-full bg-background transition-[transform,width] duration-300 ease-in-out",
126
130
  "flex flex-col",
127
131
  "overflow-hidden",
128
132
  "shrink-0",
129
- isMobile ? "w-full" : "w-64",
130
- isMobile && !open && "-translate-x-full",
133
+ collapsed ? "border-r-0" : "border-r",
134
+ collapsed ? "w-0" : isMobile ? "w-full" : "w-64",
131
135
  isMobile && open && "translate-x-0",
132
- !isMobile && !open && "-translate-x-full lg:translate-x-0",
133
- !isMobile && "lg:translate-x-0"
136
+ isMobile && !open && "-translate-x-full",
137
+ !isMobile && !collapsed && "-translate-x-full lg:translate-x-0",
138
+ !isMobile && collapsed && "-translate-x-full"
134
139
  ),
135
140
  children: /* @__PURE__ */ jsxs("div", { className: "relative flex-1 overflow-hidden", children: [
136
141
  showTopFade ? /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 h-8 bg-gradient-to-b from-background to-transparent pointer-events-none z-20" }) : null,
@@ -138,7 +143,7 @@ function Sidebar({ sections }) {
138
143
  /* @__PURE__ */ jsx(
139
144
  "nav",
140
145
  {
141
- className: "flex-1 p-4 overflow-y-auto h-full",
146
+ className: "flex-1 p-4 overflow-y-auto overscroll-contain h-full [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
142
147
  ref: scrollContainerReference,
143
148
  children: /* @__PURE__ */ jsx("div", { className: "space-y-4", children: sections.map((section, sectionIndex) => {
144
149
  const sectionItems = /* @__PURE__ */ jsx("div", { className: section.title ? "space-y-0.5" : "space-y-1", children: section.items.map((item) => /* @__PURE__ */ jsx(
@@ -2,7 +2,7 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Menu, X } from "lucide-react";
4
4
  import { cn } from "../../lib/utils";
5
- import { Button } from "../button";
5
+ import { Button } from "../button/button";
6
6
  function SidebarToggle({
7
7
  className,
8
8
  onToggle,
@@ -4,7 +4,7 @@ import { memo, useCallback, useEffect, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import { useMounted } from "../../lib/use-mounted";
6
6
  import { cn } from "../../lib/utils";
7
- import { CompletionDialog } from "../completion-dialog";
7
+ import { CompletionDialog } from "../completion-dialog/completion-dialog";
8
8
  const DEFAULT_LABELS = {
9
9
  closeLabel: "Close",
10
10
  closeTocLabel: "Close table of contents",
@@ -17,6 +17,7 @@ const DEFAULT_LABELS = {
17
17
  };
18
18
  const EMPTY_SLIDESHOW_LABELS = {};
19
19
  function SlideshowImpl({
20
+ as: SectionsHeading = "h3",
20
21
  completedSections,
21
22
  completionDialogTitle = "Mark section as complete?",
22
23
  currentIndex,
@@ -249,7 +250,7 @@ function SlideshowImpl({
249
250
  role: "dialog",
250
251
  children: [
251
252
  /* @__PURE__ */ jsxs("div", { className: "sticky top-0 flex items-center justify-between px-4 py-3 border-b border-border bg-background", children: [
252
- /* @__PURE__ */ jsx("h3", { className: "font-semibold", children: mergedLabels.sectionsLabel }),
253
+ /* @__PURE__ */ jsx(SectionsHeading, { className: "font-semibold", children: mergedLabels.sectionsLabel }),
253
254
  /* @__PURE__ */ jsx(
254
255
  "button",
255
256
  {
@@ -7,15 +7,15 @@ function ShareMenu({
7
7
  onPlatformSelect,
8
8
  platforms
9
9
  }) {
10
- return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5 rounded-md border border-gray-300 bg-background p-1.5 shadow-md dark:border-gray-600", children: platforms.map((p) => {
11
- const handleClick = () => {
10
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5 rounded-md border border-zinc-300 bg-background p-1.5 shadow-md dark:border-zinc-600", children: platforms.map((p) => {
11
+ const handleSelectSharePlatform = () => {
12
12
  onPlatformSelect(p.key);
13
13
  };
14
14
  return /* @__PURE__ */ jsx(
15
15
  "button",
16
16
  {
17
17
  className: "rounded px-3 py-1.5 text-left text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
18
- onClick: handleClick,
18
+ onClick: handleSelectSharePlatform,
19
19
  type: "button",
20
20
  children: p.label
21
21
  },
@@ -24,12 +24,12 @@ function ShareMenu({
24
24
  }) });
25
25
  }
26
26
  function ActionButton({ action }) {
27
- const handleClick = action.onClick;
27
+ const handleRunAction = action.onClick;
28
28
  return /* @__PURE__ */ jsx(
29
29
  "button",
30
30
  {
31
31
  className: "rounded px-3 py-1.5 text-left text-sm transition-colors hover:bg-accent",
32
- onClick: handleClick,
32
+ onClick: handleRunAction,
33
33
  type: "button",
34
34
  children: action.label
35
35
  }
@@ -47,7 +47,7 @@ function ActionPanel({
47
47
  const handleShareClick = shareAction?.onClick;
48
48
  const handleShareMouseEnter = isMobile ? void 0 : onShareHover;
49
49
  const handleShareMouseLeave = isMobile ? void 0 : onShareLeave;
50
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 rounded-md border border-gray-300 bg-background p-1.5 shadow-md dark:border-gray-600", children: [
50
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 rounded-md border border-zinc-300 bg-background p-1.5 shadow-md dark:border-zinc-600", children: [
51
51
  shareAction ? /* @__PURE__ */ jsx(
52
52
  "button",
53
53
  {
@@ -69,7 +69,7 @@ function MainFabButton({
69
69
  mainText,
70
70
  onToggle
71
71
  }) {
72
- const handleClick = onToggle;
72
+ const handleToggleSharePanel = onToggle;
73
73
  return /* @__PURE__ */ jsx(
74
74
  "button",
75
75
  {
@@ -77,11 +77,11 @@ function MainFabButton({
77
77
  "aria-label": isExpanded ? labels.close : labels.share,
78
78
  className: cn(
79
79
  "flex size-10 items-center justify-center rounded-md",
80
- "border border-gray-300 bg-background dark:border-gray-600",
80
+ "border border-zinc-300 bg-background dark:border-zinc-600",
81
81
  "transition-all duration-200 ease-out",
82
- "hover:-translate-y-0.5 hover:border-gray-400 hover:shadow-md dark:hover:border-gray-500"
82
+ "hover:-translate-y-0.5 hover:border-zinc-400 hover:shadow-md dark:hover:border-zinc-500"
83
83
  ),
84
- onClick: handleClick,
84
+ onClick: handleToggleSharePanel,
85
85
  type: "button",
86
86
  children: /* @__PURE__ */ jsx("span", { className: "text-sm font-mono", children: isMobile && isExpanded ? "\u2715" : mainText })
87
87
  }
@@ -14,7 +14,7 @@ function Spinner({ className, size = "md", ...props }) {
14
14
  className
15
15
  ),
16
16
  ...props,
17
- children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." })
17
+ children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading\u2026" })
18
18
  }
19
19
  );
20
20
  }
@@ -658,9 +658,11 @@ const UnicodeSpinner = React.forwardRef(
658
658
  const preset = UNICODE_SPINNER_PRESETS[animation];
659
659
  const resolvedInterval = interval ?? preset.interval;
660
660
  const [frameIndex, setFrameIndex] = React.useState(0);
661
- React.useEffect(() => {
661
+ const [previousAnimation, setPreviousAnimation] = React.useState(animation);
662
+ if (previousAnimation !== animation) {
663
+ setPreviousAnimation(animation);
662
664
  setFrameIndex(0);
663
- }, [animation]);
665
+ }
664
666
  React.useEffect(() => {
665
667
  if (paused) {
666
668
  return;
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { ArrowDownRight, ArrowRight, ArrowUpRight } from "lucide-react";
5
5
  import { cn } from "../../lib/utils";
6
- import { Card, CardContent, CardDescription, CardHeader } from "../card";
6
+ import { Card, CardContent, CardDescription, CardHeader } from "../card/card";
7
7
  const statCardVariants = cva("overflow-hidden border shadow-sm", {
8
8
  defaultVariants: {
9
9
  tone: "neutral"
@@ -74,7 +74,7 @@ const StatCard = React.forwardRef(
74
74
  ...props,
75
75
  children: [
76
76
  /* @__PURE__ */ jsx("div", { className: accentVariants({ tone }) }),
77
- /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-4 space-y-0 pb-3", children: [
77
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-4 gap-y-0 pb-3", children: [
78
78
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
79
79
  /* @__PURE__ */ jsx(CardDescription, { className: "text-xs font-medium uppercase tracking-[0.14em]", children: label }),
80
80
  /* @__PURE__ */ jsx("div", { className: "text-3xl font-semibold tracking-tight", children: value })
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "../../lib/utils";
4
- import { Badge } from "../badge";
4
+ import { Badge } from "../badge/badge";
5
5
  import {
6
6
  Card,
7
7
  CardContent,
@@ -71,10 +71,16 @@ function getSummary(items) {
71
71
  warning: 0
72
72
  }
73
73
  );
74
- return STATUS_ORDER.map((status) => ({
75
- count: counts[status],
76
- status
77
- })).filter((entry) => entry.count > 0);
74
+ return STATUS_ORDER.reduce(
75
+ (summary, status) => {
76
+ const count = counts[status];
77
+ if (count > 0) {
78
+ summary.push({ count, status });
79
+ }
80
+ return summary;
81
+ },
82
+ []
83
+ );
78
84
  }
79
85
  function StatusBoardSummary({ items }) {
80
86
  return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: getSummary(items).map(({ count, status }) => {
@@ -113,6 +119,7 @@ function StatusBoardCard({ item }) {
113
119
  }
114
120
  const StatusBoard = React.forwardRef(
115
121
  ({
122
+ as: Heading = "h2",
116
123
  className,
117
124
  columns = 3,
118
125
  description,
@@ -123,7 +130,7 @@ const StatusBoard = React.forwardRef(
123
130
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), ref, ...props, children: [
124
131
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
125
132
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
126
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold tracking-tight", children: title }),
133
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold tracking-tight", children: title }),
127
134
  description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
128
135
  ] }),
129
136
  /* @__PURE__ */ jsx(StatusBoardSummary, { items })
@@ -93,6 +93,7 @@ function InteractiveStep({
93
93
  ] });
94
94
  }
95
95
  function StepByStep({
96
+ as: Heading = "h3",
96
97
  children,
97
98
  className,
98
99
  interactive = false,
@@ -127,7 +128,7 @@ function StepByStep({
127
128
  )
128
129
  }
129
130
  ),
130
- /* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg", children: title })
131
+ /* @__PURE__ */ jsx(Heading, { className: "font-semibold text-lg", children: title })
131
132
  ] }) : null,
132
133
  /* @__PURE__ */ jsx("div", { className: "space-y-0", children: steps.map((step, index) => {
133
134
  const stepElement = step;
@@ -41,13 +41,16 @@ function useActiveSection(sections) {
41
41
  }, [sections]);
42
42
  return activeSection;
43
43
  }
44
- function TableOfContents({ sections }) {
44
+ function TableOfContents({
45
+ as: Heading = "h3",
46
+ sections
47
+ }) {
45
48
  const activeSection = useActiveSection(sections);
46
49
  if (sections.length === 0) {
47
50
  return null;
48
51
  }
49
52
  return /* @__PURE__ */ jsx("aside", { className: "hidden xl:block", children: /* @__PURE__ */ jsx("div", { className: "sticky top-8", children: /* @__PURE__ */ jsxs("div", { className: "border-l-2 border-border pl-4", children: [
50
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mb-4 text-muted-foreground uppercase tracking-wider", children: "On This Page" }),
53
+ /* @__PURE__ */ jsx(Heading, { className: "text-sm font-semibold mb-4 text-muted-foreground uppercase tracking-wider", children: "On This Page" }),
51
54
  /* @__PURE__ */ jsx("nav", { className: "space-y-2", children: sections.map((section) => /* @__PURE__ */ jsx(
52
55
  "button",
53
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 TableOfContentsPanelImpl({
6
+ as: Heading = "h2",
6
7
  className,
7
8
  closeIcon,
8
9
  completedSections,
@@ -55,6 +56,7 @@ function TableOfContentsPanelImpl({
55
56
  {
56
57
  "aria-hidden": "true",
57
58
  className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
59
+ "data-testid": "toc-backdrop",
58
60
  onClick: onClose
59
61
  }
60
62
  ),
@@ -68,7 +70,7 @@ function TableOfContentsPanelImpl({
68
70
  ref: panelRef,
69
71
  children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
70
72
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border px-4 py-3", children: [
71
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", id: "toc-title", children: title }),
73
+ /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold", id: "toc-title", children: title }),
72
74
  /* @__PURE__ */ jsx(
73
75
  "button",
74
76
  {
@@ -13,18 +13,23 @@ function useTabsContext() {
13
13
  function Tabs({
14
14
  children,
15
15
  className,
16
- defaultValue,
17
- onValueChange
16
+ defaultValue = "",
17
+ onValueChange,
18
+ value
18
19
  }) {
19
- const [activeTab, setActiveTab] = useState(defaultValue);
20
- const handleSetActiveTab = (value) => {
21
- setActiveTab(value);
22
- onValueChange?.(value);
20
+ const [internalTab, setInternalTab] = useState(defaultValue);
21
+ const isControlled = value !== void 0;
22
+ const activeTab = isControlled ? value : internalTab;
23
+ const handleSetActiveTab = (next) => {
24
+ if (!isControlled) {
25
+ setInternalTab(next);
26
+ }
27
+ onValueChange?.(next);
23
28
  };
24
29
  const contextValue = useMemo(
25
30
  () => ({ activeTab, setActiveTab: handleSetActiveTab }),
26
31
  // eslint-disable-next-line react-hooks/exhaustive-deps
27
- [activeTab]
32
+ [activeTab, isControlled]
28
33
  );
29
34
  return /* @__PURE__ */ jsx(TabsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { className: cn("my-6", className), children }) });
30
35
  }
@@ -39,8 +44,10 @@ function TabsList({ children, className }) {
39
44
  );
40
45
  }
41
46
  function TabsTrigger({
47
+ "aria-hidden": ariaHidden,
42
48
  children,
43
49
  className,
50
+ tabIndex,
44
51
  value
45
52
  }) {
46
53
  const { activeTab, setActiveTab } = useTabsContext();
@@ -48,6 +55,7 @@ function TabsTrigger({
48
55
  return /* @__PURE__ */ jsx(
49
56
  "button",
50
57
  {
58
+ "aria-hidden": ariaHidden,
51
59
  "aria-selected": isActive,
52
60
  className: cn(
53
61
  "px-4 py-2 text-sm font-medium whitespace-nowrap transition-colors",
@@ -59,19 +67,29 @@ function TabsTrigger({
59
67
  setActiveTab(value);
60
68
  },
61
69
  role: "tab",
70
+ tabIndex,
62
71
  type: "button",
63
72
  children
64
73
  }
65
74
  );
66
75
  }
67
76
  function TabsContent({
77
+ "aria-hidden": ariaHidden,
68
78
  children,
69
79
  className,
70
80
  value
71
81
  }) {
72
82
  const { activeTab } = useTabsContext();
73
83
  if (activeTab !== value) return null;
74
- return /* @__PURE__ */ jsx("div", { className: cn("pt-4", className), role: "tabpanel", children });
84
+ return /* @__PURE__ */ jsx(
85
+ "div",
86
+ {
87
+ "aria-hidden": ariaHidden,
88
+ className: cn("pt-4", className),
89
+ role: "tabpanel",
90
+ children
91
+ }
92
+ );
75
93
  }
76
94
  Tabs.List = TabsList;
77
95
  Tabs.Trigger = TabsTrigger;
@@ -7,9 +7,17 @@ function normalizeTag(tag) {
7
7
  return tag.trim();
8
8
  }
9
9
  function getNormalizedTags(tags) {
10
- return tags.map(normalizeTag).filter(
11
- (tag, index, values) => tag.length > 0 && values.indexOf(tag) === index
12
- );
10
+ return tags.reduce(
11
+ (state, tag) => {
12
+ const normalizedTag = normalizeTag(tag);
13
+ if (normalizedTag.length > 0 && !state.seen.has(normalizedTag)) {
14
+ state.seen.add(normalizedTag);
15
+ state.tags.push(normalizedTag);
16
+ }
17
+ return state;
18
+ },
19
+ { seen: /* @__PURE__ */ new Set(), tags: [] }
20
+ ).tags;
13
21
  }
14
22
  function shouldAddTagFromKey(key) {
15
23
  return key === "Enter" || key === ",";
@@ -2,14 +2,22 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Check, Copy, Terminal as TerminalIcon } from "lucide-react";
5
- import { Button } from "../button";
5
+ import { Button } from "../button/button";
6
+ function getCommandContents(lines) {
7
+ return lines.reduce((commands, line) => {
8
+ if (line.type === "command") {
9
+ commands.push(line.content);
10
+ }
11
+ return commands;
12
+ }, []);
13
+ }
6
14
  function Terminal({
7
15
  copyable = true,
8
16
  lines,
9
17
  title = "Terminal"
10
18
  }) {
11
19
  const [copied, setCopied] = useState(false);
12
- const commands = lines.filter((l) => l.type === "command").map((l) => l.content);
20
+ const commands = getCommandContents(lines);
13
21
  const handleCopy = async () => {
14
22
  await navigator.clipboard.writeText(commands.join("\n"));
15
23
  setCopied(true);
@@ -75,7 +83,7 @@ function SimpleTerminal({
75
83
  }
76
84
  return { content: line, type: "output" };
77
85
  });
78
- const commands = lines.filter((l) => l.type === "command").map((l) => l.content);
86
+ const commands = getCommandContents(lines);
79
87
  const handleCopy = async () => {
80
88
  await navigator.clipboard.writeText(commands.join("\n"));
81
89
  setCopied(true);
@@ -20,7 +20,7 @@ function ThemeButton({
20
20
  Button,
21
21
  {
22
22
  "aria-label": ariaLabel,
23
- className: "bg-background text-foreground border border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500 transition-colors",
23
+ className: "bg-background text-foreground border border-zinc-300 dark:border-zinc-600 hover:border-zinc-400 dark:hover:border-zinc-500 transition-colors",
24
24
  size: "icon",
25
25
  variant: "outline",
26
26
  ...props,
@@ -120,7 +120,7 @@ function ThemeToggle({ dict }) {
120
120
  DropdownMenuContent,
121
121
  {
122
122
  align: "end",
123
- className: "bg-background border border-gray-300 dark:border-gray-600",
123
+ className: "bg-background border border-zinc-300 dark:border-zinc-600",
124
124
  onMouseEnter: () => {
125
125
  isHoveringOverMenuAreaReference.current = true;
126
126
  clearCloseTimer();
@@ -33,7 +33,7 @@ function ThinkingBlock({
33
33
  isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-3" }),
34
34
  /* @__PURE__ */ jsxs("span", { children: [
35
35
  "Thinking",
36
- isStreaming ? /* @__PURE__ */ jsx("span", { className: "ml-1 animate-pulse", children: "..." }) : null
36
+ isStreaming ? /* @__PURE__ */ jsx("span", { className: "ml-1 animate-pulse", children: "\u2026" }) : null
37
37
  ] })
38
38
  ]
39
39
  }
@@ -41,7 +41,7 @@ function ThinkingBlock({
41
41
  isExpanded ? /* @__PURE__ */ jsxs(
42
42
  "div",
43
43
  {
44
- className: "mt-2 p-3 bg-muted/50 rounded text-xs text-muted-foreground whitespace-pre-wrap border-l-2 border-muted-foreground/30 max-h-48 overflow-y-auto",
44
+ className: "mt-2 p-3 bg-muted/50 rounded text-xs text-muted-foreground whitespace-pre-wrap border-l border-muted-foreground/30 max-h-48 overflow-y-auto",
45
45
  id: contentId,
46
46
  children: [
47
47
  thinking,
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { ArrowDownRight, ArrowUpRight, Dot } from "lucide-react";
4
4
  import { cn } from "../../lib/utils";
5
- import { Badge } from "../badge";
5
+ import { Badge } from "../badge/badge";
6
6
  const tickerTapeKeyframes = `
7
7
  @keyframes ticker-tape-scroll {
8
8
  from {
@@ -125,7 +125,7 @@ const TimelineScrubber = forwardRef((props, ref) => {
125
125
  const span = safeEnd - start;
126
126
  const clamped = clamp(value, start, safeEnd);
127
127
  const ratio = clamp((clamped - start) / span, 0, 1);
128
- const handleChange = (event) => {
128
+ const handleScrubberValueChange = (event) => {
129
129
  onValueChange(clamp(Number(event.target.value), start, safeEnd));
130
130
  };
131
131
  return /* @__PURE__ */ jsxs(
@@ -158,7 +158,7 @@ const TimelineScrubber = forwardRef((props, ref) => {
158
158
  inputId,
159
159
  max: safeEnd,
160
160
  min: start,
161
- onChange: handleChange,
161
+ onChange: handleScrubberValueChange,
162
162
  ratio,
163
163
  scrubberId: inputId,
164
164
  span,
@@ -3,20 +3,20 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  function TLDRSection({ children, label }) {
5
5
  const [isExpanded, setIsExpanded] = useState(false);
6
- const [isLoading, setIsLoading] = useState(false);
7
- const [hasBeenOpened, setHasBeenOpened] = useState(false);
6
+ const [showSkeleton, setShowSkeleton] = useState(false);
7
+ const hasBeenOpenedRef = useRef(false);
8
8
  const timerReference = useRef(null);
9
9
  useEffect(() => {
10
- if (isExpanded && !hasBeenOpened) {
10
+ if (isExpanded && !hasBeenOpenedRef.current) {
11
11
  if (timerReference.current) {
12
12
  clearTimeout(timerReference.current);
13
13
  }
14
14
  const rafId = requestAnimationFrame(() => {
15
- setIsLoading(true);
16
- setHasBeenOpened(true);
15
+ setShowSkeleton(true);
16
+ hasBeenOpenedRef.current = true;
17
17
  });
18
18
  timerReference.current = setTimeout(() => {
19
- setIsLoading(false);
19
+ setShowSkeleton(false);
20
20
  timerReference.current = null;
21
21
  }, 800);
22
22
  return () => {
@@ -25,6 +25,7 @@ function TLDRSection({ children, label }) {
25
25
  clearTimeout(timerReference.current);
26
26
  timerReference.current = null;
27
27
  }
28
+ setShowSkeleton(false);
28
29
  };
29
30
  }
30
31
  return () => {
@@ -32,6 +33,7 @@ function TLDRSection({ children, label }) {
32
33
  clearTimeout(timerReference.current);
33
34
  timerReference.current = null;
34
35
  }
36
+ setShowSkeleton(false);
35
37
  };
36
38
  }, [isExpanded]);
37
39
  return /* @__PURE__ */ jsxs("div", { className: "my-8 rounded-lg border border-border bg-muted/30 overflow-hidden", children: [
@@ -88,7 +90,7 @@ function TLDRSection({ children, label }) {
88
90
  ]
89
91
  }
90
92
  ),
91
- isExpanded ? /* @__PURE__ */ jsx("div", { className: "px-4 pb-4 pt-2 border-t border-border", children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
93
+ isExpanded ? /* @__PURE__ */ jsx("div", { className: "px-4 pb-4 pt-2 border-t border-border", children: showSkeleton ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
92
94
  /* @__PURE__ */ jsx("div", { className: "relative h-4 bg-muted/50 rounded overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" }) }),
93
95
  /* @__PURE__ */ jsx("div", { className: "relative h-4 bg-muted/50 rounded overflow-hidden w-5/6", children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" }) }),
94
96
  /* @__PURE__ */ jsx("div", { className: "relative h-4 bg-muted/50 rounded overflow-hidden w-4/5", children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" }) }),
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { cn } from "../../lib/utils";
5
- import { Badge } from "../badge";
5
+ import { Badge } from "../badge/badge";
6
6
  import { Button } from "../button";
7
7
  import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "../card";
8
8
  function TourHeader({
@@ -12,7 +12,7 @@ function getCurrencyFormatter(locale, currency) {
12
12
  const key = `${locale}|${currency}`;
13
13
  let formatter = CURRENCY_FORMATTER_CACHE.get(key);
14
14
  if (!formatter) {
15
- formatter = new Intl.NumberFormat(locale, {
15
+ formatter = Intl.NumberFormat(locale, {
16
16
  currency,
17
17
  style: "currency"
18
18
  });
@@ -24,7 +24,7 @@ const DATE_FORMATTER_CACHE = /* @__PURE__ */ new Map();
24
24
  function getTransactionDateFormatter(locale) {
25
25
  let formatter = DATE_FORMATTER_CACHE.get(locale);
26
26
  if (!formatter) {
27
- formatter = new Intl.DateTimeFormat(locale, {
27
+ formatter = Intl.DateTimeFormat(locale, {
28
28
  day: "numeric",
29
29
  month: "short",
30
30
  year: "numeric"