@wakastellar/ui 3.3.3 → 3.5.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/dist/badge-BbwO7QeZ.js +1 -0
- package/dist/badge-BfiocODp.mjs +23 -0
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunk-14q5BKub.js +1 -0
- package/dist/{chunk-BH6uBOac.mjs → chunk-Cr9pTUWm.mjs} +5 -5
- package/dist/cn-DEtaFQsA.js +1 -0
- package/dist/cn-DUn6aSIQ.mjs +24 -0
- package/dist/doc.cjs.js +2 -2
- package/dist/doc.es.js +19 -19
- package/dist/editor.cjs.js +48 -0
- package/dist/editor.d.ts +1 -0
- package/dist/editor.es.js +6551 -0
- package/dist/{exceljs.min-DG9M8IZ1.mjs → exceljs.min-DL1XYDll.mjs} +1 -1
- package/dist/{exceljs.min-BuefmDRS.js → exceljs.min-qeIfSCbF.js} +1 -1
- package/dist/export.cjs.js +1 -1
- package/dist/export.es.js +1 -1
- package/dist/index.cjs.js +150 -150
- package/dist/index.es.js +26782 -27591
- package/dist/input-BfaSAGVw.js +1 -0
- package/dist/input-DVr_Qkl8.mjs +14 -0
- package/dist/rich-text.cjs.js +1 -1
- package/dist/rich-text.es.js +1 -1
- package/dist/security-CyBpuklN.mjs +122 -0
- package/dist/security-bFWwDrlg.js +1 -0
- package/dist/separator-NrkltulH.js +1 -0
- package/dist/separator-ibN2mycs.mjs +51 -0
- package/dist/src/components/editor/blocks/index.d.ts +51 -0
- package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
- package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
- package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
- package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
- package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
- package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
- package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
- package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
- package/dist/src/components/editor/index.d.ts +66 -0
- package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
- package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
- package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
- package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
- package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
- package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
- package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
- package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
- package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
- package/dist/src/components/editor/waka-editor.d.ts +59 -0
- package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
- package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
- package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
- package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
- package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
- package/dist/src/components/index.d.ts +1 -15
- package/dist/src/editor.d.ts +26 -0
- package/dist/textarea-CdQWggYG.js +1 -0
- package/dist/textarea-DJDXJ3nd.mjs +23 -0
- package/dist/types-C2St0wOW.js +1 -0
- package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
- package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
- package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
- package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
- package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
- package/dist/waka-editor-plugins-DR6tpsUC.mjs +135 -0
- package/dist/waka-editor-plugins-sGSh9hn2.js +1 -0
- package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
- package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
- package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
- package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
- package/package.json +87 -2
- package/src/blocks/footer/index.tsx +1 -6
- package/src/blocks/login/index.tsx +1 -7
- package/src/blocks/profile/index.tsx +3 -5
- package/src/components/editor/blocks/index.ts +182 -0
- package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
- package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
- package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
- package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
- package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
- package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
- package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
- package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
- package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
- package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
- package/src/components/editor/index.ts +279 -0
- package/src/components/editor/waka-ai-writer.tsx +434 -0
- package/src/components/editor/waka-collaborative-editor.tsx +426 -0
- package/src/components/editor/waka-diff-viewer.tsx +352 -0
- package/src/components/editor/waka-dnd-editor.tsx +284 -0
- package/src/components/editor/waka-document-editor.tsx +502 -0
- package/src/components/editor/waka-editor-elements.tsx +312 -0
- package/src/components/editor/waka-editor-leaves.tsx +101 -0
- package/src/components/editor/waka-editor-plugins.ts +207 -0
- package/src/components/editor/waka-editor-toolbar.tsx +358 -0
- package/src/components/editor/waka-editor.tsx +431 -0
- package/src/components/editor/waka-floating-toolbar.tsx +268 -0
- package/src/components/editor/waka-markdown-editor.tsx +395 -0
- package/src/components/editor/waka-mention-editor.tsx +459 -0
- package/src/components/editor/waka-slash-menu.tsx +392 -0
- package/src/components/editor/waka-spec-editor.tsx +657 -0
- package/src/components/index.ts +1 -18
- package/dist/chunk-BDDJmn7V.js +0 -1
- package/dist/cn-DnPbmOCy.js +0 -1
- package/dist/cn-DpLcAzrf.mjs +0 -22
- package/dist/separator-BDReXBvI.mjs +0 -59
- package/dist/separator-BKjNl9sI.js +0 -1
- package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
- package/dist/src/components/waka-actors-list/index.d.ts +0 -18
- package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
- package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
- package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
- package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
- package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
- package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
- package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
- package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
- package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
- package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
- package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
- package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
- package/dist/types-BH9cQRqZ.js +0 -1
- package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
- package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
- package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
- package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
- package/src/components/waka-actor-badge/index.tsx +0 -34
- package/src/components/waka-actors-list/index.tsx +0 -125
- package/src/components/waka-ai-assistant-button/index.tsx +0 -31
- package/src/components/waka-document-flyover/index.tsx +0 -36
- package/src/components/waka-document-preview-popup/index.tsx +0 -103
- package/src/components/waka-hour-balance-badge/index.tsx +0 -43
- package/src/components/waka-hour-consumption-table/index.tsx +0 -72
- package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
- package/src/components/waka-project-stats-header/index.tsx +0 -69
- package/src/components/waka-step-comment-bubble/index.tsx +0 -71
- package/src/components/waka-step-comment-panel/index.tsx +0 -106
- package/src/components/waka-step-permission-matrix/index.tsx +0 -65
- package/src/components/waka-time-entry-dialog/index.tsx +0 -131
- package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../utils/cn"
|
|
5
|
+
import { Label } from "../label"
|
|
6
|
+
import { Textarea } from "../textarea"
|
|
7
|
+
|
|
8
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
type SlateNode = any
|
|
12
|
+
|
|
13
|
+
/** Predefined AI action available in the AI menu */
|
|
14
|
+
export interface AIAction {
|
|
15
|
+
/** Unique key */
|
|
16
|
+
key: string
|
|
17
|
+
/** Display label */
|
|
18
|
+
label: string
|
|
19
|
+
/** Icon name from lucide-react (optional) */
|
|
20
|
+
icon?: string
|
|
21
|
+
/** Prompt template. Use {{selection}} for the selected text placeholder. */
|
|
22
|
+
prompt: string
|
|
23
|
+
/** Group name for the menu */
|
|
24
|
+
group?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Default AI actions available in the WakaAIWriter menu */
|
|
28
|
+
export const DEFAULT_AI_ACTIONS: AIAction[] = [
|
|
29
|
+
// Generation
|
|
30
|
+
{ key: "continue", label: "Continuer la redaction", prompt: "Continue writing the following text naturally:\n\n{{selection}}", group: "Generer" },
|
|
31
|
+
{ key: "summarize", label: "Resumer", prompt: "Summarize the following text concisely:\n\n{{selection}}", group: "Generer" },
|
|
32
|
+
{ key: "explain", label: "Expliquer", prompt: "Explain the following text in simple terms:\n\n{{selection}}", group: "Generer" },
|
|
33
|
+
{ key: "outline", label: "Creer un plan", prompt: "Create a structured outline from the following text:\n\n{{selection}}", group: "Generer" },
|
|
34
|
+
// Editing
|
|
35
|
+
{ key: "improve", label: "Ameliorer le style", prompt: "Improve the writing quality of the following text while keeping the same meaning:\n\n{{selection}}", group: "Editer" },
|
|
36
|
+
{ key: "shorter", label: "Raccourcir", prompt: "Make the following text shorter while keeping the key information:\n\n{{selection}}", group: "Editer" },
|
|
37
|
+
{ key: "longer", label: "Developper", prompt: "Expand and elaborate on the following text:\n\n{{selection}}", group: "Editer" },
|
|
38
|
+
{ key: "fix-grammar", label: "Corriger l'orthographe", prompt: "Fix all spelling and grammar errors in the following text:\n\n{{selection}}", group: "Editer" },
|
|
39
|
+
{ key: "simplify", label: "Simplifier", prompt: "Simplify the language of the following text to make it easier to understand:\n\n{{selection}}", group: "Editer" },
|
|
40
|
+
// Translation
|
|
41
|
+
{ key: "translate-en", label: "Traduire en anglais", prompt: "Translate the following text to English:\n\n{{selection}}", group: "Traduire" },
|
|
42
|
+
{ key: "translate-fr", label: "Traduire en francais", prompt: "Translate the following text to French:\n\n{{selection}}", group: "Traduire" },
|
|
43
|
+
{ key: "translate-es", label: "Traduire en espagnol", prompt: "Translate the following text to Spanish:\n\n{{selection}}", group: "Traduire" },
|
|
44
|
+
{ key: "translate-de", label: "Traduire en allemand", prompt: "Translate the following text to German:\n\n{{selection}}", group: "Traduire" },
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
export interface WakaAIWriterProps {
|
|
48
|
+
/** Initial Slate content */
|
|
49
|
+
value?: SlateNode[]
|
|
50
|
+
/** Change callback */
|
|
51
|
+
onChange?: (value: SlateNode[]) => void
|
|
52
|
+
/** API endpoint for AI completions (POST) */
|
|
53
|
+
aiEndpoint: string
|
|
54
|
+
/**
|
|
55
|
+
* System prompt sent to the AI API alongside the user prompt.
|
|
56
|
+
* Allows per-tenant/per-app customization of the AI behavior.
|
|
57
|
+
*/
|
|
58
|
+
systemPrompt?: string
|
|
59
|
+
/** AI actions available in the slash menu and AI menu */
|
|
60
|
+
aiActions?: AIAction[]
|
|
61
|
+
/** Enable ghost text copilot (inline AI suggestions) */
|
|
62
|
+
enableCopilot?: boolean
|
|
63
|
+
/** Debounce delay for copilot suggestions in ms (default: 500) */
|
|
64
|
+
copilotDebounceMs?: number
|
|
65
|
+
/** Headers to send with AI requests (e.g. Authorization) */
|
|
66
|
+
aiHeaders?: Record<string, string>
|
|
67
|
+
/** Read-only mode */
|
|
68
|
+
readOnly?: boolean
|
|
69
|
+
/** Placeholder text */
|
|
70
|
+
placeholder?: string
|
|
71
|
+
/** CSS class */
|
|
72
|
+
className?: string
|
|
73
|
+
/** Editor area CSS class */
|
|
74
|
+
editorClassName?: string
|
|
75
|
+
/** Minimum height in px */
|
|
76
|
+
minHeight?: number
|
|
77
|
+
/** Label */
|
|
78
|
+
label?: string
|
|
79
|
+
/** Description */
|
|
80
|
+
description?: string
|
|
81
|
+
/** Error */
|
|
82
|
+
error?: string
|
|
83
|
+
/** Extra Plate plugins */
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
extraPlugins?: any[]
|
|
86
|
+
/** Ref to the Plate editor instance */
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
+
editorRef?: React.MutableRefObject<any>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─── Plate types (loaded dynamically) ────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
interface PlateBundle {
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
Plate: React.ComponentType<any>
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
PlateContent: React.ComponentType<any>
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
usePlateEditor: (config: any) => any
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
plugins: any[]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── Component ───────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* WakaAIWriter — AI-powered rich text editor.
|
|
108
|
+
*
|
|
109
|
+
* Wraps Plate.js with `@platejs/ai` for:
|
|
110
|
+
* - Copilot ghost text (inline suggestions while typing)
|
|
111
|
+
* - AI menu (Cmd+J or `/ai`) with customizable actions
|
|
112
|
+
* - Configurable system prompt per tenant / app
|
|
113
|
+
*
|
|
114
|
+
* Requires peer dependencies: `platejs`, `@platejs/ai`, `@platejs/markdown`,
|
|
115
|
+
* `@platejs/basic-nodes`, `@platejs/selection`.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```tsx
|
|
119
|
+
* <WakaAIWriter
|
|
120
|
+
* aiEndpoint="/api/ai/completion"
|
|
121
|
+
* systemPrompt="You are a helpful writing assistant for healthcare documentation."
|
|
122
|
+
* enableCopilot
|
|
123
|
+
* />
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export const WakaAIWriter = React.forwardRef<HTMLDivElement, WakaAIWriterProps>(
|
|
127
|
+
(
|
|
128
|
+
{
|
|
129
|
+
value,
|
|
130
|
+
onChange,
|
|
131
|
+
aiEndpoint,
|
|
132
|
+
systemPrompt = "You are an advanced AI writing assistant. Help the user write, edit, and improve their content.",
|
|
133
|
+
aiActions = DEFAULT_AI_ACTIONS,
|
|
134
|
+
enableCopilot = true,
|
|
135
|
+
copilotDebounceMs = 500,
|
|
136
|
+
aiHeaders,
|
|
137
|
+
readOnly = false,
|
|
138
|
+
placeholder = "Tapez / pour les commandes, ou commencez a ecrire...",
|
|
139
|
+
className,
|
|
140
|
+
editorClassName,
|
|
141
|
+
minHeight = 300,
|
|
142
|
+
label,
|
|
143
|
+
description,
|
|
144
|
+
error,
|
|
145
|
+
extraPlugins,
|
|
146
|
+
editorRef,
|
|
147
|
+
},
|
|
148
|
+
ref
|
|
149
|
+
) => {
|
|
150
|
+
const [bundle, setBundle] = React.useState<PlateBundle | null>(null)
|
|
151
|
+
const [loadError, setLoadError] = React.useState(false)
|
|
152
|
+
|
|
153
|
+
// Stable refs for config values used in plugin setup
|
|
154
|
+
const configRef = React.useRef({ aiEndpoint, systemPrompt, aiActions, enableCopilot, copilotDebounceMs, aiHeaders })
|
|
155
|
+
configRef.current = { aiEndpoint, systemPrompt, aiActions, enableCopilot, copilotDebounceMs, aiHeaders }
|
|
156
|
+
|
|
157
|
+
React.useEffect(() => {
|
|
158
|
+
let cancelled = false
|
|
159
|
+
|
|
160
|
+
const load = async () => {
|
|
161
|
+
try {
|
|
162
|
+
const [
|
|
163
|
+
plateReact,
|
|
164
|
+
basicNodes,
|
|
165
|
+
table,
|
|
166
|
+
layout,
|
|
167
|
+
callout,
|
|
168
|
+
aiModule,
|
|
169
|
+
markdownModule,
|
|
170
|
+
selectionModule,
|
|
171
|
+
linkModule,
|
|
172
|
+
indentModule,
|
|
173
|
+
] = await Promise.all([
|
|
174
|
+
import("platejs/react"),
|
|
175
|
+
import("@platejs/basic-nodes/react"),
|
|
176
|
+
import("@platejs/table/react"),
|
|
177
|
+
import("@platejs/layout/react"),
|
|
178
|
+
import("@platejs/callout/react"),
|
|
179
|
+
import("@platejs/ai/react"),
|
|
180
|
+
import("@platejs/markdown"),
|
|
181
|
+
import("@platejs/selection/react"),
|
|
182
|
+
import("@platejs/link/react"),
|
|
183
|
+
import("@platejs/indent/react"),
|
|
184
|
+
])
|
|
185
|
+
|
|
186
|
+
const cfg = configRef.current
|
|
187
|
+
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
189
|
+
const plugins: any[] = [
|
|
190
|
+
// Core marks
|
|
191
|
+
basicNodes.BoldPlugin,
|
|
192
|
+
basicNodes.ItalicPlugin,
|
|
193
|
+
basicNodes.UnderlinePlugin,
|
|
194
|
+
basicNodes.StrikethroughPlugin,
|
|
195
|
+
basicNodes.CodePlugin,
|
|
196
|
+
basicNodes.HighlightPlugin,
|
|
197
|
+
// Headings
|
|
198
|
+
basicNodes.H1Plugin,
|
|
199
|
+
basicNodes.H2Plugin,
|
|
200
|
+
basicNodes.H3Plugin,
|
|
201
|
+
basicNodes.H4Plugin,
|
|
202
|
+
basicNodes.BlockquotePlugin,
|
|
203
|
+
// Table
|
|
204
|
+
table.TablePlugin,
|
|
205
|
+
table.TableRowPlugin,
|
|
206
|
+
table.TableCellPlugin,
|
|
207
|
+
table.TableCellHeaderPlugin,
|
|
208
|
+
// Layout
|
|
209
|
+
layout.ColumnPlugin,
|
|
210
|
+
layout.ColumnItemPlugin,
|
|
211
|
+
// Callout
|
|
212
|
+
callout.CalloutPlugin,
|
|
213
|
+
// Link
|
|
214
|
+
linkModule.LinkPlugin.configure({
|
|
215
|
+
options: { allowedSchemes: ["http", "https", "mailto", "tel"] },
|
|
216
|
+
}),
|
|
217
|
+
// Indent
|
|
218
|
+
indentModule.IndentPlugin,
|
|
219
|
+
// Selection (required by AI)
|
|
220
|
+
selectionModule.BlockSelectionPlugin,
|
|
221
|
+
// Markdown (required by AI)
|
|
222
|
+
markdownModule.MarkdownPlugin,
|
|
223
|
+
// AI plugins
|
|
224
|
+
aiModule.AIPlugin,
|
|
225
|
+
aiModule.AIChatPlugin.configure({
|
|
226
|
+
options: {
|
|
227
|
+
chat: {
|
|
228
|
+
api: cfg.aiEndpoint,
|
|
229
|
+
body: {
|
|
230
|
+
system: cfg.systemPrompt,
|
|
231
|
+
},
|
|
232
|
+
headers: cfg.aiHeaders,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
// Copilot plugin
|
|
239
|
+
if (cfg.enableCopilot && aiModule.CopilotPlugin) {
|
|
240
|
+
plugins.push(
|
|
241
|
+
aiModule.CopilotPlugin.configure(({ api }: { api: { copilot: { setBlockSuggestion: (o: { text: string }) => void } } }) => ({
|
|
242
|
+
options: {
|
|
243
|
+
completeOptions: {
|
|
244
|
+
api: cfg.aiEndpoint,
|
|
245
|
+
body: {
|
|
246
|
+
system: cfg.systemPrompt,
|
|
247
|
+
},
|
|
248
|
+
headers: cfg.aiHeaders,
|
|
249
|
+
onFinish: (_: unknown, completion: string) => {
|
|
250
|
+
if (completion === "0") return
|
|
251
|
+
api.copilot.setBlockSuggestion({ text: completion })
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
debounceDelay: cfg.copilotDebounceMs,
|
|
255
|
+
},
|
|
256
|
+
shortcuts: {
|
|
257
|
+
accept: { keys: "tab" },
|
|
258
|
+
acceptNextWord: { keys: "mod+right" },
|
|
259
|
+
reject: { keys: "escape" },
|
|
260
|
+
triggerSuggestion: { keys: "ctrl+space" },
|
|
261
|
+
},
|
|
262
|
+
}))
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (!cancelled) {
|
|
267
|
+
setBundle({
|
|
268
|
+
Plate: plateReact.Plate,
|
|
269
|
+
PlateContent: plateReact.PlateContent,
|
|
270
|
+
usePlateEditor: plateReact.usePlateEditor,
|
|
271
|
+
plugins,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
} catch (err) {
|
|
275
|
+
console.error("[WakaAIWriter] Failed to load plugins:", err)
|
|
276
|
+
if (!cancelled) setLoadError(true)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
load()
|
|
281
|
+
return () => { cancelled = true }
|
|
282
|
+
}, []) // Load once; config changes read from ref
|
|
283
|
+
|
|
284
|
+
if (bundle) {
|
|
285
|
+
return (
|
|
286
|
+
<AIWriterInner
|
|
287
|
+
ref={ref}
|
|
288
|
+
bundle={bundle}
|
|
289
|
+
value={value}
|
|
290
|
+
onChange={onChange}
|
|
291
|
+
readOnly={readOnly}
|
|
292
|
+
placeholder={placeholder}
|
|
293
|
+
className={className}
|
|
294
|
+
editorClassName={editorClassName}
|
|
295
|
+
minHeight={minHeight}
|
|
296
|
+
label={label}
|
|
297
|
+
description={description}
|
|
298
|
+
error={error}
|
|
299
|
+
extraPlugins={extraPlugins}
|
|
300
|
+
editorRef={editorRef}
|
|
301
|
+
/>
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Fallback
|
|
306
|
+
return (
|
|
307
|
+
<div ref={ref} className={cn("space-y-1.5", className)}>
|
|
308
|
+
{label && <Label>{label}</Label>}
|
|
309
|
+
{description && <p className="text-sm text-muted-foreground">{description}</p>}
|
|
310
|
+
<div className="relative">
|
|
311
|
+
<Textarea
|
|
312
|
+
placeholder={placeholder}
|
|
313
|
+
disabled
|
|
314
|
+
style={{ minHeight }}
|
|
315
|
+
className={cn(error && "border-destructive")}
|
|
316
|
+
/>
|
|
317
|
+
<div className="absolute inset-0 flex items-center justify-center bg-background/50 rounded-lg">
|
|
318
|
+
<span className="text-sm text-muted-foreground animate-pulse">
|
|
319
|
+
{loadError ? "Erreur de chargement de l'editeur IA" : "Chargement de l'editeur IA..."}
|
|
320
|
+
</span>
|
|
321
|
+
</div>
|
|
322
|
+
</div>
|
|
323
|
+
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
324
|
+
</div>
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
WakaAIWriter.displayName = "WakaAIWriter"
|
|
330
|
+
|
|
331
|
+
// ─── Inner Plate component ──────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
interface AIWriterInnerProps {
|
|
334
|
+
bundle: PlateBundle
|
|
335
|
+
value?: SlateNode[]
|
|
336
|
+
onChange?: (value: SlateNode[]) => void
|
|
337
|
+
readOnly?: boolean
|
|
338
|
+
placeholder?: string
|
|
339
|
+
className?: string
|
|
340
|
+
editorClassName?: string
|
|
341
|
+
minHeight?: number
|
|
342
|
+
label?: string
|
|
343
|
+
description?: string
|
|
344
|
+
error?: string
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
346
|
+
extraPlugins?: any[]
|
|
347
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
348
|
+
editorRef?: React.MutableRefObject<any>
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const AIWriterInner = React.forwardRef<HTMLDivElement, AIWriterInnerProps>(
|
|
352
|
+
(
|
|
353
|
+
{
|
|
354
|
+
bundle: { Plate, PlateContent, usePlateEditor, plugins },
|
|
355
|
+
value,
|
|
356
|
+
onChange,
|
|
357
|
+
readOnly,
|
|
358
|
+
placeholder,
|
|
359
|
+
className,
|
|
360
|
+
editorClassName,
|
|
361
|
+
minHeight,
|
|
362
|
+
label,
|
|
363
|
+
description,
|
|
364
|
+
error,
|
|
365
|
+
extraPlugins,
|
|
366
|
+
editorRef,
|
|
367
|
+
},
|
|
368
|
+
ref
|
|
369
|
+
) => {
|
|
370
|
+
const allPlugins = React.useMemo(
|
|
371
|
+
() => [...plugins, ...(extraPlugins ?? [])],
|
|
372
|
+
[plugins, extraPlugins]
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
const editor = usePlateEditor({
|
|
376
|
+
plugins: allPlugins,
|
|
377
|
+
value: value ?? [{ type: "p", children: [{ text: "" }] }],
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
React.useEffect(() => {
|
|
381
|
+
if (editorRef) editorRef.current = editor
|
|
382
|
+
}, [editor, editorRef])
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<div ref={ref} className={cn("space-y-1.5", className)}>
|
|
386
|
+
{label && <Label>{label}</Label>}
|
|
387
|
+
{description && <p className="text-sm text-muted-foreground">{description}</p>}
|
|
388
|
+
|
|
389
|
+
<Plate
|
|
390
|
+
editor={editor}
|
|
391
|
+
onChange={onChange ? ({ value: v }: { value: SlateNode[] }) => onChange(v) : undefined}
|
|
392
|
+
readOnly={readOnly}
|
|
393
|
+
>
|
|
394
|
+
<div className={cn(
|
|
395
|
+
"border rounded-lg overflow-hidden",
|
|
396
|
+
error && "border-destructive",
|
|
397
|
+
readOnly && "opacity-80",
|
|
398
|
+
)}>
|
|
399
|
+
{/* AI toolbar hint */}
|
|
400
|
+
{!readOnly && (
|
|
401
|
+
<div className="flex items-center gap-2 px-3 py-1.5 border-b bg-muted/30 text-xs text-muted-foreground">
|
|
402
|
+
<kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-[10px]">Ctrl+J</kbd>
|
|
403
|
+
<span>Ouvrir le menu IA</span>
|
|
404
|
+
<span className="mx-1">|</span>
|
|
405
|
+
<kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-[10px]">/</kbd>
|
|
406
|
+
<span>Commandes</span>
|
|
407
|
+
<span className="mx-1">|</span>
|
|
408
|
+
<kbd className="rounded border bg-muted px-1.5 py-0.5 font-mono text-[10px]">Ctrl+Espace</kbd>
|
|
409
|
+
<span>Copilot</span>
|
|
410
|
+
</div>
|
|
411
|
+
)}
|
|
412
|
+
|
|
413
|
+
<div style={{ minHeight }}>
|
|
414
|
+
<PlateContent
|
|
415
|
+
placeholder={placeholder}
|
|
416
|
+
readOnly={readOnly}
|
|
417
|
+
className={cn(
|
|
418
|
+
"prose prose-sm max-w-none p-4",
|
|
419
|
+
"focus-within:outline-none",
|
|
420
|
+
"[&_[data-slate-editor]]:outline-none [&_[data-slate-editor]]:min-h-[inherit]",
|
|
421
|
+
editorClassName
|
|
422
|
+
)}
|
|
423
|
+
/>
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
</Plate>
|
|
427
|
+
|
|
428
|
+
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
429
|
+
</div>
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
AIWriterInner.displayName = "AIWriterInner"
|