ai-design-system 0.1.0

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 (290) hide show
  1. package/README.md +307 -0
  2. package/components/ai-elements/actions.tsx +65 -0
  3. package/components/ai-elements/artifact.tsx +147 -0
  4. package/components/ai-elements/branch.tsx +212 -0
  5. package/components/ai-elements/canvas.tsx +24 -0
  6. package/components/ai-elements/chain-of-thought.tsx +228 -0
  7. package/components/ai-elements/code-block.tsx +179 -0
  8. package/components/ai-elements/confirmation.tsx +169 -0
  9. package/components/ai-elements/connection.tsx +28 -0
  10. package/components/ai-elements/context.tsx +408 -0
  11. package/components/ai-elements/controls.tsx +18 -0
  12. package/components/ai-elements/conversation.tsx +97 -0
  13. package/components/ai-elements/edge.tsx +140 -0
  14. package/components/ai-elements/image.tsx +24 -0
  15. package/components/ai-elements/inline-citation.tsx +287 -0
  16. package/components/ai-elements/loader.tsx +96 -0
  17. package/components/ai-elements/message.tsx +80 -0
  18. package/components/ai-elements/node.tsx +71 -0
  19. package/components/ai-elements/open-in-chat.tsx +363 -0
  20. package/components/ai-elements/panel.tsx +15 -0
  21. package/components/ai-elements/plan.tsx +142 -0
  22. package/components/ai-elements/prompt-input.tsx +1352 -0
  23. package/components/ai-elements/queue.tsx +274 -0
  24. package/components/ai-elements/reasoning.tsx +178 -0
  25. package/components/ai-elements/response.tsx +22 -0
  26. package/components/ai-elements/shimmer.tsx +64 -0
  27. package/components/ai-elements/sources.tsx +77 -0
  28. package/components/ai-elements/suggestion.tsx +56 -0
  29. package/components/ai-elements/task.tsx +87 -0
  30. package/components/ai-elements/tool.tsx +179 -0
  31. package/components/ai-elements/toolbar.tsx +16 -0
  32. package/components/ai-elements/web-preview.tsx +263 -0
  33. package/components/blocks/AIConversation/AIConversation.stories.tsx +164 -0
  34. package/components/blocks/AIConversation/AIConversation.tsx +186 -0
  35. package/components/blocks/AIConversation/index.ts +8 -0
  36. package/components/blocks/AppSidebar/AppSidebar.stories.tsx +63 -0
  37. package/components/blocks/AppSidebar/AppSidebar.tsx +87 -0
  38. package/components/blocks/AppSidebar/index.ts +2 -0
  39. package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.stories.tsx +341 -0
  40. package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.tsx +255 -0
  41. package/components/blocks/DocumentEditorWithComments/index.ts +9 -0
  42. package/components/blocks/FileChangeQueue/FileChangeQueue.stories.tsx +207 -0
  43. package/components/blocks/FileChangeQueue/FileChangeQueue.tsx +143 -0
  44. package/components/blocks/FileChangeQueue/index.ts +7 -0
  45. package/components/blocks/LayoutProvider/LayoutProvider.tsx +34 -0
  46. package/components/blocks/LayoutProvider/index.ts +1 -0
  47. package/components/blocks/index.ts +2 -0
  48. package/components/composites/AgentIndicator/AgentIndicator.stories.tsx +154 -0
  49. package/components/composites/AgentIndicator/AgentIndicator.tsx +102 -0
  50. package/components/composites/AgentIndicator/index.ts +8 -0
  51. package/components/composites/AppHeader/AppHeader.stories.tsx +46 -0
  52. package/components/composites/AppHeader/AppHeader.tsx +24 -0
  53. package/components/composites/AppHeader/index.ts +2 -0
  54. package/components/composites/CommentBox/CommentBox.stories.tsx +192 -0
  55. package/components/composites/CommentBox/CommentBox.tsx +364 -0
  56. package/components/composites/CommentBox/index.ts +8 -0
  57. package/components/composites/Confirmation/Confirmation.stories.tsx +151 -0
  58. package/components/composites/Confirmation/Confirmation.tsx +93 -0
  59. package/components/composites/Confirmation/index.ts +7 -0
  60. package/components/composites/DataTable/DataTable.stories.tsx +35 -0
  61. package/components/composites/DataTable/DataTable.tsx +95 -0
  62. package/components/composites/DataTable/index.ts +2 -0
  63. package/components/composites/DocumentEditor/DocumentEditor.css +106 -0
  64. package/components/composites/DocumentEditor/DocumentEditor.stories.tsx +927 -0
  65. package/components/composites/DocumentEditor/DocumentEditor.tsx +279 -0
  66. package/components/composites/DocumentEditor/index.ts +8 -0
  67. package/components/composites/FileQueue/FileQueue.stories.tsx +175 -0
  68. package/components/composites/FileQueue/FileQueue.tsx +161 -0
  69. package/components/composites/FileQueue/FileStatusBadge.tsx +74 -0
  70. package/components/composites/FileQueue/index.ts +24 -0
  71. package/components/composites/InteractiveChart/InteractiveChart.stories.tsx +49 -0
  72. package/components/composites/InteractiveChart/InteractiveChart.tsx +69 -0
  73. package/components/composites/InteractiveChart/index.ts +2 -0
  74. package/components/composites/ModeToggle/ModeToggle.stories.tsx +212 -0
  75. package/components/composites/ModeToggle/ModeToggle.tsx +100 -0
  76. package/components/composites/ModeToggle/index.ts +7 -0
  77. package/components/composites/NavUser/NavUser.stories.tsx +50 -0
  78. package/components/composites/NavUser/NavUser.tsx +60 -0
  79. package/components/composites/NavUser/index.ts +2 -0
  80. package/components/composites/NavigationList/NavigationList.stories.tsx +46 -0
  81. package/components/composites/NavigationList/NavigationList.tsx +46 -0
  82. package/components/composites/NavigationList/index.ts +2 -0
  83. package/components/composites/OrchestratorMessage/OrchestratorMessage.stories.tsx +188 -0
  84. package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +72 -0
  85. package/components/composites/OrchestratorMessage/index.ts +8 -0
  86. package/components/composites/PageContainer/PageContainer.stories.tsx +41 -0
  87. package/components/composites/PageContainer/PageContainer.tsx +24 -0
  88. package/components/composites/PageContainer/index.ts +2 -0
  89. package/components/composites/PromptInput/PromptInput.stories.tsx +200 -0
  90. package/components/composites/PromptInput/PromptInput.tsx +129 -0
  91. package/components/composites/PromptInput/index.ts +8 -0
  92. package/components/composites/SpecialistMessage/SpecialistMessage.stories.tsx +286 -0
  93. package/components/composites/SpecialistMessage/SpecialistMessage.tsx +107 -0
  94. package/components/composites/SpecialistMessage/index.ts +8 -0
  95. package/components/composites/StatsCard/StatsCard.stories.tsx +76 -0
  96. package/components/composites/StatsCard/StatsCard.tsx +81 -0
  97. package/components/composites/StatsCard/index.ts +2 -0
  98. package/components/composites/TablePagination/TablePagination.stories.tsx +38 -0
  99. package/components/composites/TablePagination/TablePagination.tsx +119 -0
  100. package/components/composites/TablePagination/index.ts +2 -0
  101. package/components/composites/TableToolbar/TableToolbar.stories.tsx +60 -0
  102. package/components/composites/TableToolbar/TableToolbar.tsx +66 -0
  103. package/components/composites/TableToolbar/index.ts +2 -0
  104. package/components/composites/ThemeSelector/ThemeSelector.stories.tsx +48 -0
  105. package/components/composites/ThemeSelector/ThemeSelector.tsx +79 -0
  106. package/components/composites/ThemeSelector/index.ts +2 -0
  107. package/components/composites/ToolCallDisplay/ToolCallDisplay.stories.tsx +49 -0
  108. package/components/composites/ToolCallDisplay/ToolCallDisplay.tsx +108 -0
  109. package/components/composites/ToolCallDisplay/index.ts +8 -0
  110. package/components/composites/UserMessage/UserMessage.stories.tsx +59 -0
  111. package/components/composites/UserMessage/UserMessage.tsx +52 -0
  112. package/components/composites/UserMessage/index.ts +8 -0
  113. package/components/composites/index.ts +90 -0
  114. package/components/features/AIDocEditor/AIDocEditor.behaviors.stories.tsx +451 -0
  115. package/components/features/AIDocEditor/AIDocEditor.mocks.ts +96 -0
  116. package/components/features/AIDocEditor/AIDocEditor.stories.tsx +140 -0
  117. package/components/features/AIDocEditor/AIDocEditor.tsx +130 -0
  118. package/components/features/AIDocEditor/index.ts +8 -0
  119. package/components/features/AIDocEditor/useAIDocEditor.d.ts +97 -0
  120. package/components/features/AIDocEditor/useAIDocEditor.mock.ts +83 -0
  121. package/components/features/PageLayout/PageLayout.behaviors.stories.tsx +119 -0
  122. package/components/features/PageLayout/PageLayout.mocks.ts +27 -0
  123. package/components/features/PageLayout/PageLayout.stories.tsx +142 -0
  124. package/components/features/PageLayout/PageLayout.tsx +94 -0
  125. package/components/features/PageLayout/index.ts +4 -0
  126. package/components/features/PageLayout/usePageLayout.d.ts +24 -0
  127. package/components/features/PageLayout/usePageLayout.mock.ts +19 -0
  128. package/components/features/RefinementPanel/README.md +189 -0
  129. package/components/features/RefinementPanel/RefinementPanel.behaviors.stories.tsx +475 -0
  130. package/components/features/RefinementPanel/RefinementPanel.mocks.ts +131 -0
  131. package/components/features/RefinementPanel/RefinementPanel.stories.tsx +141 -0
  132. package/components/features/RefinementPanel/RefinementPanel.tsx +160 -0
  133. package/components/features/RefinementPanel/index.ts +25 -0
  134. package/components/features/RefinementPanel/useRefinementPanel.d.ts +74 -0
  135. package/components/features/RefinementPanel/useRefinementPanel.mock.ts +121 -0
  136. package/components/features/SpecNavigator/SpecNavigator.behaviors.stories.tsx +379 -0
  137. package/components/features/SpecNavigator/SpecNavigator.mocks.ts +131 -0
  138. package/components/features/SpecNavigator/SpecNavigator.stories.tsx +122 -0
  139. package/components/features/SpecNavigator/SpecNavigator.tsx +43 -0
  140. package/components/features/SpecNavigator/index.ts +2 -0
  141. package/components/features/SpecNavigator/useSpecNavigator.d.ts +122 -0
  142. package/components/features/SpecNavigator/useSpecNavigator.mock.ts +93 -0
  143. package/components/features/index.ts +18 -0
  144. package/components/index.ts +14 -0
  145. package/components/primitives/Accordion/Accordion.stories.tsx +87 -0
  146. package/components/primitives/Accordion/Accordion.tsx +66 -0
  147. package/components/primitives/Accordion/index.ts +13 -0
  148. package/components/primitives/Alert/Alert.stories.tsx +422 -0
  149. package/components/primitives/Alert/Alert.tsx +61 -0
  150. package/components/primitives/Alert/index.ts +8 -0
  151. package/components/primitives/AlertDialog/AlertDialog.stories.tsx +367 -0
  152. package/components/primitives/AlertDialog/AlertDialog.tsx +182 -0
  153. package/components/primitives/AlertDialog/index.ts +25 -0
  154. package/components/primitives/Avatar/Avatar.stories.tsx +321 -0
  155. package/components/primitives/Avatar/Avatar.tsx +63 -0
  156. package/components/primitives/Avatar/index.ts +8 -0
  157. package/components/primitives/Badge/Badge.stories.tsx +74 -0
  158. package/components/primitives/Badge/Badge.tsx +49 -0
  159. package/components/primitives/Badge/index.ts +2 -0
  160. package/components/primitives/Button/Button.stories.tsx +445 -0
  161. package/components/primitives/Button/Button.tsx +89 -0
  162. package/components/primitives/Button/index.ts +7 -0
  163. package/components/primitives/Card/Card.stories.tsx +831 -0
  164. package/components/primitives/Card/Card.tsx +242 -0
  165. package/components/primitives/Card/index.ts +30 -0
  166. package/components/primitives/Carousel/Carousel.stories.tsx +32 -0
  167. package/components/primitives/Carousel/Carousel.tsx +63 -0
  168. package/components/primitives/Carousel/index.ts +13 -0
  169. package/components/primitives/Chart/Chart.stories.tsx +346 -0
  170. package/components/primitives/Chart/Chart.tsx +117 -0
  171. package/components/primitives/Chart/index.ts +20 -0
  172. package/components/primitives/Checkbox/Checkbox.stories.tsx +87 -0
  173. package/components/primitives/Checkbox/Checkbox.tsx +38 -0
  174. package/components/primitives/Checkbox/index.ts +2 -0
  175. package/components/primitives/Collapsible/Collapsible.stories.tsx +38 -0
  176. package/components/primitives/Collapsible/Collapsible.tsx +39 -0
  177. package/components/primitives/Collapsible/index.ts +8 -0
  178. package/components/primitives/Command/Command.stories.tsx +150 -0
  179. package/components/primitives/Command/Command.tsx +147 -0
  180. package/components/primitives/Command/index.ts +20 -0
  181. package/components/primitives/Dialog/Dialog.stories.tsx +390 -0
  182. package/components/primitives/Dialog/Dialog.tsx +140 -0
  183. package/components/primitives/Dialog/index.ts +22 -0
  184. package/components/primitives/Drawer/Drawer.stories.tsx +327 -0
  185. package/components/primitives/Drawer/Drawer.tsx +208 -0
  186. package/components/primitives/Drawer/index.ts +27 -0
  187. package/components/primitives/DropdownMenu/DropdownMenu.stories.tsx +150 -0
  188. package/components/primitives/DropdownMenu/DropdownMenu.tsx +73 -0
  189. package/components/primitives/DropdownMenu/index.ts +1 -0
  190. package/components/primitives/HoverCard/HoverCard.stories.tsx +26 -0
  191. package/components/primitives/HoverCard/HoverCard.tsx +39 -0
  192. package/components/primitives/HoverCard/index.ts +8 -0
  193. package/components/primitives/Icon/Icon.stories.tsx +281 -0
  194. package/components/primitives/Icon/Icon.tsx +87 -0
  195. package/components/primitives/Icon/index.ts +8 -0
  196. package/components/primitives/Input/Input.stories.tsx +370 -0
  197. package/components/primitives/Input/Input.tsx +88 -0
  198. package/components/primitives/Input/index.ts +7 -0
  199. package/components/primitives/InputGroup/InputGroup.stories.tsx +40 -0
  200. package/components/primitives/InputGroup/InputGroup.tsx +72 -0
  201. package/components/primitives/InputGroup/index.ts +14 -0
  202. package/components/primitives/Label/Label.stories.tsx +227 -0
  203. package/components/primitives/Label/Label.tsx +53 -0
  204. package/components/primitives/Label/index.ts +7 -0
  205. package/components/primitives/Popover/Popover.stories.tsx +42 -0
  206. package/components/primitives/Popover/Popover.tsx +107 -0
  207. package/components/primitives/Popover/index.ts +2 -0
  208. package/components/primitives/Progress/Progress.stories.tsx +340 -0
  209. package/components/primitives/Progress/Progress.tsx +31 -0
  210. package/components/primitives/Progress/index.ts +1 -0
  211. package/components/primitives/ScrollArea/ScrollArea.stories.tsx +26 -0
  212. package/components/primitives/ScrollArea/ScrollArea.tsx +28 -0
  213. package/components/primitives/ScrollArea/index.ts +6 -0
  214. package/components/primitives/Select/Select.stories.tsx +288 -0
  215. package/components/primitives/Select/Select.tsx +162 -0
  216. package/components/primitives/Select/index.ts +22 -0
  217. package/components/primitives/Separator/Separator.stories.tsx +264 -0
  218. package/components/primitives/Separator/Separator.tsx +48 -0
  219. package/components/primitives/Separator/index.ts +7 -0
  220. package/components/primitives/Sidebar/Sidebar.stories.tsx +358 -0
  221. package/components/primitives/Sidebar/Sidebar.tsx +317 -0
  222. package/components/primitives/Sidebar/index.ts +41 -0
  223. package/components/primitives/Table/Table.stories.tsx +389 -0
  224. package/components/primitives/Table/Table.tsx +191 -0
  225. package/components/primitives/Table/index.ts +26 -0
  226. package/components/primitives/Tabs/Tabs.stories.tsx +129 -0
  227. package/components/primitives/Tabs/Tabs.tsx +70 -0
  228. package/components/primitives/Tabs/index.ts +13 -0
  229. package/components/primitives/Textarea/Textarea.stories.tsx +358 -0
  230. package/components/primitives/Textarea/Textarea.tsx +91 -0
  231. package/components/primitives/Textarea/index.ts +7 -0
  232. package/components/primitives/ToggleGroup/ToggleGroup.stories.tsx +87 -0
  233. package/components/primitives/ToggleGroup/ToggleGroup.tsx +52 -0
  234. package/components/primitives/ToggleGroup/index.ts +6 -0
  235. package/components/primitives/Tooltip/Tooltip.stories.tsx +336 -0
  236. package/components/primitives/Tooltip/Tooltip.tsx +78 -0
  237. package/components/primitives/Tooltip/index.ts +10 -0
  238. package/components/primitives/index.ts +34 -0
  239. package/components/ui/accordion.tsx +66 -0
  240. package/components/ui/alert-dialog.tsx +157 -0
  241. package/components/ui/alert.tsx +66 -0
  242. package/components/ui/avatar.tsx +53 -0
  243. package/components/ui/badge.tsx +46 -0
  244. package/components/ui/button.tsx +60 -0
  245. package/components/ui/card.tsx +117 -0
  246. package/components/ui/carousel.tsx +241 -0
  247. package/components/ui/chart.tsx +334 -0
  248. package/components/ui/checkbox.tsx +32 -0
  249. package/components/ui/collapsible.tsx +33 -0
  250. package/components/ui/command.tsx +184 -0
  251. package/components/ui/dialog.tsx +143 -0
  252. package/components/ui/drawer.tsx +118 -0
  253. package/components/ui/dropdown-menu.tsx +257 -0
  254. package/components/ui/hover-card.tsx +44 -0
  255. package/components/ui/input-group.tsx +170 -0
  256. package/components/ui/input.tsx +48 -0
  257. package/components/ui/label.tsx +26 -0
  258. package/components/ui/popover.tsx +33 -0
  259. package/components/ui/progress.tsx +31 -0
  260. package/components/ui/scroll-area.tsx +58 -0
  261. package/components/ui/select.tsx +187 -0
  262. package/components/ui/separator.tsx +31 -0
  263. package/components/ui/sidebar.tsx +577 -0
  264. package/components/ui/table.tsx +120 -0
  265. package/components/ui/tabs.tsx +66 -0
  266. package/components/ui/textarea.tsx +46 -0
  267. package/components/ui/toggle-group.tsx +83 -0
  268. package/components/ui/toggle.tsx +47 -0
  269. package/components/ui/tooltip.tsx +61 -0
  270. package/dist/index.cjs +7389 -0
  271. package/dist/index.cjs.map +1 -0
  272. package/dist/index.css +75 -0
  273. package/dist/index.css.map +1 -0
  274. package/dist/index.js +7160 -0
  275. package/dist/index.js.map +1 -0
  276. package/hooks/useAIDocReviewer.d.ts +0 -0
  277. package/lib/utils.ts +6 -0
  278. package/package.json +140 -0
  279. package/tokens/color/base.json +14 -0
  280. package/tokens/color/dark.json +40 -0
  281. package/tokens/color/green.json +21 -0
  282. package/tokens/color/light.json +52 -0
  283. package/tokens/color/neutral.json +20 -0
  284. package/tokens/color/violet.json +21 -0
  285. package/tokens/spacing.json +22 -0
  286. package/utils/ai-editor/format-date.ts +41 -0
  287. package/utils/ai-editor/index.ts +22 -0
  288. package/utils/ai-editor/type-guards.ts +72 -0
  289. package/utils/ai-editor/validation.ts +130 -0
  290. package/utils/editor-annotations.ts +122 -0
@@ -0,0 +1,212 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { cn } from "@/lib/utils";
5
+ import type { UIMessage } from "ai";
6
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
7
+ import type { ComponentProps, HTMLAttributes, ReactElement } from "react";
8
+ import { createContext, useContext, useEffect, useState } from "react";
9
+
10
+ type BranchContextType = {
11
+ currentBranch: number;
12
+ totalBranches: number;
13
+ goToPrevious: () => void;
14
+ goToNext: () => void;
15
+ branches: ReactElement[];
16
+ setBranches: (branches: ReactElement[]) => void;
17
+ };
18
+
19
+ const BranchContext = createContext<BranchContextType | null>(null);
20
+
21
+ const useBranch = () => {
22
+ const context = useContext(BranchContext);
23
+
24
+ if (!context) {
25
+ throw new Error("Branch components must be used within Branch");
26
+ }
27
+
28
+ return context;
29
+ };
30
+
31
+ export type BranchProps = HTMLAttributes<HTMLDivElement> & {
32
+ defaultBranch?: number;
33
+ onBranchChange?: (branchIndex: number) => void;
34
+ };
35
+
36
+ export const Branch = ({
37
+ defaultBranch = 0,
38
+ onBranchChange,
39
+ className,
40
+ ...props
41
+ }: BranchProps) => {
42
+ const [currentBranch, setCurrentBranch] = useState(defaultBranch);
43
+ const [branches, setBranches] = useState<ReactElement[]>([]);
44
+
45
+ const handleBranchChange = (newBranch: number) => {
46
+ setCurrentBranch(newBranch);
47
+ onBranchChange?.(newBranch);
48
+ };
49
+
50
+ const goToPrevious = () => {
51
+ const newBranch =
52
+ currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
53
+ handleBranchChange(newBranch);
54
+ };
55
+
56
+ const goToNext = () => {
57
+ const newBranch =
58
+ currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
59
+ handleBranchChange(newBranch);
60
+ };
61
+
62
+ const contextValue: BranchContextType = {
63
+ currentBranch,
64
+ totalBranches: branches.length,
65
+ goToPrevious,
66
+ goToNext,
67
+ branches,
68
+ setBranches,
69
+ };
70
+
71
+ return (
72
+ <BranchContext.Provider value={contextValue}>
73
+ <div
74
+ className={cn("grid w-full gap-2 [&>div]:pb-0", className)}
75
+ {...props}
76
+ />
77
+ </BranchContext.Provider>
78
+ );
79
+ };
80
+
81
+ export type BranchMessagesProps = HTMLAttributes<HTMLDivElement>;
82
+
83
+ export const BranchMessages = ({ children, ...props }: BranchMessagesProps) => {
84
+ const { currentBranch, setBranches, branches } = useBranch();
85
+ const childrenArray = Array.isArray(children) ? children : [children];
86
+
87
+ // Use useEffect to update branches when they change
88
+ useEffect(() => {
89
+ if (branches.length !== childrenArray.length) {
90
+ setBranches(childrenArray);
91
+ }
92
+ }, [childrenArray, branches, setBranches]);
93
+
94
+ return childrenArray.map((branch, index) => (
95
+ <div
96
+ className={cn(
97
+ "grid gap-2 overflow-hidden [&>div]:pb-0",
98
+ index === currentBranch ? "block" : "hidden"
99
+ )}
100
+ key={branch.key}
101
+ {...props}
102
+ >
103
+ {branch}
104
+ </div>
105
+ ));
106
+ };
107
+
108
+ export type BranchSelectorProps = HTMLAttributes<HTMLDivElement> & {
109
+ from: UIMessage["role"];
110
+ };
111
+
112
+ export const BranchSelector = ({
113
+ className,
114
+ from,
115
+ ...props
116
+ }: BranchSelectorProps) => {
117
+ const { totalBranches } = useBranch();
118
+
119
+ // Don't render if there's only one branch
120
+ if (totalBranches <= 1) {
121
+ return null;
122
+ }
123
+
124
+ return (
125
+ <div
126
+ className={cn(
127
+ "flex items-center gap-2 self-end px-10",
128
+ from === "assistant" ? "justify-start" : "justify-end",
129
+ className
130
+ )}
131
+ {...props}
132
+ />
133
+ );
134
+ };
135
+
136
+ export type BranchPreviousProps = ComponentProps<typeof Button>;
137
+
138
+ export const BranchPrevious = ({
139
+ className,
140
+ children,
141
+ ...props
142
+ }: BranchPreviousProps) => {
143
+ const { goToPrevious, totalBranches } = useBranch();
144
+
145
+ return (
146
+ <Button
147
+ aria-label="Previous branch"
148
+ className={cn(
149
+ "size-7 shrink-0 rounded-full text-muted-foreground transition-colors",
150
+ "hover:bg-accent hover:text-foreground",
151
+ "disabled:pointer-events-none disabled:opacity-50",
152
+ className
153
+ )}
154
+ disabled={totalBranches <= 1}
155
+ onClick={goToPrevious}
156
+ size="icon"
157
+ type="button"
158
+ variant="ghost"
159
+ {...props}
160
+ >
161
+ {children ?? <ChevronLeftIcon size={14} />}
162
+ </Button>
163
+ );
164
+ };
165
+
166
+ export type BranchNextProps = ComponentProps<typeof Button>;
167
+
168
+ export const BranchNext = ({
169
+ className,
170
+ children,
171
+ ...props
172
+ }: BranchNextProps) => {
173
+ const { goToNext, totalBranches } = useBranch();
174
+
175
+ return (
176
+ <Button
177
+ aria-label="Next branch"
178
+ className={cn(
179
+ "size-7 shrink-0 rounded-full text-muted-foreground transition-colors",
180
+ "hover:bg-accent hover:text-foreground",
181
+ "disabled:pointer-events-none disabled:opacity-50",
182
+ className
183
+ )}
184
+ disabled={totalBranches <= 1}
185
+ onClick={goToNext}
186
+ size="icon"
187
+ type="button"
188
+ variant="ghost"
189
+ {...props}
190
+ >
191
+ {children ?? <ChevronRightIcon size={14} />}
192
+ </Button>
193
+ );
194
+ };
195
+
196
+ export type BranchPageProps = HTMLAttributes<HTMLSpanElement>;
197
+
198
+ export const BranchPage = ({ className, ...props }: BranchPageProps) => {
199
+ const { currentBranch, totalBranches } = useBranch();
200
+
201
+ return (
202
+ <span
203
+ className={cn(
204
+ "font-medium text-muted-foreground text-xs tabular-nums",
205
+ className
206
+ )}
207
+ {...props}
208
+ >
209
+ {currentBranch + 1} of {totalBranches}
210
+ </span>
211
+ );
212
+ };
@@ -0,0 +1,24 @@
1
+ import { Background, ReactFlow, type ReactFlowProps } from "@xyflow/react";
2
+ import type { ReactNode } from "react";
3
+ import "@xyflow/react/dist/style.css";
4
+ import { Controls } from "./controls";
5
+
6
+ type CanvasProps = ReactFlowProps & {
7
+ children?: ReactNode;
8
+ };
9
+
10
+ export const Canvas = ({ children, ...props }: CanvasProps) => (
11
+ <ReactFlow
12
+ deleteKeyCode={["Backspace", "Delete"]}
13
+ fitView
14
+ panOnDrag={false}
15
+ panOnScroll
16
+ selectionOnDrag={true}
17
+ zoomOnDoubleClick={false}
18
+ {...props}
19
+ >
20
+ <Background bgColor="var(--sidebar)" />
21
+ <Controls />
22
+ {children}
23
+ </ReactFlow>
24
+ );
@@ -0,0 +1,228 @@
1
+ "use client";
2
+
3
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
4
+ import { Badge } from "@/components/ui/badge";
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger,
9
+ } from "@/components/ui/collapsible";
10
+ import { cn } from "@/lib/utils";
11
+ import {
12
+ BrainIcon,
13
+ ChevronDownIcon,
14
+ DotIcon,
15
+ type LucideIcon,
16
+ } from "lucide-react";
17
+ import type { ComponentProps } from "react";
18
+ import { createContext, memo, useContext, useMemo } from "react";
19
+
20
+ type ChainOfThoughtContextValue = {
21
+ isOpen: boolean;
22
+ setIsOpen: (open: boolean) => void;
23
+ };
24
+
25
+ const ChainOfThoughtContext = createContext<ChainOfThoughtContextValue | null>(
26
+ null
27
+ );
28
+
29
+ const useChainOfThought = () => {
30
+ const context = useContext(ChainOfThoughtContext);
31
+ if (!context) {
32
+ throw new Error(
33
+ "ChainOfThought components must be used within ChainOfThought"
34
+ );
35
+ }
36
+ return context;
37
+ };
38
+
39
+ export type ChainOfThoughtProps = ComponentProps<"div"> & {
40
+ open?: boolean;
41
+ defaultOpen?: boolean;
42
+ onOpenChange?: (open: boolean) => void;
43
+ };
44
+
45
+ export const ChainOfThought = memo(
46
+ ({
47
+ className,
48
+ open,
49
+ defaultOpen = false,
50
+ onOpenChange,
51
+ children,
52
+ ...props
53
+ }: ChainOfThoughtProps) => {
54
+ const [isOpen, setIsOpen] = useControllableState({
55
+ prop: open,
56
+ defaultProp: defaultOpen,
57
+ onChange: onOpenChange,
58
+ });
59
+
60
+ const chainOfThoughtContext = useMemo(
61
+ () => ({ isOpen, setIsOpen }),
62
+ [isOpen, setIsOpen]
63
+ );
64
+
65
+ return (
66
+ <ChainOfThoughtContext.Provider value={chainOfThoughtContext}>
67
+ <div
68
+ className={cn("not-prose max-w-prose space-y-4", className)}
69
+ {...props}
70
+ >
71
+ {children}
72
+ </div>
73
+ </ChainOfThoughtContext.Provider>
74
+ );
75
+ }
76
+ );
77
+
78
+ export type ChainOfThoughtHeaderProps = ComponentProps<
79
+ typeof CollapsibleTrigger
80
+ >;
81
+
82
+ export const ChainOfThoughtHeader = memo(
83
+ ({ className, children, ...props }: ChainOfThoughtHeaderProps) => {
84
+ const { isOpen, setIsOpen } = useChainOfThought();
85
+
86
+ return (
87
+ <Collapsible onOpenChange={setIsOpen} open={isOpen}>
88
+ <CollapsibleTrigger
89
+ className={cn(
90
+ "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",
91
+ className
92
+ )}
93
+ {...props}
94
+ >
95
+ <BrainIcon className="size-4" />
96
+ <span className="flex-1 text-left">
97
+ {children ?? "Chain of Thought"}
98
+ </span>
99
+ <ChevronDownIcon
100
+ className={cn(
101
+ "size-4 transition-transform",
102
+ isOpen ? "rotate-180" : "rotate-0"
103
+ )}
104
+ />
105
+ </CollapsibleTrigger>
106
+ </Collapsible>
107
+ );
108
+ }
109
+ );
110
+
111
+ export type ChainOfThoughtStepProps = ComponentProps<"div"> & {
112
+ icon?: LucideIcon;
113
+ label: string;
114
+ description?: string;
115
+ status?: "complete" | "active" | "pending";
116
+ };
117
+
118
+ export const ChainOfThoughtStep = memo(
119
+ ({
120
+ className,
121
+ icon: Icon = DotIcon,
122
+ label,
123
+ description,
124
+ status = "complete",
125
+ children,
126
+ ...props
127
+ }: ChainOfThoughtStepProps) => {
128
+ const statusStyles = {
129
+ complete: "text-muted-foreground",
130
+ active: "text-foreground",
131
+ pending: "text-muted-foreground/50",
132
+ };
133
+
134
+ return (
135
+ <div
136
+ className={cn(
137
+ "flex gap-2 text-sm",
138
+ statusStyles[status],
139
+ "fade-in-0 slide-in-from-top-2 animate-in",
140
+ className
141
+ )}
142
+ {...props}
143
+ >
144
+ <div className="relative mt-0.5">
145
+ <Icon className="size-4" />
146
+ <div className="-mx-px absolute top-7 bottom-0 left-1/2 w-px bg-border" />
147
+ </div>
148
+ <div className="flex-1 space-y-2">
149
+ <div>{label}</div>
150
+ {description && (
151
+ <div className="text-muted-foreground text-xs">{description}</div>
152
+ )}
153
+ {children}
154
+ </div>
155
+ </div>
156
+ );
157
+ }
158
+ );
159
+
160
+ export type ChainOfThoughtSearchResultsProps = ComponentProps<"div">;
161
+
162
+ export const ChainOfThoughtSearchResults = memo(
163
+ ({ className, ...props }: ChainOfThoughtSearchResultsProps) => (
164
+ <div className={cn("flex items-center gap-2", className)} {...props} />
165
+ )
166
+ );
167
+
168
+ export type ChainOfThoughtSearchResultProps = ComponentProps<typeof Badge>;
169
+
170
+ export const ChainOfThoughtSearchResult = memo(
171
+ ({ className, children, ...props }: ChainOfThoughtSearchResultProps) => (
172
+ <Badge
173
+ className={cn("gap-1 px-2 py-0.5 font-normal text-xs", className)}
174
+ variant="secondary"
175
+ {...props}
176
+ >
177
+ {children}
178
+ </Badge>
179
+ )
180
+ );
181
+
182
+ export type ChainOfThoughtContentProps = ComponentProps<
183
+ typeof CollapsibleContent
184
+ >;
185
+
186
+ export const ChainOfThoughtContent = memo(
187
+ ({ className, children, ...props }: ChainOfThoughtContentProps) => {
188
+ const { isOpen } = useChainOfThought();
189
+
190
+ return (
191
+ <Collapsible open={isOpen}>
192
+ <CollapsibleContent
193
+ className={cn(
194
+ "mt-2 space-y-3",
195
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
196
+ className
197
+ )}
198
+ {...props}
199
+ >
200
+ {children}
201
+ </CollapsibleContent>
202
+ </Collapsible>
203
+ );
204
+ }
205
+ );
206
+
207
+ export type ChainOfThoughtImageProps = ComponentProps<"div"> & {
208
+ caption?: string;
209
+ };
210
+
211
+ export const ChainOfThoughtImage = memo(
212
+ ({ className, children, caption, ...props }: ChainOfThoughtImageProps) => (
213
+ <div className={cn("mt-2 space-y-2", className)} {...props}>
214
+ <div className="relative flex max-h-88 items-center justify-center overflow-hidden rounded-lg bg-muted p-3">
215
+ {children}
216
+ </div>
217
+ {caption && <p className="text-muted-foreground text-xs">{caption}</p>}
218
+ </div>
219
+ )
220
+ );
221
+
222
+ ChainOfThought.displayName = "ChainOfThought";
223
+ ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
224
+ ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
225
+ ChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults";
226
+ ChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult";
227
+ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
228
+ ChainOfThoughtImage.displayName = "ChainOfThoughtImage";
@@ -0,0 +1,179 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { cn } from "@/lib/utils";
5
+ import type { Element } from "hast";
6
+ import { CheckIcon, CopyIcon } from "lucide-react";
7
+ import {
8
+ type ComponentProps,
9
+ createContext,
10
+ type HTMLAttributes,
11
+ useContext,
12
+ useEffect,
13
+ useRef,
14
+ useState,
15
+ } from "react";
16
+ import { type BundledLanguage, codeToHtml, type ShikiTransformer } from "shiki";
17
+
18
+ type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {
19
+ code: string;
20
+ language: BundledLanguage;
21
+ showLineNumbers?: boolean;
22
+ };
23
+
24
+ type CodeBlockContextType = {
25
+ code: string;
26
+ };
27
+
28
+ const CodeBlockContext = createContext<CodeBlockContextType>({
29
+ code: "",
30
+ });
31
+
32
+ const lineNumberTransformer: ShikiTransformer = {
33
+ name: "line-numbers",
34
+ line(node: Element, line: number) {
35
+ node.children.unshift({
36
+ type: "element",
37
+ tagName: "span",
38
+ properties: {
39
+ className: [
40
+ "inline-block",
41
+ "min-w-10",
42
+ "mr-4",
43
+ "text-right",
44
+ "select-none",
45
+ "text-muted-foreground",
46
+ ],
47
+ },
48
+ children: [{ type: "text", value: String(line) }],
49
+ });
50
+ },
51
+ };
52
+
53
+ export async function highlightCode(
54
+ code: string,
55
+ language: BundledLanguage,
56
+ showLineNumbers = false
57
+ ) {
58
+ const transformers: ShikiTransformer[] = showLineNumbers
59
+ ? [lineNumberTransformer]
60
+ : [];
61
+
62
+ return await Promise.all([
63
+ codeToHtml(code, {
64
+ lang: language,
65
+ theme: "one-light",
66
+ transformers,
67
+ }),
68
+ codeToHtml(code, {
69
+ lang: language,
70
+ theme: "one-dark-pro",
71
+ transformers,
72
+ }),
73
+ ]);
74
+ }
75
+
76
+ export const CodeBlock = ({
77
+ code,
78
+ language,
79
+ showLineNumbers = false,
80
+ className,
81
+ children,
82
+ ...props
83
+ }: CodeBlockProps) => {
84
+ const [html, setHtml] = useState<string>("");
85
+ const [darkHtml, setDarkHtml] = useState<string>("");
86
+ const mounted = useRef(false);
87
+
88
+ useEffect(() => {
89
+ highlightCode(code, language, showLineNumbers).then(([light, dark]) => {
90
+ if (!mounted.current) {
91
+ setHtml(light);
92
+ setDarkHtml(dark);
93
+ mounted.current = true;
94
+ }
95
+ });
96
+
97
+ return () => {
98
+ mounted.current = false;
99
+ };
100
+ }, [code, language, showLineNumbers]);
101
+
102
+ return (
103
+ <CodeBlockContext.Provider value={{ code }}>
104
+ <div
105
+ className={cn(
106
+ "group relative w-full overflow-hidden rounded-md border bg-background text-foreground",
107
+ className
108
+ )}
109
+ {...props}
110
+ >
111
+ <div className="relative">
112
+ <div
113
+ className="overflow-hidden dark:hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
114
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
115
+ dangerouslySetInnerHTML={{ __html: html }}
116
+ />
117
+ <div
118
+ className="hidden overflow-hidden dark:block [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&_code]:font-mono [&_code]:text-sm"
119
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: "this is needed."
120
+ dangerouslySetInnerHTML={{ __html: darkHtml }}
121
+ />
122
+ {children && (
123
+ <div className="absolute top-2 right-2 flex items-center gap-2">
124
+ {children}
125
+ </div>
126
+ )}
127
+ </div>
128
+ </div>
129
+ </CodeBlockContext.Provider>
130
+ );
131
+ };
132
+
133
+ export type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {
134
+ onCopy?: () => void;
135
+ onError?: (error: Error) => void;
136
+ timeout?: number;
137
+ };
138
+
139
+ export const CodeBlockCopyButton = ({
140
+ onCopy,
141
+ onError,
142
+ timeout = 2000,
143
+ children,
144
+ className,
145
+ ...props
146
+ }: CodeBlockCopyButtonProps) => {
147
+ const [isCopied, setIsCopied] = useState(false);
148
+ const { code } = useContext(CodeBlockContext);
149
+
150
+ const copyToClipboard = async () => {
151
+ if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
152
+ onError?.(new Error("Clipboard API not available"));
153
+ return;
154
+ }
155
+
156
+ try {
157
+ await navigator.clipboard.writeText(code);
158
+ setIsCopied(true);
159
+ onCopy?.();
160
+ setTimeout(() => setIsCopied(false), timeout);
161
+ } catch (error) {
162
+ onError?.(error as Error);
163
+ }
164
+ };
165
+
166
+ const Icon = isCopied ? CheckIcon : CopyIcon;
167
+
168
+ return (
169
+ <Button
170
+ className={cn("shrink-0", className)}
171
+ onClick={copyToClipboard}
172
+ size="icon"
173
+ variant="ghost"
174
+ {...props}
175
+ >
176
+ {children ?? <Icon size={14} />}
177
+ </Button>
178
+ );
179
+ };