ai-design-system 0.1.13 → 0.1.15
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/components/composites/AdjustableLayout/AdjustableLayout.tsx +23 -20
- package/components/composites/DocumentTabBar/DocumentTabBar.stories.tsx +122 -0
- package/components/composites/DocumentTabBar/DocumentTabBar.tsx +137 -0
- package/components/composites/DocumentTabBar/index.ts +2 -0
- package/components/composites/index.ts +4 -0
- package/components/features/AIDocEditor/AIDocEditor.behaviors.stories.tsx +208 -3
- package/components/features/AIDocEditor/AIDocEditor.mocks.ts +49 -0
- package/components/features/AIDocEditor/AIDocEditor.stories.tsx +45 -94
- package/components/features/AIDocEditor/AIDocEditor.tsx +156 -28
- package/components/features/AIDocEditor/README.md +176 -24
- package/components/features/AIDocEditor/index.ts +17 -1
- package/components/features/AIDocEditor/useAIDocEditor.d.ts +85 -2
- package/components/features/AIDocEditor/useAIDocEditor.mock.ts +179 -30
- package/dist/index.cjs +176 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +69 -0
- package/dist/index.d.ts +90 -22
- package/dist/index.js +177 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,23 @@
|
|
|
2
2
|
* AIDocEditor Feature Component
|
|
3
3
|
*
|
|
4
4
|
* Complete document editor with inline comment annotations
|
|
5
|
+
* Supports both single-document and multi-tab modes
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
export { AIDocEditor } from './AIDocEditor'
|
|
8
|
-
export type {
|
|
9
|
+
export type {
|
|
10
|
+
AIDocEditorProps,
|
|
11
|
+
AIDocEditorSingleProps,
|
|
12
|
+
AIDocEditorMultiTabProps,
|
|
13
|
+
} from './AIDocEditor'
|
|
14
|
+
|
|
15
|
+
export { useAIDocEditorMock, useAIMultiTabDocEditorMock } from './useAIDocEditor.mock'
|
|
16
|
+
export type { UseAIDocEditorReturn, UseAIMultiTabDocEditorReturn } from './useAIDocEditor'
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
sampleContent,
|
|
20
|
+
currentUser,
|
|
21
|
+
sampleAnnotations,
|
|
22
|
+
sampleDocumentFiles,
|
|
23
|
+
sampleMultiTabDocuments,
|
|
24
|
+
} from './AIDocEditor.mocks'
|
|
@@ -46,9 +46,12 @@
|
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
48
|
import type { Annotation } from '@/types/ai-editor/annotations'
|
|
49
|
+
import type { DocumentFile, DocumentWithAnnotations } from '@/types/ai-editor/editor'
|
|
50
|
+
import type { JSONContent } from '@tiptap/core'
|
|
49
51
|
|
|
50
52
|
/**
|
|
51
|
-
* Return type for useAIDocEditor hook
|
|
53
|
+
* Return type for useAIDocEditor hook (single document)
|
|
54
|
+
* @deprecated Use UseAIMultiTabDocEditorReturn for multi-tab support
|
|
52
55
|
*/
|
|
53
56
|
export interface UseAIDocEditorReturn {
|
|
54
57
|
/**
|
|
@@ -81,8 +84,73 @@ export interface UseAIDocEditorReturn {
|
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
/**
|
|
84
|
-
*
|
|
87
|
+
* Return type for multi-tab document editor hook
|
|
88
|
+
*/
|
|
89
|
+
export interface UseAIMultiTabDocEditorReturn {
|
|
90
|
+
/**
|
|
91
|
+
* Array of open documents
|
|
92
|
+
*/
|
|
93
|
+
documents: DocumentWithAnnotations[]
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* ID of currently active document
|
|
97
|
+
*/
|
|
98
|
+
activeDocumentId?: string
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Add a new document tab
|
|
102
|
+
* @param file - Document metadata
|
|
103
|
+
* @param content - Initial document content
|
|
104
|
+
*/
|
|
105
|
+
addDocument: (file: DocumentFile, content: JSONContent | string) => void,
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Close a document tab
|
|
109
|
+
* @param documentId - ID of the document to close
|
|
110
|
+
*/
|
|
111
|
+
closeDocument: (documentId: string) => void
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Switch to a different document tab
|
|
115
|
+
* @param documentId - ID of the document to switch to
|
|
116
|
+
*/
|
|
117
|
+
switchDocument: (documentId: string) => void
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Add annotation to the currently active document
|
|
121
|
+
* @param annotation - The annotation to add
|
|
122
|
+
*/
|
|
123
|
+
addAnnotation: (annotation: Annotation) => Promise<void> | void
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Update annotation in the currently active document
|
|
127
|
+
* @param annotation - The updated annotation
|
|
128
|
+
*/
|
|
129
|
+
updateAnnotation: (annotation: Annotation) => Promise<void> | void
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Delete annotation from the currently active document
|
|
133
|
+
* @param annotationId - ID of the annotation to delete
|
|
134
|
+
*/
|
|
135
|
+
deleteAnnotation: (annotationId: string) => Promise<void> | void
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Mark document as dirty
|
|
139
|
+
* @param documentId - ID of the document
|
|
140
|
+
* @param isDirty - Whether document has unsaved changes
|
|
141
|
+
*/
|
|
142
|
+
setDocumentDirty: (documentId: string, isDirty: boolean) => void
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Loading state for async operations
|
|
146
|
+
*/
|
|
147
|
+
loading: boolean
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Hook for managing document annotations (single document, backward compatible)
|
|
85
152
|
*
|
|
153
|
+
* @deprecated Use useAIMultiTabDocEditor for multi-tab support
|
|
86
154
|
* Applications must implement this hook to provide real data management.
|
|
87
155
|
* The hook should handle:
|
|
88
156
|
* - Fetching annotations from API
|
|
@@ -95,3 +163,18 @@ export interface UseAIDocEditorReturn {
|
|
|
95
163
|
* @returns Object containing annotations array and mutation methods
|
|
96
164
|
*/
|
|
97
165
|
export function useAIDocEditor(documentId: string): UseAIDocEditorReturn
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Hook for managing multi-tab documents with annotations
|
|
169
|
+
*
|
|
170
|
+
* Applications must implement this hook to provide real data management.
|
|
171
|
+
* The hook should handle:
|
|
172
|
+
* - Managing multiple open documents
|
|
173
|
+
* - Tracking active document
|
|
174
|
+
* - Fetching/creating annotations per document
|
|
175
|
+
* - Document dirty state tracking
|
|
176
|
+
* - Adding/removing document tabs
|
|
177
|
+
*
|
|
178
|
+
* @returns Object containing documents array, active document ID, and mutation methods
|
|
179
|
+
*/
|
|
180
|
+
export function useAIMultiTabDocEditor(): UseAIMultiTabDocEditorReturn
|
|
@@ -7,33 +7,26 @@
|
|
|
7
7
|
|
|
8
8
|
import { useState, useCallback } from 'react'
|
|
9
9
|
import type { Annotation } from '@/types/ai-editor/annotations'
|
|
10
|
-
import type {
|
|
10
|
+
import type { DocumentFile, DocumentWithAnnotations } from '@/types/ai-editor'
|
|
11
|
+
import type { UseAIDocEditorReturn, UseAIMultiTabDocEditorReturn } from './useAIDocEditor'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* @returns Mock implementation of UseAIDocEditorReturn
|
|
20
|
-
*/
|
|
21
|
-
export function useAIDocEditorMock(
|
|
22
|
-
initialAnnotations: Annotation[] = []
|
|
13
|
+
type MultiDocMockConfig = {
|
|
14
|
+
multiDoc: true
|
|
15
|
+
initialDocuments?: DocumentWithAnnotations[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function useSingleDocEditorMockState(
|
|
19
|
+
initialAnnotations: Annotation[]
|
|
23
20
|
): UseAIDocEditorReturn {
|
|
24
21
|
const [annotations, setAnnotations] = useState<Annotation[]>(initialAnnotations)
|
|
25
22
|
const [loading, setLoading] = useState(false)
|
|
26
23
|
|
|
27
|
-
/**
|
|
28
|
-
* Add a new annotation with simulated async delay
|
|
29
|
-
*/
|
|
30
24
|
const addAnnotation = useCallback(async (annotation: Annotation) => {
|
|
31
25
|
console.log('[AIDocEditor Mock] Adding annotation:', annotation)
|
|
32
26
|
setLoading(true)
|
|
33
|
-
|
|
34
|
-
// Simulate API call delay
|
|
27
|
+
|
|
35
28
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
36
|
-
|
|
29
|
+
|
|
37
30
|
setAnnotations(prev => {
|
|
38
31
|
const updated = [...prev, annotation]
|
|
39
32
|
console.log('[AIDocEditor Mock] Annotations after add:', updated)
|
|
@@ -42,16 +35,12 @@ export function useAIDocEditorMock(
|
|
|
42
35
|
setLoading(false)
|
|
43
36
|
}, [])
|
|
44
37
|
|
|
45
|
-
/**
|
|
46
|
-
* Update an existing annotation with simulated async delay
|
|
47
|
-
*/
|
|
48
38
|
const updateAnnotation = useCallback(async (annotation: Annotation) => {
|
|
49
39
|
console.log('[AIDocEditor Mock] Updating annotation:', annotation)
|
|
50
40
|
setLoading(true)
|
|
51
|
-
|
|
52
|
-
// Simulate API call delay
|
|
41
|
+
|
|
53
42
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
54
|
-
|
|
43
|
+
|
|
55
44
|
setAnnotations(prev => {
|
|
56
45
|
const updated = prev.map(a => (a.id === annotation.id ? annotation : a))
|
|
57
46
|
console.log('[AIDocEditor Mock] Annotations after update:', updated)
|
|
@@ -60,15 +49,11 @@ export function useAIDocEditorMock(
|
|
|
60
49
|
setLoading(false)
|
|
61
50
|
}, [])
|
|
62
51
|
|
|
63
|
-
/**
|
|
64
|
-
* Delete an annotation with simulated async delay
|
|
65
|
-
*/
|
|
66
52
|
const deleteAnnotation = useCallback(async (annotationId: string) => {
|
|
67
53
|
setLoading(true)
|
|
68
|
-
|
|
69
|
-
// Simulate API call delay
|
|
54
|
+
|
|
70
55
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
71
|
-
|
|
56
|
+
|
|
72
57
|
setAnnotations(prev => prev.filter(a => a.id !== annotationId))
|
|
73
58
|
setLoading(false)
|
|
74
59
|
}, [])
|
|
@@ -81,3 +66,167 @@ export function useAIDocEditorMock(
|
|
|
81
66
|
loading,
|
|
82
67
|
}
|
|
83
68
|
}
|
|
69
|
+
|
|
70
|
+
function useMultiTabDocEditorMockState(
|
|
71
|
+
initialDocuments: DocumentWithAnnotations[]
|
|
72
|
+
): UseAIMultiTabDocEditorReturn {
|
|
73
|
+
const [documents, setDocuments] = useState(initialDocuments)
|
|
74
|
+
const [activeDocumentId, setActiveDocumentId] = useState<string | undefined>(
|
|
75
|
+
initialDocuments[0]?.file.id
|
|
76
|
+
)
|
|
77
|
+
const [loading, setLoading] = useState(false)
|
|
78
|
+
|
|
79
|
+
const addDocument = useCallback((file: DocumentFile, content: any) => {
|
|
80
|
+
console.log('[Multi-Tab Mock] Adding document:', file.name)
|
|
81
|
+
setDocuments(prev => [...prev, { file, content, annotations: [] }])
|
|
82
|
+
setActiveDocumentId(file.id)
|
|
83
|
+
}, [])
|
|
84
|
+
|
|
85
|
+
const closeDocument = useCallback((documentId: string) => {
|
|
86
|
+
console.log('[Multi-Tab Mock] Closing document:', documentId)
|
|
87
|
+
setDocuments(prev => {
|
|
88
|
+
const filtered = prev.filter(doc => doc.file.id !== documentId)
|
|
89
|
+
if (activeDocumentId === documentId && filtered.length > 0) {
|
|
90
|
+
setActiveDocumentId(filtered[0].file.id)
|
|
91
|
+
}
|
|
92
|
+
if (filtered.length === 0) {
|
|
93
|
+
setActiveDocumentId(undefined)
|
|
94
|
+
}
|
|
95
|
+
return filtered
|
|
96
|
+
})
|
|
97
|
+
}, [activeDocumentId])
|
|
98
|
+
|
|
99
|
+
const switchDocument = useCallback((documentId: string) => {
|
|
100
|
+
console.log('[Multi-Tab Mock] Switching to document:', documentId)
|
|
101
|
+
setActiveDocumentId(documentId)
|
|
102
|
+
}, [])
|
|
103
|
+
|
|
104
|
+
const addAnnotation = useCallback(async (annotation: Annotation) => {
|
|
105
|
+
if (!activeDocumentId) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setLoading(true)
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
111
|
+
|
|
112
|
+
setDocuments(prev =>
|
|
113
|
+
prev.map(doc =>
|
|
114
|
+
doc.file.id === activeDocumentId
|
|
115
|
+
? { ...doc, annotations: [...doc.annotations, annotation] }
|
|
116
|
+
: doc
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
setLoading(false)
|
|
120
|
+
}, [activeDocumentId])
|
|
121
|
+
|
|
122
|
+
const updateAnnotation = useCallback(async (annotation: Annotation) => {
|
|
123
|
+
if (!activeDocumentId) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setLoading(true)
|
|
128
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
129
|
+
|
|
130
|
+
setDocuments(prev =>
|
|
131
|
+
prev.map(doc =>
|
|
132
|
+
doc.file.id === activeDocumentId
|
|
133
|
+
? {
|
|
134
|
+
...doc,
|
|
135
|
+
annotations: doc.annotations.map(a =>
|
|
136
|
+
a.id === annotation.id ? annotation : a
|
|
137
|
+
),
|
|
138
|
+
}
|
|
139
|
+
: doc
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
setLoading(false)
|
|
143
|
+
}, [activeDocumentId])
|
|
144
|
+
|
|
145
|
+
const deleteAnnotation = useCallback(async (annotationId: string) => {
|
|
146
|
+
if (!activeDocumentId) {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setLoading(true)
|
|
151
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
152
|
+
|
|
153
|
+
setDocuments(prev =>
|
|
154
|
+
prev.map(doc =>
|
|
155
|
+
doc.file.id === activeDocumentId
|
|
156
|
+
? {
|
|
157
|
+
...doc,
|
|
158
|
+
annotations: doc.annotations.filter(a => a.id !== annotationId),
|
|
159
|
+
}
|
|
160
|
+
: doc
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
setLoading(false)
|
|
164
|
+
}, [activeDocumentId])
|
|
165
|
+
|
|
166
|
+
const setDocumentDirty = useCallback((documentId: string, isDirty: boolean) => {
|
|
167
|
+
setDocuments(prev =>
|
|
168
|
+
prev.map(doc =>
|
|
169
|
+
doc.file.id === documentId
|
|
170
|
+
? { ...doc, file: { ...doc.file, isDirty } }
|
|
171
|
+
: doc
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
}, [])
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
documents,
|
|
178
|
+
activeDocumentId,
|
|
179
|
+
addDocument,
|
|
180
|
+
closeDocument,
|
|
181
|
+
switchDocument,
|
|
182
|
+
addAnnotation,
|
|
183
|
+
updateAnnotation,
|
|
184
|
+
deleteAnnotation,
|
|
185
|
+
setDocumentDirty,
|
|
186
|
+
loading,
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Mock hook for managing document annotations in Storybook
|
|
192
|
+
*
|
|
193
|
+
* Simulates async operations with setTimeout to demonstrate loading states
|
|
194
|
+
* and realistic user interactions.
|
|
195
|
+
*
|
|
196
|
+
* @param initialAnnotations - Initial annotations to display
|
|
197
|
+
* @returns Mock implementation of UseAIDocEditorReturn
|
|
198
|
+
*/
|
|
199
|
+
export function useAIDocEditorMock(
|
|
200
|
+
initialAnnotations: Annotation[]
|
|
201
|
+
): UseAIDocEditorReturn
|
|
202
|
+
export function useAIDocEditorMock(config: MultiDocMockConfig): UseAIMultiTabDocEditorReturn
|
|
203
|
+
export function useAIDocEditorMock(
|
|
204
|
+
initialAnnotationsOrConfig: Annotation[] | MultiDocMockConfig = []
|
|
205
|
+
): UseAIDocEditorReturn | UseAIMultiTabDocEditorReturn {
|
|
206
|
+
const isMultiDoc = !Array.isArray(initialAnnotationsOrConfig) && initialAnnotationsOrConfig.multiDoc === true
|
|
207
|
+
const singleInitialAnnotations = Array.isArray(initialAnnotationsOrConfig) ? initialAnnotationsOrConfig : []
|
|
208
|
+
const multiInitialDocuments = !Array.isArray(initialAnnotationsOrConfig)
|
|
209
|
+
? (initialAnnotationsOrConfig.initialDocuments ?? [])
|
|
210
|
+
: []
|
|
211
|
+
|
|
212
|
+
const singleState = useSingleDocEditorMockState(singleInitialAnnotations)
|
|
213
|
+
const multiState = useMultiTabDocEditorMockState(multiInitialDocuments)
|
|
214
|
+
|
|
215
|
+
if (isMultiDoc) {
|
|
216
|
+
return multiState
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return singleState
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Mock hook for managing multi-tab documents in Storybook
|
|
224
|
+
*
|
|
225
|
+
* Simulates multi-document state management with tab switching, closing, and annotation updates.
|
|
226
|
+
*
|
|
227
|
+
* @param initialDocuments - Initial documents to display
|
|
228
|
+
* @returns Mock implementation of UseAIMultiTabDocEditorReturn
|
|
229
|
+
*/
|
|
230
|
+
export function useAIMultiTabDocEditorMock(initialDocuments: any[] = []): UseAIMultiTabDocEditorReturn {
|
|
231
|
+
return useMultiTabDocEditorMockState(initialDocuments as DocumentWithAnnotations[])
|
|
232
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -6479,6 +6479,85 @@ var ModeToggle = React33__namespace.memo(({ className }) => {
|
|
|
6479
6479
|
] });
|
|
6480
6480
|
});
|
|
6481
6481
|
ModeToggle.displayName = "ModeToggle";
|
|
6482
|
+
var DocumentTabBar = React33__namespace.default.memo(
|
|
6483
|
+
({ tabs, activeTabId, onTabSelect, onTabClose, className }) => {
|
|
6484
|
+
var _a;
|
|
6485
|
+
if (tabs.length === 0) {
|
|
6486
|
+
return null;
|
|
6487
|
+
}
|
|
6488
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6489
|
+
"div",
|
|
6490
|
+
{
|
|
6491
|
+
className: cn(
|
|
6492
|
+
"flex items-center border-b border-border bg-background",
|
|
6493
|
+
className
|
|
6494
|
+
),
|
|
6495
|
+
"data-slot": "document-tab-bar",
|
|
6496
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ScrollArea2, { orientation: "horizontal", className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6497
|
+
Tabs2,
|
|
6498
|
+
{
|
|
6499
|
+
value: activeTabId || ((_a = tabs[0]) == null ? void 0 : _a.id),
|
|
6500
|
+
onValueChange: onTabSelect,
|
|
6501
|
+
className: "h-auto flex-1",
|
|
6502
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(TabsList, { className: "h-10 rounded-none border-none bg-transparent p-0 w-full justify-start gap-0", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6503
|
+
"div",
|
|
6504
|
+
{
|
|
6505
|
+
className: "flex items-center border-r border-border/50 last:border-r-0",
|
|
6506
|
+
children: [
|
|
6507
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6508
|
+
TabsTrigger,
|
|
6509
|
+
{
|
|
6510
|
+
value: tab.id,
|
|
6511
|
+
className: cn(
|
|
6512
|
+
"data-[state=inactive]:bg-muted/20 data-[state=inactive]:text-muted-foreground",
|
|
6513
|
+
"rounded-none border-b-2 border-transparent data-[state=active]:border-primary",
|
|
6514
|
+
"px-3 py-2 text-sm font-medium whitespace-nowrap",
|
|
6515
|
+
"flex items-center gap-2 h-10",
|
|
6516
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0",
|
|
6517
|
+
"transition-colors"
|
|
6518
|
+
),
|
|
6519
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
6520
|
+
tab.isDirty && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6521
|
+
lucideReact.Circle,
|
|
6522
|
+
{
|
|
6523
|
+
className: "size-2 fill-primary text-primary flex-shrink-0",
|
|
6524
|
+
"aria-label": "unsaved changes"
|
|
6525
|
+
}
|
|
6526
|
+
),
|
|
6527
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[200px]", children: tab.name })
|
|
6528
|
+
] })
|
|
6529
|
+
}
|
|
6530
|
+
),
|
|
6531
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6532
|
+
Button2,
|
|
6533
|
+
{
|
|
6534
|
+
variant: "ghost",
|
|
6535
|
+
size: "icon-sm",
|
|
6536
|
+
onClick: (e) => {
|
|
6537
|
+
e.stopPropagation();
|
|
6538
|
+
onTabClose == null ? void 0 : onTabClose(tab.id);
|
|
6539
|
+
},
|
|
6540
|
+
className: cn(
|
|
6541
|
+
"h-8 w-8 mr-0.5",
|
|
6542
|
+
"hover:bg-destructive/10 hover:text-destructive",
|
|
6543
|
+
"focus-visible:ring-2 focus-visible:ring-ring",
|
|
6544
|
+
"transition-colors"
|
|
6545
|
+
),
|
|
6546
|
+
"aria-label": `Close ${tab.name}`,
|
|
6547
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3" })
|
|
6548
|
+
}
|
|
6549
|
+
)
|
|
6550
|
+
]
|
|
6551
|
+
},
|
|
6552
|
+
tab.id
|
|
6553
|
+
)) })
|
|
6554
|
+
}
|
|
6555
|
+
) })
|
|
6556
|
+
}
|
|
6557
|
+
);
|
|
6558
|
+
}
|
|
6559
|
+
);
|
|
6560
|
+
DocumentTabBar.displayName = "DocumentTabBar";
|
|
6482
6561
|
var ThemeSelector = React33__namespace.memo(
|
|
6483
6562
|
({ themes, value, onValueChange, placeholder = "Select theme", className }) => {
|
|
6484
6563
|
return /* @__PURE__ */ jsxRuntime.jsxs(Select2, { value, onValueChange, children: [
|
|
@@ -6797,25 +6876,27 @@ var AdjustableLayout = React33__namespace.memo(
|
|
|
6797
6876
|
muted: "group-hover:bg-muted/30"
|
|
6798
6877
|
};
|
|
6799
6878
|
const containerRef = React33__namespace.useRef(null);
|
|
6800
|
-
const
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6879
|
+
const defaultSizes = React33__namespace.useMemo(() => {
|
|
6880
|
+
const raw = sections.map((section) => {
|
|
6881
|
+
var _a2;
|
|
6882
|
+
return (_a2 = section.defaultSize) != null ? _a2 : 100 / sections.length;
|
|
6883
|
+
});
|
|
6884
|
+
const total = raw.reduce((sum, size) => sum + size, 0);
|
|
6885
|
+
return raw.map((size) => size / total * 100);
|
|
6886
|
+
}, [sections]);
|
|
6887
|
+
const [sizes, setSizes] = React33__namespace.useState(defaultSizes);
|
|
6888
|
+
React33__namespace.useEffect(() => {
|
|
6889
|
+
if (!storageKey) return;
|
|
6890
|
+
const saved = localStorage.getItem(storageKey);
|
|
6891
|
+
if (!saved) return;
|
|
6892
|
+
try {
|
|
6893
|
+
const parsed = JSON.parse(saved);
|
|
6894
|
+
if (Array.isArray(parsed) && parsed.length === sections.length) {
|
|
6895
|
+
setSizes(parsed);
|
|
6808
6896
|
}
|
|
6897
|
+
} catch (e) {
|
|
6809
6898
|
}
|
|
6810
|
-
|
|
6811
|
-
(section) => {
|
|
6812
|
-
var _a2;
|
|
6813
|
-
return (_a2 = section.defaultSize) != null ? _a2 : 100 / sections.length;
|
|
6814
|
-
}
|
|
6815
|
-
);
|
|
6816
|
-
const total = defaultSizes.reduce((sum, size) => sum + size, 0);
|
|
6817
|
-
return defaultSizes.map((size) => size / total * 100);
|
|
6818
|
-
});
|
|
6899
|
+
}, [storageKey]);
|
|
6819
6900
|
const [draggingIndex, setDraggingIndex] = React33__namespace.useState(null);
|
|
6820
6901
|
const [startX, setStartX] = React33__namespace.useState(0);
|
|
6821
6902
|
const [startSizes, setStartSizes] = React33__namespace.useState([]);
|
|
@@ -8065,18 +8146,28 @@ var SpecNavigator = React33__namespace.memo(
|
|
|
8065
8146
|
}
|
|
8066
8147
|
);
|
|
8067
8148
|
SpecNavigator.displayName = "SpecNavigator";
|
|
8149
|
+
function isMultiTabMode(props) {
|
|
8150
|
+
return "documents" in props && props.documents !== void 0;
|
|
8151
|
+
}
|
|
8068
8152
|
var AIDocEditor = React33__namespace.default.memo(
|
|
8069
|
-
({
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8153
|
+
(props) => {
|
|
8154
|
+
var _a, _b;
|
|
8155
|
+
const {
|
|
8156
|
+
currentUser: currentUser2,
|
|
8157
|
+
mode,
|
|
8158
|
+
onAnnotationAdd,
|
|
8159
|
+
onAnnotationUpdate,
|
|
8160
|
+
onAnnotationDelete: _onAnnotationDelete,
|
|
8161
|
+
// Callback for consumers; passed through on demand
|
|
8162
|
+
className
|
|
8163
|
+
} = props;
|
|
8164
|
+
const isMultiTab = isMultiTabMode(props);
|
|
8165
|
+
const currentDocument = React33.useMemo(() => {
|
|
8166
|
+
if (isMultiTab && props.documents) {
|
|
8167
|
+
return props.documents.find((doc) => doc.file.id === props.activeDocumentId) || props.documents[0];
|
|
8168
|
+
}
|
|
8169
|
+
return null;
|
|
8170
|
+
}, [isMultiTab, props]);
|
|
8080
8171
|
const handleAnnotationAdd = React33.useCallback(
|
|
8081
8172
|
(annotation) => {
|
|
8082
8173
|
onAnnotationAdd == null ? void 0 : onAnnotationAdd(annotation);
|
|
@@ -8089,21 +8180,62 @@ var AIDocEditor = React33__namespace.default.memo(
|
|
|
8089
8180
|
},
|
|
8090
8181
|
[onAnnotationUpdate]
|
|
8091
8182
|
);
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8183
|
+
if (!isMultiTab) {
|
|
8184
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8185
|
+
DocumentEditorWithComments,
|
|
8186
|
+
{
|
|
8187
|
+
content: props.content,
|
|
8188
|
+
format: props.format,
|
|
8189
|
+
annotations: props.annotations,
|
|
8190
|
+
selectedAnnotationId: props.selectedAnnotationId,
|
|
8191
|
+
currentUserId: currentUser2.id,
|
|
8192
|
+
currentUserName: currentUser2.name,
|
|
8193
|
+
readOnly: mode === "readonly",
|
|
8194
|
+
onAnnotationAdd: handleAnnotationAdd,
|
|
8195
|
+
onAnnotationUpdate: handleAnnotationUpdate,
|
|
8196
|
+
className: cn("ai-doc-editor p-6", className)
|
|
8197
|
+
}
|
|
8198
|
+
);
|
|
8199
|
+
}
|
|
8200
|
+
if (!currentDocument) {
|
|
8201
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("ai-doc-editor flex flex-col h-full", className), children: [
|
|
8202
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8203
|
+
DocumentTabBar,
|
|
8204
|
+
{
|
|
8205
|
+
tabs: ((_a = props.documents) == null ? void 0 : _a.map((doc) => doc.file)) || [],
|
|
8206
|
+
activeTabId: props.activeDocumentId,
|
|
8207
|
+
onTabSelect: props.onTabSelect,
|
|
8208
|
+
onTabClose: props.onTabClose
|
|
8209
|
+
}
|
|
8210
|
+
),
|
|
8211
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center p-6 text-muted-foreground", children: "No documents open" })
|
|
8212
|
+
] });
|
|
8213
|
+
}
|
|
8214
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("ai-doc-editor flex flex-col h-full", className), children: [
|
|
8215
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8216
|
+
DocumentTabBar,
|
|
8217
|
+
{
|
|
8218
|
+
tabs: ((_b = props.documents) == null ? void 0 : _b.map((doc) => doc.file)) || [],
|
|
8219
|
+
activeTabId: props.activeDocumentId,
|
|
8220
|
+
onTabSelect: props.onTabSelect,
|
|
8221
|
+
onTabClose: props.onTabClose
|
|
8222
|
+
}
|
|
8223
|
+
),
|
|
8224
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8225
|
+
DocumentEditorWithComments,
|
|
8226
|
+
{
|
|
8227
|
+
content: currentDocument.content,
|
|
8228
|
+
format: currentDocument.file.format,
|
|
8229
|
+
annotations: currentDocument.annotations,
|
|
8230
|
+
currentUserId: currentUser2.id,
|
|
8231
|
+
currentUserName: currentUser2.name,
|
|
8232
|
+
readOnly: mode === "readonly",
|
|
8233
|
+
onAnnotationAdd: handleAnnotationAdd,
|
|
8234
|
+
onAnnotationUpdate: handleAnnotationUpdate,
|
|
8235
|
+
className: "p-6"
|
|
8236
|
+
}
|
|
8237
|
+
) })
|
|
8238
|
+
] });
|
|
8107
8239
|
}
|
|
8108
8240
|
);
|
|
8109
8241
|
AIDocEditor.displayName = "AIDocEditor";
|
|
@@ -8244,6 +8376,7 @@ exports.DialogTitle = DialogTitle;
|
|
|
8244
8376
|
exports.DialogTrigger = DialogTrigger;
|
|
8245
8377
|
exports.DocumentEditor = DocumentEditor;
|
|
8246
8378
|
exports.DocumentEditorWithComments = DocumentEditorWithComments;
|
|
8379
|
+
exports.DocumentTabBar = DocumentTabBar;
|
|
8247
8380
|
exports.Drawer = Drawer2;
|
|
8248
8381
|
exports.DrawerClose = DrawerClose2;
|
|
8249
8382
|
exports.DrawerContent = DrawerContent2;
|