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