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,364 @@
1
+ /**
2
+ * CommentBox Block
3
+ *
4
+ * Floating inline panel that appears for viewing/adding comments
5
+ * Integrates comment thread display and reply functionality
6
+ * Block layer: uses primitives and AI elements only
7
+ */
8
+
9
+ import React from 'react'
10
+ import type { JSONContent } from '@tiptap/react'
11
+
12
+ /**
13
+ * Extract plain text from Tiptap JSONContent
14
+ */
15
+ function extractTextFromJSONContent(content: JSONContent): string {
16
+ if (!content) return ''
17
+
18
+ let text = ''
19
+
20
+ // Add text from current node
21
+ if (content.text) {
22
+ text += content.text
23
+ }
24
+
25
+ // Recursively extract text from child nodes
26
+ if (content.content && Array.isArray(content.content)) {
27
+ for (const child of content.content) {
28
+ const childText = extractTextFromJSONContent(child)
29
+ // Add space between block-level elements
30
+ if (childText && text && content.type !== 'text') {
31
+ text += ' '
32
+ }
33
+ text += childText
34
+ }
35
+ }
36
+
37
+ return text
38
+ }
39
+ import {
40
+ Popover,
41
+ PopoverContent,
42
+ Card,
43
+ Badge,
44
+ } from '@/components/primitives'
45
+ import {
46
+ Conversation,
47
+ ConversationContent,
48
+ } from '@/components/ai-elements/conversation'
49
+ import {
50
+ Message,
51
+ MessageAvatar,
52
+ MessageContent,
53
+ } from '@/components/ai-elements/message'
54
+ import {
55
+ InputGroup,
56
+ InputGroupTextarea,
57
+ InputGroupButton,
58
+ } from '@/components/primitives/InputGroup'
59
+ import { Send } from 'lucide-react'
60
+ import { formatCommentDate } from '@/utils/ai-editor'
61
+ import type { CommentBoxProps, Comment } from '@/types/ai-editor'
62
+ import { cn } from '@/lib/utils'
63
+
64
+ /**
65
+ * Internal component for rendering comment thread
66
+ */
67
+ const CommentThreadDisplay = React.memo<{
68
+ comments: Comment[]
69
+ currentUserId: string
70
+ onReply?: (commentId: string, content: string) => void
71
+ }>(({ comments, currentUserId, onReply }) => {
72
+ return (
73
+ <Conversation className="flex-none">
74
+ <ConversationContent className="p-0 space-y-3">
75
+ {comments.map((comment) => (
76
+ <Message
77
+ key={comment.id}
78
+ from={comment.userId === currentUserId ? 'user' : 'assistant'}
79
+ className="!bg-transparent !mx-0 !px-0"
80
+ >
81
+ <MessageAvatar
82
+ src={comment.avatarSrc || '/default-avatar.png'}
83
+ name={comment.userName}
84
+ />
85
+ <MessageContent variant="flat" className="!bg-transparent !px-0 !py-0">
86
+ <div className={cn(
87
+ "flex items-center gap-2 text-xs text-muted-foreground",
88
+ comment.userId === currentUserId && "flex-row-reverse"
89
+ )}>
90
+ <span className="font-medium text-foreground">
91
+ {comment.userName}
92
+ </span>
93
+ <span>{formatCommentDate(comment.timestamp)}</span>
94
+ {comment.isEdited && <span className="italic">(edited)</span>}
95
+ </div>
96
+ <div className="text-sm">
97
+ {typeof comment.contentRich === 'string'
98
+ ? comment.contentRich
99
+ : extractTextFromJSONContent(comment.contentRich)}
100
+ </div>
101
+ </MessageContent>
102
+ </Message>
103
+ ))}
104
+
105
+ <InputGroup className="w-full">
106
+ <InputGroupTextarea
107
+ placeholder="Reply..."
108
+ className="min-h-[40px] max-h-[200px] py-2 resize-none min-w-0"
109
+ onKeyDown={(e) => {
110
+ if (e.key === 'Enter' && !e.shiftKey) {
111
+ e.preventDefault()
112
+ const value = e.currentTarget.value
113
+ if (value.trim() && comments.length > 0 && onReply) {
114
+ onReply(comments[0].id, value)
115
+ e.currentTarget.value = ''
116
+ }
117
+ }
118
+ }}
119
+ />
120
+ <InputGroupButton
121
+ type="button"
122
+ variant="ghost"
123
+ size="icon-sm"
124
+ onClick={(e) => {
125
+ const textarea = e.currentTarget.parentElement?.querySelector('textarea')
126
+ if (textarea) {
127
+ const value = textarea.value
128
+ if (value.trim() && comments.length > 0 && onReply) {
129
+ onReply(comments[0].id, value)
130
+ textarea.value = ''
131
+ }
132
+ }
133
+ }}
134
+ >
135
+ <Send className="h-4 w-4" />
136
+ </InputGroupButton>
137
+ </InputGroup>
138
+ </ConversationContent>
139
+ </Conversation>
140
+ )
141
+ })
142
+
143
+ CommentThreadDisplay.displayName = 'CommentThreadDisplay'
144
+
145
+ export const CommentBox = React.memo<CommentBoxProps>(
146
+ ({
147
+ annotation,
148
+ position,
149
+ visible,
150
+ currentUserId,
151
+ onClose,
152
+ onCommentAdd,
153
+ onCommentReply,
154
+ }) => {
155
+ if (!visible) return null
156
+
157
+ const handleInputSubmit = (textareaElement: HTMLTextAreaElement) => {
158
+ const value = textareaElement.value
159
+ if (!value.trim()) return
160
+
161
+ // Convert string to JSONContent format
162
+ const content = {
163
+ type: 'doc',
164
+ content: [
165
+ {
166
+ type: 'paragraph',
167
+ content: [{ type: 'text', text: value }],
168
+ },
169
+ ],
170
+ }
171
+
172
+ if (!annotation) {
173
+ // New comment
174
+ onCommentAdd?.(content)
175
+ onClose?.()
176
+ } else {
177
+ // Reply to existing annotation
178
+ onCommentReply?.(annotation.id, content)
179
+ textareaElement.value = ''
180
+ }
181
+ }
182
+
183
+ const renderContent = () => {
184
+ // New comment (no annotation)
185
+ if (!annotation) {
186
+ return (
187
+ <div className="p-4">
188
+ <InputGroup className="w-full">
189
+ <InputGroupTextarea
190
+ placeholder="Add a comment..."
191
+ autoFocus
192
+ className="min-h-[40px] max-h-[200px] py-2 resize-none min-w-0"
193
+ onKeyDown={(e) => {
194
+ if (e.key === 'Enter' && !e.shiftKey) {
195
+ e.preventDefault()
196
+ handleInputSubmit(e.currentTarget)
197
+ }
198
+ }}
199
+ />
200
+ <InputGroupButton
201
+ type="button"
202
+ variant="ghost"
203
+ size="icon-sm"
204
+ onClick={(e) => {
205
+ const textarea = e.currentTarget.parentElement?.querySelector('textarea')
206
+ if (textarea) {
207
+ handleInputSubmit(textarea)
208
+ }
209
+ }}
210
+ >
211
+ <Send className="h-4 w-4" />
212
+ </InputGroupButton>
213
+ </InputGroup>
214
+ </div>
215
+ )
216
+ }
217
+
218
+ // Existing comment annotation
219
+ if (annotation.type === 'comment') {
220
+ return (
221
+ <div className="p-4">
222
+ <CommentThreadDisplay
223
+ comments={annotation.data.thread}
224
+ currentUserId={currentUserId}
225
+ onReply={(_, content) => {
226
+ // Convert string to JSONContent format
227
+ const jsonContent = {
228
+ type: 'doc',
229
+ content: [
230
+ {
231
+ type: 'paragraph',
232
+ content: [{ type: 'text', text: content }],
233
+ },
234
+ ],
235
+ }
236
+ onCommentReply?.(annotation.id, jsonContent)
237
+ }}
238
+ />
239
+ </div>
240
+ )
241
+ }
242
+
243
+ // Suggestion annotation (no diff display, just reason and comments)
244
+ if (annotation.type === 'suggestion') {
245
+ const { action } = annotation.data
246
+ const badgeConfig = {
247
+ insert: { label: 'Added', className: 'bg-green-500' },
248
+ delete: { label: 'Removed', className: 'bg-red-500' },
249
+ modify: { label: 'Modified', className: 'bg-yellow-500' },
250
+ }
251
+ const badge = badgeConfig[action]
252
+
253
+ return (
254
+ <div className="flex flex-col gap-1 p-1">
255
+ <div className="flex items-center gap-2">
256
+ {badge && (
257
+ <Badge variant="default" className={badge.className}>
258
+ {badge.label}
259
+ </Badge>
260
+ )}
261
+ {annotation.data.reason && (
262
+ <span className="text-sm text-muted-foreground">
263
+ {annotation.data.reason}
264
+ </span>
265
+ )}
266
+ </div>
267
+ {annotation.data.thread.length > 0 && (
268
+ <>
269
+ <div className="h-px bg-border" />
270
+ <CommentThreadDisplay
271
+ comments={annotation.data.thread}
272
+ currentUserId={currentUserId}
273
+ onReply={(_, content) => {
274
+ // Convert string to JSONContent format
275
+ const jsonContent = {
276
+ type: 'doc',
277
+ content: [
278
+ {
279
+ type: 'paragraph',
280
+ content: [{ type: 'text', text: content }],
281
+ },
282
+ ],
283
+ }
284
+ onCommentReply?.(annotation.id, jsonContent)
285
+ }}
286
+ />
287
+ </>
288
+ )}
289
+ </div>
290
+ )
291
+ }
292
+
293
+ // Block addition annotation
294
+ if (annotation.type === 'block-addition') {
295
+ return (
296
+ <div className="flex flex-col gap-1 p-1">
297
+ <div className="flex items-center gap-2">
298
+ <Badge variant="default" className="bg-green-500">
299
+ Added
300
+ </Badge>
301
+ {annotation.data.reason && (
302
+ <span className="text-sm text-muted-foreground">
303
+ {annotation.data.reason}
304
+ </span>
305
+ )}
306
+ </div>
307
+ {annotation.data.thread.length > 0 && (
308
+ <>
309
+ <div className="h-px bg-border" />
310
+ <CommentThreadDisplay
311
+ comments={annotation.data.thread}
312
+ currentUserId={currentUserId}
313
+ onReply={(_, content) => {
314
+ // Convert string to JSONContent format
315
+ const jsonContent = {
316
+ type: 'doc',
317
+ content: [
318
+ {
319
+ type: 'paragraph',
320
+ content: [{ type: 'text', text: content }],
321
+ },
322
+ ],
323
+ }
324
+ onCommentReply?.(annotation.id, jsonContent)
325
+ }}
326
+ />
327
+ </>
328
+ )}
329
+ </div>
330
+ )
331
+ }
332
+
333
+ return null
334
+ }
335
+
336
+ return (
337
+ <Popover open={visible} modal={false}>
338
+ <PopoverContent
339
+ className="w-[400px] p-0"
340
+ style={{
341
+ position: 'absolute',
342
+ left: position.x,
343
+ top: position.y,
344
+ }}
345
+ side="right"
346
+ align="start"
347
+ onEscapeKeyDown={onClose}
348
+ onPointerDownOutside={(e) => {
349
+ // Only close if clicking outside the popover AND outside any annotation
350
+ const target = e.target as HTMLElement
351
+ const isAnnotation = target.closest('[data-comment-id], [data-suggestion-id], [data-addition-id]')
352
+ if (!isAnnotation) {
353
+ onClose?.()
354
+ }
355
+ }}
356
+ >
357
+ <Card className="border-0 shadow-none p-0">{renderContent()}</Card>
358
+ </PopoverContent>
359
+ </Popover>
360
+ )
361
+ }
362
+ )
363
+
364
+ CommentBox.displayName = 'CommentBox'
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CommentBox Block
3
+ *
4
+ * Exports the CommentBox component
5
+ */
6
+
7
+ export { CommentBox } from './CommentBox'
8
+ export type { CommentBoxProps } from '@/types/ai-editor'
@@ -0,0 +1,151 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Confirmation } from "./Confirmation";
3
+ import * as React from "react";
4
+
5
+ const meta: Meta<typeof Confirmation> = {
6
+ title: "Blocks/Confirmation",
7
+ component: Confirmation,
8
+ parameters: {
9
+ layout: "padded",
10
+ },
11
+ } satisfies Meta<typeof Confirmation>;
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+
16
+ /**
17
+ * Approval requested state with title and actions
18
+ */
19
+ export const ApprovalRequested: Story = {
20
+ args: {
21
+ title: "Do you want to delete this file?",
22
+ state: "approval-requested",
23
+ approval: {},
24
+ onApprove: () => console.log("Approved"),
25
+ onReject: () => console.log("Rejected"),
26
+ children: (
27
+ <p className="text-sm">This action cannot be undone. The file will be permanently deleted.</p>
28
+ ),
29
+ },
30
+ };
31
+
32
+ /**
33
+ * Approved state showing confirmed action
34
+ */
35
+ export const Approved: Story = {
36
+ args: {
37
+ title: "File deletion confirmed",
38
+ state: "approval-responded",
39
+ approval: { approved: true },
40
+ onApprove: () => console.log("Approved"),
41
+ onReject: () => console.log("Rejected"),
42
+ children: (
43
+ <p className="text-sm">The file has been successfully deleted.</p>
44
+ ),
45
+ },
46
+ };
47
+
48
+ /**
49
+ * Rejected state showing cancelled action
50
+ */
51
+ export const Rejected: Story = {
52
+ args: {
53
+ title: "File deletion cancelled",
54
+ state: "approval-responded",
55
+ approval: { approved: false },
56
+ onApprove: () => console.log("Approved"),
57
+ onReject: () => console.log("Rejected"),
58
+ children: (
59
+ <p className="text-sm">The file has not been deleted.</p>
60
+ ),
61
+ },
62
+ };
63
+
64
+ /**
65
+ * Interactive example with state management
66
+ */
67
+ export const WithStateManagement: Story = {
68
+ render: () => {
69
+ const [state, setState] = React.useState<"approval-requested" | "approval-responded">("approval-requested");
70
+ const [approval, setApproval] = React.useState<{ approved: boolean } | undefined>(undefined);
71
+
72
+ const handleApprove = () => {
73
+ setState("approval-responded");
74
+ setApproval({ approved: true });
75
+ console.log("Approved");
76
+ };
77
+
78
+ const handleReject = () => {
79
+ setState("approval-responded");
80
+ setApproval({ approved: false });
81
+ console.log("Rejected");
82
+ };
83
+
84
+ const handleReset = () => {
85
+ setState("approval-requested");
86
+ setApproval(undefined);
87
+ };
88
+
89
+ return (
90
+ <div className="space-y-4">
91
+ {state === "approval-responded" && (
92
+ <div className="rounded-md border border-border bg-muted p-4">
93
+ <p className="text-sm">
94
+ {approval?.approved && "✅ Action approved!"}
95
+ {approval?.approved === false && "❌ Action rejected!"}
96
+ </p>
97
+ <button
98
+ onClick={handleReset}
99
+ className="mt-2 text-sm text-primary underline"
100
+ >
101
+ Reset to approval requested
102
+ </button>
103
+ </div>
104
+ )}
105
+
106
+ <Confirmation
107
+ title="Review and confirm this action"
108
+ state={state}
109
+ approval={approval}
110
+ onApprove={handleApprove}
111
+ onReject={handleReject}
112
+ >
113
+ <p className="text-sm">This is the content that needs approval. Click the buttons below to approve or reject.</p>
114
+ </Confirmation>
115
+ </div>
116
+ );
117
+ },
118
+ };
119
+
120
+ /**
121
+ * Without title - content only
122
+ */
123
+ export const WithoutTitle: Story = {
124
+ args: {
125
+ state: "approval-requested",
126
+ approval: {},
127
+ onApprove: () => console.log("Approved"),
128
+ onReject: () => console.log("Rejected"),
129
+ children: (
130
+ <>
131
+ <p className="text-sm font-medium">Are you sure you want to proceed?</p>
132
+ <p className="text-xs text-muted-foreground mt-1">This action cannot be undone.</p>
133
+ </>
134
+ ),
135
+ },
136
+ };
137
+
138
+ /**
139
+ * With only approve action
140
+ */
141
+ export const OnlyApprove: Story = {
142
+ args: {
143
+ title: "Acknowledge this message",
144
+ state: "approval-requested",
145
+ approval: {},
146
+ onApprove: () => console.log("Acknowledged"),
147
+ children: (
148
+ <p className="text-sm">Please review this information before continuing.</p>
149
+ ),
150
+ },
151
+ };
@@ -0,0 +1,93 @@
1
+ import * as React from "react";
2
+ import {
3
+ Confirmation as AIConfirmation,
4
+ ConfirmationTitle,
5
+ ConfirmationActions,
6
+ ConfirmationAction,
7
+ } from "@/components/ai-elements/confirmation";
8
+ import { Icon } from "@/components/primitives/Icon";
9
+ import { cn } from "@/lib/utils";
10
+
11
+ /**
12
+ * Confirmation Block
13
+ *
14
+ * Approval workflow component that internally manages action buttons.
15
+ * Wraps content with Confirmation AI element and provides approve/reject actions.
16
+ */
17
+
18
+ // Define our own types since ToolUIPart doesn't exist in current AI SDK
19
+ type ToolUIState =
20
+ | "input-streaming"
21
+ | "input-available"
22
+ | "approval-requested"
23
+ | "approval-responded"
24
+ | "output-denied"
25
+ | "output-available"
26
+ | "output-error";
27
+
28
+ type ToolApproval = {
29
+ approved?: boolean;
30
+ };
31
+
32
+ export interface ConfirmationProps {
33
+ /**
34
+ * Title text displayed at the top of the confirmation
35
+ */
36
+ title?: string;
37
+ /**
38
+ * Callback when user approves
39
+ */
40
+ onApprove?: () => void;
41
+ /**
42
+ * Callback when user rejects
43
+ */
44
+ onReject?: () => void;
45
+ /**
46
+ * Confirmation state
47
+ */
48
+ state: ToolUIState;
49
+ /**
50
+ * Approval data
51
+ */
52
+ approval?: ToolApproval;
53
+ /**
54
+ * Content to display (e.g., FileQueue)
55
+ */
56
+ children: React.ReactNode;
57
+ /**
58
+ * Additional CSS classes
59
+ */
60
+ className?: string;
61
+ }
62
+
63
+ /**
64
+ * Confirmation component - manages approval workflow with internal action buttons
65
+ */
66
+ export const Confirmation = React.memo<ConfirmationProps>(
67
+ ({ title, onApprove, onReject, state, approval, children, className }) => {
68
+ return (
69
+ <AIConfirmation state={state} approval={approval} className={cn(className)}>
70
+ {title && <ConfirmationTitle>{title}</ConfirmationTitle>}
71
+
72
+ {children}
73
+
74
+ <ConfirmationActions>
75
+ {onReject && (
76
+ <ConfirmationAction variant="outline" onClick={onReject}>
77
+ <Icon name="x" size="sm" className="mr-2" />
78
+ Reject All
79
+ </ConfirmationAction>
80
+ )}
81
+ {onApprove && (
82
+ <ConfirmationAction variant="default" onClick={onApprove}>
83
+ <Icon name="check" size="sm" className="mr-2" />
84
+ Approve All
85
+ </ConfirmationAction>
86
+ )}
87
+ </ConfirmationActions>
88
+ </AIConfirmation>
89
+ );
90
+ }
91
+ );
92
+
93
+ Confirmation.displayName = "Confirmation";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Confirmation Block
3
+ *
4
+ * Central export point for the Confirmation block component and its related types.
5
+ */
6
+ export { Confirmation } from "./Confirmation";
7
+ export type { ConfirmationProps } from "./Confirmation";
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { DataTable } from './DataTable'
3
+ import { createColumnHelper } from '@tanstack/react-table'
4
+
5
+ const meta = {
6
+ title: 'Composites/DataTable',
7
+ component: DataTable,
8
+ tags: ['autodocs'],
9
+ parameters: { layout: 'padded' },
10
+ } satisfies Meta<typeof DataTable>
11
+
12
+ export default meta
13
+ type Story = StoryObj<typeof meta>
14
+
15
+ type Person = { id: number; name: string; email: string; role: string }
16
+ const data: Person[] = [
17
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
18
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
19
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
20
+ ]
21
+
22
+ const columnHelper = createColumnHelper<Person>()
23
+ const columns = [
24
+ columnHelper.accessor('name', { header: 'Name' }),
25
+ columnHelper.accessor('email', { header: 'Email' }),
26
+ columnHelper.accessor('role', { header: 'Role' }),
27
+ ]
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ data,
32
+ columns,
33
+ searchColumn: 'name',
34
+ },
35
+ }