oneuxi-editor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2270 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BubbleToolbar: () => BubbleToolbar,
34
+ CodeExtension: () => CodeExtension,
35
+ DefaultIcons: () => DefaultIcons,
36
+ Editor: () => Editor,
37
+ EmojiExtension: () => EmojiExtension,
38
+ FileExtension: () => FileExtension,
39
+ ImageExtension: () => ImageExtension,
40
+ MediaExtension: () => MediaExtension,
41
+ MentionExtension: () => MentionExtension,
42
+ SlashCommandExtension: () => SlashCommandExtension,
43
+ TableExtension: () => TableExtension,
44
+ Toolbar: () => Toolbar,
45
+ basicPreset: () => basicPreset,
46
+ cleanPastedHTML: () => cleanPastedHTML,
47
+ createExtension: () => createExtension,
48
+ fullPreset: () => fullPreset,
49
+ sanitizeHTML: () => sanitizeHTML,
50
+ useEditor: () => useEditor
51
+ });
52
+ module.exports = __toCommonJS(index_exports);
53
+
54
+ // src/core/Editor.tsx
55
+ var import_react9 = require("react");
56
+ var import_LexicalComposer = require("@lexical/react/LexicalComposer");
57
+ var import_LexicalRichTextPlugin = require("@lexical/react/LexicalRichTextPlugin");
58
+ var import_LexicalContentEditable = require("@lexical/react/LexicalContentEditable");
59
+ var import_LexicalHistoryPlugin = require("@lexical/react/LexicalHistoryPlugin");
60
+ var import_LexicalOnChangePlugin = require("@lexical/react/LexicalOnChangePlugin");
61
+ var import_LexicalListPlugin = require("@lexical/react/LexicalListPlugin");
62
+ var import_LexicalCheckListPlugin = require("@lexical/react/LexicalCheckListPlugin");
63
+ var import_LexicalLinkPlugin = require("@lexical/react/LexicalLinkPlugin");
64
+ var import_LexicalHorizontalRulePlugin = require("@lexical/react/LexicalHorizontalRulePlugin");
65
+ var import_LexicalErrorBoundary = require("@lexical/react/LexicalErrorBoundary");
66
+ var import_LexicalComposerContext = require("@lexical/react/LexicalComposerContext");
67
+ var import_rich_text = require("@lexical/rich-text");
68
+ var import_list = require("@lexical/list");
69
+ var import_link = require("@lexical/link");
70
+ var import_table2 = require("@lexical/table");
71
+ var import_code2 = require("@lexical/code");
72
+ var import_LexicalHorizontalRuleNode = require("@lexical/react/LexicalHorizontalRuleNode");
73
+ var import_lexical3 = require("lexical");
74
+ var import_selection = require("@lexical/selection");
75
+ var import_rich_text2 = require("@lexical/rich-text");
76
+ var import_list2 = require("@lexical/list");
77
+
78
+ // src/core/EditorContext.tsx
79
+ var import_react = require("react");
80
+ var EditorContext = (0, import_react.createContext)(null);
81
+ function useEditor() {
82
+ const context = (0, import_react.useContext)(EditorContext);
83
+ if (!context) {
84
+ throw new Error("useEditor must be used within an <Editor> component");
85
+ }
86
+ return context;
87
+ }
88
+
89
+ // src/extensions/image/ImageNode.tsx
90
+ var import_lexical = require("lexical");
91
+ var import_jsx_runtime = require("react/jsx-runtime");
92
+ var ImageNode = class _ImageNode extends import_lexical.DecoratorNode {
93
+ __src;
94
+ __alt;
95
+ __width;
96
+ __height;
97
+ __alignment;
98
+ __caption;
99
+ static getType() {
100
+ return "image";
101
+ }
102
+ static clone(node) {
103
+ return new _ImageNode(
104
+ node.__src,
105
+ node.__alt,
106
+ node.__width,
107
+ node.__height,
108
+ node.__alignment,
109
+ node.__caption,
110
+ node.__key
111
+ );
112
+ }
113
+ constructor(src, alt, width, height, alignment, caption, key) {
114
+ super(key);
115
+ this.__src = src;
116
+ this.__alt = alt || "";
117
+ this.__width = width;
118
+ this.__height = height;
119
+ this.__alignment = alignment || "center";
120
+ this.__caption = caption || "";
121
+ }
122
+ static importJSON(serializedNode) {
123
+ const { src, alt, width, height, alignment, caption } = serializedNode;
124
+ return new _ImageNode(src, alt, width, height, alignment, caption);
125
+ }
126
+ exportJSON() {
127
+ return {
128
+ type: "image",
129
+ version: 1,
130
+ src: this.__src,
131
+ alt: this.__alt,
132
+ width: this.__width,
133
+ height: this.__height,
134
+ alignment: this.__alignment,
135
+ caption: this.__caption
136
+ };
137
+ }
138
+ createDOM() {
139
+ const span = document.createElement("span");
140
+ span.className = `oue-image-container oue-align-${this.__alignment}`;
141
+ return span;
142
+ }
143
+ updateDOM() {
144
+ return false;
145
+ }
146
+ exportDOM() {
147
+ const element = document.createElement("img");
148
+ element.setAttribute("src", this.__src);
149
+ if (this.__alt) element.setAttribute("alt", this.__alt);
150
+ if (this.__width) element.setAttribute("width", String(this.__width));
151
+ if (this.__height) element.setAttribute("height", String(this.__height));
152
+ return { element };
153
+ }
154
+ decorate() {
155
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: `oue-image-container oue-align-${this.__alignment}`, children: [
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
157
+ "img",
158
+ {
159
+ src: this.__src,
160
+ alt: this.__alt,
161
+ style: {
162
+ width: this.__width ? `${this.__width}px` : "auto",
163
+ height: this.__height ? `${this.__height}px` : "auto"
164
+ }
165
+ }
166
+ ),
167
+ this.__caption && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "oue-image-caption", children: this.__caption })
168
+ ] });
169
+ }
170
+ };
171
+ function $createImageNode(payload) {
172
+ return new ImageNode(
173
+ payload.src,
174
+ payload.alt,
175
+ payload.width,
176
+ payload.height,
177
+ payload.alignment,
178
+ payload.caption,
179
+ payload.key
180
+ );
181
+ }
182
+
183
+ // src/html/serializer.ts
184
+ var import_html = require("@lexical/html");
185
+
186
+ // src/security/sanitizer.ts
187
+ var import_dompurify = __toESM(require("dompurify"));
188
+ var DEFAULT_ALLOWED_TAGS = [
189
+ "p",
190
+ "h1",
191
+ "h2",
192
+ "h3",
193
+ "h4",
194
+ "h5",
195
+ "h6",
196
+ "blockquote",
197
+ "pre",
198
+ "code",
199
+ "ul",
200
+ "ol",
201
+ "li",
202
+ "hr",
203
+ "br",
204
+ "span",
205
+ "div",
206
+ "strong",
207
+ "b",
208
+ "em",
209
+ "i",
210
+ "u",
211
+ "s",
212
+ "sub",
213
+ "sup",
214
+ "a",
215
+ "img",
216
+ "iframe",
217
+ "video",
218
+ "source",
219
+ "table",
220
+ "thead",
221
+ "tbody",
222
+ "tfoot",
223
+ "tr",
224
+ "th",
225
+ "td",
226
+ "figure",
227
+ "figcaption",
228
+ "input"
229
+ ];
230
+ var DEFAULT_ALLOWED_ATTR = [
231
+ "href",
232
+ "target",
233
+ "rel",
234
+ "title",
235
+ "src",
236
+ "alt",
237
+ "width",
238
+ "height",
239
+ "align",
240
+ "class",
241
+ "style",
242
+ "id",
243
+ "data-*",
244
+ "type",
245
+ "checked",
246
+ "disabled",
247
+ "frameborder",
248
+ "allowfullscreen",
249
+ "allow"
250
+ ];
251
+ function sanitizeHTML(html, policy) {
252
+ if (!html) return "";
253
+ if (typeof window === "undefined") {
254
+ return html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/on\w+\s*=\s*(["']).*?\1/gi, "").replace(/on\w+\s*=\s*[^"'\s>]+/gi, "").replace(/href\s*=\s*["']?\s*javascript:[^"'>]*/gi, 'href="#"');
255
+ }
256
+ const allowedTags = policy?.allowedTags || DEFAULT_ALLOWED_TAGS;
257
+ const allowedAttributes = policy?.allowedAttributes ? Object.keys(policy.allowedAttributes).reduce((acc, tag) => {
258
+ return [...acc, ...policy.allowedAttributes[tag] || []];
259
+ }, DEFAULT_ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
260
+ const clean = import_dompurify.default.sanitize(html, {
261
+ ALLOWED_TAGS: allowedTags,
262
+ ALLOWED_ATTR: allowedAttributes,
263
+ ADD_ATTR: ["target", "rel"],
264
+ FORCE_BODY: true
265
+ });
266
+ return clean.replace(/href=["']?\s*javascript:[^"'>]*/gi, 'href="#"');
267
+ }
268
+
269
+ // src/html/serializer.ts
270
+ function getEditorHTML(editor, policy) {
271
+ let html = "";
272
+ editor.getEditorState().read(() => {
273
+ html = (0, import_html.$generateHtmlFromNodes)(editor, null);
274
+ });
275
+ return sanitizeHTML(html, policy);
276
+ }
277
+
278
+ // src/html/parser.ts
279
+ var import_html2 = require("@lexical/html");
280
+ var import_lexical2 = require("lexical");
281
+ function setEditorHTML(editor, html, policy) {
282
+ const sanitized = sanitizeHTML(html, policy);
283
+ editor.update(() => {
284
+ const root = (0, import_lexical2.$getRoot)();
285
+ root.clear();
286
+ if (sanitized.trim().length === 0) return;
287
+ const parser = typeof window !== "undefined" ? new DOMParser() : null;
288
+ if (!parser) return;
289
+ const dom = parser.parseFromString(sanitized, "text/html");
290
+ const nodes = (0, import_html2.$generateNodesFromDOM)(editor, dom);
291
+ (0, import_lexical2.$getRoot)().append(...nodes);
292
+ });
293
+ }
294
+
295
+ // src/theme/theme.ts
296
+ var import_react2 = require("react");
297
+ var COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
298
+ function getSystemThemeSnapshot() {
299
+ if (typeof window === "undefined") return false;
300
+ return window.matchMedia(COLOR_SCHEME_QUERY).matches;
301
+ }
302
+ function getServerThemeSnapshot() {
303
+ return false;
304
+ }
305
+ function subscribeSystemTheme(callback) {
306
+ if (typeof window === "undefined") return () => {
307
+ };
308
+ const matchMedia = window.matchMedia(COLOR_SCHEME_QUERY);
309
+ matchMedia.addEventListener("change", callback);
310
+ return () => matchMedia.removeEventListener("change", callback);
311
+ }
312
+ function useResolvedTheme(theme = "system") {
313
+ const isSystemDark = (0, import_react2.useSyncExternalStore)(
314
+ subscribeSystemTheme,
315
+ getSystemThemeSnapshot,
316
+ getServerThemeSnapshot
317
+ );
318
+ const [mounted, setMounted] = (0, import_react2.useState)(false);
319
+ (0, import_react2.useEffect)(() => {
320
+ setMounted(true);
321
+ }, []);
322
+ let themeObj = {};
323
+ let mode = "system";
324
+ if (typeof theme === "string") {
325
+ mode = theme;
326
+ } else if (typeof theme === "object" && theme !== null) {
327
+ themeObj = theme;
328
+ mode = theme.mode || "system";
329
+ }
330
+ const resolvedMode = mode === "system" ? mounted ? isSystemDark ? "dark" : "light" : "light" : mode;
331
+ const cssVarMap = {
332
+ mode: "",
333
+ background: "--oue-bg",
334
+ surface: "--oue-surface",
335
+ text: "--oue-text",
336
+ textMuted: "--oue-text-muted",
337
+ border: "--oue-border",
338
+ primary: "--oue-primary",
339
+ primaryForeground: "--oue-primary-foreground",
340
+ hover: "--oue-hover",
341
+ active: "--oue-active",
342
+ selection: "--oue-selection",
343
+ codeBg: "--oue-code-bg",
344
+ toolbarBg: "--oue-toolbar-bg",
345
+ toolbarBorder: "--oue-toolbar-border",
346
+ popupBg: "--oue-popup-bg",
347
+ radius: "--oue-radius",
348
+ shadow: "--oue-shadow",
349
+ fontFamily: "--oue-font-family",
350
+ fontSize: "--oue-font-size"
351
+ };
352
+ const styleObject = {};
353
+ Object.entries(themeObj).forEach(([key, val]) => {
354
+ if (val && cssVarMap[key]) {
355
+ styleObject[cssVarMap[key]] = String(val);
356
+ }
357
+ });
358
+ return {
359
+ mode: resolvedMode,
360
+ styleObject
361
+ };
362
+ }
363
+
364
+ // src/toolbar/Toolbar.tsx
365
+ var import_react7 = require("react");
366
+
367
+ // src/components/Icons.tsx
368
+ var import_jsx_runtime2 = require("react/jsx-runtime");
369
+ var createSvg = (path) => {
370
+ return function SvgIcon(props) {
371
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
372
+ "svg",
373
+ {
374
+ width: "16",
375
+ height: "16",
376
+ viewBox: "0 0 24 24",
377
+ fill: "none",
378
+ stroke: "currentColor",
379
+ strokeWidth: "2",
380
+ strokeLinecap: "round",
381
+ strokeLinejoin: "round",
382
+ ...props,
383
+ children: path
384
+ }
385
+ );
386
+ };
387
+ };
388
+ var DefaultIcons = {
389
+ bold: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" })),
390
+ italic: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 4h-9M14 20H5M15 4L9 20" })),
391
+ underline: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3M4 21h16" })),
392
+ strike: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 4H9a3 3 0 0 0-2.83 4M14 12a4 4 0 0 1 0 8H6M4 12h16" })),
393
+ code: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 18l6-6-6-6M8 6l-6 6 6 6" })),
394
+ subscript: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 5l8 8M12 5l-8 8M20 19h-4l3-3a1.5 1.5 0 0 0-2.12-2.12" })),
395
+ superscript: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 19l8-8M12 19l-8-8M20 9h-4l3-3a1.5 1.5 0 0 0-2.12-2.12" })),
396
+ clearFormatting: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 6L6 18M6 6l12 12" })),
397
+ heading: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 12h8M4 18V6M12 18V6M17 12l3-3v9" })),
398
+ paragraph: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M13 4v16M17 4v16M19 4H9.5a4.5 4.5 0 0 0 0 9H13" })),
399
+ bulletList: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" })),
400
+ orderedList: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 6h11M10 12h11M10 18h11M4 6h1v4M4 10h2M4 18h3M4 14h3v4" })),
401
+ checkList: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 11l3 3L22 4M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" })),
402
+ indent: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 12h18M3 6h18M3 18h18M13 8l4 4-4 4" })),
403
+ outdent: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 12h18M3 6h18M3 18h18M11 8l-4 4 4 4" })),
404
+ alignLeft: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M17 10H3M21 6H3M21 14H3M17 18H3" })),
405
+ alignCenter: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 10H6M21 6H3M21 14H3M18 18H6" })),
406
+ alignRight: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 10H7M21 6H3M21 14H3M21 18H7" })),
407
+ alignJustify: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 10H3M21 6H3M21 14H3M21 18H3" })),
408
+ link: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })),
409
+ image: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z M8.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z M21 15l-5-5L5 21" })),
410
+ table: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 3h18v18H3V3z M3 9h18 M3 15h18 M9 3v18 M15 3v18" })),
411
+ media: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M23 7l-7 5 7 5V7z M14 5H3a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z" })),
412
+ file: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z M13 2v7h7" })),
413
+ codeBlock: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M10 12l-2 2 2 2 M14 12l2 2-2 2" })),
414
+ blockquote: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 21c3 0 7-1 7-8V5H3v8h4c0 4-2 6-4 7zm11 0c3 0 7-1 7-8V5h-7v8h4c0 4-2 6-4 7z" })),
415
+ hr: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12h14" })),
416
+ undo: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 7v6h6M3 13L9 7c6-6 12 0 12 0" })),
417
+ redo: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 7v6h-6M21 13L15 7c-6-6-12 0-12 0" })),
418
+ sourceMode: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 4l-4 16M4 8l-4 4 4 4M20 8l4 4-4 4" })),
419
+ findReplace: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z M21 21l-4.35-4.35" })),
420
+ fullscreen: createSvg(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3" }))
421
+ };
422
+ function getIcon(key, customIcons) {
423
+ if (customIcons && customIcons[key]) {
424
+ const CustomComp = customIcons[key];
425
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CustomComp, {});
426
+ }
427
+ const DefaultSvg = DefaultIcons[key];
428
+ return DefaultSvg ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultSvg, {}) : null;
429
+ }
430
+
431
+ // src/components/LinkModal.tsx
432
+ var import_react3 = require("react");
433
+ var import_jsx_runtime3 = require("react/jsx-runtime");
434
+ function LinkModal({
435
+ initialUrl = "",
436
+ initialTarget = "_self",
437
+ initialTitle = "",
438
+ isOpen,
439
+ onClose,
440
+ onSubmit
441
+ }) {
442
+ const [url, setUrl] = (0, import_react3.useState)(initialUrl);
443
+ const [target, setTarget] = (0, import_react3.useState)(initialTarget);
444
+ const [title, setTitle] = (0, import_react3.useState)(initialTitle);
445
+ const [error, setError] = (0, import_react3.useState)("");
446
+ if (!isOpen) return null;
447
+ const handleSubmit = (e) => {
448
+ e.preventDefault();
449
+ if (!url.trim()) {
450
+ setError("URL is required");
451
+ return;
452
+ }
453
+ if (url.trim().toLowerCase().startsWith("javascript:")) {
454
+ setError("Unsafe URL protocol (javascript:) is not permitted.");
455
+ return;
456
+ }
457
+ let finalUrl = url.trim();
458
+ if (!/^https?:\/\//i.test(finalUrl) && !/^\//.test(finalUrl) && !/^mailto:/i.test(finalUrl) && !/^tel:/i.test(finalUrl)) {
459
+ finalUrl = `https://${finalUrl}`;
460
+ }
461
+ onSubmit({
462
+ url: finalUrl,
463
+ target: target === "_blank" ? "_blank" : void 0,
464
+ title: title.trim() || void 0
465
+ });
466
+ onClose();
467
+ };
468
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "oue-dialog-overlay", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oue-dialog", onClick: (e) => e.stopPropagation(), children: [
469
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "oue-dialog-title", children: "Insert / Edit Link" }),
470
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleSubmit, children: [
471
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oue-dialog-field", children: [
472
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "oue-dialog-label", children: "URL" }),
473
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
474
+ "input",
475
+ {
476
+ type: "text",
477
+ className: "oue-dialog-input",
478
+ placeholder: "https://example.com",
479
+ value: url,
480
+ onChange: (e) => {
481
+ setUrl(e.target.value);
482
+ setError("");
483
+ },
484
+ autoFocus: true
485
+ }
486
+ ),
487
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "#ef4444", fontSize: 12 }, children: error })
488
+ ] }),
489
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oue-dialog-field", children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { className: "oue-dialog-label", children: "Title (optional)" }),
491
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
492
+ "input",
493
+ {
494
+ type: "text",
495
+ className: "oue-dialog-input",
496
+ placeholder: "Link title",
497
+ value: title,
498
+ onChange: (e) => setTitle(e.target.value)
499
+ }
500
+ )
501
+ ] }),
502
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oue-dialog-field", style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
503
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
504
+ "input",
505
+ {
506
+ type: "checkbox",
507
+ id: "oue-link-target",
508
+ checked: target === "_blank",
509
+ onChange: (e) => setTarget(e.target.checked ? "_blank" : "_self")
510
+ }
511
+ ),
512
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: "oue-link-target", className: "oue-dialog-label", style: { cursor: "pointer" }, children: 'Open in new tab (target="_blank")' })
513
+ ] }),
514
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oue-dialog-actions", children: [
515
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "oue-toolbar-btn", onClick: onClose, children: "Cancel" }),
516
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
517
+ "button",
518
+ {
519
+ type: "submit",
520
+ className: "oue-toolbar-btn",
521
+ style: { backgroundColor: "var(--oue-primary)", color: "var(--oue-primary-foreground)", padding: "0 14px" },
522
+ children: "Save Link"
523
+ }
524
+ )
525
+ ] })
526
+ ] })
527
+ ] }) });
528
+ }
529
+
530
+ // src/components/ImageModal.tsx
531
+ var import_react4 = require("react");
532
+ var import_jsx_runtime4 = require("react/jsx-runtime");
533
+ function ImageModal({
534
+ isOpen,
535
+ onClose,
536
+ uploader,
537
+ maxSize,
538
+ allowedTypes,
539
+ onSubmit
540
+ }) {
541
+ const [tab, setTab] = (0, import_react4.useState)("url");
542
+ const [url, setUrl] = (0, import_react4.useState)("");
543
+ const [alt, setAlt] = (0, import_react4.useState)("");
544
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
545
+ const [error, setError] = (0, import_react4.useState)("");
546
+ if (!isOpen) return null;
547
+ const handleUrlSubmit = (e) => {
548
+ e.preventDefault();
549
+ if (!url.trim()) {
550
+ setError("Image URL is required");
551
+ return;
552
+ }
553
+ onSubmit({ src: url.trim(), alt: alt.trim() || void 0 });
554
+ onClose();
555
+ };
556
+ const handleFileUpload = async (e) => {
557
+ const file = e.target.files?.[0];
558
+ if (!file) return;
559
+ if (maxSize && file.size > maxSize) {
560
+ setError(`File size exceeds maximum allowed limit (${(maxSize / (1024 * 1024)).toFixed(1)} MB)`);
561
+ return;
562
+ }
563
+ if (allowedTypes && allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {
564
+ setError(`File type ${file.type} is not allowed.`);
565
+ return;
566
+ }
567
+ setIsLoading(true);
568
+ setError("");
569
+ try {
570
+ if (uploader) {
571
+ const res = await uploader(file);
572
+ onSubmit({
573
+ src: res.src,
574
+ alt: res.alt || alt.trim() || file.name,
575
+ width: res.width,
576
+ height: res.height
577
+ });
578
+ } else {
579
+ const reader = new FileReader();
580
+ reader.onload = () => {
581
+ if (typeof reader.result === "string") {
582
+ onSubmit({ src: reader.result, alt: alt.trim() || file.name });
583
+ }
584
+ };
585
+ reader.readAsDataURL(file);
586
+ }
587
+ onClose();
588
+ } catch (err) {
589
+ setError(err?.message || "Failed to upload image");
590
+ } finally {
591
+ setIsLoading(false);
592
+ }
593
+ };
594
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "oue-dialog-overlay", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oue-dialog", onClick: (e) => e.stopPropagation(), children: [
595
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "oue-dialog-title", children: "Insert Image" }),
596
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 12, marginBottom: 16, borderBottom: "1px solid var(--oue-border)" }, children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
598
+ "button",
599
+ {
600
+ type: "button",
601
+ className: `oue-toolbar-btn ${tab === "url" ? "oue-active" : ""}`,
602
+ onClick: () => setTab("url"),
603
+ children: "Image URL"
604
+ }
605
+ ),
606
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
607
+ "button",
608
+ {
609
+ type: "button",
610
+ className: `oue-toolbar-btn ${tab === "upload" ? "oue-active" : ""}`,
611
+ onClick: () => setTab("upload"),
612
+ children: "Upload File"
613
+ }
614
+ )
615
+ ] }),
616
+ tab === "url" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { onSubmit: handleUrlSubmit, children: [
617
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oue-dialog-field", children: [
618
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "oue-dialog-label", children: "Image URL" }),
619
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
620
+ "input",
621
+ {
622
+ type: "text",
623
+ className: "oue-dialog-input",
624
+ placeholder: "https://example.com/image.jpg",
625
+ value: url,
626
+ onChange: (e) => {
627
+ setUrl(e.target.value);
628
+ setError("");
629
+ },
630
+ autoFocus: true
631
+ }
632
+ )
633
+ ] }),
634
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oue-dialog-field", children: [
635
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "oue-dialog-label", children: "Alt Text (description)" }),
636
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
637
+ "input",
638
+ {
639
+ type: "text",
640
+ className: "oue-dialog-input",
641
+ placeholder: "Describe image...",
642
+ value: alt,
643
+ onChange: (e) => setAlt(e.target.value)
644
+ }
645
+ )
646
+ ] }),
647
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#ef4444", fontSize: 12, marginBottom: 12 }, children: error }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oue-dialog-actions", children: [
649
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "oue-toolbar-btn", onClick: onClose, children: "Cancel" }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
651
+ "button",
652
+ {
653
+ type: "submit",
654
+ className: "oue-toolbar-btn",
655
+ style: { backgroundColor: "var(--oue-primary)", color: "var(--oue-primary-foreground)", padding: "0 14px" },
656
+ children: "Insert Image"
657
+ }
658
+ )
659
+ ] })
660
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
661
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oue-dialog-field", children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "oue-dialog-label", children: "Select Image File" }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
664
+ "input",
665
+ {
666
+ type: "file",
667
+ accept: allowedTypes ? allowedTypes.join(",") : "image/*",
668
+ onChange: handleFileUpload,
669
+ disabled: isLoading
670
+ }
671
+ )
672
+ ] }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oue-dialog-field", children: [
674
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { className: "oue-dialog-label", children: "Alt Text (optional)" }),
675
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
676
+ "input",
677
+ {
678
+ type: "text",
679
+ className: "oue-dialog-input",
680
+ placeholder: "Describe image...",
681
+ value: alt,
682
+ onChange: (e) => setAlt(e.target.value)
683
+ }
684
+ )
685
+ ] }),
686
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 13, color: "var(--oue-text-muted)", margin: "8px 0" }, children: "Uploading..." }),
687
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { color: "#ef4444", fontSize: 12, marginBottom: 12 }, children: error }),
688
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "oue-dialog-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "oue-toolbar-btn", onClick: onClose, children: "Cancel" }) })
689
+ ] })
690
+ ] }) });
691
+ }
692
+
693
+ // src/components/TableModal.tsx
694
+ var import_react5 = require("react");
695
+ var import_jsx_runtime5 = require("react/jsx-runtime");
696
+ function TableModal({ isOpen, onClose, onSubmit }) {
697
+ const [rows, setRows] = (0, import_react5.useState)(3);
698
+ const [cols, setCols] = (0, import_react5.useState)(3);
699
+ if (!isOpen) return null;
700
+ const handleSubmit = (e) => {
701
+ e.preventDefault();
702
+ onSubmit(Math.max(1, rows), Math.max(1, cols));
703
+ onClose();
704
+ };
705
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "oue-dialog-overlay", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "oue-dialog", onClick: (e) => e.stopPropagation(), children: [
706
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "oue-dialog-title", children: "Insert Table" }),
707
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { onSubmit: handleSubmit, children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 16 }, children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "oue-dialog-field", style: { flex: 1 }, children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "oue-dialog-label", children: "Rows" }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
712
+ "input",
713
+ {
714
+ type: "number",
715
+ min: "1",
716
+ max: "20",
717
+ className: "oue-dialog-input",
718
+ value: rows,
719
+ onChange: (e) => setRows(parseInt(e.target.value) || 1),
720
+ autoFocus: true
721
+ }
722
+ )
723
+ ] }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "oue-dialog-field", style: { flex: 1 }, children: [
725
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "oue-dialog-label", children: "Columns" }),
726
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
727
+ "input",
728
+ {
729
+ type: "number",
730
+ min: "1",
731
+ max: "20",
732
+ className: "oue-dialog-input",
733
+ value: cols,
734
+ onChange: (e) => setCols(parseInt(e.target.value) || 1)
735
+ }
736
+ )
737
+ ] })
738
+ ] }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "oue-dialog-actions", children: [
740
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "oue-toolbar-btn", onClick: onClose, children: "Cancel" }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
742
+ "button",
743
+ {
744
+ type: "submit",
745
+ className: "oue-toolbar-btn",
746
+ style: { backgroundColor: "var(--oue-primary)", color: "var(--oue-primary-foreground)", padding: "0 14px" },
747
+ children: "Insert Table"
748
+ }
749
+ )
750
+ ] })
751
+ ] })
752
+ ] }) });
753
+ }
754
+
755
+ // src/components/FindReplaceDialog.tsx
756
+ var import_react6 = require("react");
757
+ var import_jsx_runtime6 = require("react/jsx-runtime");
758
+ function FindReplaceDialog({ isOpen, onClose }) {
759
+ const { editor, getHTML, setHTML } = useEditor();
760
+ const [findText, setFindText] = (0, import_react6.useState)("");
761
+ const [replaceText, setReplaceText] = (0, import_react6.useState)("");
762
+ const [matchCase, setMatchCase] = (0, import_react6.useState)(false);
763
+ const [statusMessage, setStatusMessage] = (0, import_react6.useState)("");
764
+ if (!isOpen) return null;
765
+ const handleReplaceAll = () => {
766
+ if (!findText) return;
767
+ const html = getHTML();
768
+ const flags = matchCase ? "g" : "gi";
769
+ const regex = new RegExp(findText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags);
770
+ const matches = (html.match(regex) || []).length;
771
+ if (matches === 0) {
772
+ setStatusMessage("No matches found");
773
+ return;
774
+ }
775
+ const updatedHtml = html.replace(regex, replaceText);
776
+ setHTML(updatedHtml);
777
+ setStatusMessage(`Replaced ${matches} occurrence(s)`);
778
+ };
779
+ const handleReplaceNext = () => {
780
+ if (!findText) return;
781
+ const html = getHTML();
782
+ const flags = matchCase ? "" : "i";
783
+ const regex = new RegExp(findText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags);
784
+ if (!regex.test(html)) {
785
+ setStatusMessage("No matches found");
786
+ return;
787
+ }
788
+ const updatedHtml = html.replace(regex, replaceText);
789
+ setHTML(updatedHtml);
790
+ setStatusMessage("Replaced 1 occurrence");
791
+ };
792
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "oue-dialog-overlay", onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "oue-dialog", onClick: (e) => e.stopPropagation(), children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "oue-dialog-title", children: "Find and Replace" }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "oue-dialog-field", children: [
795
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "oue-dialog-label", children: "Find" }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
797
+ "input",
798
+ {
799
+ type: "text",
800
+ className: "oue-dialog-input",
801
+ placeholder: "Search text...",
802
+ value: findText,
803
+ onChange: (e) => {
804
+ setFindText(e.target.value);
805
+ setStatusMessage("");
806
+ },
807
+ autoFocus: true
808
+ }
809
+ )
810
+ ] }),
811
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "oue-dialog-field", children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "oue-dialog-label", children: "Replace with" }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
814
+ "input",
815
+ {
816
+ type: "text",
817
+ className: "oue-dialog-input",
818
+ placeholder: "Replacement text...",
819
+ value: replaceText,
820
+ onChange: (e) => {
821
+ setReplaceText(e.target.value);
822
+ setStatusMessage("");
823
+ }
824
+ }
825
+ )
826
+ ] }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "oue-dialog-field", style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
828
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
829
+ "input",
830
+ {
831
+ type: "checkbox",
832
+ id: "oue-match-case",
833
+ checked: matchCase,
834
+ onChange: (e) => setMatchCase(e.target.checked)
835
+ }
836
+ ),
837
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { htmlFor: "oue-match-case", className: "oue-dialog-label", style: { cursor: "pointer" }, children: "Match case" })
838
+ ] }),
839
+ statusMessage && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "var(--oue-primary)", margin: "8px 0" }, children: statusMessage }),
840
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "oue-dialog-actions", children: [
841
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "oue-toolbar-btn", onClick: onClose, children: "Close" }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "oue-toolbar-btn", onClick: handleReplaceNext, children: "Replace" }),
843
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
844
+ "button",
845
+ {
846
+ type: "button",
847
+ className: "oue-toolbar-btn",
848
+ style: { backgroundColor: "var(--oue-primary)", color: "var(--oue-primary-foreground)", padding: "0 14px" },
849
+ onClick: handleReplaceAll,
850
+ children: "Replace All"
851
+ }
852
+ )
853
+ ] })
854
+ ] }) });
855
+ }
856
+
857
+ // src/toolbar/Toolbar.tsx
858
+ var import_jsx_runtime7 = require("react/jsx-runtime");
859
+ function Toolbar({ config, imageUploader }) {
860
+ const editorCtx = useEditor();
861
+ const { activeStates, icons } = editorCtx;
862
+ const [isLinkOpen, setIsLinkOpen] = (0, import_react7.useState)(false);
863
+ const [isImageOpen, setIsImageOpen] = (0, import_react7.useState)(false);
864
+ const [isTableOpen, setIsTableOpen] = (0, import_react7.useState)(false);
865
+ const [isFindOpen, setIsFindOpen] = (0, import_react7.useState)(false);
866
+ if (config === false) return null;
867
+ const items = config || [
868
+ "undo",
869
+ "redo",
870
+ "|",
871
+ "heading",
872
+ "|",
873
+ "bold",
874
+ "italic",
875
+ "underline",
876
+ "strike",
877
+ "code",
878
+ "|",
879
+ "align",
880
+ "|",
881
+ "bulletList",
882
+ "orderedList",
883
+ "checkList",
884
+ "|",
885
+ "link",
886
+ "image",
887
+ "table",
888
+ "codeBlock",
889
+ "blockquote",
890
+ "hr",
891
+ "|",
892
+ "sourceMode",
893
+ "findReplace",
894
+ "fullscreen"
895
+ ];
896
+ const handleHeadingChange = (e) => {
897
+ const val = e.target.value;
898
+ editorCtx.setHeading(val);
899
+ };
900
+ const handleAlignChange = (e) => {
901
+ const val = e.target.value;
902
+ editorCtx.setAlignment(val);
903
+ };
904
+ const getCurrentHeadingValue = () => {
905
+ if (activeStates.isH1) return "h1";
906
+ if (activeStates.isH2) return "h2";
907
+ if (activeStates.isH3) return "h3";
908
+ if (activeStates.isH4) return "h4";
909
+ if (activeStates.isH5) return "h5";
910
+ if (activeStates.isH6) return "h6";
911
+ return "p";
912
+ };
913
+ const getCurrentAlignValue = () => {
914
+ if (activeStates.isAlignCenter) return "center";
915
+ if (activeStates.isAlignRight) return "right";
916
+ if (activeStates.isAlignJustify) return "justify";
917
+ return "left";
918
+ };
919
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
920
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
921
+ "div",
922
+ {
923
+ className: "oue-toolbar",
924
+ role: "toolbar",
925
+ "aria-label": "Editor formatting toolbar",
926
+ onMouseDown: (e) => {
927
+ if (e.target.closest("select") === null) {
928
+ e.preventDefault();
929
+ }
930
+ },
931
+ children: items.map((item, idx) => {
932
+ if (typeof item === "function" || typeof item === "object") {
933
+ const CustomComp = item;
934
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomComp, {}, idx);
935
+ }
936
+ if (item === "|") {
937
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "oue-toolbar-separator", "aria-hidden": "true" }, idx);
938
+ }
939
+ switch (item) {
940
+ case "undo":
941
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
942
+ "button",
943
+ {
944
+ type: "button",
945
+ "aria-label": "Undo",
946
+ title: "Undo (Ctrl+Z)",
947
+ className: "oue-toolbar-btn",
948
+ disabled: !activeStates.canUndo,
949
+ onClick: editorCtx.undo,
950
+ children: getIcon("undo", icons)
951
+ },
952
+ idx
953
+ );
954
+ case "redo":
955
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
956
+ "button",
957
+ {
958
+ type: "button",
959
+ "aria-label": "Redo",
960
+ title: "Redo (Ctrl+Y / Cmd+Shift+Z)",
961
+ className: "oue-toolbar-btn",
962
+ disabled: !activeStates.canRedo,
963
+ onClick: editorCtx.redo,
964
+ children: getIcon("redo", icons)
965
+ },
966
+ idx
967
+ );
968
+ case "heading":
969
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
970
+ "select",
971
+ {
972
+ "aria-label": "Text style heading",
973
+ className: "oue-toolbar-select",
974
+ value: getCurrentHeadingValue(),
975
+ onChange: handleHeadingChange,
976
+ children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "p", children: "Paragraph" }),
978
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h1", children: "Heading 1" }),
979
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h2", children: "Heading 2" }),
980
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h3", children: "Heading 3" }),
981
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h4", children: "Heading 4" }),
982
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h5", children: "Heading 5" }),
983
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "h6", children: "Heading 6" })
984
+ ]
985
+ },
986
+ idx
987
+ );
988
+ case "bold":
989
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
990
+ "button",
991
+ {
992
+ type: "button",
993
+ "aria-label": "Bold",
994
+ title: "Bold (Ctrl+B)",
995
+ className: `oue-toolbar-btn ${activeStates.isBold ? "oue-active" : ""}`,
996
+ onClick: editorCtx.toggleBold,
997
+ children: getIcon("bold", icons)
998
+ },
999
+ idx
1000
+ );
1001
+ case "italic":
1002
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1003
+ "button",
1004
+ {
1005
+ type: "button",
1006
+ "aria-label": "Italic",
1007
+ title: "Italic (Ctrl+I)",
1008
+ className: `oue-toolbar-btn ${activeStates.isItalic ? "oue-active" : ""}`,
1009
+ onClick: editorCtx.toggleItalic,
1010
+ children: getIcon("italic", icons)
1011
+ },
1012
+ idx
1013
+ );
1014
+ case "underline":
1015
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1016
+ "button",
1017
+ {
1018
+ type: "button",
1019
+ "aria-label": "Underline",
1020
+ title: "Underline (Ctrl+U)",
1021
+ className: `oue-toolbar-btn ${activeStates.isUnderline ? "oue-active" : ""}`,
1022
+ onClick: editorCtx.toggleUnderline,
1023
+ children: getIcon("underline", icons)
1024
+ },
1025
+ idx
1026
+ );
1027
+ case "strike":
1028
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1029
+ "button",
1030
+ {
1031
+ type: "button",
1032
+ "aria-label": "Strikethrough",
1033
+ title: "Strikethrough",
1034
+ className: `oue-toolbar-btn ${activeStates.isStrikethrough ? "oue-active" : ""}`,
1035
+ onClick: editorCtx.toggleStrikethrough,
1036
+ children: getIcon("strike", icons)
1037
+ },
1038
+ idx
1039
+ );
1040
+ case "code":
1041
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1042
+ "button",
1043
+ {
1044
+ type: "button",
1045
+ "aria-label": "Inline Code",
1046
+ title: "Inline Code",
1047
+ className: `oue-toolbar-btn ${activeStates.isCode ? "oue-active" : ""}`,
1048
+ onClick: editorCtx.toggleCode,
1049
+ children: getIcon("code", icons)
1050
+ },
1051
+ idx
1052
+ );
1053
+ case "subscript":
1054
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1055
+ "button",
1056
+ {
1057
+ type: "button",
1058
+ "aria-label": "Subscript",
1059
+ title: "Subscript",
1060
+ className: `oue-toolbar-btn ${activeStates.isSubscript ? "oue-active" : ""}`,
1061
+ onClick: editorCtx.toggleSubscript,
1062
+ children: getIcon("subscript", icons)
1063
+ },
1064
+ idx
1065
+ );
1066
+ case "superscript":
1067
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1068
+ "button",
1069
+ {
1070
+ type: "button",
1071
+ "aria-label": "Superscript",
1072
+ title: "Superscript",
1073
+ className: `oue-toolbar-btn ${activeStates.isSuperscript ? "oue-active" : ""}`,
1074
+ onClick: editorCtx.toggleSuperscript,
1075
+ children: getIcon("superscript", icons)
1076
+ },
1077
+ idx
1078
+ );
1079
+ case "clearFormatting":
1080
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1081
+ "button",
1082
+ {
1083
+ type: "button",
1084
+ "aria-label": "Clear formatting",
1085
+ title: "Clear formatting",
1086
+ className: "oue-toolbar-btn",
1087
+ onClick: editorCtx.clearFormatting,
1088
+ children: getIcon("clearFormatting", icons)
1089
+ },
1090
+ idx
1091
+ );
1092
+ case "align":
1093
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1094
+ "select",
1095
+ {
1096
+ "aria-label": "Text Alignment",
1097
+ className: "oue-toolbar-select",
1098
+ value: getCurrentAlignValue(),
1099
+ onChange: handleAlignChange,
1100
+ children: [
1101
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "left", children: "Align Left" }),
1102
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "center", children: "Align Center" }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "right", children: "Align Right" }),
1104
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "justify", children: "Justify" })
1105
+ ]
1106
+ },
1107
+ idx
1108
+ );
1109
+ case "bulletList":
1110
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1111
+ "button",
1112
+ {
1113
+ type: "button",
1114
+ "aria-label": "Bullet List",
1115
+ title: "Bullet List",
1116
+ className: `oue-toolbar-btn ${activeStates.isBulletList ? "oue-active" : ""}`,
1117
+ onClick: editorCtx.toggleBulletList,
1118
+ children: getIcon("bulletList", icons)
1119
+ },
1120
+ idx
1121
+ );
1122
+ case "orderedList":
1123
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1124
+ "button",
1125
+ {
1126
+ type: "button",
1127
+ "aria-label": "Numbered List",
1128
+ title: "Numbered List",
1129
+ className: `oue-toolbar-btn ${activeStates.isOrderedList ? "oue-active" : ""}`,
1130
+ onClick: editorCtx.toggleOrderedList,
1131
+ children: getIcon("orderedList", icons)
1132
+ },
1133
+ idx
1134
+ );
1135
+ case "checkList":
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1137
+ "button",
1138
+ {
1139
+ type: "button",
1140
+ "aria-label": "Task List",
1141
+ title: "Task List",
1142
+ className: `oue-toolbar-btn ${activeStates.isCheckList ? "oue-active" : ""}`,
1143
+ onClick: editorCtx.toggleCheckList,
1144
+ children: getIcon("checkList", icons)
1145
+ },
1146
+ idx
1147
+ );
1148
+ case "indent":
1149
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1150
+ "button",
1151
+ {
1152
+ type: "button",
1153
+ "aria-label": "Indent",
1154
+ title: "Indent (Tab)",
1155
+ className: "oue-toolbar-btn",
1156
+ onClick: editorCtx.indent,
1157
+ children: getIcon("indent", icons)
1158
+ },
1159
+ idx
1160
+ );
1161
+ case "outdent":
1162
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1163
+ "button",
1164
+ {
1165
+ type: "button",
1166
+ "aria-label": "Outdent",
1167
+ title: "Outdent (Shift+Tab)",
1168
+ className: "oue-toolbar-btn",
1169
+ onClick: editorCtx.outdent,
1170
+ children: getIcon("outdent", icons)
1171
+ },
1172
+ idx
1173
+ );
1174
+ case "link":
1175
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1176
+ "button",
1177
+ {
1178
+ type: "button",
1179
+ "aria-label": "Insert Link",
1180
+ title: "Insert Link",
1181
+ className: `oue-toolbar-btn ${activeStates.isLink ? "oue-active" : ""}`,
1182
+ onClick: () => setIsLinkOpen(true),
1183
+ children: getIcon("link", icons)
1184
+ },
1185
+ idx
1186
+ );
1187
+ case "image":
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1189
+ "button",
1190
+ {
1191
+ type: "button",
1192
+ "aria-label": "Insert Image",
1193
+ title: "Insert Image",
1194
+ className: "oue-toolbar-btn",
1195
+ onClick: () => setIsImageOpen(true),
1196
+ children: getIcon("image", icons)
1197
+ },
1198
+ idx
1199
+ );
1200
+ case "table":
1201
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1202
+ "button",
1203
+ {
1204
+ type: "button",
1205
+ "aria-label": "Insert Table",
1206
+ title: "Insert Table",
1207
+ className: "oue-toolbar-btn",
1208
+ onClick: () => setIsTableOpen(true),
1209
+ children: getIcon("table", icons)
1210
+ },
1211
+ idx
1212
+ );
1213
+ case "codeBlock":
1214
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1215
+ "button",
1216
+ {
1217
+ type: "button",
1218
+ "aria-label": "Code Block",
1219
+ title: "Code Block",
1220
+ className: `oue-toolbar-btn ${activeStates.isCodeBlock ? "oue-active" : ""}`,
1221
+ onClick: () => editorCtx.insertCodeBlock(),
1222
+ children: getIcon("codeBlock", icons)
1223
+ },
1224
+ idx
1225
+ );
1226
+ case "blockquote":
1227
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1228
+ "button",
1229
+ {
1230
+ type: "button",
1231
+ "aria-label": "Blockquote",
1232
+ title: "Blockquote",
1233
+ className: `oue-toolbar-btn ${activeStates.isBlockquote ? "oue-active" : ""}`,
1234
+ onClick: editorCtx.toggleBlockquote,
1235
+ children: getIcon("blockquote", icons)
1236
+ },
1237
+ idx
1238
+ );
1239
+ case "hr":
1240
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1241
+ "button",
1242
+ {
1243
+ type: "button",
1244
+ "aria-label": "Horizontal Rule",
1245
+ title: "Horizontal Rule",
1246
+ className: "oue-toolbar-btn",
1247
+ onClick: editorCtx.insertHR,
1248
+ children: getIcon("hr", icons)
1249
+ },
1250
+ idx
1251
+ );
1252
+ case "sourceMode":
1253
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1254
+ "button",
1255
+ {
1256
+ type: "button",
1257
+ "aria-label": "HTML Source Mode",
1258
+ title: "Toggle HTML Source",
1259
+ className: `oue-toolbar-btn ${editorCtx.isSourceMode ? "oue-active" : ""}`,
1260
+ onClick: editorCtx.toggleSourceMode,
1261
+ children: getIcon("sourceMode", icons)
1262
+ },
1263
+ idx
1264
+ );
1265
+ case "findReplace":
1266
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1267
+ "button",
1268
+ {
1269
+ type: "button",
1270
+ "aria-label": "Find and Replace",
1271
+ title: "Find & Replace (Ctrl+F)",
1272
+ className: "oue-toolbar-btn",
1273
+ onClick: () => setIsFindOpen(true),
1274
+ children: getIcon("findReplace", icons)
1275
+ },
1276
+ idx
1277
+ );
1278
+ case "fullscreen":
1279
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1280
+ "button",
1281
+ {
1282
+ type: "button",
1283
+ "aria-label": "Fullscreen",
1284
+ title: "Toggle Fullscreen",
1285
+ className: `oue-toolbar-btn ${editorCtx.isFullscreen ? "oue-active" : ""}`,
1286
+ onClick: editorCtx.toggleFullscreen,
1287
+ children: getIcon("fullscreen", icons)
1288
+ },
1289
+ idx
1290
+ );
1291
+ default:
1292
+ return null;
1293
+ }
1294
+ })
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1298
+ LinkModal,
1299
+ {
1300
+ isOpen: isLinkOpen,
1301
+ onClose: () => setIsLinkOpen(false),
1302
+ onSubmit: ({ url, target, title }) => editorCtx.insertLink(url, target, title)
1303
+ }
1304
+ ),
1305
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1306
+ ImageModal,
1307
+ {
1308
+ isOpen: isImageOpen,
1309
+ uploader: imageUploader,
1310
+ onClose: () => setIsImageOpen(false),
1311
+ onSubmit: ({ src, alt, width, height }) => editorCtx.insertImage(src, alt, width, height)
1312
+ }
1313
+ ),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1315
+ TableModal,
1316
+ {
1317
+ isOpen: isTableOpen,
1318
+ onClose: () => setIsTableOpen(false),
1319
+ onSubmit: (rows, cols) => editorCtx.insertTable(rows, cols)
1320
+ }
1321
+ ),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FindReplaceDialog, { isOpen: isFindOpen, onClose: () => setIsFindOpen(false) })
1323
+ ] });
1324
+ }
1325
+
1326
+ // src/toolbar/BubbleToolbar.tsx
1327
+ var import_react8 = require("react");
1328
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1329
+ function BubbleToolbar({ config }) {
1330
+ const editorCtx = useEditor();
1331
+ const { editor, activeStates, icons } = editorCtx;
1332
+ const [position, setPosition] = (0, import_react8.useState)(null);
1333
+ const [isLinkOpen, setIsLinkOpen] = (0, import_react8.useState)(false);
1334
+ const toolbarRef = (0, import_react8.useRef)(null);
1335
+ (0, import_react8.useEffect)(() => {
1336
+ if (!editor || config === false) return;
1337
+ const updatePosition = () => {
1338
+ const selection = window.getSelection();
1339
+ if (!selection || selection.isCollapsed || !selection.rangeCount) {
1340
+ setPosition(null);
1341
+ return;
1342
+ }
1343
+ const range = selection.getRangeAt(0);
1344
+ const rect = range.getBoundingClientRect();
1345
+ if (rect.width === 0 || rect.height === 0) {
1346
+ setPosition(null);
1347
+ return;
1348
+ }
1349
+ const top = rect.top - 48 + window.scrollY;
1350
+ const left = rect.left + rect.width / 2 + window.scrollX;
1351
+ setPosition({ top: Math.max(10, top), left });
1352
+ };
1353
+ document.addEventListener("selectionchange", updatePosition);
1354
+ return () => document.removeEventListener("selectionchange", updatePosition);
1355
+ }, [editor, config]);
1356
+ if (config === false || !position) return null;
1357
+ const items = config || ["bold", "italic", "underline", "strike", "link"];
1358
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1359
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1360
+ "div",
1361
+ {
1362
+ ref: toolbarRef,
1363
+ className: "oue-bubble-toolbar",
1364
+ style: {
1365
+ top: position.top,
1366
+ left: position.left,
1367
+ transform: "translateX(-50%)"
1368
+ },
1369
+ onMouseDown: (e) => e.preventDefault(),
1370
+ children: items.map((item, idx) => {
1371
+ if (typeof item !== "string") return null;
1372
+ switch (item) {
1373
+ case "bold":
1374
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1375
+ "button",
1376
+ {
1377
+ type: "button",
1378
+ "aria-label": "Bold",
1379
+ className: `oue-toolbar-btn ${activeStates.isBold ? "oue-active" : ""}`,
1380
+ onClick: editorCtx.toggleBold,
1381
+ children: getIcon("bold", icons)
1382
+ },
1383
+ idx
1384
+ );
1385
+ case "italic":
1386
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1387
+ "button",
1388
+ {
1389
+ type: "button",
1390
+ "aria-label": "Italic",
1391
+ className: `oue-toolbar-btn ${activeStates.isItalic ? "oue-active" : ""}`,
1392
+ onClick: editorCtx.toggleItalic,
1393
+ children: getIcon("italic", icons)
1394
+ },
1395
+ idx
1396
+ );
1397
+ case "underline":
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1399
+ "button",
1400
+ {
1401
+ type: "button",
1402
+ "aria-label": "Underline",
1403
+ className: `oue-toolbar-btn ${activeStates.isUnderline ? "oue-active" : ""}`,
1404
+ onClick: editorCtx.toggleUnderline,
1405
+ children: getIcon("underline", icons)
1406
+ },
1407
+ idx
1408
+ );
1409
+ case "strike":
1410
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1411
+ "button",
1412
+ {
1413
+ type: "button",
1414
+ "aria-label": "Strikethrough",
1415
+ className: `oue-toolbar-btn ${activeStates.isStrikethrough ? "oue-active" : ""}`,
1416
+ onClick: editorCtx.toggleStrikethrough,
1417
+ children: getIcon("strike", icons)
1418
+ },
1419
+ idx
1420
+ );
1421
+ case "link":
1422
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1423
+ "button",
1424
+ {
1425
+ type: "button",
1426
+ "aria-label": "Link",
1427
+ className: `oue-toolbar-btn ${activeStates.isLink ? "oue-active" : ""}`,
1428
+ onClick: () => setIsLinkOpen(true),
1429
+ children: getIcon("link", icons)
1430
+ },
1431
+ idx
1432
+ );
1433
+ default:
1434
+ return null;
1435
+ }
1436
+ })
1437
+ }
1438
+ ),
1439
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1440
+ LinkModal,
1441
+ {
1442
+ isOpen: isLinkOpen,
1443
+ onClose: () => setIsLinkOpen(false),
1444
+ onSubmit: ({ url, target, title }) => editorCtx.insertLink(url, target, title)
1445
+ }
1446
+ )
1447
+ ] });
1448
+ }
1449
+
1450
+ // src/core/Extension.ts
1451
+ function createExtension(config) {
1452
+ const extension = {
1453
+ name: config.name,
1454
+ nodes: config.nodes || [],
1455
+ plugins: config.plugins || [],
1456
+ commands: config.commands || [],
1457
+ toolbarItems: config.toolbarItems || [],
1458
+ configure: (options) => {
1459
+ if (config.configure) {
1460
+ return config.configure(options);
1461
+ }
1462
+ return extension;
1463
+ }
1464
+ };
1465
+ return extension;
1466
+ }
1467
+
1468
+ // src/extensions/image/ImageExtension.ts
1469
+ var ImageExtension = createExtension({
1470
+ name: "image",
1471
+ nodes: [ImageNode],
1472
+ configure: (options) => {
1473
+ return {
1474
+ name: "image",
1475
+ nodes: [ImageNode],
1476
+ options
1477
+ };
1478
+ }
1479
+ });
1480
+
1481
+ // src/extensions/table/TableExtension.ts
1482
+ var import_table = require("@lexical/table");
1483
+ var TableExtension = createExtension({
1484
+ name: "table",
1485
+ nodes: [import_table.TableNode, import_table.TableRowNode, import_table.TableCellNode]
1486
+ });
1487
+
1488
+ // src/extensions/code/CodeExtension.ts
1489
+ var import_code = require("@lexical/code");
1490
+ var CodeExtension = createExtension({
1491
+ name: "code",
1492
+ nodes: [import_code.CodeNode, import_code.CodeHighlightNode]
1493
+ });
1494
+
1495
+ // src/extensions/media/MediaExtension.ts
1496
+ var MediaExtension = createExtension({
1497
+ name: "media",
1498
+ configure: (options) => {
1499
+ return {
1500
+ name: "media",
1501
+ options: options || { youtube: true, allowedDomains: ["youtube.com", "youtu.be"] }
1502
+ };
1503
+ }
1504
+ });
1505
+
1506
+ // src/extensions/file/FileExtension.ts
1507
+ var FileExtension = createExtension({
1508
+ name: "file",
1509
+ configure: (options) => {
1510
+ return {
1511
+ name: "file",
1512
+ options
1513
+ };
1514
+ }
1515
+ });
1516
+
1517
+ // src/extensions/mention/MentionExtension.ts
1518
+ var MentionExtension = createExtension({
1519
+ name: "mention",
1520
+ configure: (options) => {
1521
+ return {
1522
+ name: "mention",
1523
+ options: options || { trigger: "@" }
1524
+ };
1525
+ }
1526
+ });
1527
+
1528
+ // src/extensions/emoji/EmojiExtension.ts
1529
+ var EmojiExtension = createExtension({
1530
+ name: "emoji"
1531
+ });
1532
+
1533
+ // src/extensions/slash-command/SlashCommandExtension.ts
1534
+ var SlashCommandExtension = createExtension({
1535
+ name: "slashCommand",
1536
+ configure: (options) => {
1537
+ return {
1538
+ name: "slashCommand",
1539
+ options
1540
+ };
1541
+ }
1542
+ });
1543
+
1544
+ // src/presets/index.ts
1545
+ var basicPreset = [];
1546
+ var fullPreset = [
1547
+ ImageExtension,
1548
+ TableExtension,
1549
+ CodeExtension,
1550
+ MediaExtension,
1551
+ FileExtension,
1552
+ MentionExtension,
1553
+ EmojiExtension,
1554
+ SlashCommandExtension
1555
+ ];
1556
+
1557
+ // src/core/Editor.tsx
1558
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1559
+ function EditorInstancePlugin({
1560
+ onMount
1561
+ }) {
1562
+ const [editor] = (0, import_LexicalComposerContext.useLexicalComposerContext)();
1563
+ (0, import_react9.useEffect)(() => {
1564
+ onMount(editor);
1565
+ }, [editor, onMount]);
1566
+ return null;
1567
+ }
1568
+ function EditorStateTracker({
1569
+ onStateChange
1570
+ }) {
1571
+ const [editor] = (0, import_LexicalComposerContext.useLexicalComposerContext)();
1572
+ (0, import_react9.useEffect)(() => {
1573
+ const unregisterUndo = editor.registerCommand(
1574
+ import_lexical3.CAN_UNDO_COMMAND,
1575
+ (payload) => {
1576
+ onStateChange((prev) => ({ ...prev, canUndo: payload }));
1577
+ return false;
1578
+ },
1579
+ import_lexical3.COMMAND_PRIORITY_CRITICAL
1580
+ );
1581
+ const unregisterRedo = editor.registerCommand(
1582
+ import_lexical3.CAN_REDO_COMMAND,
1583
+ (payload) => {
1584
+ onStateChange((prev) => ({ ...prev, canRedo: payload }));
1585
+ return false;
1586
+ },
1587
+ import_lexical3.COMMAND_PRIORITY_CRITICAL
1588
+ );
1589
+ const updateStates = () => {
1590
+ editor.getEditorState().read(() => {
1591
+ const selection = (0, import_lexical3.$getSelection)();
1592
+ if (!(0, import_lexical3.$isRangeSelection)(selection)) return;
1593
+ const anchorNode = selection.anchor.getNode();
1594
+ const element = anchorNode.getKey() === "root" ? anchorNode : anchorNode.getTopLevelElementOrThrow();
1595
+ const elementType = element.getType();
1596
+ const snapshot = {
1597
+ isBold: selection.hasFormat("bold"),
1598
+ isItalic: selection.hasFormat("italic"),
1599
+ isUnderline: selection.hasFormat("underline"),
1600
+ isStrikethrough: selection.hasFormat("strikethrough"),
1601
+ isCode: selection.hasFormat("code"),
1602
+ isSubscript: selection.hasFormat("subscript"),
1603
+ isSuperscript: selection.hasFormat("superscript"),
1604
+ isLink: anchorNode.getParent()?.getType() === "link",
1605
+ isH1: elementType === "heading" && element.getTag() === "h1",
1606
+ isH2: elementType === "heading" && element.getTag() === "h2",
1607
+ isH3: elementType === "heading" && element.getTag() === "h3",
1608
+ isH4: elementType === "heading" && element.getTag() === "h4",
1609
+ isH5: elementType === "heading" && element.getTag() === "h5",
1610
+ isH6: elementType === "heading" && element.getTag() === "h6",
1611
+ isParagraph: elementType === "paragraph",
1612
+ isBulletList: elementType === "list" && element.getListType() === "bullet",
1613
+ isOrderedList: elementType === "list" && element.getListType() === "number",
1614
+ isCheckList: elementType === "list" && element.getListType() === "check",
1615
+ isBlockquote: elementType === "quote",
1616
+ isCodeBlock: elementType === "code",
1617
+ isAlignLeft: false,
1618
+ isAlignCenter: false,
1619
+ isAlignRight: false,
1620
+ isAlignJustify: false
1621
+ };
1622
+ onStateChange((prev) => ({ ...prev, ...snapshot }));
1623
+ });
1624
+ };
1625
+ const unregisterUpdate = editor.registerUpdateListener(updateStates);
1626
+ return () => {
1627
+ unregisterUndo();
1628
+ unregisterRedo();
1629
+ unregisterUpdate();
1630
+ };
1631
+ }, [editor, onStateChange]);
1632
+ return null;
1633
+ }
1634
+ function InitialContentPlugin({ html }) {
1635
+ const [editor] = (0, import_LexicalComposerContext.useLexicalComposerContext)();
1636
+ const isInitialized = (0, import_react9.useRef)(false);
1637
+ (0, import_react9.useEffect)(() => {
1638
+ if (isInitialized.current || !html) return;
1639
+ isInitialized.current = true;
1640
+ setEditorHTML(editor, html);
1641
+ }, [editor, html]);
1642
+ return null;
1643
+ }
1644
+ var Editor = (0, import_react9.forwardRef)((props, ref) => {
1645
+ const {
1646
+ id,
1647
+ className = "",
1648
+ style,
1649
+ "data-testid": testId,
1650
+ value,
1651
+ defaultValue,
1652
+ output = "html",
1653
+ placeholder = "Start writing...",
1654
+ readOnly = false,
1655
+ disabled = false,
1656
+ theme = "system",
1657
+ toolbar,
1658
+ bubbleToolbar,
1659
+ icons,
1660
+ extensions,
1661
+ preset,
1662
+ sourceMode: initialSourceMode = false,
1663
+ fullscreen: initialFullscreen = false,
1664
+ characterCount,
1665
+ wordCount,
1666
+ maxLength,
1667
+ html: htmlPolicy,
1668
+ autosave,
1669
+ onChange,
1670
+ onUpdate,
1671
+ onError,
1672
+ onDirtyChange,
1673
+ onCharacterCountChange
1674
+ } = props;
1675
+ const [editorInstance, setEditorInstance] = (0, import_react9.useState)(null);
1676
+ const [isFullscreen, setIsFullscreen] = (0, import_react9.useState)(initialFullscreen);
1677
+ const [isSourceMode, setIsSourceMode] = (0, import_react9.useState)(initialSourceMode);
1678
+ const [sourceHtml, setSourceHtml] = (0, import_react9.useState)("");
1679
+ const [isDirtyState, setIsDirtyState] = (0, import_react9.useState)(false);
1680
+ const [charCount, setCharCount] = (0, import_react9.useState)(0);
1681
+ const [wCount, setWCount] = (0, import_react9.useState)(0);
1682
+ const initialHtmlRef = (0, import_react9.useRef)(value !== void 0 ? value : defaultValue || "");
1683
+ const lastHtmlRef = (0, import_react9.useRef)(initialHtmlRef.current);
1684
+ const { mode: resolvedMode, styleObject: themeStyles } = useResolvedTheme(theme);
1685
+ const [activeStates, setActiveStates] = (0, import_react9.useState)({
1686
+ isBold: false,
1687
+ isItalic: false,
1688
+ isUnderline: false,
1689
+ isStrikethrough: false,
1690
+ isCode: false,
1691
+ isSubscript: false,
1692
+ isSuperscript: false,
1693
+ isLink: false,
1694
+ isH1: false,
1695
+ isH2: false,
1696
+ isH3: false,
1697
+ isH4: false,
1698
+ isH5: false,
1699
+ isH6: false,
1700
+ isParagraph: true,
1701
+ isBulletList: false,
1702
+ isOrderedList: false,
1703
+ isCheckList: false,
1704
+ isBlockquote: false,
1705
+ isCodeBlock: false,
1706
+ isAlignLeft: true,
1707
+ isAlignCenter: false,
1708
+ isAlignRight: false,
1709
+ isAlignJustify: false,
1710
+ canUndo: false,
1711
+ canRedo: false
1712
+ });
1713
+ const activeExtensions = extensions || (preset === "full" ? fullPreset : preset === "basic" ? basicPreset : []);
1714
+ const extensionNodes = activeExtensions.flatMap((ext) => ext.nodes || []);
1715
+ const initialConfig = {
1716
+ namespace: "OneUXIEditor",
1717
+ editable: !readOnly && !disabled,
1718
+ theme: {
1719
+ paragraph: "oue-paragraph",
1720
+ quote: "oue-quote",
1721
+ heading: {
1722
+ h1: "oue-h1",
1723
+ h2: "oue-h2",
1724
+ h3: "oue-h3",
1725
+ h4: "oue-h4",
1726
+ h5: "oue-h5",
1727
+ h6: "oue-h6"
1728
+ },
1729
+ list: {
1730
+ nested: {
1731
+ listitem: "oue-nested-listitem"
1732
+ },
1733
+ ol: "oue-ol",
1734
+ ul: "oue-ul",
1735
+ listitem: "oue-listitem",
1736
+ checklist: "oue-checklist",
1737
+ listitemChecked: "oue-checklist-item-checked",
1738
+ listitemUnchecked: "oue-checklist-item-unchecked"
1739
+ },
1740
+ image: "oue-image",
1741
+ link: "oue-link",
1742
+ text: {
1743
+ bold: "oue-bold",
1744
+ italic: "oue-italic",
1745
+ underline: "oue-underline",
1746
+ strikethrough: "oue-strike",
1747
+ code: "oue-code",
1748
+ subscript: "oue-subscript",
1749
+ superscript: "oue-superscript"
1750
+ },
1751
+ code: "oue-code-block"
1752
+ },
1753
+ onError: (error) => {
1754
+ if (onError) onError(error);
1755
+ },
1756
+ nodes: [
1757
+ import_rich_text.HeadingNode,
1758
+ import_rich_text.QuoteNode,
1759
+ import_list.ListNode,
1760
+ import_list.ListItemNode,
1761
+ import_link.LinkNode,
1762
+ import_link.AutoLinkNode,
1763
+ import_table2.TableNode,
1764
+ import_table2.TableRowNode,
1765
+ import_table2.TableCellNode,
1766
+ import_code2.CodeNode,
1767
+ import_code2.CodeHighlightNode,
1768
+ import_LexicalHorizontalRuleNode.HorizontalRuleNode,
1769
+ ImageNode,
1770
+ ...extensionNodes
1771
+ ]
1772
+ };
1773
+ (0, import_react9.useEffect)(() => {
1774
+ if (!editorInstance || value === void 0) return;
1775
+ if (value !== lastHtmlRef.current) {
1776
+ setEditorHTML(editorInstance, value, htmlPolicy);
1777
+ lastHtmlRef.current = value;
1778
+ }
1779
+ }, [editorInstance, value, htmlPolicy]);
1780
+ const toggleBold = (0, import_react9.useCallback)(() => {
1781
+ if (!editorInstance) return;
1782
+ editorInstance.focus();
1783
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "bold");
1784
+ }, [editorInstance]);
1785
+ const toggleItalic = (0, import_react9.useCallback)(() => {
1786
+ if (!editorInstance) return;
1787
+ editorInstance.focus();
1788
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "italic");
1789
+ }, [editorInstance]);
1790
+ const toggleUnderline = (0, import_react9.useCallback)(() => {
1791
+ if (!editorInstance) return;
1792
+ editorInstance.focus();
1793
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "underline");
1794
+ }, [editorInstance]);
1795
+ const toggleStrikethrough = (0, import_react9.useCallback)(() => {
1796
+ if (!editorInstance) return;
1797
+ editorInstance.focus();
1798
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "strikethrough");
1799
+ }, [editorInstance]);
1800
+ const toggleCode = (0, import_react9.useCallback)(() => {
1801
+ if (!editorInstance) return;
1802
+ editorInstance.focus();
1803
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "code");
1804
+ }, [editorInstance]);
1805
+ const toggleSubscript = (0, import_react9.useCallback)(() => {
1806
+ if (!editorInstance) return;
1807
+ editorInstance.focus();
1808
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "subscript");
1809
+ }, [editorInstance]);
1810
+ const toggleSuperscript = (0, import_react9.useCallback)(() => {
1811
+ if (!editorInstance) return;
1812
+ editorInstance.focus();
1813
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_TEXT_COMMAND, "superscript");
1814
+ }, [editorInstance]);
1815
+ const clearFormatting = (0, import_react9.useCallback)(() => {
1816
+ if (!editorInstance) return;
1817
+ editorInstance.focus();
1818
+ editorInstance.update(() => {
1819
+ const selection = (0, import_lexical3.$getSelection)();
1820
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1821
+ selection.getNodes().forEach((node) => {
1822
+ if ("setFormat" in node && typeof node.setFormat === "function") {
1823
+ node.setFormat(0);
1824
+ }
1825
+ });
1826
+ }
1827
+ });
1828
+ }, [editorInstance]);
1829
+ const setHeading = (0, import_react9.useCallback)(
1830
+ (tag) => {
1831
+ if (!editorInstance) return;
1832
+ editorInstance.focus();
1833
+ editorInstance.update(() => {
1834
+ const selection = (0, import_lexical3.$getSelection)();
1835
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1836
+ if (tag === "p") {
1837
+ (0, import_selection.$setBlocksType)(selection, () => (0, import_lexical3.$createParagraphNode)());
1838
+ } else {
1839
+ (0, import_selection.$setBlocksType)(selection, () => (0, import_rich_text2.$createHeadingNode)(tag));
1840
+ }
1841
+ }
1842
+ });
1843
+ },
1844
+ [editorInstance]
1845
+ );
1846
+ const toggleBlockquote = (0, import_react9.useCallback)(() => {
1847
+ if (!editorInstance) return;
1848
+ editorInstance.focus();
1849
+ editorInstance.update(() => {
1850
+ const selection = (0, import_lexical3.$getSelection)();
1851
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1852
+ (0, import_selection.$setBlocksType)(selection, () => (0, import_rich_text2.$createQuoteNode)());
1853
+ }
1854
+ });
1855
+ }, [editorInstance]);
1856
+ const insertHR = (0, import_react9.useCallback)(() => {
1857
+ if (!editorInstance) return;
1858
+ editorInstance.focus();
1859
+ editorInstance.update(() => {
1860
+ const selection = (0, import_lexical3.$getSelection)();
1861
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1862
+ const hr = (0, import_LexicalHorizontalRuleNode.$createHorizontalRuleNode)();
1863
+ selection.insertNodes([hr]);
1864
+ }
1865
+ });
1866
+ }, [editorInstance]);
1867
+ const toggleBulletList = (0, import_react9.useCallback)(() => {
1868
+ if (!editorInstance) return;
1869
+ editorInstance.focus();
1870
+ editorInstance.dispatchCommand(import_list2.INSERT_UNORDERED_LIST_COMMAND, void 0);
1871
+ }, [editorInstance]);
1872
+ const toggleOrderedList = (0, import_react9.useCallback)(() => {
1873
+ if (!editorInstance) return;
1874
+ editorInstance.focus();
1875
+ editorInstance.dispatchCommand(import_list2.INSERT_ORDERED_LIST_COMMAND, void 0);
1876
+ }, [editorInstance]);
1877
+ const toggleCheckList = (0, import_react9.useCallback)(() => {
1878
+ if (!editorInstance) return;
1879
+ editorInstance.focus();
1880
+ editorInstance.dispatchCommand(import_list2.INSERT_CHECK_LIST_COMMAND, void 0);
1881
+ }, [editorInstance]);
1882
+ const indent = (0, import_react9.useCallback)(() => {
1883
+ if (!editorInstance) return;
1884
+ editorInstance.focus();
1885
+ editorInstance.dispatchCommand(import_lexical3.INDENT_CONTENT_COMMAND, void 0);
1886
+ }, [editorInstance]);
1887
+ const outdent = (0, import_react9.useCallback)(() => {
1888
+ if (!editorInstance) return;
1889
+ editorInstance.focus();
1890
+ editorInstance.dispatchCommand(import_lexical3.OUTDENT_CONTENT_COMMAND, void 0);
1891
+ }, [editorInstance]);
1892
+ const setAlignment = (0, import_react9.useCallback)(
1893
+ (align) => {
1894
+ if (!editorInstance) return;
1895
+ editorInstance.focus();
1896
+ editorInstance.dispatchCommand(import_lexical3.FORMAT_ELEMENT_COMMAND, align);
1897
+ },
1898
+ [editorInstance]
1899
+ );
1900
+ const insertLink = (0, import_react9.useCallback)(
1901
+ (url, target, title) => {
1902
+ if (!editorInstance) return;
1903
+ editorInstance.focus();
1904
+ editorInstance.dispatchCommand(import_link.TOGGLE_LINK_COMMAND, { url, target, title });
1905
+ },
1906
+ [editorInstance]
1907
+ );
1908
+ const removeLink = (0, import_react9.useCallback)(() => {
1909
+ if (!editorInstance) return;
1910
+ editorInstance.focus();
1911
+ editorInstance.dispatchCommand(import_link.TOGGLE_LINK_COMMAND, null);
1912
+ }, [editorInstance]);
1913
+ const insertTable = (0, import_react9.useCallback)(
1914
+ (rows = 3, cols = 3) => {
1915
+ if (!editorInstance) return;
1916
+ editorInstance.update(() => {
1917
+ const selection = (0, import_lexical3.$getSelection)();
1918
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1919
+ const tableNode = (0, import_table2.$createTableNodeWithDimensions)(rows, cols, false);
1920
+ selection.insertNodes([tableNode]);
1921
+ }
1922
+ });
1923
+ },
1924
+ [editorInstance]
1925
+ );
1926
+ const insertImage = (0, import_react9.useCallback)(
1927
+ (src, alt, width, height) => {
1928
+ if (!editorInstance) return;
1929
+ editorInstance.update(() => {
1930
+ const selection = (0, import_lexical3.$getSelection)();
1931
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1932
+ const imageNode = $createImageNode({ src, alt, width, height });
1933
+ selection.insertNodes([imageNode]);
1934
+ }
1935
+ });
1936
+ },
1937
+ [editorInstance]
1938
+ );
1939
+ const insertCodeBlock = (0, import_react9.useCallback)(
1940
+ (language = "javascript") => {
1941
+ if (!editorInstance) return;
1942
+ editorInstance.update(() => {
1943
+ const selection = (0, import_lexical3.$getSelection)();
1944
+ if ((0, import_lexical3.$isRangeSelection)(selection)) {
1945
+ const codeNode = (0, import_code2.$createCodeNode)(language);
1946
+ selection.insertNodes([codeNode]);
1947
+ }
1948
+ });
1949
+ },
1950
+ [editorInstance]
1951
+ );
1952
+ const undo = (0, import_react9.useCallback)(() => {
1953
+ if (!editorInstance) return;
1954
+ editorInstance.focus();
1955
+ editorInstance.dispatchCommand(import_lexical3.UNDO_COMMAND, void 0);
1956
+ }, [editorInstance]);
1957
+ const redo = (0, import_react9.useCallback)(() => {
1958
+ if (!editorInstance) return;
1959
+ editorInstance.focus();
1960
+ editorInstance.dispatchCommand(import_lexical3.REDO_COMMAND, void 0);
1961
+ }, [editorInstance]);
1962
+ const focus = (0, import_react9.useCallback)(() => editorInstance?.focus(), [editorInstance]);
1963
+ const blur = (0, import_react9.useCallback)(() => editorInstance?.blur(), [editorInstance]);
1964
+ const clear = (0, import_react9.useCallback)(() => {
1965
+ if (!editorInstance) return;
1966
+ editorInstance.update(() => {
1967
+ const root = (0, import_lexical3.$getRoot)();
1968
+ root.clear();
1969
+ const p = (0, import_lexical3.$createParagraphNode)();
1970
+ root.append(p);
1971
+ p.select();
1972
+ });
1973
+ }, [editorInstance]);
1974
+ const getHTML = (0, import_react9.useCallback)(() => {
1975
+ if (!editorInstance) return "";
1976
+ return getEditorHTML(editorInstance, htmlPolicy);
1977
+ }, [editorInstance, htmlPolicy]);
1978
+ const setHTML = (0, import_react9.useCallback)(
1979
+ (html) => {
1980
+ if (!editorInstance) return;
1981
+ setEditorHTML(editorInstance, html, htmlPolicy);
1982
+ lastHtmlRef.current = html;
1983
+ },
1984
+ [editorInstance, htmlPolicy]
1985
+ );
1986
+ const getText = (0, import_react9.useCallback)(() => {
1987
+ if (!editorInstance) return "";
1988
+ let text = "";
1989
+ editorInstance.getEditorState().read(() => {
1990
+ text = (0, import_lexical3.$getRoot)().getTextContent();
1991
+ });
1992
+ return text;
1993
+ }, [editorInstance]);
1994
+ const getJSON = (0, import_react9.useCallback)(() => {
1995
+ if (!editorInstance) return { root: {} };
1996
+ return editorInstance.getEditorState().toJSON();
1997
+ }, [editorInstance]);
1998
+ const setJSON = (0, import_react9.useCallback)(
1999
+ (json) => {
2000
+ if (!editorInstance) return;
2001
+ const state = editorInstance.parseEditorState(JSON.stringify(json));
2002
+ editorInstance.setEditorState(state);
2003
+ },
2004
+ [editorInstance]
2005
+ );
2006
+ const isEmpty = (0, import_react9.useCallback)(() => {
2007
+ if (!editorInstance) return true;
2008
+ let empty = true;
2009
+ editorInstance.getEditorState().read(() => {
2010
+ const root = (0, import_lexical3.$getRoot)();
2011
+ const children = root.getChildren();
2012
+ if (children.length === 0) {
2013
+ empty = true;
2014
+ return;
2015
+ }
2016
+ const text = root.getTextContent().trim();
2017
+ empty = text.length === 0;
2018
+ });
2019
+ return empty;
2020
+ }, [editorInstance]);
2021
+ const isDirty = (0, import_react9.useCallback)(() => isDirtyState, [isDirtyState]);
2022
+ const markClean = (0, import_react9.useCallback)(() => {
2023
+ setIsDirtyState(false);
2024
+ if (onDirtyChange) onDirtyChange(false);
2025
+ }, [onDirtyChange]);
2026
+ const toggleFullscreen = (0, import_react9.useCallback)(() => setIsFullscreen((prev) => !prev), []);
2027
+ const toggleSourceMode = (0, import_react9.useCallback)(() => {
2028
+ setIsSourceMode((prev) => {
2029
+ const next = !prev;
2030
+ if (next && editorInstance) {
2031
+ setSourceHtml(getEditorHTML(editorInstance, htmlPolicy));
2032
+ } else if (!next && editorInstance) {
2033
+ setEditorHTML(editorInstance, sourceHtml, htmlPolicy);
2034
+ }
2035
+ return next;
2036
+ });
2037
+ }, [editorInstance, htmlPolicy, sourceHtml]);
2038
+ (0, import_react9.useImperativeHandle)(
2039
+ ref,
2040
+ () => ({
2041
+ focus,
2042
+ blur,
2043
+ clear,
2044
+ getHTML,
2045
+ setHTML,
2046
+ getText,
2047
+ getJSON,
2048
+ setJSON,
2049
+ isEmpty,
2050
+ isDirty,
2051
+ markClean,
2052
+ undo,
2053
+ redo
2054
+ }),
2055
+ [focus, blur, clear, getHTML, setHTML, getText, getJSON, setJSON, isEmpty, isDirty, markClean, undo, redo]
2056
+ );
2057
+ const handleEditorChange = (0, import_react9.useCallback)(() => {
2058
+ if (!editorInstance) return;
2059
+ const currentText = editorInstance.getEditorState().read(() => (0, import_lexical3.$getRoot)().getTextContent());
2060
+ const currentLength = currentText.length;
2061
+ const currentWords = currentText.trim() ? currentText.trim().split(/\s+/).length : 0;
2062
+ setCharCount(currentLength);
2063
+ setWCount(currentWords);
2064
+ if (onCharacterCountChange) onCharacterCountChange(currentLength);
2065
+ if (maxLength && currentLength > maxLength) {
2066
+ editorInstance.update(() => {
2067
+ const root = (0, import_lexical3.$getRoot)();
2068
+ const text = root.getTextContent().slice(0, maxLength);
2069
+ root.clear();
2070
+ const p = (0, import_lexical3.$createParagraphNode)();
2071
+ p.append(document.createTextNode(text));
2072
+ root.append(p);
2073
+ });
2074
+ return;
2075
+ }
2076
+ const htmlOutput = getEditorHTML(editorInstance, htmlPolicy);
2077
+ const jsonOutput = editorInstance.getEditorState().toJSON();
2078
+ const isCurrentlyEmpty = isEmpty();
2079
+ if (htmlOutput !== lastHtmlRef.current) {
2080
+ lastHtmlRef.current = htmlOutput;
2081
+ if (!isDirtyState) {
2082
+ setIsDirtyState(true);
2083
+ if (onDirtyChange) onDirtyChange(true);
2084
+ }
2085
+ if (onChange) {
2086
+ const result = output === "json" ? JSON.stringify(jsonOutput) : output === "text" ? currentText : htmlOutput;
2087
+ onChange(result);
2088
+ }
2089
+ if (onUpdate) {
2090
+ onUpdate({
2091
+ html: htmlOutput,
2092
+ text: currentText,
2093
+ json: jsonOutput,
2094
+ isEmpty: isCurrentlyEmpty
2095
+ });
2096
+ }
2097
+ if (autosave?.onSave) {
2098
+ autosave.onSave({ html: htmlOutput, json: jsonOutput, text: currentText });
2099
+ }
2100
+ }
2101
+ }, [
2102
+ editorInstance,
2103
+ onCharacterCountChange,
2104
+ maxLength,
2105
+ htmlPolicy,
2106
+ isEmpty,
2107
+ isDirtyState,
2108
+ onDirtyChange,
2109
+ onChange,
2110
+ output,
2111
+ onUpdate,
2112
+ autosave
2113
+ ]);
2114
+ const contextValue = {
2115
+ editor: editorInstance,
2116
+ activeStates,
2117
+ icons,
2118
+ toggleBold,
2119
+ toggleItalic,
2120
+ toggleUnderline,
2121
+ toggleStrikethrough,
2122
+ toggleCode,
2123
+ toggleSubscript,
2124
+ toggleSuperscript,
2125
+ clearFormatting,
2126
+ setHeading,
2127
+ toggleBlockquote,
2128
+ insertHR,
2129
+ toggleBulletList,
2130
+ toggleOrderedList,
2131
+ toggleCheckList,
2132
+ indent,
2133
+ outdent,
2134
+ setAlignment,
2135
+ insertLink,
2136
+ removeLink,
2137
+ insertTable,
2138
+ insertImage,
2139
+ insertCodeBlock,
2140
+ undo,
2141
+ redo,
2142
+ focus,
2143
+ blur,
2144
+ clear,
2145
+ getHTML,
2146
+ setHTML,
2147
+ getText,
2148
+ getJSON,
2149
+ setJSON,
2150
+ isEmpty,
2151
+ isDirty,
2152
+ markClean,
2153
+ isFullscreen,
2154
+ toggleFullscreen,
2155
+ isSourceMode,
2156
+ toggleSourceMode
2157
+ };
2158
+ const imageExt = activeExtensions.find((e) => e.name === "image");
2159
+ const imageUploader = imageExt?.options?.upload;
2160
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalComposer.LexicalComposer, { initialConfig, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2161
+ "div",
2162
+ {
2163
+ id,
2164
+ "data-testid": testId,
2165
+ "data-theme": resolvedMode,
2166
+ className: `oue-editor-container oue-editor-wrapper ${isFullscreen ? "oue-fullscreen" : ""} ${disabled ? "oue-disabled" : ""} ${className}`,
2167
+ style: { ...themeStyles, ...style },
2168
+ children: [
2169
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorInstancePlugin, { onMount: setEditorInstance }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EditorStateTracker, { onStateChange: setActiveStates }),
2171
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(InitialContentPlugin, { html: initialHtmlRef.current }),
2172
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Toolbar, { config: toolbar, imageUploader }),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BubbleToolbar, { config: bubbleToolbar }),
2174
+ isSourceMode ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2175
+ "textarea",
2176
+ {
2177
+ className: "oue-source-textarea",
2178
+ value: sourceHtml,
2179
+ onChange: (e) => {
2180
+ setSourceHtml(e.target.value);
2181
+ if (editorInstance) {
2182
+ setEditorHTML(editorInstance, e.target.value, htmlPolicy);
2183
+ }
2184
+ }
2185
+ }
2186
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2187
+ import_LexicalRichTextPlugin.RichTextPlugin,
2188
+ {
2189
+ contentEditable: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2190
+ import_LexicalContentEditable.ContentEditable,
2191
+ {
2192
+ className: "oue-content-editable",
2193
+ "aria-placeholder": placeholder,
2194
+ placeholder: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "oue-placeholder", children: placeholder })
2195
+ }
2196
+ ),
2197
+ ErrorBoundary: import_LexicalErrorBoundary.LexicalErrorBoundary
2198
+ }
2199
+ ) }),
2200
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalHistoryPlugin.HistoryPlugin, {}),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalListPlugin.ListPlugin, {}),
2202
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalCheckListPlugin.CheckListPlugin, {}),
2203
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalLinkPlugin.LinkPlugin, {}),
2204
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalHorizontalRulePlugin.HorizontalRulePlugin, {}),
2205
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_LexicalOnChangePlugin.OnChangePlugin, { onChange: handleEditorChange }),
2206
+ (characterCount || wordCount) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "oue-footer", children: [
2207
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
2208
+ wordCount && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2209
+ wCount,
2210
+ " words "
2211
+ ] }),
2212
+ characterCount && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2213
+ "(",
2214
+ charCount,
2215
+ " ",
2216
+ maxLength ? `/ ${maxLength}` : "",
2217
+ " chars)"
2218
+ ] })
2219
+ ] }),
2220
+ isDirtyState && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Unsaved changes" })
2221
+ ] })
2222
+ ]
2223
+ }
2224
+ ) }) });
2225
+ });
2226
+ Editor.displayName = "Editor";
2227
+
2228
+ // src/paste/cleaner.ts
2229
+ function cleanPastedHTML(rawHtml) {
2230
+ if (!rawHtml) return "";
2231
+ let cleaned = rawHtml;
2232
+ cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, "");
2233
+ cleaned = cleaned.replace(/<\/?(?:xml|meta|link|style)\b[^>]*>/gi, "");
2234
+ cleaned = cleaned.replace(/class=["']?\bMso[a-zA-Z0-9_-]+\b["']?/gi, "");
2235
+ cleaned = cleaned.replace(/style=["']([^"']*)["']/gi, (match, styleContent) => {
2236
+ const cleanStyles = styleContent.split(";").filter((style) => {
2237
+ const trimmed = style.trim().toLowerCase();
2238
+ return trimmed && !trimmed.startsWith("mso-") && !trimmed.startsWith("font-family") && !trimmed.startsWith("line-height") && !trimmed.startsWith("tab-stops");
2239
+ }).join(";");
2240
+ return cleanStyles ? `style="${cleanStyles}"` : "";
2241
+ });
2242
+ cleaned = cleaned.replace(/<span\s*>(.*?)<\/span>/gi, "$1");
2243
+ cleaned = cleaned.replace(/<span\s+style=["']\s*["']>(.*?)<\/span>/gi, "$1");
2244
+ cleaned = cleaned.replace(/<b\s+style=["']font-weight:\s*normal;?["']>(.*?)<\/b>/gi, "$1");
2245
+ cleaned = cleaned.replace(/<span\s+style=["'][^"']*font-weight:\s*700;?[^"']*["']>(.*?)<\/span>/gi, "<strong>$1</strong>");
2246
+ cleaned = cleaned.replace(/<span\s+style=["'][^"']*font-style:\s*italic;?[^"']*["']>(.*?)<\/span>/gi, "<em>$1</em>");
2247
+ return sanitizeHTML(cleaned);
2248
+ }
2249
+ // Annotate the CommonJS export names for ESM import in node:
2250
+ 0 && (module.exports = {
2251
+ BubbleToolbar,
2252
+ CodeExtension,
2253
+ DefaultIcons,
2254
+ Editor,
2255
+ EmojiExtension,
2256
+ FileExtension,
2257
+ ImageExtension,
2258
+ MediaExtension,
2259
+ MentionExtension,
2260
+ SlashCommandExtension,
2261
+ TableExtension,
2262
+ Toolbar,
2263
+ basicPreset,
2264
+ cleanPastedHTML,
2265
+ createExtension,
2266
+ fullPreset,
2267
+ sanitizeHTML,
2268
+ useEditor
2269
+ });
2270
+ //# sourceMappingURL=index.js.map