@qwanyx/ai-editor 1.1.0 → 1.2.1
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/components/AIEditor.js +37 -34
- package/dist/components/AIToolbar.js +13 -7
- package/dist/components/EditorToolbar.js +85 -82
- package/dist/components/MarkdownPreview.js +8 -5
- package/dist/components/PromptModal.js +13 -10
- package/dist/components/RichTextEditor.js +91 -55
- package/dist/components/RichTextViewer.d.ts +6 -0
- package/dist/components/RichTextViewer.d.ts.map +1 -0
- package/dist/components/RichTextViewer.js +161 -0
- package/dist/hooks/useAIEditor.js +17 -14
- package/dist/hooks/useSelection.js +9 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -9
- package/dist/nodes/ImageLinkNode.js +17 -10
- package/dist/nodes/ImageNode.js +66 -60
- package/dist/nodes/LinkNode.js +13 -6
- package/dist/plugins/ImageLinkPlugin.js +26 -23
- package/dist/plugins/InsertObjectPlugin.js +74 -70
- package/dist/plugins/LinkPlugin.js +16 -13
- package/package.json +2 -3
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptModal = PromptModal;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
function PromptModal({ isOpen, onClose, onSubmit, selectedText, isLoading }) {
|
|
7
|
+
const [prompt, setPrompt] = (0, react_1.useState)('');
|
|
8
|
+
const inputRef = (0, react_1.useRef)(null);
|
|
9
|
+
(0, react_1.useEffect)(() => {
|
|
7
10
|
if (isOpen && inputRef.current) {
|
|
8
11
|
inputRef.current.focus();
|
|
9
12
|
}
|
|
10
13
|
}, [isOpen]);
|
|
11
|
-
useEffect(() => {
|
|
14
|
+
(0, react_1.useEffect)(() => {
|
|
12
15
|
const handleKeyDown = (e) => {
|
|
13
16
|
if (e.key === 'Escape' && isOpen) {
|
|
14
17
|
onClose();
|
|
@@ -26,13 +29,13 @@ export function PromptModal({ isOpen, onClose, onSubmit, selectedText, isLoading
|
|
|
26
29
|
};
|
|
27
30
|
if (!isOpen)
|
|
28
31
|
return null;
|
|
29
|
-
return (
|
|
32
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50", children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-white rounded-xl shadow-2xl w-full max-w-lg mx-4 overflow-hidden", children: [(0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-4 border-b border-gray-200 flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold text-gray-900", children: "Instruction IA" }), (0, jsx_runtime_1.jsx)("button", { onClick: onClose, className: "p-1 text-gray-400 hover:text-gray-600 transition-colors", children: (0, jsx_runtime_1.jsx)("span", { className: "material-icons", children: "close" }) })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, className: "p-6", children: [selectedText && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Texte s\u00E9lectionn\u00E9" }), (0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-blue-50 border border-blue-200 rounded-lg text-sm text-gray-700 max-h-32 overflow-y-auto", children: selectedText.length > 200
|
|
30
33
|
? `${selectedText.substring(0, 200)}...`
|
|
31
|
-
: selectedText })] })),
|
|
34
|
+
: selectedText })] })), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: selectedText ? 'Que voulez-vous faire avec ce texte ?' : 'Instruction pour l\'IA' }), (0, jsx_runtime_1.jsx)("textarea", { ref: inputRef, value: prompt, onChange: (e) => setPrompt(e.target.value), placeholder: "Ex: Rends ce texte plus concis, ajoute des exemples, traduis en anglais...", className: "w-full px-4 py-3 border border-gray-300 rounded-lg focus:border-blue-500 focus:ring-1 focus:ring-blue-500 resize-none", rows: 3, disabled: isLoading })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsx)("label", { className: "block text-sm font-medium text-gray-500 mb-2", children: "Actions rapides" }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-2", children: [
|
|
32
35
|
'Rends plus concis',
|
|
33
36
|
'Ajoute des détails',
|
|
34
37
|
'Simplifie le vocabulaire',
|
|
35
38
|
'Traduis en anglais',
|
|
36
39
|
'Reformule autrement'
|
|
37
|
-
].map((action) => (
|
|
40
|
+
].map((action) => ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setPrompt(action), className: "px-3 py-1 text-xs bg-gray-100 text-gray-700 rounded-full hover:bg-gray-200 transition-colors", children: action }, action))) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClose, className: "px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors", disabled: isLoading, children: "Annuler" }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: !prompt.trim() || isLoading, className: "px-6 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center gap-2", children: isLoading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons text-sm animate-spin", children: "sync" }), "Traitement..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons text-sm", children: "send" }), "Appliquer"] })) })] })] })] }) }));
|
|
38
41
|
}
|
|
@@ -1,26 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
'use client';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.RichTextEditor = RichTextEditor;
|
|
38
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
|
+
const react_1 = __importStar(require("react"));
|
|
40
|
+
const LexicalComposer_1 = require("@lexical/react/LexicalComposer");
|
|
41
|
+
const LexicalRichTextPlugin_1 = require("@lexical/react/LexicalRichTextPlugin");
|
|
42
|
+
const LexicalContentEditable_1 = require("@lexical/react/LexicalContentEditable");
|
|
43
|
+
const LexicalHistoryPlugin_1 = require("@lexical/react/LexicalHistoryPlugin");
|
|
44
|
+
const LexicalOnChangePlugin_1 = require("@lexical/react/LexicalOnChangePlugin");
|
|
45
|
+
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
|
|
46
|
+
const LexicalErrorBoundary_1 = require("@lexical/react/LexicalErrorBoundary");
|
|
47
|
+
const rich_text_1 = require("@lexical/rich-text");
|
|
48
|
+
const list_1 = require("@lexical/list");
|
|
49
|
+
const link_1 = require("@lexical/link");
|
|
50
|
+
const ImageNode_1 = require("../nodes/ImageNode");
|
|
51
|
+
const ImageLinkNode_1 = require("../nodes/ImageLinkNode");
|
|
52
|
+
const LinkNode_1 = require("../nodes/LinkNode");
|
|
53
|
+
const InsertObjectPlugin_1 = require("../plugins/InsertObjectPlugin");
|
|
54
|
+
const ImageLinkPlugin_1 = require("../plugins/ImageLinkPlugin");
|
|
55
|
+
const LinkPlugin_1 = require("../plugins/LinkPlugin");
|
|
56
|
+
const LexicalListPlugin_1 = require("@lexical/react/LexicalListPlugin");
|
|
57
|
+
const LexicalLinkPlugin_1 = require("@lexical/react/LexicalLinkPlugin");
|
|
58
|
+
const lexical_1 = require("lexical");
|
|
59
|
+
const EditorToolbar_1 = require("./EditorToolbar");
|
|
24
60
|
// Theme for Lexical editor
|
|
25
61
|
const editorTheme = {
|
|
26
62
|
paragraph: 'editor-paragraph',
|
|
@@ -123,22 +159,22 @@ function getEditorConfig(initialContent) {
|
|
|
123
159
|
console.error('Lexical error:', error);
|
|
124
160
|
},
|
|
125
161
|
nodes: [
|
|
126
|
-
HeadingNode,
|
|
127
|
-
QuoteNode,
|
|
128
|
-
ListNode,
|
|
129
|
-
ListItemNode,
|
|
130
|
-
LinkNode,
|
|
131
|
-
ImageNode,
|
|
132
|
-
ImageLinkNode,
|
|
133
|
-
SimpleLinkNode,
|
|
162
|
+
rich_text_1.HeadingNode,
|
|
163
|
+
rich_text_1.QuoteNode,
|
|
164
|
+
list_1.ListNode,
|
|
165
|
+
list_1.ListItemNode,
|
|
166
|
+
link_1.LinkNode,
|
|
167
|
+
ImageNode_1.ImageNode,
|
|
168
|
+
ImageLinkNode_1.ImageLinkNode,
|
|
169
|
+
LinkNode_1.SimpleLinkNode,
|
|
134
170
|
],
|
|
135
171
|
};
|
|
136
172
|
}
|
|
137
173
|
// Plugin to set initial content (supports both JSON and plain text)
|
|
138
174
|
function InitialContentPlugin({ content }) {
|
|
139
|
-
const [editor] = useLexicalComposerContext();
|
|
140
|
-
const isFirstRender =
|
|
141
|
-
useEffect(() => {
|
|
175
|
+
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
176
|
+
const isFirstRender = react_1.default.useRef(true);
|
|
177
|
+
(0, react_1.useEffect)(() => {
|
|
142
178
|
if (isFirstRender.current && content) {
|
|
143
179
|
isFirstRender.current = false;
|
|
144
180
|
// Try to parse as JSON (Lexical state)
|
|
@@ -156,10 +192,10 @@ function InitialContentPlugin({ content }) {
|
|
|
156
192
|
}
|
|
157
193
|
// Fallback: plain text
|
|
158
194
|
editor.update(() => {
|
|
159
|
-
const root =
|
|
195
|
+
const root = (0, lexical_1.$getRoot)();
|
|
160
196
|
root.clear();
|
|
161
|
-
const paragraph =
|
|
162
|
-
paragraph.append(
|
|
197
|
+
const paragraph = (0, lexical_1.$createParagraphNode)();
|
|
198
|
+
paragraph.append((0, lexical_1.$createTextNode)(content));
|
|
163
199
|
root.append(paragraph);
|
|
164
200
|
});
|
|
165
201
|
}
|
|
@@ -168,24 +204,24 @@ function InitialContentPlugin({ content }) {
|
|
|
168
204
|
}
|
|
169
205
|
// Plugin to get editor reference
|
|
170
206
|
function EditorRefPlugin({ editorRef }) {
|
|
171
|
-
const [editor] = useLexicalComposerContext();
|
|
172
|
-
useEffect(() => {
|
|
207
|
+
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
208
|
+
(0, react_1.useEffect)(() => {
|
|
173
209
|
editorRef.current = editor;
|
|
174
210
|
}, [editor, editorRef]);
|
|
175
211
|
return null;
|
|
176
212
|
}
|
|
177
|
-
|
|
178
|
-
const editorRef =
|
|
179
|
-
const [isLoading, setIsLoading] =
|
|
180
|
-
const [isFullscreen, setIsFullscreen] =
|
|
181
|
-
const toggleFullscreen = useCallback(() => {
|
|
213
|
+
function RichTextEditor({ initialContent = '', onChange, onAIRequest, placeholder = 'Commencez à écrire...', className = '', minHeight = '300px' }) {
|
|
214
|
+
const editorRef = react_1.default.useRef(null);
|
|
215
|
+
const [isLoading, setIsLoading] = react_1.default.useState(false);
|
|
216
|
+
const [isFullscreen, setIsFullscreen] = react_1.default.useState(false);
|
|
217
|
+
const toggleFullscreen = (0, react_1.useCallback)(() => {
|
|
182
218
|
setIsFullscreen(prev => !prev);
|
|
183
219
|
}, []);
|
|
184
220
|
// Handle editor changes
|
|
185
|
-
const handleChange = useCallback((editorState, editor) => {
|
|
221
|
+
const handleChange = (0, react_1.useCallback)((editorState, editor) => {
|
|
186
222
|
editorState.read(() => {
|
|
187
223
|
// Get HTML
|
|
188
|
-
const root =
|
|
224
|
+
const root = (0, lexical_1.$getRoot)();
|
|
189
225
|
const textContent = root.getTextContent();
|
|
190
226
|
// Get JSON
|
|
191
227
|
const json = JSON.stringify(editorState.toJSON());
|
|
@@ -194,30 +230,30 @@ export function RichTextEditor({ initialContent = '', onChange, onAIRequest, pla
|
|
|
194
230
|
});
|
|
195
231
|
}, [onChange]);
|
|
196
232
|
// Get selected text for AI operations
|
|
197
|
-
const getSelectedText = useCallback(() => {
|
|
233
|
+
const getSelectedText = (0, react_1.useCallback)(() => {
|
|
198
234
|
if (!editorRef.current)
|
|
199
235
|
return undefined;
|
|
200
236
|
let selectedText;
|
|
201
237
|
editorRef.current.getEditorState().read(() => {
|
|
202
|
-
const selection =
|
|
203
|
-
if (
|
|
238
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
239
|
+
if ((0, lexical_1.$isRangeSelection)(selection)) {
|
|
204
240
|
selectedText = selection.getTextContent();
|
|
205
241
|
}
|
|
206
242
|
});
|
|
207
243
|
return selectedText || undefined;
|
|
208
244
|
}, []);
|
|
209
245
|
// Get full text for AI operations
|
|
210
|
-
const getFullText = useCallback(() => {
|
|
246
|
+
const getFullText = (0, react_1.useCallback)(() => {
|
|
211
247
|
if (!editorRef.current)
|
|
212
248
|
return '';
|
|
213
249
|
let fullText = '';
|
|
214
250
|
editorRef.current.getEditorState().read(() => {
|
|
215
|
-
fullText =
|
|
251
|
+
fullText = (0, lexical_1.$getRoot)().getTextContent();
|
|
216
252
|
});
|
|
217
253
|
return fullText;
|
|
218
254
|
}, []);
|
|
219
255
|
// Handle AI actions
|
|
220
|
-
const handleAIAction = useCallback(async (action, customPrompt) => {
|
|
256
|
+
const handleAIAction = (0, react_1.useCallback)(async (action, customPrompt) => {
|
|
221
257
|
if (!onAIRequest || !editorRef.current)
|
|
222
258
|
return;
|
|
223
259
|
setIsLoading(true);
|
|
@@ -232,10 +268,10 @@ export function RichTextEditor({ initialContent = '', onChange, onAIRequest, pla
|
|
|
232
268
|
});
|
|
233
269
|
// Replace content with AI result
|
|
234
270
|
editorRef.current.update(() => {
|
|
235
|
-
const root =
|
|
271
|
+
const root = (0, lexical_1.$getRoot)();
|
|
236
272
|
root.clear();
|
|
237
|
-
const paragraph =
|
|
238
|
-
paragraph.append(
|
|
273
|
+
const paragraph = (0, lexical_1.$createParagraphNode)();
|
|
274
|
+
paragraph.append((0, lexical_1.$createTextNode)(result));
|
|
239
275
|
root.append(paragraph);
|
|
240
276
|
});
|
|
241
277
|
}
|
|
@@ -249,5 +285,5 @@ export function RichTextEditor({ initialContent = '', onChange, onAIRequest, pla
|
|
|
249
285
|
const fullscreenClasses = isFullscreen
|
|
250
286
|
? 'fixed inset-0 z-50'
|
|
251
287
|
: '';
|
|
252
|
-
return (
|
|
288
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `border border-gray-200 rounded-lg bg-white flex flex-col ${fullscreenClasses} ${className}`, children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: editorStyles } }), (0, jsx_runtime_1.jsxs)(LexicalComposer_1.LexicalComposer, { initialConfig: getEditorConfig(initialContent), children: [(0, jsx_runtime_1.jsx)(EditorToolbar_1.EditorToolbar, { onAIAction: handleAIAction, isLoading: isLoading, isFullscreen: isFullscreen, onToggleFullscreen: toggleFullscreen }), (0, jsx_runtime_1.jsxs)("div", { className: "relative flex-1 overflow-auto", style: minHeight && !isFullscreen ? { minHeight } : {}, children: [(0, jsx_runtime_1.jsx)(LexicalRichTextPlugin_1.RichTextPlugin, { contentEditable: (0, jsx_runtime_1.jsx)(LexicalContentEditable_1.ContentEditable, { className: "outline-none p-4 text-gray-900 h-full min-h-full" }), placeholder: (0, jsx_runtime_1.jsx)("div", { className: "absolute top-4 left-4 text-gray-400 pointer-events-none", children: placeholder }), ErrorBoundary: LexicalErrorBoundary_1.LexicalErrorBoundary }), (0, jsx_runtime_1.jsx)(LexicalHistoryPlugin_1.HistoryPlugin, {}), (0, jsx_runtime_1.jsx)(LexicalListPlugin_1.ListPlugin, {}), (0, jsx_runtime_1.jsx)(LexicalLinkPlugin_1.LinkPlugin, {}), (0, jsx_runtime_1.jsx)(LexicalOnChangePlugin_1.OnChangePlugin, { onChange: handleChange }), (0, jsx_runtime_1.jsx)(InitialContentPlugin, { content: initialContent }), (0, jsx_runtime_1.jsx)(EditorRefPlugin, { editorRef: editorRef }), (0, jsx_runtime_1.jsx)(InsertObjectPlugin_1.InsertObjectPlugin, {}), (0, jsx_runtime_1.jsx)(ImageLinkPlugin_1.ImageLinkPlugin, {}), (0, jsx_runtime_1.jsx)(LinkPlugin_1.SimpleLinkPlugin, {}), isLoading && ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 bg-white/80 flex items-center justify-center z-10", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-blue-600", children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons animate-spin", children: "sync" }), (0, jsx_runtime_1.jsx)("span", { children: "L'IA travaille..." })] }) }))] })] })] }));
|
|
253
289
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichTextViewer.d.ts","sourceRoot":"","sources":["../../src/components/RichTextViewer.tsx"],"names":[],"mappings":"AAmBA,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4ID,wBAAgB,cAAc,CAAC,EAC7B,OAAY,EACZ,SAAc,EACf,EAAE,mBAAmB,kDA4BrB"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.RichTextViewer = RichTextViewer;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const LexicalComposer_1 = require("@lexical/react/LexicalComposer");
|
|
8
|
+
const LexicalRichTextPlugin_1 = require("@lexical/react/LexicalRichTextPlugin");
|
|
9
|
+
const LexicalContentEditable_1 = require("@lexical/react/LexicalContentEditable");
|
|
10
|
+
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
|
|
11
|
+
const LexicalErrorBoundary_1 = require("@lexical/react/LexicalErrorBoundary");
|
|
12
|
+
const rich_text_1 = require("@lexical/rich-text");
|
|
13
|
+
const list_1 = require("@lexical/list");
|
|
14
|
+
const link_1 = require("@lexical/link");
|
|
15
|
+
const ImageNode_1 = require("../nodes/ImageNode");
|
|
16
|
+
const ImageLinkNode_1 = require("../nodes/ImageLinkNode");
|
|
17
|
+
const LinkNode_1 = require("../nodes/LinkNode");
|
|
18
|
+
const ImageLinkPlugin_1 = require("../plugins/ImageLinkPlugin");
|
|
19
|
+
const LinkPlugin_1 = require("../plugins/LinkPlugin");
|
|
20
|
+
const LexicalListPlugin_1 = require("@lexical/react/LexicalListPlugin");
|
|
21
|
+
const LexicalLinkPlugin_1 = require("@lexical/react/LexicalLinkPlugin");
|
|
22
|
+
// Theme for Lexical viewer (same as editor)
|
|
23
|
+
const viewerTheme = {
|
|
24
|
+
paragraph: 'editor-paragraph',
|
|
25
|
+
heading: {
|
|
26
|
+
h1: 'editor-heading-h1',
|
|
27
|
+
h2: 'editor-heading-h2',
|
|
28
|
+
h3: 'editor-heading-h3',
|
|
29
|
+
},
|
|
30
|
+
text: {
|
|
31
|
+
bold: 'editor-text-bold',
|
|
32
|
+
italic: 'editor-text-italic',
|
|
33
|
+
underline: 'editor-text-underline',
|
|
34
|
+
strikethrough: 'editor-text-strikethrough',
|
|
35
|
+
},
|
|
36
|
+
list: {
|
|
37
|
+
ul: 'editor-list-ul',
|
|
38
|
+
ol: 'editor-list-ol',
|
|
39
|
+
listitem: 'editor-listitem',
|
|
40
|
+
nested: {
|
|
41
|
+
listitem: 'editor-nested-listitem',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
quote: 'editor-quote',
|
|
45
|
+
link: 'editor-link',
|
|
46
|
+
};
|
|
47
|
+
// CSS styles for the viewer (same as editor)
|
|
48
|
+
const viewerStyles = `
|
|
49
|
+
.editor-paragraph {
|
|
50
|
+
margin-bottom: 8px;
|
|
51
|
+
}
|
|
52
|
+
.editor-heading-h1 {
|
|
53
|
+
font-size: 1.875rem;
|
|
54
|
+
font-weight: bold;
|
|
55
|
+
margin-bottom: 16px;
|
|
56
|
+
margin-top: 24px;
|
|
57
|
+
}
|
|
58
|
+
.editor-heading-h2 {
|
|
59
|
+
font-size: 1.5rem;
|
|
60
|
+
font-weight: bold;
|
|
61
|
+
margin-bottom: 12px;
|
|
62
|
+
margin-top: 20px;
|
|
63
|
+
}
|
|
64
|
+
.editor-heading-h3 {
|
|
65
|
+
font-size: 1.25rem;
|
|
66
|
+
font-weight: 600;
|
|
67
|
+
margin-bottom: 8px;
|
|
68
|
+
margin-top: 16px;
|
|
69
|
+
}
|
|
70
|
+
.editor-text-bold {
|
|
71
|
+
font-weight: bold;
|
|
72
|
+
}
|
|
73
|
+
.editor-text-italic {
|
|
74
|
+
font-style: italic;
|
|
75
|
+
}
|
|
76
|
+
.editor-text-underline {
|
|
77
|
+
text-decoration: underline;
|
|
78
|
+
}
|
|
79
|
+
.editor-text-strikethrough {
|
|
80
|
+
text-decoration: line-through;
|
|
81
|
+
}
|
|
82
|
+
.editor-list-ul {
|
|
83
|
+
list-style-type: disc;
|
|
84
|
+
margin-left: 24px;
|
|
85
|
+
margin-bottom: 8px;
|
|
86
|
+
padding-left: 0;
|
|
87
|
+
}
|
|
88
|
+
.editor-list-ol {
|
|
89
|
+
list-style-type: decimal;
|
|
90
|
+
margin-left: 24px;
|
|
91
|
+
margin-bottom: 8px;
|
|
92
|
+
padding-left: 0;
|
|
93
|
+
}
|
|
94
|
+
.editor-listitem {
|
|
95
|
+
margin-bottom: 4px;
|
|
96
|
+
}
|
|
97
|
+
.editor-nested-listitem {
|
|
98
|
+
list-style-type: none;
|
|
99
|
+
}
|
|
100
|
+
.editor-quote {
|
|
101
|
+
border-left: 4px solid #d1d5db;
|
|
102
|
+
padding-left: 16px;
|
|
103
|
+
font-style: italic;
|
|
104
|
+
color: #6b7280;
|
|
105
|
+
margin: 16px 0;
|
|
106
|
+
}
|
|
107
|
+
.editor-link {
|
|
108
|
+
color: #2563eb;
|
|
109
|
+
text-decoration: underline;
|
|
110
|
+
}
|
|
111
|
+
.editor-link:hover {
|
|
112
|
+
color: #1e40af;
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
// Viewer config
|
|
116
|
+
function getViewerConfig() {
|
|
117
|
+
return {
|
|
118
|
+
namespace: 'RichTextViewer',
|
|
119
|
+
theme: viewerTheme,
|
|
120
|
+
editable: false,
|
|
121
|
+
onError: (error) => {
|
|
122
|
+
console.error('Lexical viewer error:', error);
|
|
123
|
+
},
|
|
124
|
+
nodes: [
|
|
125
|
+
rich_text_1.HeadingNode,
|
|
126
|
+
rich_text_1.QuoteNode,
|
|
127
|
+
list_1.ListNode,
|
|
128
|
+
list_1.ListItemNode,
|
|
129
|
+
link_1.LinkNode,
|
|
130
|
+
ImageNode_1.ImageNode,
|
|
131
|
+
ImageLinkNode_1.ImageLinkNode,
|
|
132
|
+
LinkNode_1.SimpleLinkNode,
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// Plugin to load content
|
|
137
|
+
function ContentLoaderPlugin({ content }) {
|
|
138
|
+
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
139
|
+
(0, react_1.useEffect)(() => {
|
|
140
|
+
if (content) {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(content);
|
|
143
|
+
if (parsed.root) {
|
|
144
|
+
const editorState = editor.parseEditorState(parsed);
|
|
145
|
+
editor.setEditorState(editorState);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Not valid JSON, ignore
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}, [editor, content]);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function RichTextViewer({ content = '', className = '' }) {
|
|
156
|
+
// Don't render anything if no content
|
|
157
|
+
if (!content) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: className, children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: viewerStyles } }), (0, jsx_runtime_1.jsxs)(LexicalComposer_1.LexicalComposer, { initialConfig: getViewerConfig(), children: [(0, jsx_runtime_1.jsx)(LexicalRichTextPlugin_1.RichTextPlugin, { contentEditable: (0, jsx_runtime_1.jsx)(LexicalContentEditable_1.ContentEditable, { className: "outline-none text-gray-900", style: { cursor: 'default' } }), placeholder: null, ErrorBoundary: LexicalErrorBoundary_1.LexicalErrorBoundary }), (0, jsx_runtime_1.jsx)(LexicalListPlugin_1.ListPlugin, {}), (0, jsx_runtime_1.jsx)(LexicalLinkPlugin_1.LinkPlugin, {}), (0, jsx_runtime_1.jsx)(ContentLoaderPlugin, { content: content }), (0, jsx_runtime_1.jsx)(ImageLinkPlugin_1.ImageLinkPlugin, {}), (0, jsx_runtime_1.jsx)(LinkPlugin_1.SimpleLinkPlugin, {})] })] }));
|
|
161
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAIEditor = useAIEditor;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useAIEditor(initialContent = '') {
|
|
6
|
+
const [content, setContentState] = (0, react_1.useState)(initialContent);
|
|
7
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
8
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
6
9
|
// History for undo/redo
|
|
7
|
-
const historyRef = useRef([{ content: initialContent, timestamp: Date.now() }]);
|
|
8
|
-
const historyIndexRef = useRef(0);
|
|
9
|
-
const [canUndo, setCanUndo] = useState(false);
|
|
10
|
-
const [canRedo, setCanRedo] = useState(false);
|
|
11
|
-
const updateUndoRedoState = useCallback(() => {
|
|
10
|
+
const historyRef = (0, react_1.useRef)([{ content: initialContent, timestamp: Date.now() }]);
|
|
11
|
+
const historyIndexRef = (0, react_1.useRef)(0);
|
|
12
|
+
const [canUndo, setCanUndo] = (0, react_1.useState)(false);
|
|
13
|
+
const [canRedo, setCanRedo] = (0, react_1.useState)(false);
|
|
14
|
+
const updateUndoRedoState = (0, react_1.useCallback)(() => {
|
|
12
15
|
setCanUndo(historyIndexRef.current > 0);
|
|
13
16
|
setCanRedo(historyIndexRef.current < historyRef.current.length - 1);
|
|
14
17
|
}, []);
|
|
15
|
-
const setContent = useCallback((newContent) => {
|
|
18
|
+
const setContent = (0, react_1.useCallback)((newContent) => {
|
|
16
19
|
// Add to history, removing any future states
|
|
17
20
|
historyRef.current = historyRef.current.slice(0, historyIndexRef.current + 1);
|
|
18
21
|
historyRef.current.push({ content: newContent, timestamp: Date.now() });
|
|
@@ -20,21 +23,21 @@ export function useAIEditor(initialContent = '') {
|
|
|
20
23
|
setContentState(newContent);
|
|
21
24
|
updateUndoRedoState();
|
|
22
25
|
}, [updateUndoRedoState]);
|
|
23
|
-
const undo = useCallback(() => {
|
|
26
|
+
const undo = (0, react_1.useCallback)(() => {
|
|
24
27
|
if (historyIndexRef.current > 0) {
|
|
25
28
|
historyIndexRef.current--;
|
|
26
29
|
setContentState(historyRef.current[historyIndexRef.current].content);
|
|
27
30
|
updateUndoRedoState();
|
|
28
31
|
}
|
|
29
32
|
}, [updateUndoRedoState]);
|
|
30
|
-
const redo = useCallback(() => {
|
|
33
|
+
const redo = (0, react_1.useCallback)(() => {
|
|
31
34
|
if (historyIndexRef.current < historyRef.current.length - 1) {
|
|
32
35
|
historyIndexRef.current++;
|
|
33
36
|
setContentState(historyRef.current[historyIndexRef.current].content);
|
|
34
37
|
updateUndoRedoState();
|
|
35
38
|
}
|
|
36
39
|
}, [updateUndoRedoState]);
|
|
37
|
-
const applyAIEdit = useCallback((newContent) => {
|
|
40
|
+
const applyAIEdit = (0, react_1.useCallback)((newContent) => {
|
|
38
41
|
setContent(newContent);
|
|
39
42
|
setIsLoading(false);
|
|
40
43
|
setError(null);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useSelection = useSelection;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useSelection(containerRef) {
|
|
6
|
+
const [selection, setSelection] = (0, react_1.useState)(null);
|
|
7
|
+
const handleSelectionChange = (0, react_1.useCallback)(() => {
|
|
5
8
|
const sel = window.getSelection();
|
|
6
9
|
if (!sel || sel.isCollapsed || !containerRef.current) {
|
|
7
10
|
setSelection(null);
|
|
@@ -31,13 +34,13 @@ export function useSelection(containerRef) {
|
|
|
31
34
|
originalEnd: startOffset + text.length
|
|
32
35
|
});
|
|
33
36
|
}, [containerRef]);
|
|
34
|
-
useEffect(() => {
|
|
37
|
+
(0, react_1.useEffect)(() => {
|
|
35
38
|
document.addEventListener('selectionchange', handleSelectionChange);
|
|
36
39
|
return () => {
|
|
37
40
|
document.removeEventListener('selectionchange', handleSelectionChange);
|
|
38
41
|
};
|
|
39
42
|
}, [handleSelectionChange]);
|
|
40
|
-
const clearSelection = useCallback(() => {
|
|
43
|
+
const clearSelection = (0, react_1.useCallback)(() => {
|
|
41
44
|
setSelection(null);
|
|
42
45
|
window.getSelection()?.removeAllRanges();
|
|
43
46
|
}, []);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { RichTextEditor } from './components/RichTextEditor';
|
|
2
2
|
export type { RichTextEditorProps } from './components/RichTextEditor';
|
|
3
|
+
export { RichTextViewer } from './components/RichTextViewer';
|
|
4
|
+
export type { RichTextViewerProps } from './components/RichTextViewer';
|
|
3
5
|
export { EditorToolbar } from './components/EditorToolbar';
|
|
4
6
|
export type { EditorToolbarProps } from './components/EditorToolbar';
|
|
5
7
|
export { ImageNode, $createImageNode, $isImageNode } from './nodes/ImageNode';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAGtE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAGpE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAC7E,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AACxH,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7D,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AACvH,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGzD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAGvD,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAGtE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAGtE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAGpE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAC7E,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AACxH,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7D,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AACvH,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGzD,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAGvD,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,6BAA6B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AIEditor = exports.SimpleLinkPlugin = exports.ImageLinkPlugin = exports.INSERT_OBJECT_COMMAND = exports.InsertObjectPlugin = exports.$wrapSelectionInSimpleLink = exports.$isSimpleLinkNode = exports.$createSimpleLinkNode = exports.SimpleLinkNode = exports.$wrapSelectionInImageLink = exports.$isImageLinkNode = exports.$createImageLinkNode = exports.ImageLinkNode = exports.$isImageNode = exports.$createImageNode = exports.ImageNode = exports.EditorToolbar = exports.RichTextViewer = exports.RichTextEditor = void 0;
|
|
1
4
|
// Main WYSIWYG editor component
|
|
2
|
-
|
|
5
|
+
var RichTextEditor_1 = require("./components/RichTextEditor");
|
|
6
|
+
Object.defineProperty(exports, "RichTextEditor", { enumerable: true, get: function () { return RichTextEditor_1.RichTextEditor; } });
|
|
7
|
+
// Read-only viewer component
|
|
8
|
+
var RichTextViewer_1 = require("./components/RichTextViewer");
|
|
9
|
+
Object.defineProperty(exports, "RichTextViewer", { enumerable: true, get: function () { return RichTextViewer_1.RichTextViewer; } });
|
|
3
10
|
// Toolbar component (for custom implementations)
|
|
4
|
-
|
|
11
|
+
var EditorToolbar_1 = require("./components/EditorToolbar");
|
|
12
|
+
Object.defineProperty(exports, "EditorToolbar", { enumerable: true, get: function () { return EditorToolbar_1.EditorToolbar; } });
|
|
5
13
|
// Custom nodes
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
14
|
+
var ImageNode_1 = require("./nodes/ImageNode");
|
|
15
|
+
Object.defineProperty(exports, "ImageNode", { enumerable: true, get: function () { return ImageNode_1.ImageNode; } });
|
|
16
|
+
Object.defineProperty(exports, "$createImageNode", { enumerable: true, get: function () { return ImageNode_1.$createImageNode; } });
|
|
17
|
+
Object.defineProperty(exports, "$isImageNode", { enumerable: true, get: function () { return ImageNode_1.$isImageNode; } });
|
|
18
|
+
var ImageLinkNode_1 = require("./nodes/ImageLinkNode");
|
|
19
|
+
Object.defineProperty(exports, "ImageLinkNode", { enumerable: true, get: function () { return ImageLinkNode_1.ImageLinkNode; } });
|
|
20
|
+
Object.defineProperty(exports, "$createImageLinkNode", { enumerable: true, get: function () { return ImageLinkNode_1.$createImageLinkNode; } });
|
|
21
|
+
Object.defineProperty(exports, "$isImageLinkNode", { enumerable: true, get: function () { return ImageLinkNode_1.$isImageLinkNode; } });
|
|
22
|
+
Object.defineProperty(exports, "$wrapSelectionInImageLink", { enumerable: true, get: function () { return ImageLinkNode_1.$wrapSelectionInImageLink; } });
|
|
23
|
+
var LinkNode_1 = require("./nodes/LinkNode");
|
|
24
|
+
Object.defineProperty(exports, "SimpleLinkNode", { enumerable: true, get: function () { return LinkNode_1.SimpleLinkNode; } });
|
|
25
|
+
Object.defineProperty(exports, "$createSimpleLinkNode", { enumerable: true, get: function () { return LinkNode_1.$createSimpleLinkNode; } });
|
|
26
|
+
Object.defineProperty(exports, "$isSimpleLinkNode", { enumerable: true, get: function () { return LinkNode_1.$isSimpleLinkNode; } });
|
|
27
|
+
Object.defineProperty(exports, "$wrapSelectionInSimpleLink", { enumerable: true, get: function () { return LinkNode_1.$wrapSelectionInSimpleLink; } });
|
|
9
28
|
// Plugins
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
29
|
+
var InsertObjectPlugin_1 = require("./plugins/InsertObjectPlugin");
|
|
30
|
+
Object.defineProperty(exports, "InsertObjectPlugin", { enumerable: true, get: function () { return InsertObjectPlugin_1.InsertObjectPlugin; } });
|
|
31
|
+
Object.defineProperty(exports, "INSERT_OBJECT_COMMAND", { enumerable: true, get: function () { return InsertObjectPlugin_1.INSERT_OBJECT_COMMAND; } });
|
|
32
|
+
var ImageLinkPlugin_1 = require("./plugins/ImageLinkPlugin");
|
|
33
|
+
Object.defineProperty(exports, "ImageLinkPlugin", { enumerable: true, get: function () { return ImageLinkPlugin_1.ImageLinkPlugin; } });
|
|
34
|
+
var LinkPlugin_1 = require("./plugins/LinkPlugin");
|
|
35
|
+
Object.defineProperty(exports, "SimpleLinkPlugin", { enumerable: true, get: function () { return LinkPlugin_1.SimpleLinkPlugin; } });
|
|
13
36
|
// Alias for backwards compatibility
|
|
14
|
-
|
|
37
|
+
var RichTextEditor_2 = require("./components/RichTextEditor");
|
|
38
|
+
Object.defineProperty(exports, "AIEditor", { enumerable: true, get: function () { return RichTextEditor_2.RichTextEditor; } });
|