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.
- package/README.md +307 -0
- package/components/ai-elements/actions.tsx +65 -0
- package/components/ai-elements/artifact.tsx +147 -0
- package/components/ai-elements/branch.tsx +212 -0
- package/components/ai-elements/canvas.tsx +24 -0
- package/components/ai-elements/chain-of-thought.tsx +228 -0
- package/components/ai-elements/code-block.tsx +179 -0
- package/components/ai-elements/confirmation.tsx +169 -0
- package/components/ai-elements/connection.tsx +28 -0
- package/components/ai-elements/context.tsx +408 -0
- package/components/ai-elements/controls.tsx +18 -0
- package/components/ai-elements/conversation.tsx +97 -0
- package/components/ai-elements/edge.tsx +140 -0
- package/components/ai-elements/image.tsx +24 -0
- package/components/ai-elements/inline-citation.tsx +287 -0
- package/components/ai-elements/loader.tsx +96 -0
- package/components/ai-elements/message.tsx +80 -0
- package/components/ai-elements/node.tsx +71 -0
- package/components/ai-elements/open-in-chat.tsx +363 -0
- package/components/ai-elements/panel.tsx +15 -0
- package/components/ai-elements/plan.tsx +142 -0
- package/components/ai-elements/prompt-input.tsx +1352 -0
- package/components/ai-elements/queue.tsx +274 -0
- package/components/ai-elements/reasoning.tsx +178 -0
- package/components/ai-elements/response.tsx +22 -0
- package/components/ai-elements/shimmer.tsx +64 -0
- package/components/ai-elements/sources.tsx +77 -0
- package/components/ai-elements/suggestion.tsx +56 -0
- package/components/ai-elements/task.tsx +87 -0
- package/components/ai-elements/tool.tsx +179 -0
- package/components/ai-elements/toolbar.tsx +16 -0
- package/components/ai-elements/web-preview.tsx +263 -0
- package/components/blocks/AIConversation/AIConversation.stories.tsx +164 -0
- package/components/blocks/AIConversation/AIConversation.tsx +186 -0
- package/components/blocks/AIConversation/index.ts +8 -0
- package/components/blocks/AppSidebar/AppSidebar.stories.tsx +63 -0
- package/components/blocks/AppSidebar/AppSidebar.tsx +87 -0
- package/components/blocks/AppSidebar/index.ts +2 -0
- package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.stories.tsx +341 -0
- package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.tsx +255 -0
- package/components/blocks/DocumentEditorWithComments/index.ts +9 -0
- package/components/blocks/FileChangeQueue/FileChangeQueue.stories.tsx +207 -0
- package/components/blocks/FileChangeQueue/FileChangeQueue.tsx +143 -0
- package/components/blocks/FileChangeQueue/index.ts +7 -0
- package/components/blocks/LayoutProvider/LayoutProvider.tsx +34 -0
- package/components/blocks/LayoutProvider/index.ts +1 -0
- package/components/blocks/index.ts +2 -0
- package/components/composites/AgentIndicator/AgentIndicator.stories.tsx +154 -0
- package/components/composites/AgentIndicator/AgentIndicator.tsx +102 -0
- package/components/composites/AgentIndicator/index.ts +8 -0
- package/components/composites/AppHeader/AppHeader.stories.tsx +46 -0
- package/components/composites/AppHeader/AppHeader.tsx +24 -0
- package/components/composites/AppHeader/index.ts +2 -0
- package/components/composites/CommentBox/CommentBox.stories.tsx +192 -0
- package/components/composites/CommentBox/CommentBox.tsx +364 -0
- package/components/composites/CommentBox/index.ts +8 -0
- package/components/composites/Confirmation/Confirmation.stories.tsx +151 -0
- package/components/composites/Confirmation/Confirmation.tsx +93 -0
- package/components/composites/Confirmation/index.ts +7 -0
- package/components/composites/DataTable/DataTable.stories.tsx +35 -0
- package/components/composites/DataTable/DataTable.tsx +95 -0
- package/components/composites/DataTable/index.ts +2 -0
- package/components/composites/DocumentEditor/DocumentEditor.css +106 -0
- package/components/composites/DocumentEditor/DocumentEditor.stories.tsx +927 -0
- package/components/composites/DocumentEditor/DocumentEditor.tsx +279 -0
- package/components/composites/DocumentEditor/index.ts +8 -0
- package/components/composites/FileQueue/FileQueue.stories.tsx +175 -0
- package/components/composites/FileQueue/FileQueue.tsx +161 -0
- package/components/composites/FileQueue/FileStatusBadge.tsx +74 -0
- package/components/composites/FileQueue/index.ts +24 -0
- package/components/composites/InteractiveChart/InteractiveChart.stories.tsx +49 -0
- package/components/composites/InteractiveChart/InteractiveChart.tsx +69 -0
- package/components/composites/InteractiveChart/index.ts +2 -0
- package/components/composites/ModeToggle/ModeToggle.stories.tsx +212 -0
- package/components/composites/ModeToggle/ModeToggle.tsx +100 -0
- package/components/composites/ModeToggle/index.ts +7 -0
- package/components/composites/NavUser/NavUser.stories.tsx +50 -0
- package/components/composites/NavUser/NavUser.tsx +60 -0
- package/components/composites/NavUser/index.ts +2 -0
- package/components/composites/NavigationList/NavigationList.stories.tsx +46 -0
- package/components/composites/NavigationList/NavigationList.tsx +46 -0
- package/components/composites/NavigationList/index.ts +2 -0
- package/components/composites/OrchestratorMessage/OrchestratorMessage.stories.tsx +188 -0
- package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +72 -0
- package/components/composites/OrchestratorMessage/index.ts +8 -0
- package/components/composites/PageContainer/PageContainer.stories.tsx +41 -0
- package/components/composites/PageContainer/PageContainer.tsx +24 -0
- package/components/composites/PageContainer/index.ts +2 -0
- package/components/composites/PromptInput/PromptInput.stories.tsx +200 -0
- package/components/composites/PromptInput/PromptInput.tsx +129 -0
- package/components/composites/PromptInput/index.ts +8 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.stories.tsx +286 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +107 -0
- package/components/composites/SpecialistMessage/index.ts +8 -0
- package/components/composites/StatsCard/StatsCard.stories.tsx +76 -0
- package/components/composites/StatsCard/StatsCard.tsx +81 -0
- package/components/composites/StatsCard/index.ts +2 -0
- package/components/composites/TablePagination/TablePagination.stories.tsx +38 -0
- package/components/composites/TablePagination/TablePagination.tsx +119 -0
- package/components/composites/TablePagination/index.ts +2 -0
- package/components/composites/TableToolbar/TableToolbar.stories.tsx +60 -0
- package/components/composites/TableToolbar/TableToolbar.tsx +66 -0
- package/components/composites/TableToolbar/index.ts +2 -0
- package/components/composites/ThemeSelector/ThemeSelector.stories.tsx +48 -0
- package/components/composites/ThemeSelector/ThemeSelector.tsx +79 -0
- package/components/composites/ThemeSelector/index.ts +2 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.stories.tsx +49 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.tsx +108 -0
- package/components/composites/ToolCallDisplay/index.ts +8 -0
- package/components/composites/UserMessage/UserMessage.stories.tsx +59 -0
- package/components/composites/UserMessage/UserMessage.tsx +52 -0
- package/components/composites/UserMessage/index.ts +8 -0
- package/components/composites/index.ts +90 -0
- package/components/features/AIDocEditor/AIDocEditor.behaviors.stories.tsx +451 -0
- package/components/features/AIDocEditor/AIDocEditor.mocks.ts +96 -0
- package/components/features/AIDocEditor/AIDocEditor.stories.tsx +140 -0
- package/components/features/AIDocEditor/AIDocEditor.tsx +130 -0
- package/components/features/AIDocEditor/index.ts +8 -0
- package/components/features/AIDocEditor/useAIDocEditor.d.ts +97 -0
- package/components/features/AIDocEditor/useAIDocEditor.mock.ts +83 -0
- package/components/features/PageLayout/PageLayout.behaviors.stories.tsx +119 -0
- package/components/features/PageLayout/PageLayout.mocks.ts +27 -0
- package/components/features/PageLayout/PageLayout.stories.tsx +142 -0
- package/components/features/PageLayout/PageLayout.tsx +94 -0
- package/components/features/PageLayout/index.ts +4 -0
- package/components/features/PageLayout/usePageLayout.d.ts +24 -0
- package/components/features/PageLayout/usePageLayout.mock.ts +19 -0
- package/components/features/RefinementPanel/README.md +189 -0
- package/components/features/RefinementPanel/RefinementPanel.behaviors.stories.tsx +475 -0
- package/components/features/RefinementPanel/RefinementPanel.mocks.ts +131 -0
- package/components/features/RefinementPanel/RefinementPanel.stories.tsx +141 -0
- package/components/features/RefinementPanel/RefinementPanel.tsx +160 -0
- package/components/features/RefinementPanel/index.ts +25 -0
- package/components/features/RefinementPanel/useRefinementPanel.d.ts +74 -0
- package/components/features/RefinementPanel/useRefinementPanel.mock.ts +121 -0
- package/components/features/SpecNavigator/SpecNavigator.behaviors.stories.tsx +379 -0
- package/components/features/SpecNavigator/SpecNavigator.mocks.ts +131 -0
- package/components/features/SpecNavigator/SpecNavigator.stories.tsx +122 -0
- package/components/features/SpecNavigator/SpecNavigator.tsx +43 -0
- package/components/features/SpecNavigator/index.ts +2 -0
- package/components/features/SpecNavigator/useSpecNavigator.d.ts +122 -0
- package/components/features/SpecNavigator/useSpecNavigator.mock.ts +93 -0
- package/components/features/index.ts +18 -0
- package/components/index.ts +14 -0
- package/components/primitives/Accordion/Accordion.stories.tsx +87 -0
- package/components/primitives/Accordion/Accordion.tsx +66 -0
- package/components/primitives/Accordion/index.ts +13 -0
- package/components/primitives/Alert/Alert.stories.tsx +422 -0
- package/components/primitives/Alert/Alert.tsx +61 -0
- package/components/primitives/Alert/index.ts +8 -0
- package/components/primitives/AlertDialog/AlertDialog.stories.tsx +367 -0
- package/components/primitives/AlertDialog/AlertDialog.tsx +182 -0
- package/components/primitives/AlertDialog/index.ts +25 -0
- package/components/primitives/Avatar/Avatar.stories.tsx +321 -0
- package/components/primitives/Avatar/Avatar.tsx +63 -0
- package/components/primitives/Avatar/index.ts +8 -0
- package/components/primitives/Badge/Badge.stories.tsx +74 -0
- package/components/primitives/Badge/Badge.tsx +49 -0
- package/components/primitives/Badge/index.ts +2 -0
- package/components/primitives/Button/Button.stories.tsx +445 -0
- package/components/primitives/Button/Button.tsx +89 -0
- package/components/primitives/Button/index.ts +7 -0
- package/components/primitives/Card/Card.stories.tsx +831 -0
- package/components/primitives/Card/Card.tsx +242 -0
- package/components/primitives/Card/index.ts +30 -0
- package/components/primitives/Carousel/Carousel.stories.tsx +32 -0
- package/components/primitives/Carousel/Carousel.tsx +63 -0
- package/components/primitives/Carousel/index.ts +13 -0
- package/components/primitives/Chart/Chart.stories.tsx +346 -0
- package/components/primitives/Chart/Chart.tsx +117 -0
- package/components/primitives/Chart/index.ts +20 -0
- package/components/primitives/Checkbox/Checkbox.stories.tsx +87 -0
- package/components/primitives/Checkbox/Checkbox.tsx +38 -0
- package/components/primitives/Checkbox/index.ts +2 -0
- package/components/primitives/Collapsible/Collapsible.stories.tsx +38 -0
- package/components/primitives/Collapsible/Collapsible.tsx +39 -0
- package/components/primitives/Collapsible/index.ts +8 -0
- package/components/primitives/Command/Command.stories.tsx +150 -0
- package/components/primitives/Command/Command.tsx +147 -0
- package/components/primitives/Command/index.ts +20 -0
- package/components/primitives/Dialog/Dialog.stories.tsx +390 -0
- package/components/primitives/Dialog/Dialog.tsx +140 -0
- package/components/primitives/Dialog/index.ts +22 -0
- package/components/primitives/Drawer/Drawer.stories.tsx +327 -0
- package/components/primitives/Drawer/Drawer.tsx +208 -0
- package/components/primitives/Drawer/index.ts +27 -0
- package/components/primitives/DropdownMenu/DropdownMenu.stories.tsx +150 -0
- package/components/primitives/DropdownMenu/DropdownMenu.tsx +73 -0
- package/components/primitives/DropdownMenu/index.ts +1 -0
- package/components/primitives/HoverCard/HoverCard.stories.tsx +26 -0
- package/components/primitives/HoverCard/HoverCard.tsx +39 -0
- package/components/primitives/HoverCard/index.ts +8 -0
- package/components/primitives/Icon/Icon.stories.tsx +281 -0
- package/components/primitives/Icon/Icon.tsx +87 -0
- package/components/primitives/Icon/index.ts +8 -0
- package/components/primitives/Input/Input.stories.tsx +370 -0
- package/components/primitives/Input/Input.tsx +88 -0
- package/components/primitives/Input/index.ts +7 -0
- package/components/primitives/InputGroup/InputGroup.stories.tsx +40 -0
- package/components/primitives/InputGroup/InputGroup.tsx +72 -0
- package/components/primitives/InputGroup/index.ts +14 -0
- package/components/primitives/Label/Label.stories.tsx +227 -0
- package/components/primitives/Label/Label.tsx +53 -0
- package/components/primitives/Label/index.ts +7 -0
- package/components/primitives/Popover/Popover.stories.tsx +42 -0
- package/components/primitives/Popover/Popover.tsx +107 -0
- package/components/primitives/Popover/index.ts +2 -0
- package/components/primitives/Progress/Progress.stories.tsx +340 -0
- package/components/primitives/Progress/Progress.tsx +31 -0
- package/components/primitives/Progress/index.ts +1 -0
- package/components/primitives/ScrollArea/ScrollArea.stories.tsx +26 -0
- package/components/primitives/ScrollArea/ScrollArea.tsx +28 -0
- package/components/primitives/ScrollArea/index.ts +6 -0
- package/components/primitives/Select/Select.stories.tsx +288 -0
- package/components/primitives/Select/Select.tsx +162 -0
- package/components/primitives/Select/index.ts +22 -0
- package/components/primitives/Separator/Separator.stories.tsx +264 -0
- package/components/primitives/Separator/Separator.tsx +48 -0
- package/components/primitives/Separator/index.ts +7 -0
- package/components/primitives/Sidebar/Sidebar.stories.tsx +358 -0
- package/components/primitives/Sidebar/Sidebar.tsx +317 -0
- package/components/primitives/Sidebar/index.ts +41 -0
- package/components/primitives/Table/Table.stories.tsx +389 -0
- package/components/primitives/Table/Table.tsx +191 -0
- package/components/primitives/Table/index.ts +26 -0
- package/components/primitives/Tabs/Tabs.stories.tsx +129 -0
- package/components/primitives/Tabs/Tabs.tsx +70 -0
- package/components/primitives/Tabs/index.ts +13 -0
- package/components/primitives/Textarea/Textarea.stories.tsx +358 -0
- package/components/primitives/Textarea/Textarea.tsx +91 -0
- package/components/primitives/Textarea/index.ts +7 -0
- package/components/primitives/ToggleGroup/ToggleGroup.stories.tsx +87 -0
- package/components/primitives/ToggleGroup/ToggleGroup.tsx +52 -0
- package/components/primitives/ToggleGroup/index.ts +6 -0
- package/components/primitives/Tooltip/Tooltip.stories.tsx +336 -0
- package/components/primitives/Tooltip/Tooltip.tsx +78 -0
- package/components/primitives/Tooltip/index.ts +10 -0
- package/components/primitives/index.ts +34 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +157 -0
- package/components/ui/alert.tsx +66 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/card.tsx +117 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +334 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +184 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/drawer.tsx +118 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/input-group.tsx +170 -0
- package/components/ui/input.tsx +48 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/popover.tsx +33 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/select.tsx +187 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/sidebar.tsx +577 -0
- package/components/ui/table.tsx +120 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +46 -0
- package/components/ui/toggle-group.tsx +83 -0
- package/components/ui/toggle.tsx +47 -0
- package/components/ui/tooltip.tsx +61 -0
- package/dist/index.cjs +7389 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +75 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +7160 -0
- package/dist/index.js.map +1 -0
- package/hooks/useAIDocReviewer.d.ts +0 -0
- package/lib/utils.ts +6 -0
- package/package.json +140 -0
- package/tokens/color/base.json +14 -0
- package/tokens/color/dark.json +40 -0
- package/tokens/color/green.json +21 -0
- package/tokens/color/light.json +52 -0
- package/tokens/color/neutral.json +20 -0
- package/tokens/color/violet.json +21 -0
- package/tokens/spacing.json +22 -0
- package/utils/ai-editor/format-date.ts +41 -0
- package/utils/ai-editor/index.ts +22 -0
- package/utils/ai-editor/type-guards.ts +72 -0
- package/utils/ai-editor/validation.ts +130 -0
- 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,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
|
+
}
|