ai-design-system 0.1.58 → 0.1.60
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/SessionHeader/SessionHeader.tsx +0 -17
- package/components/features/{WorkflowBuilder/WorkflowBuilder.behaviors.stories.tsx → NodeEditor/NodeEditor.behaviors.stories.tsx} +6 -6
- package/components/features/{WorkflowBuilder/WorkflowBuilder.mocks.ts → NodeEditor/NodeEditor.mocks.ts} +3 -3
- package/components/features/{WorkflowBuilder/WorkflowBuilder.stories.tsx → NodeEditor/NodeEditor.stories.tsx} +16 -16
- package/components/features/{WorkflowBuilder/WorkflowBuilder.tsx → NodeEditor/NodeEditor.tsx} +8 -6
- package/components/features/NodeEditor/index.ts +4 -0
- package/components/features/{WorkflowBuilder/useWorkflowBuilder.d.ts → NodeEditor/useNodeEditor.d.ts} +4 -4
- package/components/features/{WorkflowBuilder/useWorkflowBuilder.mock.ts → NodeEditor/useNodeEditor.mock.ts} +2 -2
- package/components/features/PageLayout/PageLayout.stories.tsx +3 -3
- package/components/features/{AIDocEditor/AIDocEditor.behaviors.stories.tsx → TextEditor/TextEditor.behaviors.stories.tsx} +6 -6
- package/components/features/{AIDocEditor/AIDocEditor.mocks.ts → TextEditor/TextEditor.mocks.ts} +4 -4
- package/components/features/{AIDocEditor/AIDocEditor.stories.tsx → TextEditor/TextEditor.stories.tsx} +10 -10
- package/components/features/{AIDocEditor/AIDocEditor.tsx → TextEditor/TextEditor.tsx} +22 -22
- package/components/features/TextEditor/index.ts +24 -0
- package/components/features/{AIDocEditor/useAIDocEditor.d.ts → TextEditor/useTextEditor.d.ts} +7 -7
- package/components/features/{AIDocEditor/useAIDocEditor.mock.ts → TextEditor/useTextEditor.mock.ts} +18 -18
- package/components/features/index.ts +6 -6
- package/dist/index.cjs +16 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +218 -217
- package/dist/index.js +15 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/components/features/AIDocEditor/index.ts +0 -24
- package/components/features/WorkflowBuilder/index.ts +0 -4
- /package/components/features/{WorkflowBuilder → NodeEditor}/README.md +0 -0
- /package/components/features/{AIDocEditor → TextEditor}/README.md +0 -0
|
@@ -46,23 +46,6 @@ export const SessionHeader = React.forwardRef<HTMLDivElement, SessionHeaderProps
|
|
|
46
46
|
? (activeSession?.title || 'Untitled Session')
|
|
47
47
|
: 'New Session'}
|
|
48
48
|
</span>
|
|
49
|
-
<Button
|
|
50
|
-
variant="ghost"
|
|
51
|
-
size="icon"
|
|
52
|
-
className="h-6 w-6 flex-none"
|
|
53
|
-
onClick={() => {
|
|
54
|
-
if (activeSessionId) {
|
|
55
|
-
onCloseSession?.(activeSessionId);
|
|
56
|
-
} else {
|
|
57
|
-
// If closing a 'New Session', we might want to just trigger onCloseSession with null or handle it.
|
|
58
|
-
// But since the interface requires a string, we can just pass an empty string or 'new'.
|
|
59
|
-
// Actually, if activeSessionId is null, clicking X should probably revert to the last session.
|
|
60
|
-
onCloseSession?.('draft');
|
|
61
|
-
}
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
64
|
-
<Icon name="x" className="h-4 w-4" />
|
|
65
|
-
</Button>
|
|
66
49
|
</div>
|
|
67
50
|
<div className="flex items-center space-x-1 flex-none">
|
|
68
51
|
<Button variant="ghost" size="icon" onClick={onNewSession} className="h-8 w-8">
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* NodeEditor Behavior Tests
|
|
3
3
|
*
|
|
4
4
|
* Tests user interactions and state changes to prevent regressions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
8
8
|
import { expect, fn, userEvent, waitFor, within } from "@storybook/test";
|
|
9
|
-
import {
|
|
10
|
-
import { mockVersions, mockNodes, mockEdges } from "./
|
|
9
|
+
import { NodeEditor } from "./NodeEditor";
|
|
10
|
+
import { mockVersions, mockNodes, mockEdges } from "./NodeEditor.mocks";
|
|
11
11
|
|
|
12
12
|
const meta = {
|
|
13
|
-
title: "Features/
|
|
14
|
-
component:
|
|
13
|
+
title: "Features/NodeEditor/Behaviors",
|
|
14
|
+
component: NodeEditor,
|
|
15
15
|
tags: ["test"],
|
|
16
16
|
parameters: {
|
|
17
17
|
layout: "fullscreen",
|
|
18
18
|
},
|
|
19
|
-
} satisfies Meta<typeof
|
|
19
|
+
} satisfies Meta<typeof NodeEditor>;
|
|
20
20
|
|
|
21
21
|
export default meta;
|
|
22
22
|
type Story = StoryObj<typeof meta>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Mock data for
|
|
2
|
+
* Mock data for NodeEditor stories and tests
|
|
3
3
|
*
|
|
4
4
|
* Reusable mock data imported by:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
5
|
+
* - NodeEditor.stories.tsx
|
|
6
|
+
* - NodeEditor.behaviors.stories.tsx
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { WorkflowNode, WorkflowEdge } from "@/components/blocks/WorkflowCanvas";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { mockVersions, mockNodes, mockEdges } from "./
|
|
2
|
+
import { NodeEditor } from "./NodeEditor";
|
|
3
|
+
import { useNodeEditorMock } from "./useNodeEditor.mock";
|
|
4
|
+
import { mockVersions, mockNodes, mockEdges } from "./NodeEditor.mocks";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* NodeEditor Feature
|
|
8
8
|
*
|
|
9
9
|
* Complete workflow editing experience combining a floating toolbar
|
|
10
10
|
* with a ReactFlow canvas for building state machine workflows.
|
|
@@ -17,7 +17,7 @@ import { mockVersions, mockNodes, mockEdges } from "./WorkflowBuilder.mocks";
|
|
|
17
17
|
* - Minimap support
|
|
18
18
|
*
|
|
19
19
|
* ## Usage
|
|
20
|
-
* Pass individual props directly or wire up via `
|
|
20
|
+
* Pass individual props directly or wire up via `useNodeEditor` hook
|
|
21
21
|
* in the consuming application.
|
|
22
22
|
*
|
|
23
23
|
* ## Accessibility
|
|
@@ -25,22 +25,22 @@ import { mockVersions, mockNodes, mockEdges } from "./WorkflowBuilder.mocks";
|
|
|
25
25
|
* - Keyboard navigation supported
|
|
26
26
|
*/
|
|
27
27
|
const meta = {
|
|
28
|
-
title: "Features/
|
|
29
|
-
component:
|
|
28
|
+
title: "Features/NodeEditor",
|
|
29
|
+
component: NodeEditor,
|
|
30
30
|
tags: ["autodocs"],
|
|
31
31
|
parameters: {
|
|
32
32
|
layout: "fullscreen",
|
|
33
33
|
},
|
|
34
|
-
} satisfies Meta<typeof
|
|
34
|
+
} satisfies Meta<typeof NodeEditor>;
|
|
35
35
|
|
|
36
36
|
export default meta;
|
|
37
37
|
type Story = StoryObj<typeof meta>;
|
|
38
38
|
|
|
39
|
-
function
|
|
40
|
-
const hook =
|
|
39
|
+
function NodeEditorStateManagementStory() {
|
|
40
|
+
const hook = useNodeEditorMock("wf-1");
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<
|
|
43
|
+
<NodeEditor
|
|
44
44
|
workflowName={hook.workflowName}
|
|
45
45
|
versions={hook.versions}
|
|
46
46
|
currentVersionId={hook.currentVersionId}
|
|
@@ -63,11 +63,11 @@ function WorkflowBuilderStateManagementStory() {
|
|
|
63
63
|
);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function
|
|
67
|
-
const hook =
|
|
66
|
+
function NodeEditorWithMinimapStory() {
|
|
67
|
+
const hook = useNodeEditorMock("wf-1");
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
|
-
<
|
|
70
|
+
<NodeEditor
|
|
71
71
|
workflowName={hook.workflowName}
|
|
72
72
|
versions={hook.versions}
|
|
73
73
|
currentVersionId={hook.currentVersionId}
|
|
@@ -100,7 +100,7 @@ export const Default: Story = {
|
|
|
100
100
|
* WithStateManagement — full interactive mock hook wired up
|
|
101
101
|
*/
|
|
102
102
|
export const WithStateManagement: Story = {
|
|
103
|
-
render: () => <
|
|
103
|
+
render: () => <NodeEditorStateManagementStory />,
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
/**
|
|
@@ -120,7 +120,7 @@ export const Empty: Story = {
|
|
|
120
120
|
* WithMinimap — canvas with minimap enabled
|
|
121
121
|
*/
|
|
122
122
|
export const WithMinimap: Story = {
|
|
123
|
-
render: () => <
|
|
123
|
+
render: () => <NodeEditorWithMinimapStory />,
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
/**
|
package/components/features/{WorkflowBuilder/WorkflowBuilder.tsx → NodeEditor/NodeEditor.tsx}
RENAMED
|
@@ -7,7 +7,7 @@ import type { ToolbarAction, WorkflowVersion } from "@/components/composites/Wor
|
|
|
7
7
|
import type { Connection, NodeChange, EdgeChange } from "@xyflow/react";
|
|
8
8
|
import { cn } from "@/lib/utils";
|
|
9
9
|
|
|
10
|
-
export interface
|
|
10
|
+
export interface NodeEditorProps {
|
|
11
11
|
// Toolbar — left
|
|
12
12
|
workflowName?: string;
|
|
13
13
|
versions?: WorkflowVersion[];
|
|
@@ -37,10 +37,11 @@ export interface WorkflowBuilderProps {
|
|
|
37
37
|
|
|
38
38
|
showMinimap?: boolean;
|
|
39
39
|
interactive?: boolean;
|
|
40
|
+
hideDefaultActions?: boolean;
|
|
40
41
|
className?: string;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
export function
|
|
44
|
+
export function NodeEditor({
|
|
44
45
|
workflowName,
|
|
45
46
|
versions,
|
|
46
47
|
currentVersionId,
|
|
@@ -62,8 +63,9 @@ export function WorkflowBuilder({
|
|
|
62
63
|
extraActions,
|
|
63
64
|
showMinimap,
|
|
64
65
|
interactive = false,
|
|
66
|
+
hideDefaultActions = false,
|
|
65
67
|
className,
|
|
66
|
-
}:
|
|
68
|
+
}: NodeEditorProps) {
|
|
67
69
|
const defaultActionGroups: ToolbarAction[][] = [
|
|
68
70
|
[
|
|
69
71
|
{ id: "undo", icon: "undo-2", title: "Undo", onClick: onUndo, disabled: !canUndo },
|
|
@@ -72,9 +74,9 @@ export function WorkflowBuilder({
|
|
|
72
74
|
],
|
|
73
75
|
];
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
let allActionGroups: ToolbarAction[][] = [];
|
|
78
|
+
if (extraActions) allActionGroups = [...allActionGroups, ...extraActions];
|
|
79
|
+
if (!hideDefaultActions) allActionGroups = [...allActionGroups, ...defaultActionGroups];
|
|
78
80
|
|
|
79
81
|
return (
|
|
80
82
|
<div
|
|
@@ -4,7 +4,7 @@ import type { WorkflowVersion } from "@/components/composites/WorkflowToolbar";
|
|
|
4
4
|
|
|
5
5
|
export type { WorkflowVersion };
|
|
6
6
|
|
|
7
|
-
export interface
|
|
7
|
+
export interface UseNodeEditorReturn {
|
|
8
8
|
// Workflow state
|
|
9
9
|
nodes: WorkflowNode[];
|
|
10
10
|
edges: WorkflowEdge[];
|
|
@@ -39,8 +39,8 @@ export interface UseWorkflowBuilderReturn {
|
|
|
39
39
|
currentWorkflowId?: string;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export type
|
|
42
|
+
export type UseNodeEditor = (workflowId?: string) => UseNodeEditorReturn;
|
|
43
43
|
|
|
44
|
-
export function
|
|
45
|
-
throw new Error("
|
|
44
|
+
export function useNodeEditor(workflowId?: string): UseNodeEditorReturn {
|
|
45
|
+
throw new Error("useNodeEditor must be implemented by the consuming application");
|
|
46
46
|
}
|
|
@@ -2,7 +2,7 @@ import { useState, useCallback } from "react";
|
|
|
2
2
|
import { applyNodeChanges, applyEdgeChanges, addEdge } from "@xyflow/react";
|
|
3
3
|
import type { Connection, NodeChange, EdgeChange } from "@xyflow/react";
|
|
4
4
|
import type { WorkflowNode, WorkflowEdge } from "@/components/blocks/WorkflowCanvas";
|
|
5
|
-
import type {
|
|
5
|
+
import type { UseNodeEditorReturn } from "./useNodeEditor";
|
|
6
6
|
|
|
7
7
|
const mockNodes: WorkflowNode[] = [
|
|
8
8
|
{
|
|
@@ -43,7 +43,7 @@ const mockVersions = [
|
|
|
43
43
|
{ id: "v4", label: "v4" },
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
-
export function
|
|
46
|
+
export function useNodeEditorMock(workflowId?: string): UseNodeEditorReturn {
|
|
47
47
|
const [nodes, setNodes] = useState<WorkflowNode[]>(mockNodes);
|
|
48
48
|
const [edges, setEdges] = useState<WorkflowEdge[]>(mockEdges);
|
|
49
49
|
const [workflowName, setWorkflowName] = useState("Order Processing Workflow");
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
mockPageLayoutRefinementMessages,
|
|
10
10
|
mockSidebarConfig,
|
|
11
11
|
} from './PageLayout.mocks'
|
|
12
|
-
import {
|
|
12
|
+
import { NodeEditor } from '../NodeEditor/NodeEditor'
|
|
13
13
|
import { RefinementPanel } from '../RefinementPanel/RefinementPanel'
|
|
14
|
-
import { mockEdges, mockNodes, mockVersions } from '../
|
|
14
|
+
import { mockEdges, mockNodes, mockVersions } from '../NodeEditor/NodeEditor.mocks'
|
|
15
15
|
import { DashboardFeature } from '../DashboardFeature/DashboardFeature'
|
|
16
16
|
import { useDashboardIntegrationMock } from './useDashboardIntegration.mock'
|
|
17
17
|
|
|
@@ -157,7 +157,7 @@ export const WithStateManagement: Story = {
|
|
|
157
157
|
content: activeTab === 'dashboard' ? (
|
|
158
158
|
<DashboardFeature {...dashboardState.dashboardProps} />
|
|
159
159
|
) : (
|
|
160
|
-
<
|
|
160
|
+
<NodeEditor
|
|
161
161
|
workflowName="Order Processing Workflow"
|
|
162
162
|
currentVersionId="v4"
|
|
163
163
|
versions={mockVersions}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* TextEditor Behavior Tests
|
|
3
3
|
*
|
|
4
4
|
* Tests user interactions and state changes to prevent regressions.
|
|
5
5
|
* These tests validate functionality, not visual appearance.
|
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
9
9
|
import { expect, fn, userEvent, within, waitFor, screen } from '@storybook/test'
|
|
10
10
|
import type { Annotation } from '@/types/ai-editor'
|
|
11
|
-
import {
|
|
12
|
-
import { currentUser, sampleAnnotations, sampleContent } from './
|
|
11
|
+
import { TextEditor } from './TextEditor'
|
|
12
|
+
import { currentUser, sampleAnnotations, sampleContent } from './TextEditor.mocks'
|
|
13
13
|
|
|
14
14
|
const meta = {
|
|
15
|
-
title: 'Features/
|
|
16
|
-
component:
|
|
15
|
+
title: 'Features/TextEditor/Behaviors',
|
|
16
|
+
component: TextEditor,
|
|
17
17
|
tags: ['test'],
|
|
18
18
|
parameters: {
|
|
19
19
|
layout: 'fullscreen',
|
|
20
20
|
},
|
|
21
|
-
} satisfies Meta<typeof
|
|
21
|
+
} satisfies Meta<typeof TextEditor>
|
|
22
22
|
|
|
23
23
|
export default meta
|
|
24
24
|
type Story = StoryObj<typeof meta>
|
package/components/features/{AIDocEditor/AIDocEditor.mocks.ts → TextEditor/TextEditor.mocks.ts}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Mock data for
|
|
2
|
+
* Mock data for TextEditor stories and tests
|
|
3
3
|
*
|
|
4
4
|
* This file contains reusable mock data that can be imported by:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* - Any other test files that need
|
|
5
|
+
* - TextEditor.stories.tsx (regular stories)
|
|
6
|
+
* - TextEditor.behaviors.stories.tsx (behavior tests)
|
|
7
|
+
* - Any other test files that need TextEditor mock data
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { JSONContent } from '@tiptap/core'
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* TextEditor Feature Stories
|
|
3
3
|
*
|
|
4
|
-
* Required story patterns for
|
|
4
|
+
* Required story patterns for TextEditor feature:
|
|
5
5
|
* - Default: Static single-document mode (backward compatible)
|
|
6
6
|
* - Empty: Empty state for multi-tab mode
|
|
7
7
|
* - WithStateManagement: Interactive multi-tab with mock hook
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { TextEditor } from './TextEditor'
|
|
12
|
+
import { useTextEditorMock } from './useTextEditor.mock'
|
|
13
13
|
import {
|
|
14
14
|
sampleContent,
|
|
15
15
|
currentUser,
|
|
16
16
|
sampleAnnotations,
|
|
17
17
|
sampleMultiTabDocuments,
|
|
18
|
-
} from './
|
|
18
|
+
} from './TextEditor.mocks'
|
|
19
19
|
|
|
20
20
|
const meta = {
|
|
21
|
-
title: 'Features/
|
|
22
|
-
component:
|
|
21
|
+
title: 'Features/TextEditor',
|
|
22
|
+
component: TextEditor,
|
|
23
23
|
parameters: {
|
|
24
24
|
layout: 'fullscreen',
|
|
25
25
|
},
|
|
26
26
|
tags: ['autodocs'],
|
|
27
|
-
} satisfies Meta<typeof
|
|
27
|
+
} satisfies Meta<typeof TextEditor>
|
|
28
28
|
|
|
29
29
|
export default meta
|
|
30
30
|
type Story = StoryObj<typeof meta>
|
|
@@ -70,13 +70,13 @@ export const Empty: Story = {
|
|
|
70
70
|
*/
|
|
71
71
|
export const WithStateManagement: Story = {
|
|
72
72
|
render: () => {
|
|
73
|
-
const mockState =
|
|
73
|
+
const mockState = useTextEditorMock({
|
|
74
74
|
multiDoc: true,
|
|
75
75
|
initialDocuments: sampleMultiTabDocuments,
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
return (
|
|
79
|
-
<
|
|
79
|
+
<TextEditor
|
|
80
80
|
documents={mockState.documents}
|
|
81
81
|
activeDocumentId={mockState.activeDocumentId}
|
|
82
82
|
fileTree={mockState.fileTree}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* TextEditor Feature Component
|
|
3
3
|
*
|
|
4
4
|
* Complete document editor with inline comment annotations.
|
|
5
5
|
* Supports both single-document and multi-tab modes.
|
|
6
6
|
*
|
|
7
7
|
* Single-document mode (backward compatible):
|
|
8
8
|
* ```tsx
|
|
9
|
-
* <
|
|
9
|
+
* <TextEditor
|
|
10
10
|
* content={document}
|
|
11
11
|
* annotations={annotations}
|
|
12
12
|
* currentUser={user}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Multi-tab mode:
|
|
19
19
|
* ```tsx
|
|
20
|
-
* <
|
|
20
|
+
* <TextEditor
|
|
21
21
|
* documents={[
|
|
22
22
|
* { file: { id: 'doc-1', name: 'File.md', isDirty: false, lastModified: now },
|
|
23
23
|
* content: '...',
|
|
@@ -50,9 +50,9 @@ interface DocumentWithAnnotations {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Props for
|
|
53
|
+
* Props for TextEditor feature component - Single document mode (backward compatible)
|
|
54
54
|
*/
|
|
55
|
-
export interface
|
|
55
|
+
export interface TextEditorSingleProps {
|
|
56
56
|
/**
|
|
57
57
|
* Document content - can be either:
|
|
58
58
|
* - JSONContent: Tiptap's JSON format (default)
|
|
@@ -82,9 +82,9 @@ export interface AIDocEditorSingleProps {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Props for
|
|
85
|
+
* Props for TextEditor feature component - Multi-tab mode
|
|
86
86
|
*/
|
|
87
|
-
export interface
|
|
87
|
+
export interface TextEditorMultiTabProps {
|
|
88
88
|
/** Array of open documents with their content and annotations */
|
|
89
89
|
documents: DocumentWithAnnotations[]
|
|
90
90
|
/** ID of currently active document */
|
|
@@ -108,9 +108,9 @@ export interface AIDocEditorMultiTabProps {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
|
-
* Props for
|
|
111
|
+
* Props for TextEditor feature component
|
|
112
112
|
*/
|
|
113
|
-
export type
|
|
113
|
+
export type TextEditorProps = (TextEditorSingleProps | TextEditorMultiTabProps) & {
|
|
114
114
|
/** Current user information */
|
|
115
115
|
currentUser: User
|
|
116
116
|
/** Editor mode: 'review' allows commenting, 'readonly' disables interactions */
|
|
@@ -136,7 +136,7 @@ export type AIDocEditorProps = (AIDocEditorSingleProps | AIDocEditorMultiTabProp
|
|
|
136
136
|
/**
|
|
137
137
|
* Determine if props are multi-tab mode
|
|
138
138
|
*/
|
|
139
|
-
function isMultiTabMode(props:
|
|
139
|
+
function isMultiTabMode(props: TextEditorProps): props is TextEditorMultiTabProps & {
|
|
140
140
|
currentUser: User
|
|
141
141
|
mode: 'review' | 'readonly'
|
|
142
142
|
} {
|
|
@@ -152,7 +152,7 @@ function formatJson(text: string): string {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
*
|
|
155
|
+
* TextEditor - Document editor with inline comment annotations
|
|
156
156
|
*
|
|
157
157
|
* This feature component provides a complete document review experience with:
|
|
158
158
|
* - Single-document or multi-tab display modes
|
|
@@ -166,7 +166,7 @@ function formatJson(text: string): string {
|
|
|
166
166
|
* Note: The refinement panel (right sidebar with Accept All/Reject All) is a
|
|
167
167
|
* separate component in the parent application, not part of this feature.
|
|
168
168
|
*/
|
|
169
|
-
export const
|
|
169
|
+
export const TextEditor = React.memo<TextEditorProps>(
|
|
170
170
|
(props) => {
|
|
171
171
|
const {
|
|
172
172
|
mode = 'review',
|
|
@@ -214,7 +214,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
214
214
|
const fileTreeNodes = useMemo(() => {
|
|
215
215
|
if (!isMultiTab) return []
|
|
216
216
|
|
|
217
|
-
const multiProps = props as
|
|
217
|
+
const multiProps = props as TextEditorMultiTabProps
|
|
218
218
|
if (multiProps.fileTree) {
|
|
219
219
|
return multiProps.fileTree
|
|
220
220
|
}
|
|
@@ -256,7 +256,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
256
256
|
})
|
|
257
257
|
})
|
|
258
258
|
return rootNodes
|
|
259
|
-
}, [isMultiTab, (props as
|
|
259
|
+
}, [isMultiTab, (props as TextEditorMultiTabProps).documents, (props as TextEditorMultiTabProps).fileTree])
|
|
260
260
|
|
|
261
261
|
/**
|
|
262
262
|
* Single-document mode
|
|
@@ -269,7 +269,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
269
269
|
? `\`\`\`json\n${formatJson(props.content as string)}\n\`\`\``
|
|
270
270
|
: (props.content as string)
|
|
271
271
|
return (
|
|
272
|
-
<div className={cn('
|
|
272
|
+
<div className={cn('text-editor p-6 flex flex-col h-screen w-full flex-1', className)}>
|
|
273
273
|
<StreamingMarkdown mode="streaming">
|
|
274
274
|
{content}
|
|
275
275
|
</StreamingMarkdown>
|
|
@@ -277,7 +277,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
277
277
|
)
|
|
278
278
|
}
|
|
279
279
|
return (
|
|
280
|
-
<div className={cn('
|
|
280
|
+
<div className={cn('text-editor flex flex-col h-screen w-full flex-1', className)}>
|
|
281
281
|
<DocumentEditorWithComments
|
|
282
282
|
content={props.content}
|
|
283
283
|
format={props.format}
|
|
@@ -288,7 +288,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
288
288
|
readOnly={mode === 'readonly'}
|
|
289
289
|
onAnnotationAdd={handleAnnotationAdd}
|
|
290
290
|
onAnnotationUpdate={handleAnnotationUpdate}
|
|
291
|
-
className={cn('
|
|
291
|
+
className={cn('text-editor p-6 h-full flex flex-col', className)}
|
|
292
292
|
/>
|
|
293
293
|
</div>
|
|
294
294
|
)
|
|
@@ -297,13 +297,13 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
297
297
|
/**
|
|
298
298
|
* Multi-tab mode
|
|
299
299
|
*/
|
|
300
|
-
const { hideTabBar } = props as
|
|
300
|
+
const { hideTabBar } = props as TextEditorMultiTabProps
|
|
301
301
|
|
|
302
302
|
let editorPane: React.ReactNode
|
|
303
303
|
|
|
304
304
|
if (!currentDocument) {
|
|
305
305
|
editorPane = (
|
|
306
|
-
<div className="
|
|
306
|
+
<div className="text-editor flex flex-col h-full w-full">
|
|
307
307
|
{!hideTabBar && (
|
|
308
308
|
<DocumentTabBar
|
|
309
309
|
className="w-full"
|
|
@@ -324,7 +324,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
324
324
|
const renderAsStreamdown = isMarkdownMulti || isJsonMulti
|
|
325
325
|
|
|
326
326
|
editorPane = (
|
|
327
|
-
<div className="
|
|
327
|
+
<div className="text-editor flex flex-col h-full w-full">
|
|
328
328
|
{!hideTabBar && (
|
|
329
329
|
<DocumentTabBar
|
|
330
330
|
className="w-full"
|
|
@@ -358,7 +358,7 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
358
358
|
readOnly={mode === 'readonly'}
|
|
359
359
|
onAnnotationAdd={handleAnnotationAdd}
|
|
360
360
|
onAnnotationUpdate={handleAnnotationUpdate}
|
|
361
|
-
className="
|
|
361
|
+
className="text-editor p-6 min-h-full"
|
|
362
362
|
/>
|
|
363
363
|
)}
|
|
364
364
|
</div>
|
|
@@ -400,4 +400,4 @@ export const AIDocEditor = React.memo<AIDocEditorProps>(
|
|
|
400
400
|
}
|
|
401
401
|
)
|
|
402
402
|
|
|
403
|
-
|
|
403
|
+
TextEditor.displayName = 'TextEditor'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextEditor Feature Component
|
|
3
|
+
*
|
|
4
|
+
* Complete document editor with inline comment annotations
|
|
5
|
+
* Supports both single-document and multi-tab modes
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { TextEditor } from './TextEditor'
|
|
9
|
+
export type {
|
|
10
|
+
TextEditorProps,
|
|
11
|
+
TextEditorSingleProps,
|
|
12
|
+
TextEditorMultiTabProps,
|
|
13
|
+
} from './TextEditor'
|
|
14
|
+
|
|
15
|
+
export { useTextEditorMock, useAIMultiTabDocEditorMock } from './useTextEditor.mock'
|
|
16
|
+
export type { UseTextEditorReturn, UseAIMultiTabDocEditorReturn } from './useTextEditor'
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
sampleContent,
|
|
20
|
+
currentUser,
|
|
21
|
+
sampleAnnotations,
|
|
22
|
+
sampleDocumentFiles,
|
|
23
|
+
sampleMultiTabDocuments,
|
|
24
|
+
} from './TextEditor.mocks'
|
package/components/features/{AIDocEditor/useAIDocEditor.d.ts → TextEditor/useTextEditor.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hook contract for
|
|
2
|
+
* Hook contract for TextEditor feature
|
|
3
3
|
*
|
|
4
4
|
* This interface defines the contract for managing document annotations.
|
|
5
5
|
* Applications should implement this hook to provide real data management,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @example
|
|
9
9
|
* ```tsx
|
|
10
10
|
* // Real implementation in application
|
|
11
|
-
* export function
|
|
11
|
+
* export function useTextEditor(documentId: string): UseTextEditorReturn {
|
|
12
12
|
* const [annotations, setAnnotations] = useState<Annotation[]>([])
|
|
13
13
|
* const [loading, setLoading] = useState(false)
|
|
14
14
|
*
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
* ```tsx
|
|
30
30
|
* // Usage in component
|
|
31
31
|
* const MyDocumentEditor = ({ documentId }) => {
|
|
32
|
-
* const { annotations, addAnnotation, updateAnnotation, loading } =
|
|
32
|
+
* const { annotations, addAnnotation, updateAnnotation, loading } = useTextEditor(documentId)
|
|
33
33
|
*
|
|
34
34
|
* return (
|
|
35
|
-
* <
|
|
35
|
+
* <TextEditor
|
|
36
36
|
* content={document}
|
|
37
37
|
* annotations={annotations}
|
|
38
38
|
* currentUser={user}
|
|
@@ -51,10 +51,10 @@ import type { FileTreeNode } from '@/components/composites/FileTreeExplorer'
|
|
|
51
51
|
import type { JSONContent } from '@tiptap/core'
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Return type for
|
|
54
|
+
* Return type for useTextEditor hook (single document)
|
|
55
55
|
* @deprecated Use UseAIMultiTabDocEditorReturn for multi-tab support
|
|
56
56
|
*/
|
|
57
|
-
export interface
|
|
57
|
+
export interface UseTextEditorReturn {
|
|
58
58
|
/**
|
|
59
59
|
* Array of annotations for the document
|
|
60
60
|
*/
|
|
@@ -168,7 +168,7 @@ export interface UseAIMultiTabDocEditorReturn {
|
|
|
168
168
|
* @param documentId - ID of the document to manage annotations for
|
|
169
169
|
* @returns Object containing annotations array and mutation methods
|
|
170
170
|
*/
|
|
171
|
-
export function
|
|
171
|
+
export function useTextEditor(documentId: string): UseTextEditorReturn
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
174
|
* Hook for managing multi-tab documents with annotations
|