@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,22 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
'use client';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.INSERT_OBJECT_COMMAND = void 0;
|
|
5
|
+
exports.InsertObjectPlugin = InsertObjectPlugin;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
|
|
9
|
+
const lexical_1 = require("lexical");
|
|
10
|
+
const ImageNode_1 = require("../nodes/ImageNode");
|
|
11
|
+
const ImageLinkNode_1 = require("../nodes/ImageLinkNode");
|
|
12
|
+
const LinkNode_1 = require("../nodes/LinkNode");
|
|
9
13
|
// Command to open the insert object dialog
|
|
10
|
-
|
|
14
|
+
exports.INSERT_OBJECT_COMMAND = (0, lexical_1.createCommand)('INSERT_OBJECT_COMMAND');
|
|
11
15
|
function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
12
|
-
const [objectType, setObjectType] = useState('image');
|
|
13
|
-
const [url, setUrl] = useState('');
|
|
14
|
-
const [altText, setAltText] = useState('');
|
|
15
|
-
const [copyright, setCopyright] = useState('');
|
|
16
|
-
const [photographer, setPhotographer] = useState('');
|
|
17
|
-
const [comment, setComment] = useState('');
|
|
16
|
+
const [objectType, setObjectType] = (0, react_1.useState)('image');
|
|
17
|
+
const [url, setUrl] = (0, react_1.useState)('');
|
|
18
|
+
const [altText, setAltText] = (0, react_1.useState)('');
|
|
19
|
+
const [copyright, setCopyright] = (0, react_1.useState)('');
|
|
20
|
+
const [photographer, setPhotographer] = (0, react_1.useState)('');
|
|
21
|
+
const [comment, setComment] = (0, react_1.useState)('');
|
|
18
22
|
// Pre-fill form when editing
|
|
19
|
-
useEffect(() => {
|
|
23
|
+
(0, react_1.useEffect)(() => {
|
|
20
24
|
if (isOpen && initialData) {
|
|
21
25
|
setObjectType(initialData.type);
|
|
22
26
|
setUrl(initialData.url);
|
|
@@ -59,7 +63,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
59
63
|
};
|
|
60
64
|
if (!isOpen)
|
|
61
65
|
return null;
|
|
62
|
-
return (
|
|
66
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: {
|
|
63
67
|
position: 'fixed',
|
|
64
68
|
inset: 0,
|
|
65
69
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
@@ -67,16 +71,16 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
67
71
|
alignItems: 'center',
|
|
68
72
|
justifyContent: 'center',
|
|
69
73
|
zIndex: 100000,
|
|
70
|
-
}, onClick: onClose, onKeyDown: handleKeyDown, children:
|
|
74
|
+
}, onClick: onClose, onKeyDown: handleKeyDown, children: (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
71
75
|
backgroundColor: 'white',
|
|
72
76
|
borderRadius: '8px',
|
|
73
77
|
padding: '24px',
|
|
74
78
|
minWidth: '400px',
|
|
75
79
|
maxWidth: '500px',
|
|
76
80
|
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
|
77
|
-
}, onClick: (e) => e.stopPropagation(), children: [
|
|
81
|
+
}, onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("h3", { style: { margin: '0 0 16px 0', fontSize: '18px', fontWeight: 600, color: '#111827' }, children: isEditing
|
|
78
82
|
? (initialData?.type === 'image' ? "Modifier l'image" : initialData?.type === 'link' ? 'Modifier le lien' : 'Modifier le lien image')
|
|
79
|
-
: 'Insérer un objet' }),
|
|
83
|
+
: 'Insérer un objet' }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, children: [!isEditing && ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px' }, children: [(0, jsx_runtime_1.jsx)("label", { style: { display: 'block', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: "Type" }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: '8px' }, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => setObjectType('image'), style: {
|
|
80
84
|
flex: 1,
|
|
81
85
|
padding: '8px 16px',
|
|
82
86
|
border: '1px solid',
|
|
@@ -90,7 +94,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
90
94
|
alignItems: 'center',
|
|
91
95
|
justifyContent: 'center',
|
|
92
96
|
gap: '6px',
|
|
93
|
-
}, children: [
|
|
97
|
+
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px' }, children: "image" }), "Image"] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => setObjectType('image-link'), style: {
|
|
94
98
|
flex: 1,
|
|
95
99
|
padding: '8px 16px',
|
|
96
100
|
border: '1px solid',
|
|
@@ -104,7 +108,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
104
108
|
alignItems: 'center',
|
|
105
109
|
justifyContent: 'center',
|
|
106
110
|
gap: '6px',
|
|
107
|
-
}, title: "Lien vers une image (s'ouvre en plein \u00E9cran)", children: [
|
|
111
|
+
}, title: "Lien vers une image (s'ouvre en plein \u00E9cran)", children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px' }, children: "photo_library" }), "Lien image"] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => setObjectType('link'), style: {
|
|
108
112
|
flex: 1,
|
|
109
113
|
padding: '8px 16px',
|
|
110
114
|
border: '1px solid',
|
|
@@ -118,7 +122,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
118
122
|
alignItems: 'center',
|
|
119
123
|
justifyContent: 'center',
|
|
120
124
|
gap: '6px',
|
|
121
|
-
}, title: "Lien internet (s'ouvre dans un nouvel onglet)", children: [
|
|
125
|
+
}, title: "Lien internet (s'ouvre dans un nouvel onglet)", children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px' }, children: "link" }), "Lien"] })] })] })), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px' }, children: [(0, jsx_runtime_1.jsx)("label", { style: { display: 'block', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: objectType === 'link' ? "URL du lien" : objectType === 'image-link' ? "URL de l'image à afficher" : "URL de l'image" }), (0, jsx_runtime_1.jsx)("input", { type: "url", value: url, onChange: (e) => setUrl(e.target.value), placeholder: "https://example.com/image.jpg", autoFocus: true, style: {
|
|
122
126
|
width: '100%',
|
|
123
127
|
padding: '10px 12px',
|
|
124
128
|
border: '1px solid #d1d5db',
|
|
@@ -126,7 +130,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
126
130
|
fontSize: '14px',
|
|
127
131
|
outline: 'none',
|
|
128
132
|
boxSizing: 'border-box',
|
|
129
|
-
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }),
|
|
133
|
+
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px' }, children: [(0, jsx_runtime_1.jsx)("label", { style: { display: 'block', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: objectType === 'link' ? 'Titre (tooltip, optionnel)' : 'Texte alternatif (optionnel)' }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: altText, onChange: (e) => setAltText(e.target.value), placeholder: objectType === 'link' ? 'Texte affiché au survol' : 'Description de l\'image', style: {
|
|
130
134
|
width: '100%',
|
|
131
135
|
padding: '10px 12px',
|
|
132
136
|
border: '1px solid #d1d5db',
|
|
@@ -134,7 +138,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
134
138
|
fontSize: '14px',
|
|
135
139
|
outline: 'none',
|
|
136
140
|
boxSizing: 'border-box',
|
|
137
|
-
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }), objectType === 'image-link' && (
|
|
141
|
+
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }), objectType === 'image-link' && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px' }, children: [(0, jsx_runtime_1.jsxs)("label", { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px', color: '#6b7280' }, children: "camera_alt" }), "Photographe"] }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: photographer, onChange: (e) => setPhotographer(e.target.value), placeholder: "Nom du photographe", style: {
|
|
138
142
|
width: '100%',
|
|
139
143
|
padding: '10px 12px',
|
|
140
144
|
border: '1px solid #d1d5db',
|
|
@@ -142,7 +146,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
142
146
|
fontSize: '14px',
|
|
143
147
|
outline: 'none',
|
|
144
148
|
boxSizing: 'border-box',
|
|
145
|
-
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }),
|
|
149
|
+
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px' }, children: [(0, jsx_runtime_1.jsxs)("label", { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px', color: '#6b7280' }, children: "copyright" }), "Copyright"] }), (0, jsx_runtime_1.jsx)("input", { type: "text", value: copyright, onChange: (e) => setCopyright(e.target.value), placeholder: "\u00A9 2024 Nom", style: {
|
|
146
150
|
width: '100%',
|
|
147
151
|
padding: '10px 12px',
|
|
148
152
|
border: '1px solid #d1d5db',
|
|
@@ -150,7 +154,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
150
154
|
fontSize: '14px',
|
|
151
155
|
outline: 'none',
|
|
152
156
|
boxSizing: 'border-box',
|
|
153
|
-
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }),
|
|
157
|
+
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px' }, children: [(0, jsx_runtime_1.jsxs)("label", { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px', color: '#6b7280' }, children: "chat" }), "Commentaire (tooltip)"] }), (0, jsx_runtime_1.jsx)("textarea", { value: comment, onChange: (e) => setComment(e.target.value), placeholder: "Commentaire affich\u00E9 au survol", rows: 2, style: {
|
|
154
158
|
width: '100%',
|
|
155
159
|
padding: '10px 12px',
|
|
156
160
|
border: '1px solid #d1d5db',
|
|
@@ -160,7 +164,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
160
164
|
boxSizing: 'border-box',
|
|
161
165
|
resize: 'vertical',
|
|
162
166
|
fontFamily: 'inherit',
|
|
163
|
-
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] })] })), objectType === 'image-link' && (
|
|
167
|
+
}, onFocus: (e) => e.target.style.borderColor = '#3b82f6', onBlur: (e) => e.target.style.borderColor = '#d1d5db' })] })] })), objectType === 'image-link' && ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
164
168
|
marginBottom: '16px',
|
|
165
169
|
padding: '12px',
|
|
166
170
|
backgroundColor: '#f3e8ff',
|
|
@@ -170,7 +174,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
170
174
|
display: 'flex',
|
|
171
175
|
alignItems: 'center',
|
|
172
176
|
gap: '8px',
|
|
173
|
-
}, children: [
|
|
177
|
+
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px' }, children: "info" }), "Le texte s\u00E9lectionn\u00E9 deviendra un lien. Un clic ouvrira l'image en plein \u00E9cran."] })), objectType === 'link' && ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
174
178
|
marginBottom: '16px',
|
|
175
179
|
padding: '12px',
|
|
176
180
|
backgroundColor: '#dbeafe',
|
|
@@ -180,16 +184,16 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
180
184
|
display: 'flex',
|
|
181
185
|
alignItems: 'center',
|
|
182
186
|
gap: '8px',
|
|
183
|
-
}, children: [
|
|
187
|
+
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "material-icons", style: { fontSize: '18px' }, children: "info" }), "Le texte s\u00E9lectionn\u00E9 deviendra un lien. Ctrl+Clic pour ouvrir dans un nouvel onglet."] })), url && (objectType === 'image' || objectType === 'image-link') && ((0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '24px' }, children: [(0, jsx_runtime_1.jsx)("label", { style: { display: 'block', marginBottom: '6px', fontSize: '14px', fontWeight: 500, color: '#374151' }, children: "Aper\u00E7u" }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
184
188
|
border: '1px solid #e5e7eb',
|
|
185
189
|
borderRadius: '6px',
|
|
186
190
|
padding: '8px',
|
|
187
191
|
backgroundColor: '#f9fafb',
|
|
188
192
|
maxHeight: '150px',
|
|
189
193
|
overflow: 'hidden',
|
|
190
|
-
}, children:
|
|
194
|
+
}, children: (0, jsx_runtime_1.jsx)("img", { src: url, alt: altText || 'Preview', style: { maxWidth: '100%', maxHeight: '130px', display: 'block', margin: '0 auto' }, onError: (e) => {
|
|
191
195
|
e.target.style.display = 'none';
|
|
192
|
-
} }) })] })),
|
|
196
|
+
} }) })] })), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: '12px', justifyContent: 'flex-end' }, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClose, style: {
|
|
193
197
|
padding: '10px 20px',
|
|
194
198
|
border: '1px solid #d1d5db',
|
|
195
199
|
backgroundColor: 'white',
|
|
@@ -198,7 +202,7 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
198
202
|
cursor: 'pointer',
|
|
199
203
|
fontSize: '14px',
|
|
200
204
|
fontWeight: 500,
|
|
201
|
-
}, children: "Annuler" }),
|
|
205
|
+
}, children: "Annuler" }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: !url.trim(), style: {
|
|
202
206
|
padding: '10px 20px',
|
|
203
207
|
border: 'none',
|
|
204
208
|
backgroundColor: url.trim() ? '#3b82f6' : '#9ca3af',
|
|
@@ -209,24 +213,24 @@ function InsertDialog({ isOpen, onClose, onInsert, initialData, isEditing }) {
|
|
|
209
213
|
fontWeight: 500,
|
|
210
214
|
}, children: "Ins\u00E9rer" })] })] })] }) }));
|
|
211
215
|
}
|
|
212
|
-
|
|
213
|
-
const [editor] = useLexicalComposerContext();
|
|
214
|
-
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
215
|
-
const [editingImageLinkNode, setEditingImageLinkNode] = useState(null);
|
|
216
|
-
const [editingImageNode, setEditingImageNode] = useState(null);
|
|
217
|
-
const [editingLinkNode, setEditingLinkNode] = useState(null);
|
|
218
|
-
const [initialData, setInitialData] = useState(null);
|
|
219
|
-
const [dialogKey, setDialogKey] = useState(0);
|
|
216
|
+
function InsertObjectPlugin() {
|
|
217
|
+
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
218
|
+
const [isDialogOpen, setIsDialogOpen] = (0, react_1.useState)(false);
|
|
219
|
+
const [editingImageLinkNode, setEditingImageLinkNode] = (0, react_1.useState)(null);
|
|
220
|
+
const [editingImageNode, setEditingImageNode] = (0, react_1.useState)(null);
|
|
221
|
+
const [editingLinkNode, setEditingLinkNode] = (0, react_1.useState)(null);
|
|
222
|
+
const [initialData, setInitialData] = (0, react_1.useState)(null);
|
|
223
|
+
const [dialogKey, setDialogKey] = (0, react_1.useState)(0);
|
|
220
224
|
// Find ImageLinkNode at current selection
|
|
221
|
-
const getImageLinkNodeAtSelection = useCallback(() => {
|
|
225
|
+
const getImageLinkNodeAtSelection = (0, react_1.useCallback)(() => {
|
|
222
226
|
let foundNode = null;
|
|
223
227
|
editor.getEditorState().read(() => {
|
|
224
|
-
const selection =
|
|
225
|
-
if (
|
|
228
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
229
|
+
if ((0, lexical_1.$isRangeSelection)(selection)) {
|
|
226
230
|
const anchorNode = selection.anchor.getNode();
|
|
227
231
|
let node = anchorNode;
|
|
228
232
|
while (node) {
|
|
229
|
-
if (
|
|
233
|
+
if ((0, ImageLinkNode_1.$isImageLinkNode)(node)) {
|
|
230
234
|
foundNode = node;
|
|
231
235
|
break;
|
|
232
236
|
}
|
|
@@ -240,14 +244,14 @@ export function InsertObjectPlugin() {
|
|
|
240
244
|
return foundNode;
|
|
241
245
|
}, [editor]);
|
|
242
246
|
// Find ImageNode at current selection (for DecoratorNodes we check siblings)
|
|
243
|
-
const getImageNodeAtSelection = useCallback(() => {
|
|
247
|
+
const getImageNodeAtSelection = (0, react_1.useCallback)(() => {
|
|
244
248
|
let foundNode = null;
|
|
245
249
|
editor.getEditorState().read(() => {
|
|
246
|
-
const selection =
|
|
247
|
-
if (
|
|
250
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
251
|
+
if ((0, lexical_1.$isRangeSelection)(selection)) {
|
|
248
252
|
const nodes = selection.getNodes();
|
|
249
253
|
for (const node of nodes) {
|
|
250
|
-
if (
|
|
254
|
+
if ((0, ImageNode_1.$isImageNode)(node)) {
|
|
251
255
|
foundNode = node;
|
|
252
256
|
break;
|
|
253
257
|
}
|
|
@@ -257,15 +261,15 @@ export function InsertObjectPlugin() {
|
|
|
257
261
|
return foundNode;
|
|
258
262
|
}, [editor]);
|
|
259
263
|
// Find SimpleLinkNode at current selection
|
|
260
|
-
const getSimpleLinkNodeAtSelection = useCallback(() => {
|
|
264
|
+
const getSimpleLinkNodeAtSelection = (0, react_1.useCallback)(() => {
|
|
261
265
|
let foundNode = null;
|
|
262
266
|
editor.getEditorState().read(() => {
|
|
263
|
-
const selection =
|
|
264
|
-
if (
|
|
267
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
268
|
+
if ((0, lexical_1.$isRangeSelection)(selection)) {
|
|
265
269
|
const anchorNode = selection.anchor.getNode();
|
|
266
270
|
let node = anchorNode;
|
|
267
271
|
while (node) {
|
|
268
|
-
if (
|
|
272
|
+
if ((0, LinkNode_1.$isSimpleLinkNode)(node)) {
|
|
269
273
|
foundNode = node;
|
|
270
274
|
break;
|
|
271
275
|
}
|
|
@@ -279,8 +283,8 @@ export function InsertObjectPlugin() {
|
|
|
279
283
|
return foundNode;
|
|
280
284
|
}, [editor]);
|
|
281
285
|
// Handle Ctrl+K
|
|
282
|
-
useEffect(() => {
|
|
283
|
-
return editor.registerCommand(KEY_DOWN_COMMAND, (event) => {
|
|
286
|
+
(0, react_1.useEffect)(() => {
|
|
287
|
+
return editor.registerCommand(lexical_1.KEY_DOWN_COMMAND, (event) => {
|
|
284
288
|
if (event.ctrlKey && event.key === 'k') {
|
|
285
289
|
event.preventDefault();
|
|
286
290
|
// Check if we're on an ImageNode first
|
|
@@ -340,11 +344,11 @@ export function InsertObjectPlugin() {
|
|
|
340
344
|
return true;
|
|
341
345
|
}
|
|
342
346
|
return false;
|
|
343
|
-
}, COMMAND_PRIORITY_HIGH);
|
|
347
|
+
}, lexical_1.COMMAND_PRIORITY_HIGH);
|
|
344
348
|
}, [editor, getImageLinkNodeAtSelection, getImageNodeAtSelection, getSimpleLinkNodeAtSelection]);
|
|
345
349
|
// Handle INSERT_OBJECT_COMMAND (from toolbar)
|
|
346
|
-
useEffect(() => {
|
|
347
|
-
return editor.registerCommand(INSERT_OBJECT_COMMAND, () => {
|
|
350
|
+
(0, react_1.useEffect)(() => {
|
|
351
|
+
return editor.registerCommand(exports.INSERT_OBJECT_COMMAND, () => {
|
|
348
352
|
// Check if we're on an ImageNode first
|
|
349
353
|
const imageNode = getImageNodeAtSelection();
|
|
350
354
|
if (imageNode) {
|
|
@@ -397,9 +401,9 @@ export function InsertObjectPlugin() {
|
|
|
397
401
|
setDialogKey(k => k + 1);
|
|
398
402
|
setIsDialogOpen(true);
|
|
399
403
|
return true;
|
|
400
|
-
}, COMMAND_PRIORITY_HIGH);
|
|
404
|
+
}, lexical_1.COMMAND_PRIORITY_HIGH);
|
|
401
405
|
}, [editor, getImageLinkNodeAtSelection, getImageNodeAtSelection, getSimpleLinkNodeAtSelection]);
|
|
402
|
-
const handleInsert = useCallback((type, data) => {
|
|
406
|
+
const handleInsert = (0, react_1.useCallback)((type, data) => {
|
|
403
407
|
if (editingImageNode && type === 'image') {
|
|
404
408
|
// Update existing ImageNode
|
|
405
409
|
editor.update(() => {
|
|
@@ -419,9 +423,9 @@ export function InsertObjectPlugin() {
|
|
|
419
423
|
}
|
|
420
424
|
else if (type === 'image') {
|
|
421
425
|
editor.update(() => {
|
|
422
|
-
const selection =
|
|
423
|
-
if (
|
|
424
|
-
const imageNode =
|
|
426
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
427
|
+
if ((0, lexical_1.$isRangeSelection)(selection)) {
|
|
428
|
+
const imageNode = (0, ImageNode_1.$createImageNode)({
|
|
425
429
|
src: data.url,
|
|
426
430
|
altText: data.altText || '',
|
|
427
431
|
});
|
|
@@ -438,27 +442,27 @@ export function InsertObjectPlugin() {
|
|
|
438
442
|
}
|
|
439
443
|
else if (type === 'image-link') {
|
|
440
444
|
editor.update(() => {
|
|
441
|
-
const selection =
|
|
442
|
-
if (
|
|
443
|
-
|
|
445
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
446
|
+
if ((0, lexical_1.$isRangeSelection)(selection) && !selection.isCollapsed()) {
|
|
447
|
+
(0, ImageLinkNode_1.$wrapSelectionInImageLink)(selection, data.url, data.altText || '', data.copyright || '', data.photographer || '', data.comment || '');
|
|
444
448
|
}
|
|
445
449
|
});
|
|
446
450
|
}
|
|
447
451
|
else if (type === 'link') {
|
|
448
452
|
editor.update(() => {
|
|
449
|
-
const selection =
|
|
450
|
-
if (
|
|
451
|
-
|
|
453
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
454
|
+
if ((0, lexical_1.$isRangeSelection)(selection) && !selection.isCollapsed()) {
|
|
455
|
+
(0, LinkNode_1.$wrapSelectionInSimpleLink)(selection, data.url, data.altText || undefined);
|
|
452
456
|
}
|
|
453
457
|
});
|
|
454
458
|
}
|
|
455
459
|
}, [editor, editingImageNode, editingImageLinkNode, editingLinkNode]);
|
|
456
|
-
const handleClose = useCallback(() => {
|
|
460
|
+
const handleClose = (0, react_1.useCallback)(() => {
|
|
457
461
|
setIsDialogOpen(false);
|
|
458
462
|
setEditingImageLinkNode(null);
|
|
459
463
|
setEditingImageNode(null);
|
|
460
464
|
setEditingLinkNode(null);
|
|
461
465
|
setInitialData(null);
|
|
462
466
|
}, []);
|
|
463
|
-
return (
|
|
467
|
+
return ((0, jsx_runtime_1.jsx)(InsertDialog, { isOpen: isDialogOpen, onClose: handleClose, onInsert: handleInsert, initialData: initialData, isEditing: !!(editingImageNode || editingImageLinkNode || editingLinkNode) }, dialogKey));
|
|
464
468
|
}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
'use client';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SimpleLinkPlugin = SimpleLinkPlugin;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
|
|
7
|
+
const lexical_1 = require("lexical");
|
|
8
|
+
const LinkNode_1 = require("../nodes/LinkNode");
|
|
9
|
+
function SimpleLinkPlugin() {
|
|
10
|
+
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
|
|
8
11
|
// Find SimpleLinkNode at current selection
|
|
9
|
-
const getSimpleLinkNodeAtSelection = useCallback(() => {
|
|
12
|
+
const getSimpleLinkNodeAtSelection = (0, react_1.useCallback)(() => {
|
|
10
13
|
let foundNode = null;
|
|
11
14
|
editor.getEditorState().read(() => {
|
|
12
|
-
const selection =
|
|
13
|
-
if (
|
|
15
|
+
const selection = (0, lexical_1.$getSelection)();
|
|
16
|
+
if ((0, lexical_1.$isRangeSelection)(selection)) {
|
|
14
17
|
const anchorNode = selection.anchor.getNode();
|
|
15
18
|
// Check if we're inside a SimpleLinkNode
|
|
16
19
|
let node = anchorNode;
|
|
17
20
|
while (node) {
|
|
18
|
-
if (
|
|
21
|
+
if ((0, LinkNode_1.$isSimpleLinkNode)(node)) {
|
|
19
22
|
foundNode = node;
|
|
20
23
|
break;
|
|
21
24
|
}
|
|
@@ -29,8 +32,8 @@ export function SimpleLinkPlugin() {
|
|
|
29
32
|
return foundNode;
|
|
30
33
|
}, [editor]);
|
|
31
34
|
// Handle Ctrl+Click on SimpleLinkNode to open in new tab
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
return editor.registerCommand(CLICK_COMMAND, (event) => {
|
|
35
|
+
(0, react_1.useEffect)(() => {
|
|
36
|
+
return editor.registerCommand(lexical_1.CLICK_COMMAND, (event) => {
|
|
34
37
|
const linkNode = getSimpleLinkNodeAtSelection();
|
|
35
38
|
if (linkNode && event.ctrlKey) {
|
|
36
39
|
// Ctrl+Click opens in new tab
|
|
@@ -39,7 +42,7 @@ export function SimpleLinkPlugin() {
|
|
|
39
42
|
return true;
|
|
40
43
|
}
|
|
41
44
|
return false;
|
|
42
|
-
}, COMMAND_PRIORITY_HIGH);
|
|
45
|
+
}, lexical_1.COMMAND_PRIORITY_HIGH);
|
|
43
46
|
}, [editor, getSimpleLinkNodeAtSelection]);
|
|
44
47
|
return null;
|
|
45
48
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qwanyx/ai-editor",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "AI-powered WYSIWYG rich text editor",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.js",
|
|
7
6
|
"types": "./dist/index.d.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
9
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"
|
|
10
|
+
"require": "./dist/index.js",
|
|
12
11
|
"default": "./dist/index.js"
|
|
13
12
|
}
|
|
14
13
|
},
|