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,255 @@
1
+ /**
2
+ * DocumentEditorWithComments Section
3
+ *
4
+ * Combines DocumentEditor and CommentBox blocks to provide inline commenting functionality
5
+ * Section layer: composes blocks and manages UI state
6
+ */
7
+
8
+ import React, { useState, useCallback, useMemo, useEffect } from 'react'
9
+ import { DocumentEditor } from '@/components/composites/DocumentEditor'
10
+ import { CommentBox } from '@/components/composites/CommentBox'
11
+ import { Card } from '@/components/primitives/Card'
12
+ import type { JSONContent } from '@tiptap/core'
13
+ import type { Annotation, CommentAnnotation, Range } from '@/types/ai-editor'
14
+
15
+ /**
16
+ * Props for DocumentEditorWithComments section
17
+ */
18
+ export interface DocumentEditorWithCommentsProps {
19
+ /**
20
+ * Document content - can be either:
21
+ * - JSONContent: Tiptap's JSON format (default)
22
+ * - string: Markdown string (when format='markdown')
23
+ */
24
+ content: JSONContent | string
25
+ /**
26
+ * Content format - determines how content prop is interpreted
27
+ * @default 'json'
28
+ */
29
+ format?: 'json' | 'markdown'
30
+ /** Array of annotations to display */
31
+ annotations: Annotation[]
32
+ /** ID of currently selected annotation */
33
+ selectedAnnotationId?: string
34
+ /** Current user ID for comment ownership */
35
+ currentUserId: string
36
+ /** Current user name for new comments and replies */
37
+ currentUserName: string
38
+ /** Whether editor is in read-only mode */
39
+ readOnly?: boolean
40
+ /** Callback when new annotation is added */
41
+ onAnnotationAdd?: (annotation: Annotation) => void
42
+ /** Callback when annotation is updated (e.g., reply added) */
43
+ onAnnotationUpdate?: (annotation: Annotation) => void
44
+ /** Additional CSS classes */
45
+ className?: string
46
+ }
47
+
48
+ export const DocumentEditorWithComments = React.memo<DocumentEditorWithCommentsProps>(
49
+ ({
50
+ content,
51
+ format = 'json',
52
+ annotations,
53
+ selectedAnnotationId,
54
+ currentUserId,
55
+ currentUserName,
56
+ readOnly = false,
57
+ onAnnotationAdd,
58
+ onAnnotationUpdate,
59
+ className,
60
+ }) => {
61
+ // Internal UI state
62
+ const [commentBoxVisible, setCommentBoxVisible] = useState(false)
63
+ const [commentBoxPosition, setCommentBoxPosition] = useState({ x: 0, y: 0 })
64
+ const [activeAnnotation, setActiveAnnotation] = useState<Annotation | undefined>()
65
+ const [selectedRange, setSelectedRange] = useState<Range | undefined>()
66
+ const [hoveredAnnotationId, setHoveredAnnotationId] = useState<string | null>(null)
67
+
68
+ /**
69
+ * Sync activeAnnotation with latest data from annotations array
70
+ * This ensures CommentBox shows updated data when replies are added
71
+ */
72
+ useEffect(() => {
73
+ if (activeAnnotation && commentBoxVisible) {
74
+ const updatedAnnotation = annotations.find((a) => a.id === activeAnnotation.id)
75
+ if (updatedAnnotation) {
76
+ setActiveAnnotation(updatedAnnotation)
77
+ }
78
+ }
79
+ }, [annotations, activeAnnotation, commentBoxVisible])
80
+
81
+ /**
82
+ * Handle annotation click - show CommentBox near the annotation
83
+ */
84
+ const handleAnnotationClick = useCallback(
85
+ (annotationId: string, position: { x: number; y: number }) => {
86
+ const annotation = annotations.find((a) => a.id === annotationId)
87
+ if (annotation) {
88
+ setActiveAnnotation(annotation)
89
+ setSelectedRange(undefined)
90
+ setCommentBoxPosition(position)
91
+ setCommentBoxVisible(true)
92
+ }
93
+ },
94
+ [annotations]
95
+ )
96
+
97
+ /**
98
+ * Handle text selection - show CommentBox for new comment
99
+ */
100
+ const handleTextSelect = useCallback(
101
+ (range: Range, text: string, position?: { x: number; y: number }) => {
102
+ if (readOnly || !text.trim()) return
103
+
104
+ // Show CommentBox for new comment
105
+ setActiveAnnotation(undefined)
106
+ setSelectedRange(range)
107
+
108
+ // Use calculated position from DocumentEditor, or fallback to default
109
+ const finalPosition = position || { x: 600, y: 100 }
110
+ setCommentBoxPosition(finalPosition)
111
+ setCommentBoxVisible(true)
112
+ },
113
+ [readOnly]
114
+ )
115
+
116
+ /**
117
+ * Handle new comment submission
118
+ */
119
+ const handleCommentAdd = useCallback(
120
+ (content: JSONContent) => {
121
+ if (!selectedRange || !onAnnotationAdd) return
122
+
123
+ // Create new comment annotation
124
+ const newAnnotation: CommentAnnotation = {
125
+ type: 'comment',
126
+ id: `comment-${Date.now()}`,
127
+ range: selectedRange,
128
+ createdAt: Date.now(),
129
+ userId: currentUserId,
130
+ data: {
131
+ thread: [
132
+ {
133
+ id: `comment-msg-${Date.now()}`,
134
+ userId: currentUserId,
135
+ userName: currentUserName,
136
+ contentRich: content,
137
+ timestamp: Date.now(),
138
+ },
139
+ ],
140
+ },
141
+ }
142
+
143
+ onAnnotationAdd(newAnnotation)
144
+ setCommentBoxVisible(false)
145
+ setSelectedRange(undefined)
146
+ },
147
+ [selectedRange, currentUserId, currentUserName, onAnnotationAdd]
148
+ )
149
+
150
+ /**
151
+ * Handle reply to existing annotation
152
+ */
153
+ const handleCommentReply = useCallback(
154
+ (annotationId: string, content: JSONContent) => {
155
+ if (!onAnnotationUpdate) return
156
+
157
+ const annotation = annotations.find((a) => a.id === annotationId)
158
+ if (!annotation) return
159
+
160
+ // Add reply to the annotation's thread
161
+ const newComment = {
162
+ id: `comment-msg-${Date.now()}`,
163
+ userId: currentUserId,
164
+ userName: currentUserName,
165
+ contentRich: content,
166
+ timestamp: Date.now(),
167
+ }
168
+
169
+ let updatedAnnotation: Annotation
170
+
171
+ if (annotation.type === 'comment') {
172
+ updatedAnnotation = {
173
+ ...annotation,
174
+ data: {
175
+ ...annotation.data,
176
+ thread: [...annotation.data.thread, newComment],
177
+ },
178
+ }
179
+ } else if (annotation.type === 'suggestion') {
180
+ updatedAnnotation = {
181
+ ...annotation,
182
+ data: {
183
+ ...annotation.data,
184
+ thread: [...annotation.data.thread, newComment],
185
+ },
186
+ }
187
+ } else {
188
+ updatedAnnotation = {
189
+ ...annotation,
190
+ data: {
191
+ ...annotation.data,
192
+ thread: [...annotation.data.thread, newComment],
193
+ },
194
+ }
195
+ }
196
+
197
+ onAnnotationUpdate(updatedAnnotation)
198
+ },
199
+ [annotations, currentUserId, currentUserName, onAnnotationUpdate]
200
+ )
201
+
202
+ /**
203
+ * Handle annotation hover - update hover state
204
+ */
205
+ const handleAnnotationHover = useCallback((annotationId: string | null) => {
206
+ setHoveredAnnotationId(annotationId)
207
+ }, [])
208
+
209
+ /**
210
+ * Handle CommentBox close - clear pending comment highlight
211
+ */
212
+ const handleCommentBoxClose = useCallback(() => {
213
+ setCommentBoxVisible(false)
214
+ setActiveAnnotation(undefined)
215
+ setSelectedRange(undefined) // This clears the yellow highlight
216
+ }, [])
217
+
218
+ // Memoize props to prevent unnecessary re-renders of DocumentEditor
219
+ // This is critical because re-renders trigger setContent() which clears marks
220
+ const documentEditorProps = useMemo(
221
+ () => ({
222
+ content,
223
+ format,
224
+ annotations,
225
+ selectedAnnotationId,
226
+ hoveredAnnotationId,
227
+ pendingCommentRange: selectedRange, // Pass pending range to show yellow highlight
228
+ onTextSelect: handleTextSelect,
229
+ onAnnotationClick: handleAnnotationClick,
230
+ onAnnotationHover: handleAnnotationHover,
231
+ readOnly,
232
+ }),
233
+ [content, format, annotations, selectedAnnotationId, hoveredAnnotationId, selectedRange, handleTextSelect, handleAnnotationClick, handleAnnotationHover, readOnly]
234
+ )
235
+
236
+ return (
237
+ <Card className={className}>
238
+ <DocumentEditor {...documentEditorProps} />
239
+
240
+ <CommentBox
241
+ annotation={activeAnnotation}
242
+ position={commentBoxPosition}
243
+ visible={commentBoxVisible}
244
+ currentUserId={currentUserId}
245
+ onClose={handleCommentBoxClose}
246
+ onCommentAdd={handleCommentAdd}
247
+ onCommentReply={handleCommentReply}
248
+ />
249
+ </Card>
250
+ )
251
+ }
252
+ )
253
+
254
+ DocumentEditorWithComments.displayName = 'DocumentEditorWithComments'
255
+
@@ -0,0 +1,9 @@
1
+ /**
2
+ * DocumentEditorWithComments Section
3
+ *
4
+ * Exports the DocumentEditorWithComments section component
5
+ */
6
+
7
+ export { DocumentEditorWithComments } from './DocumentEditorWithComments'
8
+ export type { DocumentEditorWithCommentsProps } from './DocumentEditorWithComments'
9
+
@@ -0,0 +1,207 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { FileChangeQueue } from "./FileChangeQueue";
3
+ import type { FileChangeData } from "@/components/composites/FileQueue";
4
+
5
+ const meta: Meta<typeof FileChangeQueue> = {
6
+ title: "Blocks/FileChangeQueue",
7
+ component: FileChangeQueue,
8
+ parameters: {
9
+ layout: "padded",
10
+ },
11
+ } satisfies Meta<typeof FileChangeQueue>;
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+
16
+ // Sample file changes for stories
17
+ const mixedChanges: FileChangeData[] = [
18
+ {
19
+ id: "1",
20
+ filename: "Button.tsx",
21
+ status: "modified",
22
+ path: "src/components/primitives/Button/Button.tsx",
23
+ },
24
+ {
25
+ id: "2",
26
+ filename: "Icon.tsx",
27
+ status: "created",
28
+ path: "src/components/primitives/Icon/Icon.tsx",
29
+ },
30
+ {
31
+ id: "3",
32
+ filename: "FileQueue.tsx",
33
+ status: "created",
34
+ path: "src/components/composites/FileQueue/FileQueue.tsx",
35
+ },
36
+ {
37
+ id: "4",
38
+ filename: "OldComponent.tsx",
39
+ status: "deleted",
40
+ path: "src/components/deprecated/OldComponent.tsx",
41
+ },
42
+ {
43
+ id: "5",
44
+ filename: "README.md",
45
+ status: "modified",
46
+ path: "docs/README.md",
47
+ },
48
+ {
49
+ id: "6",
50
+ filename: "package.json",
51
+ status: "pending",
52
+ path: "package.json",
53
+ },
54
+ ];
55
+
56
+ /**
57
+ * Default state - approval requested with mixed file statuses
58
+ */
59
+ export const ApprovalRequested: Story = {
60
+ args: {
61
+ changes: mixedChanges,
62
+ title: "Review and approve these file changes",
63
+ state: "approval-requested",
64
+ approval: {},
65
+ onApprove: () => console.log("Approved all changes"),
66
+ onReject: () => console.log("Rejected all changes"),
67
+ },
68
+ };
69
+
70
+ /**
71
+ * Approved state - shows after approval
72
+ */
73
+ export const Approved: Story = {
74
+ args: {
75
+ changes: mixedChanges,
76
+ title: "Review and approve these file changes",
77
+ state: "approval-responded",
78
+ approval: { approved: true },
79
+ onApprove: () => console.log("Approved all changes"),
80
+ onReject: () => console.log("Rejected all changes"),
81
+ },
82
+ };
83
+
84
+ /**
85
+ * Rejected state - shows after rejection
86
+ */
87
+ export const Rejected: Story = {
88
+ args: {
89
+ changes: mixedChanges,
90
+ title: "Review and approve these file changes",
91
+ state: "approval-responded",
92
+ approval: { approved: false },
93
+ onApprove: () => console.log("Approved all changes"),
94
+ onReject: () => console.log("Rejected all changes"),
95
+ },
96
+ };
97
+
98
+ /**
99
+ * All modified files
100
+ */
101
+ export const AllModified: Story = {
102
+ args: {
103
+ changes: [
104
+ {
105
+ id: "1",
106
+ filename: "Button.tsx",
107
+ status: "modified",
108
+ path: "src/components/primitives/Button/Button.tsx",
109
+ },
110
+ {
111
+ id: "2",
112
+ filename: "Input.tsx",
113
+ status: "modified",
114
+ path: "src/components/primitives/Input/Input.tsx",
115
+ },
116
+ {
117
+ id: "3",
118
+ filename: "Card.tsx",
119
+ status: "modified",
120
+ path: "src/components/primitives/Card/Card.tsx",
121
+ },
122
+ ],
123
+ title: "Review modified files",
124
+ state: "approval-requested",
125
+ approval: {},
126
+ onApprove: () => console.log("Approved"),
127
+ onReject: () => console.log("Rejected"),
128
+ },
129
+ };
130
+
131
+ /**
132
+ * Large change set
133
+ */
134
+ export const LargeChangeSet: Story = {
135
+ args: {
136
+ changes: [
137
+ ...Array.from({ length: 15 }, (_, i) => ({
138
+ id: `modified-${i}`,
139
+ filename: `Component${i + 1}.tsx`,
140
+ status: "modified" as const,
141
+ path: `src/components/composites/Component${i + 1}/Component${i + 1}.tsx`,
142
+ })),
143
+ ...Array.from({ length: 10 }, (_, i) => ({
144
+ id: `created-${i}`,
145
+ filename: `NewComponent${i + 1}.tsx`,
146
+ status: "created" as const,
147
+ path: `src/components/new/NewComponent${i + 1}/NewComponent${i + 1}.tsx`,
148
+ })),
149
+ ...Array.from({ length: 5 }, (_, i) => ({
150
+ id: `deleted-${i}`,
151
+ filename: `OldComponent${i + 1}.tsx`,
152
+ status: "deleted" as const,
153
+ path: `src/components/deprecated/OldComponent${i + 1}.tsx`,
154
+ })),
155
+ ],
156
+ title: "Review 30 file changes",
157
+ state: "approval-requested",
158
+ approval: {},
159
+ onApprove: () => console.log("Approved"),
160
+ onReject: () => console.log("Rejected"),
161
+ },
162
+ };
163
+
164
+ /**
165
+ * Empty state (no changes) - should not render
166
+ */
167
+ export const EmptyState: Story = {
168
+ args: {
169
+ changes: [],
170
+ title: "No changes to review",
171
+ state: "approval-requested",
172
+ },
173
+ };
174
+
175
+ /**
176
+ * Without title
177
+ */
178
+ export const WithoutTitle: Story = {
179
+ args: {
180
+ changes: mixedChanges,
181
+ state: "approval-requested",
182
+ approval: {},
183
+ onApprove: () => console.log("Approved"),
184
+ onReject: () => console.log("Rejected"),
185
+ },
186
+ };
187
+
188
+ /**
189
+ * Single file change
190
+ */
191
+ export const SingleFile: Story = {
192
+ args: {
193
+ changes: [
194
+ {
195
+ id: "1",
196
+ filename: "Button.tsx",
197
+ status: "modified",
198
+ path: "src/components/primitives/Button/Button.tsx",
199
+ },
200
+ ],
201
+ title: "Review single file change",
202
+ state: "approval-requested",
203
+ approval: {},
204
+ onApprove: () => console.log("Approved"),
205
+ onReject: () => console.log("Rejected"),
206
+ },
207
+ };
@@ -0,0 +1,143 @@
1
+ import * as React from "react";
2
+ import { Confirmation, type ConfirmationProps } from "@/components/composites/Confirmation";
3
+ import { FileQueue, type FileGroup } from "@/components/composites/FileQueue";
4
+ import type { FileChangeData, FileStatus } from "@/components/composites/FileQueue";
5
+
6
+ /**
7
+ * FileChangeQueue Section
8
+ *
9
+ * Composes FileQueue block with Confirmation block for file approval workflow.
10
+ * Displays file modifications with approve/reject actions.
11
+ */
12
+
13
+ export interface FileChangeQueueProps {
14
+ /**
15
+ * Array of file changes to display
16
+ */
17
+ changes: FileChangeData[];
18
+ /**
19
+ * Title/prompt text for the confirmation
20
+ */
21
+ title?: string;
22
+ /**
23
+ * Confirmation state
24
+ */
25
+ state: ConfirmationProps["state"];
26
+ /**
27
+ * Approval data
28
+ */
29
+ approval?: ConfirmationProps["approval"];
30
+ /**
31
+ * Approve all changes callback
32
+ */
33
+ onApprove?: () => void;
34
+ /**
35
+ * Reject all changes callback
36
+ */
37
+ onReject?: () => void;
38
+ /**
39
+ * Additional CSS classes
40
+ */
41
+ className?: string;
42
+ }
43
+
44
+ /**
45
+ * Transform file changes to groups for FileQueue
46
+ * Maintains existing visual appearance with status-based grouping
47
+ */
48
+ const transformFileChangesToGroups = (
49
+ changes: FileChangeData[]
50
+ ): FileGroup[] => {
51
+ const statusConfig = {
52
+ modified: {
53
+ title: "Modified",
54
+ icon: "file-text",
55
+ iconColor: "text-blue-600 dark:text-blue-500",
56
+ },
57
+ created: {
58
+ title: "Created",
59
+ icon: "plus",
60
+ iconColor: "text-green-600 dark:text-green-500",
61
+ },
62
+ deleted: {
63
+ title: "Deleted",
64
+ icon: "x",
65
+ iconColor: "text-red-600 dark:text-red-500",
66
+ },
67
+ pending: {
68
+ title: "Pending",
69
+ icon: "loader-2",
70
+ iconColor: "text-yellow-600 dark:text-yellow-500",
71
+ },
72
+ };
73
+
74
+ const grouped: Record<FileStatus, FileChangeData[]> = {
75
+ modified: [],
76
+ created: [],
77
+ deleted: [],
78
+ pending: [],
79
+ };
80
+
81
+ changes.forEach((change) => {
82
+ grouped[change.status].push(change);
83
+ });
84
+
85
+ const statusOrder: FileStatus[] = ["modified", "created", "deleted", "pending"];
86
+
87
+ const result: FileGroup[] = [];
88
+
89
+ statusOrder.forEach((status) => {
90
+ const files = grouped[status];
91
+ if (files.length === 0) return;
92
+
93
+ const config = statusConfig[status];
94
+
95
+ result.push({
96
+ id: status,
97
+ title: config.title,
98
+ icon: config.icon,
99
+ iconColor: config.iconColor,
100
+ files: files.map((file) => ({
101
+ id: file.id,
102
+ name: file.filename,
103
+ path: file.path,
104
+ })),
105
+ defaultOpen: false,
106
+ });
107
+ });
108
+
109
+ return result;
110
+ };
111
+
112
+ /**
113
+ * FileChangeQueue component - section-level component for file approval workflow
114
+ */
115
+ export const FileChangeQueue = React.memo<FileChangeQueueProps>(
116
+ ({ changes, title, state, approval, onApprove, onReject, className }) => {
117
+ // Only display if at least 1 file change exists
118
+ if (changes.length === 0) {
119
+ return null;
120
+ }
121
+
122
+ // Transform file changes to groups
123
+ const groups = React.useMemo(
124
+ () => transformFileChangesToGroups(changes),
125
+ [changes]
126
+ );
127
+
128
+ return (
129
+ <Confirmation
130
+ title={title}
131
+ state={state}
132
+ approval={approval}
133
+ onApprove={onApprove}
134
+ onReject={onReject}
135
+ className={className}
136
+ >
137
+ <FileQueue groups={groups} />
138
+ </Confirmation>
139
+ );
140
+ }
141
+ );
142
+
143
+ FileChangeQueue.displayName = "FileChangeQueue";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * FileChangeQueue Section
3
+ *
4
+ * Central export point for the FileChangeQueue section component and its related types.
5
+ */
6
+ export { FileChangeQueue } from "./FileChangeQueue";
7
+ export type { FileChangeQueueProps } from "./FileChangeQueue";
@@ -0,0 +1,34 @@
1
+ import * as React from "react"
2
+ import { SidebarProvider } from "@/components/primitives/Sidebar"
3
+
4
+ /**
5
+ * LayoutProvider Block
6
+ *
7
+ * Wraps SidebarProvider primitive for use in feature layer.
8
+ * Provides sidebar state management and CSS variables.
9
+ */
10
+
11
+ export interface LayoutProviderProps {
12
+ children: React.ReactNode
13
+ defaultOpen?: boolean
14
+ sidebarWidth?: string
15
+ sidebarWidthIcon?: string
16
+ }
17
+
18
+ export const LayoutProvider = React.memo<LayoutProviderProps>(
19
+ ({ children, defaultOpen = true, sidebarWidth = "var(--spacing-sidebar-width)", sidebarWidthIcon = "var(--spacing-sidebar-width-icon)" }) => {
20
+ return (
21
+ <SidebarProvider
22
+ defaultOpen={defaultOpen}
23
+ style={{
24
+ "--sidebar-width": sidebarWidth,
25
+ "--sidebar-width-icon": sidebarWidthIcon,
26
+ } as React.CSSProperties}
27
+ >
28
+ {children}
29
+ </SidebarProvider>
30
+ )
31
+ }
32
+ )
33
+
34
+ LayoutProvider.displayName = "LayoutProvider"
@@ -0,0 +1 @@
1
+ export { LayoutProvider, type LayoutProviderProps } from "./LayoutProvider"
@@ -0,0 +1,2 @@
1
+ export { AppSidebar } from './AppSidebar'
2
+ export type { AppSidebarProps } from './AppSidebar'