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,140 @@
1
+ /**
2
+ * AIDocEditor Stories
3
+ *
4
+ * Demonstrates the AIDocEditor feature with various scenarios
5
+ */
6
+
7
+ import type { Meta, StoryObj } from '@storybook/react'
8
+ import { AIDocEditor } from './AIDocEditor'
9
+ import { useAIDocEditorMock } from './useAIDocEditor.mock'
10
+ import { sampleContent, currentUser, sampleAnnotations } from './AIDocEditor.mocks'
11
+
12
+ const meta: Meta<typeof AIDocEditor> = {
13
+ title: 'Features/AIDocEditor',
14
+ component: AIDocEditor,
15
+ parameters: {
16
+ layout: 'fullscreen',
17
+ },
18
+ tags: ['autodocs'],
19
+ }
20
+
21
+ export default meta
22
+ type Story = StoryObj<typeof AIDocEditor>
23
+
24
+ /**
25
+ * Default story with static props
26
+ * Shows the editor with existing annotations
27
+ */
28
+ export const Default: Story = {
29
+ args: {
30
+ content: sampleContent,
31
+ annotations: sampleAnnotations,
32
+ currentUser,
33
+ mode: 'review',
34
+ onAnnotationAdd: (annotation) => console.log('Added annotation:', annotation),
35
+ onAnnotationUpdate: (annotation) => console.log('Updated annotation:', annotation),
36
+ },
37
+ }
38
+
39
+ /**
40
+ * Story with empty document
41
+ */
42
+ export const EmptyDocument: Story = {
43
+ args: {
44
+ content: {
45
+ type: 'doc',
46
+ content: [
47
+ {
48
+ type: 'paragraph',
49
+ content: [],
50
+ },
51
+ ],
52
+ },
53
+ annotations: [],
54
+ currentUser,
55
+ mode: 'review',
56
+ },
57
+ }
58
+
59
+ /**
60
+ * Story in readonly mode (no interactions)
61
+ */
62
+ export const ReadonlyMode: Story = {
63
+ args: {
64
+ content: sampleContent,
65
+ annotations: sampleAnnotations,
66
+ currentUser,
67
+ mode: 'readonly',
68
+ },
69
+ }
70
+
71
+ /**
72
+ * Interactive story with state management using mock hook
73
+ * Demonstrates adding comments and replies with simulated async operations
74
+ * Uses markdown format for easy content creation
75
+ */
76
+ export const WithStateManagement: Story = {
77
+ render: () => {
78
+ const { annotations, addAnnotation, updateAnnotation, loading } = useAIDocEditorMock(sampleAnnotations)
79
+
80
+ const markdownContent = `# AI Document Review Demo
81
+
82
+ This document demonstrates the **AIDocEditor** feature with full state management.
83
+
84
+ ## Interactive Features
85
+
86
+ Try these interactions:
87
+
88
+ - **Select text** to add a new comment
89
+ - **Click highlighted text** to view existing annotations
90
+ - **Reply to comments** in the comment thread
91
+ - All changes are saved with simulated async operations
92
+
93
+ ## Sample Content for Review
94
+
95
+ The AI has analyzed this document and provided several suggestions. You can review each suggestion and add your own comments.
96
+
97
+ ### Code Example
98
+
99
+ \`\`\`javascript
100
+ function processData(items) {
101
+ return items.map(item => ({
102
+ id: item.id,
103
+ name: item.name,
104
+ processed: true
105
+ }));
106
+ }
107
+ \`\`\`
108
+
109
+ ### Key Points
110
+
111
+ 1. **Performance**: The current implementation is efficient
112
+ 2. **Maintainability**: Code is well-structured and documented
113
+ 3. **Testing**: Unit tests cover all critical paths
114
+
115
+ > **Note:** This is a live demo with state management. Try selecting text and adding comments!
116
+
117
+ ---
118
+
119
+ **Status:** Ready for review | **Last updated:** ${new Date().toLocaleDateString()}`
120
+
121
+ return (
122
+ <div className="h-screen p-8">
123
+ <AIDocEditor
124
+ content={markdownContent}
125
+ format="markdown"
126
+ annotations={annotations}
127
+ currentUser={currentUser}
128
+ mode="review"
129
+ onAnnotationAdd={addAnnotation}
130
+ onAnnotationUpdate={updateAnnotation}
131
+ />
132
+ {loading && (
133
+ <div className="fixed bottom-4 right-4 bg-primary text-primary-foreground px-4 py-2 rounded-md">
134
+ Saving...
135
+ </div>
136
+ )}
137
+ </div>
138
+ )
139
+ },
140
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * AIDocEditor Feature Component
3
+ *
4
+ * Complete document editor with inline comment annotations.
5
+ * Feature layer: uses DocumentEditorWithComments section with data management.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <AIDocEditor
10
+ * content={document}
11
+ * annotations={annotations}
12
+ * currentUser={user}
13
+ * mode="review"
14
+ * onAnnotationAdd={(annotation) => saveAnnotation(annotation)}
15
+ * />
16
+ * ```
17
+ */
18
+
19
+ import React, { useCallback } from 'react'
20
+ import { DocumentEditorWithComments } from '@/components/blocks/DocumentEditorWithComments'
21
+ import { cn } from '@/lib/utils'
22
+ import type { JSONContent } from '@tiptap/core'
23
+ import type { Annotation, User } from '@/types/ai-editor/annotations'
24
+
25
+ /**
26
+ * Props for AIDocEditor feature component
27
+ */
28
+ export interface AIDocEditorProps {
29
+ /**
30
+ * Document content - can be either:
31
+ * - JSONContent: Tiptap's JSON format (default)
32
+ * - string: Markdown string (when format='markdown')
33
+ */
34
+ content: JSONContent | string
35
+ /**
36
+ * Content format - determines how content prop is interpreted
37
+ * - 'json': Content is Tiptap JSONContent (default)
38
+ * - 'markdown': Content is a markdown string
39
+ *
40
+ * @default 'json'
41
+ */
42
+ format?: 'json' | 'markdown'
43
+ /** Array of annotations to display */
44
+ annotations: Annotation[]
45
+ /** ID of currently selected annotation */
46
+ selectedAnnotationId?: string
47
+ /** Current user information */
48
+ currentUser: User
49
+ /** Editor mode: 'review' allows commenting, 'readonly' disables interactions */
50
+ mode: 'review' | 'readonly'
51
+ /** Callback when document content is updated */
52
+ onContentUpdate?: (content: JSONContent) => void
53
+ /** Callback when annotation is clicked */
54
+ onAnnotationClick?: (annotation: Annotation) => void
55
+ /** Callback when annotation is hovered */
56
+ onAnnotationHover?: (annotation: Annotation | null) => void
57
+ /** Callback when text is selected */
58
+ onTextSelect?: (range: { from: number; to: number }, selectedText: string) => void
59
+ /** Callback when new annotation is added */
60
+ onAnnotationAdd?: (annotation: Annotation) => void
61
+ /** Callback when annotation is updated (e.g., reply added) */
62
+ onAnnotationUpdate?: (annotation: Annotation) => void
63
+ /** Callback when annotation is deleted */
64
+ onAnnotationDelete?: (annotationId: string) => void
65
+ /** Additional CSS classes */
66
+ className?: string
67
+ }
68
+
69
+ /**
70
+ * AIDocEditor - Document editor with inline comment annotations
71
+ *
72
+ * This feature component provides a complete document review experience with:
73
+ * - Read-only document display with annotation highlights
74
+ * - Inline comment box for viewing and adding comments
75
+ * - Support for comments, suggestions, and block additions
76
+ * - Controlled component pattern (all state managed by parent)
77
+ *
78
+ * Note: The refinement panel (right sidebar with Accept All/Reject All) is a
79
+ * separate component in the parent application, not part of this feature.
80
+ */
81
+ export const AIDocEditor = React.memo<AIDocEditorProps>(
82
+ ({
83
+ content,
84
+ format = 'json',
85
+ annotations,
86
+ selectedAnnotationId,
87
+ currentUser,
88
+ mode,
89
+ onAnnotationAdd,
90
+ onAnnotationUpdate,
91
+ className,
92
+ }) => {
93
+ /**
94
+ * Handle annotation add - emit event to parent
95
+ */
96
+ const handleAnnotationAdd = useCallback(
97
+ (annotation: Annotation) => {
98
+ onAnnotationAdd?.(annotation)
99
+ },
100
+ [onAnnotationAdd]
101
+ )
102
+
103
+ /**
104
+ * Handle annotation update - emit event to parent
105
+ */
106
+ const handleAnnotationUpdate = useCallback(
107
+ (annotation: Annotation) => {
108
+ onAnnotationUpdate?.(annotation)
109
+ },
110
+ [onAnnotationUpdate]
111
+ )
112
+
113
+ return (
114
+ <DocumentEditorWithComments
115
+ content={content}
116
+ format={format}
117
+ annotations={annotations}
118
+ selectedAnnotationId={selectedAnnotationId}
119
+ currentUserId={currentUser.id}
120
+ currentUserName={currentUser.name}
121
+ readOnly={mode === 'readonly'}
122
+ onAnnotationAdd={handleAnnotationAdd}
123
+ onAnnotationUpdate={handleAnnotationUpdate}
124
+ className={cn('ai-doc-editor p-6', className)}
125
+ />
126
+ )
127
+ }
128
+ )
129
+
130
+ AIDocEditor.displayName = 'AIDocEditor'
@@ -0,0 +1,8 @@
1
+ /**
2
+ * AIDocEditor Feature Component
3
+ *
4
+ * Complete document editor with inline comment annotations
5
+ */
6
+
7
+ export { AIDocEditor } from './AIDocEditor'
8
+ export type { AIDocEditorProps } from './AIDocEditor'
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Hook contract for AIDocEditor feature
3
+ *
4
+ * This interface defines the contract for managing document annotations.
5
+ * Applications should implement this hook to provide real data management,
6
+ * while Storybook uses mock implementations for visual testing.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Real implementation in application
11
+ * export function useAIDocEditor(documentId: string): UseAIDocEditorReturn {
12
+ * const [annotations, setAnnotations] = useState<Annotation[]>([])
13
+ * const [loading, setLoading] = useState(false)
14
+ *
15
+ * const addAnnotation = async (annotation: Annotation) => {
16
+ * setLoading(true)
17
+ * await api.createAnnotation(documentId, annotation)
18
+ * setAnnotations(prev => [...prev, annotation])
19
+ * setLoading(false)
20
+ * }
21
+ *
22
+ * // ... other methods
23
+ *
24
+ * return { annotations, addAnnotation, updateAnnotation, deleteAnnotation, loading }
25
+ * }
26
+ * ```
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * // Usage in component
31
+ * const MyDocumentEditor = ({ documentId }) => {
32
+ * const { annotations, addAnnotation, updateAnnotation, loading } = useAIDocEditor(documentId)
33
+ *
34
+ * return (
35
+ * <AIDocEditor
36
+ * content={document}
37
+ * annotations={annotations}
38
+ * currentUser={user}
39
+ * mode="review"
40
+ * onAnnotationAdd={addAnnotation}
41
+ * onAnnotationUpdate={updateAnnotation}
42
+ * />
43
+ * )
44
+ * }
45
+ * ```
46
+ */
47
+
48
+ import type { Annotation } from '@/types/ai-editor/annotations'
49
+
50
+ /**
51
+ * Return type for useAIDocEditor hook
52
+ */
53
+ export interface UseAIDocEditorReturn {
54
+ /**
55
+ * Array of annotations for the document
56
+ */
57
+ annotations: Annotation[]
58
+
59
+ /**
60
+ * Add a new annotation to the document
61
+ * @param annotation - The annotation to add
62
+ */
63
+ addAnnotation: (annotation: Annotation) => Promise<void> | void
64
+
65
+ /**
66
+ * Update an existing annotation (e.g., add reply, edit comment)
67
+ * @param annotation - The updated annotation
68
+ */
69
+ updateAnnotation: (annotation: Annotation) => Promise<void> | void
70
+
71
+ /**
72
+ * Delete an annotation from the document
73
+ * @param annotationId - ID of the annotation to delete
74
+ */
75
+ deleteAnnotation: (annotationId: string) => Promise<void> | void
76
+
77
+ /**
78
+ * Loading state for async operations
79
+ */
80
+ loading: boolean
81
+ }
82
+
83
+ /**
84
+ * Hook for managing document annotations
85
+ *
86
+ * Applications must implement this hook to provide real data management.
87
+ * The hook should handle:
88
+ * - Fetching annotations from API
89
+ * - Creating new annotations
90
+ * - Updating existing annotations
91
+ * - Deleting annotations
92
+ * - Managing loading states
93
+ *
94
+ * @param documentId - ID of the document to manage annotations for
95
+ * @returns Object containing annotations array and mutation methods
96
+ */
97
+ export function useAIDocEditor(documentId: string): UseAIDocEditorReturn
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Mock implementation of useAIDocEditor hook for Storybook
3
+ *
4
+ * This mock provides realistic state management and simulated async operations
5
+ * for visual testing and interactive demos in Storybook.
6
+ */
7
+
8
+ import { useState, useCallback } from 'react'
9
+ import type { Annotation } from '@/types/ai-editor/annotations'
10
+ import type { UseAIDocEditorReturn } from './useAIDocEditor'
11
+
12
+ /**
13
+ * Mock hook for managing document annotations in Storybook
14
+ *
15
+ * Simulates async operations with setTimeout to demonstrate loading states
16
+ * and realistic user interactions.
17
+ *
18
+ * @param initialAnnotations - Initial annotations to display
19
+ * @returns Mock implementation of UseAIDocEditorReturn
20
+ */
21
+ export function useAIDocEditorMock(
22
+ initialAnnotations: Annotation[] = []
23
+ ): UseAIDocEditorReturn {
24
+ const [annotations, setAnnotations] = useState<Annotation[]>(initialAnnotations)
25
+ const [loading, setLoading] = useState(false)
26
+
27
+ /**
28
+ * Add a new annotation with simulated async delay
29
+ */
30
+ const addAnnotation = useCallback(async (annotation: Annotation) => {
31
+ console.log('[AIDocEditor Mock] Adding annotation:', annotation)
32
+ setLoading(true)
33
+
34
+ // Simulate API call delay
35
+ await new Promise(resolve => setTimeout(resolve, 500))
36
+
37
+ setAnnotations(prev => {
38
+ const updated = [...prev, annotation]
39
+ console.log('[AIDocEditor Mock] Annotations after add:', updated)
40
+ return updated
41
+ })
42
+ setLoading(false)
43
+ }, [])
44
+
45
+ /**
46
+ * Update an existing annotation with simulated async delay
47
+ */
48
+ const updateAnnotation = useCallback(async (annotation: Annotation) => {
49
+ console.log('[AIDocEditor Mock] Updating annotation:', annotation)
50
+ setLoading(true)
51
+
52
+ // Simulate API call delay
53
+ await new Promise(resolve => setTimeout(resolve, 500))
54
+
55
+ setAnnotations(prev => {
56
+ const updated = prev.map(a => (a.id === annotation.id ? annotation : a))
57
+ console.log('[AIDocEditor Mock] Annotations after update:', updated)
58
+ return updated
59
+ })
60
+ setLoading(false)
61
+ }, [])
62
+
63
+ /**
64
+ * Delete an annotation with simulated async delay
65
+ */
66
+ const deleteAnnotation = useCallback(async (annotationId: string) => {
67
+ setLoading(true)
68
+
69
+ // Simulate API call delay
70
+ await new Promise(resolve => setTimeout(resolve, 500))
71
+
72
+ setAnnotations(prev => prev.filter(a => a.id !== annotationId))
73
+ setLoading(false)
74
+ }, [])
75
+
76
+ return {
77
+ annotations,
78
+ addAnnotation,
79
+ updateAnnotation,
80
+ deleteAnnotation,
81
+ loading,
82
+ }
83
+ }
@@ -0,0 +1,119 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { expect, fn, userEvent, waitFor, within } from '@storybook/test'
3
+ import { PageLayout } from './PageLayout'
4
+ import { mockSidebarConfig, mockHeaderConfig } from './PageLayout.mocks'
5
+
6
+ const meta = {
7
+ title: 'Features/PageLayout/Behaviors',
8
+ component: PageLayout,
9
+ tags: ['test'],
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ },
13
+ } satisfies Meta<typeof PageLayout>
14
+
15
+ export default meta
16
+ type Story = StoryObj<typeof meta>
17
+
18
+ /**
19
+ * Sidebar Toggle Behavior
20
+ *
21
+ * Tests that the sidebar can be toggled using the trigger button.
22
+ */
23
+ export const SidebarToggle: Story = {
24
+ args: {
25
+ sidebar: mockSidebarConfig,
26
+ header: mockHeaderConfig,
27
+ defaultSidebarOpen: true,
28
+ children: <div>Content</div>,
29
+ },
30
+ play: async ({ canvasElement }) => {
31
+ const canvas = within(canvasElement)
32
+
33
+ // Find the sidebar trigger button
34
+ const trigger = canvas.getByRole('button', { name: /toggle sidebar/i })
35
+ await expect(trigger).toBeInTheDocument()
36
+
37
+ // Get the sidebar element to check its state
38
+ const sidebar = canvasElement.querySelector('[data-sidebar="sidebar"]')
39
+ await expect(sidebar).toBeInTheDocument()
40
+
41
+ // Click to toggle sidebar
42
+ await userEvent.click(trigger)
43
+
44
+ // Wait for the state change and verify sidebar collapsed
45
+ await waitFor(() => {
46
+ const sidebarWrapper = canvasElement.querySelector('[data-state]')
47
+ expect(sidebarWrapper).toHaveAttribute('data-state', 'collapsed')
48
+ })
49
+ },
50
+ }
51
+
52
+ /**
53
+ * Navigation Click Behavior
54
+ *
55
+ * Tests that navigation items are clickable and callbacks are invoked.
56
+ */
57
+ export const NavigationClick: Story = {
58
+ args: {
59
+ sidebar: mockSidebarConfig,
60
+ header: mockHeaderConfig,
61
+ defaultSidebarOpen: true,
62
+ children: <div>Content</div>,
63
+ },
64
+ play: async ({ canvasElement }) => {
65
+ const canvas = within(canvasElement)
66
+
67
+ // Find a navigation link
68
+ const dashboardLink = canvas.getByText('Dashboard')
69
+ await expect(dashboardLink).toBeInTheDocument()
70
+
71
+ // Click the navigation item
72
+ await userEvent.click(dashboardLink)
73
+
74
+ // Verify it's still in the document (navigation occurred)
75
+ await expect(dashboardLink).toBeInTheDocument()
76
+ },
77
+ }
78
+
79
+ /**
80
+ * User Menu Interaction
81
+ *
82
+ * Tests that the user menu can be opened and callbacks are invoked.
83
+ */
84
+ export const UserMenuInteraction: Story = {
85
+ args: {
86
+ sidebar: {
87
+ ...mockSidebarConfig,
88
+ userActions: [
89
+ { label: 'Profile', onClick: fn() },
90
+ { label: 'Logout', onClick: fn() },
91
+ ],
92
+ },
93
+ header: mockHeaderConfig,
94
+ defaultSidebarOpen: true,
95
+ children: <div>Content</div>,
96
+ },
97
+ play: async ({ args, canvasElement }) => {
98
+ const canvas = within(canvasElement)
99
+
100
+ // Find the user menu button (by user name)
101
+ const userButton = canvas.getByText('John Doe')
102
+ await expect(userButton).toBeInTheDocument()
103
+
104
+ // Click to open menu
105
+ await userEvent.click(userButton)
106
+
107
+ // Verify menu items appear
108
+ const profileItem = await canvas.findByText('Profile')
109
+ await expect(profileItem).toBeInTheDocument()
110
+
111
+ // Click the Profile menu item
112
+ await userEvent.click(profileItem)
113
+
114
+ // Verify the callback was called
115
+ await waitFor(() => {
116
+ expect(args.sidebar.userActions?.[0].onClick).toHaveBeenCalled()
117
+ })
118
+ },
119
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Mock data for PageLayout feature stories
3
+ */
4
+
5
+ export const mockSidebarConfig = {
6
+ logo: { icon: 'command', text: 'Acme Inc.', href: '/' },
7
+ mainNavigation: [
8
+ { title: 'Dashboard', url: '#', icon: 'layout-dashboard', isActive: true },
9
+ { title: 'Projects', url: '#', icon: 'folder' },
10
+ { title: 'Tasks', url: '#', icon: 'check-square' },
11
+ { title: 'Analytics', url: '#', icon: 'bar-chart' },
12
+ ],
13
+ user: {
14
+ name: 'John Doe',
15
+ email: 'john@example.com',
16
+ avatar: 'https://github.com/shadcn.png',
17
+ },
18
+ userActions: [
19
+ { label: 'Profile', onClick: () => console.log('Profile') },
20
+ { label: 'Settings', onClick: () => console.log('Settings') },
21
+ { label: 'Logout', onClick: () => console.log('Logout') },
22
+ ],
23
+ }
24
+
25
+ export const mockHeaderConfig = {
26
+ title: 'Dashboard',
27
+ }