@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.
@@ -1,14 +1,17 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef } from 'react';
3
- export function PromptModal({ isOpen, onClose, onSubmit, selectedText, isLoading }) {
4
- const [prompt, setPrompt] = useState('');
5
- const inputRef = useRef(null);
6
- useEffect(() => {
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 (_jsx("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50", children: _jsxs("div", { className: "bg-white rounded-xl shadow-2xl w-full max-w-lg mx-4 overflow-hidden", children: [_jsxs("div", { className: "px-6 py-4 border-b border-gray-200 flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Instruction IA" }), _jsx("button", { onClick: onClose, className: "p-1 text-gray-400 hover:text-gray-600 transition-colors", children: _jsx("span", { className: "material-icons", children: "close" }) })] }), _jsxs("form", { onSubmit: handleSubmit, className: "p-6", children: [selectedText && (_jsxs("div", { className: "mb-4", children: [_jsx("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: "Texte s\u00E9lectionn\u00E9" }), _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
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 })] })), _jsxs("div", { className: "mb-4", children: [_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' }), _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 })] }), _jsxs("div", { className: "mb-4", children: [_jsx("label", { className: "block text-sm font-medium text-gray-500 mb-2", children: "Actions rapides" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
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) => (_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))) })] }), _jsxs("div", { className: "flex justify-end gap-3", children: [_jsx("button", { type: "button", onClick: onClose, className: "px-4 py-2 text-gray-600 hover:text-gray-900 transition-colors", disabled: isLoading, children: "Annuler" }), _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 ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "material-icons text-sm animate-spin", children: "sync" }), "Traitement..."] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "material-icons text-sm", children: "send" }), "Appliquer"] })) })] })] })] }) }));
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
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import React, { useCallback, useEffect } from 'react';
4
- import { LexicalComposer } from '@lexical/react/LexicalComposer';
5
- import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
6
- import { ContentEditable } from '@lexical/react/LexicalContentEditable';
7
- import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
8
- import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
9
- import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
10
- import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
11
- import { HeadingNode, QuoteNode } from '@lexical/rich-text';
12
- import { ListNode, ListItemNode } from '@lexical/list';
13
- import { LinkNode } from '@lexical/link';
14
- import { ImageNode } from '../nodes/ImageNode';
15
- import { ImageLinkNode } from '../nodes/ImageLinkNode';
16
- import { SimpleLinkNode } from '../nodes/LinkNode';
17
- import { InsertObjectPlugin } from '../plugins/InsertObjectPlugin';
18
- import { ImageLinkPlugin } from '../plugins/ImageLinkPlugin';
19
- import { SimpleLinkPlugin } from '../plugins/LinkPlugin';
20
- import { ListPlugin } from '@lexical/react/LexicalListPlugin';
21
- import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
22
- import { $getRoot, $getSelection, $isRangeSelection, $createParagraphNode, $createTextNode } from 'lexical';
23
- import { EditorToolbar } from './EditorToolbar';
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 = React.useRef(true);
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 = $getRoot();
195
+ const root = (0, lexical_1.$getRoot)();
160
196
  root.clear();
161
- const paragraph = $createParagraphNode();
162
- paragraph.append($createTextNode(content));
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
- export function RichTextEditor({ initialContent = '', onChange, onAIRequest, placeholder = 'Commencez à écrire...', className = '', minHeight = '300px' }) {
178
- const editorRef = React.useRef(null);
179
- const [isLoading, setIsLoading] = React.useState(false);
180
- const [isFullscreen, setIsFullscreen] = React.useState(false);
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 = $getRoot();
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 = $getSelection();
203
- if ($isRangeSelection(selection)) {
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 = $getRoot().getTextContent();
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 = $getRoot();
271
+ const root = (0, lexical_1.$getRoot)();
236
272
  root.clear();
237
- const paragraph = $createParagraphNode();
238
- paragraph.append($createTextNode(result));
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 (_jsxs("div", { className: `border border-gray-200 rounded-lg bg-white flex flex-col ${fullscreenClasses} ${className}`, children: [_jsx("style", { dangerouslySetInnerHTML: { __html: editorStyles } }), _jsxs(LexicalComposer, { initialConfig: getEditorConfig(initialContent), children: [_jsx(EditorToolbar, { onAIAction: handleAIAction, isLoading: isLoading, isFullscreen: isFullscreen, onToggleFullscreen: toggleFullscreen }), _jsxs("div", { className: "relative flex-1 overflow-auto", style: minHeight && !isFullscreen ? { minHeight } : {}, children: [_jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "outline-none p-4 text-gray-900 h-full min-h-full" }), placeholder: _jsx("div", { className: "absolute top-4 left-4 text-gray-400 pointer-events-none", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, {}), _jsx(ListPlugin, {}), _jsx(LinkPlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange }), _jsx(InitialContentPlugin, { content: initialContent }), _jsx(EditorRefPlugin, { editorRef: editorRef }), _jsx(InsertObjectPlugin, {}), _jsx(ImageLinkPlugin, {}), _jsx(SimpleLinkPlugin, {}), isLoading && (_jsx("div", { className: "absolute inset-0 bg-white/80 flex items-center justify-center z-10", children: _jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [_jsx("span", { className: "material-icons animate-spin", children: "sync" }), _jsx("span", { children: "L'IA travaille..." })] }) }))] })] })] }));
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,6 @@
1
+ export interface RichTextViewerProps {
2
+ content?: string;
3
+ className?: string;
4
+ }
5
+ export declare function RichTextViewer({ content, className }: RichTextViewerProps): import("react/jsx-runtime").JSX.Element | null;
6
+ //# sourceMappingURL=RichTextViewer.d.ts.map
@@ -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
- import { useState, useCallback, useRef } from 'react';
2
- export function useAIEditor(initialContent = '') {
3
- const [content, setContentState] = useState(initialContent);
4
- const [isLoading, setIsLoading] = useState(false);
5
- const [error, setError] = useState(null);
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
- import { useState, useCallback, useEffect } from 'react';
2
- export function useSelection(containerRef) {
3
- const [selection, setSelection] = useState(null);
4
- const handleSelectionChange = useCallback(() => {
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';
@@ -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
- export { RichTextEditor } from './components/RichTextEditor';
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
- export { EditorToolbar } from './components/EditorToolbar';
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
- export { ImageNode, $createImageNode, $isImageNode } from './nodes/ImageNode';
7
- export { ImageLinkNode, $createImageLinkNode, $isImageLinkNode, $wrapSelectionInImageLink } from './nodes/ImageLinkNode';
8
- export { SimpleLinkNode, $createSimpleLinkNode, $isSimpleLinkNode, $wrapSelectionInSimpleLink } from './nodes/LinkNode';
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
- export { InsertObjectPlugin, INSERT_OBJECT_COMMAND } from './plugins/InsertObjectPlugin';
11
- export { ImageLinkPlugin } from './plugins/ImageLinkPlugin';
12
- export { SimpleLinkPlugin } from './plugins/LinkPlugin';
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
- export { RichTextEditor as AIEditor } from './components/RichTextEditor';
37
+ var RichTextEditor_2 = require("./components/RichTextEditor");
38
+ Object.defineProperty(exports, "AIEditor", { enumerable: true, get: function () { return RichTextEditor_2.RichTextEditor; } });