rich-html-editor 1.2.2 → 1.2.4
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/README.md +59 -25
- package/dist/{chunk-GJUQLM52.mjs → chunk-RTJONDEC.mjs} +1 -1
- package/dist/chunk-RTJONDEC.mjs.map +1 -0
- package/dist/index.global.js +80 -4
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +80 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -6
- package/dist/index.mjs.map +1 -1
- package/dist/{state-XHVVMFUB.mjs → state-C6OJUTU7.mjs} +2 -2
- package/package.json +2 -2
- package/CHANGELOG.md +0 -240
- package/dist/chunk-GJUQLM52.mjs.map +0 -1
- /package/dist/{state-XHVVMFUB.mjs.map → state-C6OJUTU7.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# rich-html-editor
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/rich-html-editor)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/rich-html-editor)
|
|
6
|
+
[](https://akshaypatil1.github.io/rich-html-editor/)
|
|
7
|
+
[](https://akshaypatil1.github.io/rich-html-editor/docs/)
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
4
11
|
|
|
5
12
|
> **Edit HTML templates safely — without breaking layout or CSS.**
|
|
6
13
|
|
|
@@ -9,7 +16,23 @@ It enables **controlled, template-driven editing**, not free-form WYSIWYG chaos.
|
|
|
9
16
|
|
|
10
17
|
---
|
|
11
18
|
|
|
12
|
-
## 🎥 Demo
|
|
19
|
+
## 🎥 Demo & Playground
|
|
20
|
+
|
|
21
|
+
### ▶️ Live Playground (Try it yourself)
|
|
22
|
+
|
|
23
|
+
👉 **https://akshaypatil1.github.io/rich-html-editor/**
|
|
24
|
+
|
|
25
|
+
Use the playground to:
|
|
26
|
+
|
|
27
|
+
- Edit real HTML inside an iframe
|
|
28
|
+
- Apply formatting safely
|
|
29
|
+
- Export clean HTML instantly
|
|
30
|
+
|
|
31
|
+
> No install. No build. Runs directly in the browser.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### 📽️ Video Demo
|
|
13
36
|
|
|
14
37
|
Below is a short demo showing how **rich-html-editor** allows inline editing of an HTML template using a toolbar embedded inside an iframe.
|
|
15
38
|
|
|
@@ -33,6 +56,14 @@ Most rich text editors allow users to edit _anything_ — which often leads to b
|
|
|
33
56
|
|
|
34
57
|
---
|
|
35
58
|
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
Explore guides, concepts, API reference, and examples:
|
|
62
|
+
|
|
63
|
+
👉 **https://akshaypatil1.github.io/rich-html-editor/docs/**
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
36
67
|
## 🚀 Features
|
|
37
68
|
|
|
38
69
|
### ✏️ Text Formatting
|
|
@@ -85,36 +116,39 @@ yarn add rich-html-editor
|
|
|
85
116
|
|
|
86
117
|
---
|
|
87
118
|
|
|
88
|
-
##
|
|
119
|
+
## Quick Start (Browser / iframe)
|
|
89
120
|
|
|
90
121
|
The editor initializes on an `HTMLIFrameElement`.
|
|
91
122
|
|
|
92
123
|
> ⚠️ The iframe must be **same-origin**. Use `srcdoc` for safety.
|
|
93
124
|
|
|
94
125
|
```html
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
iframe.srcdoc =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
<!-- CDN (browser) -->
|
|
127
|
+
<script src="https://unpkg.com/rich-html-editor@latest"></script>
|
|
128
|
+
|
|
129
|
+
<iframe id="frame"></iframe>
|
|
130
|
+
|
|
131
|
+
<script>
|
|
132
|
+
const iframe = document.getElementById("frame");
|
|
133
|
+
|
|
134
|
+
iframe.srcdoc = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><div>Edit me</div></body></html>`;
|
|
135
|
+
|
|
136
|
+
const EditorClass =
|
|
137
|
+
(window.RichHtmlEditor &&
|
|
138
|
+
(window.RichHtmlEditor.RichHtmlEditor || window.RichHtmlEditor)) ||
|
|
139
|
+
undefined;
|
|
140
|
+
let editor;
|
|
141
|
+
|
|
142
|
+
iframe.addEventListener("load", () => {
|
|
143
|
+
if (!EditorClass) {
|
|
144
|
+
console.warn(
|
|
145
|
+
"RichHtmlEditor not found on window. Did you load the CDN script?"
|
|
146
|
+
);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
editor = new EditorClass({ iframe, highlightEditable: true });
|
|
150
|
+
editor.init();
|
|
112
151
|
});
|
|
113
|
-
|
|
114
|
-
const html = getCleanHTML();
|
|
115
|
-
console.log(html);
|
|
116
|
-
|
|
117
|
-
// off(); // unsubscribe when needed
|
|
118
152
|
</script>
|
|
119
153
|
```
|
|
120
154
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/events.ts","../src/core/constants.ts","../src/utils/sanitize.ts","../src/core/state.ts"],"sourcesContent":["import type { EditorEventType, EditorEvent, EditorEventHandler } from \"./types\";\n\nexport class EditorEventEmitter {\n private listeners: Map<EditorEventType, Set<EditorEventHandler>> = new Map();\n\n on(type: EditorEventType, handler: EditorEventHandler): () => void {\n if (!this.listeners.has(type)) {\n this.listeners.set(type, new Set());\n }\n this.listeners.get(type)!.add(handler);\n return () => this.off(type, handler);\n }\n\n once(type: EditorEventType, handler: EditorEventHandler): void {\n const unsubscribe = this.on(type, (event) => {\n handler(event);\n unsubscribe();\n });\n }\n\n off(type: EditorEventType, handler: EditorEventHandler): void {\n const handlers = this.listeners.get(type);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) this.listeners.delete(type);\n }\n }\n\n emit(event: EditorEvent): void {\n const handlers = this.listeners.get(event.type);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(event);\n } catch (error) {\n console.error(\n `[rich-html-editor] Error in event handler for ${event.type}:`,\n error,\n );\n }\n });\n }\n }\n\n removeAllListeners(type?: EditorEventType): void {\n if (type) this.listeners.delete(type);\n else this.listeners.clear();\n }\n\n listenerCount(type: EditorEventType): number {\n return this.listeners.get(type)?.size ?? 0;\n }\n}\n\nexport const editorEventEmitter = new EditorEventEmitter();\n\nexport function getEditorEventEmitter(): EditorEventEmitter {\n return editorEventEmitter;\n}\n","// Shared constants for the rich-html-editor (moved to core)\nexport const TOOLBAR_ID = \"editor-toolbar\";\nexport const STYLE_ID = \"editor-styles\";\nexport const CLASS_EDITABLE = \"editor-editable-element\";\nexport const CLASS_ACTIVE = \"editor-active-element\";\n\n// Default undo/redo stack size\nexport const DEFAULT_MAX_STACK = 60;\n\n// Toolbar styling constants\nexport const TOOLBAR_BG = \"#f8fafc\";\nexport const TOOLBAR_BORDER = \"#e5e7eb\";\nexport const BUTTON_BORDER = \"#d1d5db\";\nexport const BUTTON_ACTIVE_BG = \"#e0e7ff\";\nexport const BUTTON_BG = \"#fff\";\nexport const BUTTON_COLOR = \"#222\";\nexport const INFO_COLOR = \"#888\";\n\n// Outline colors used by injected styles\nexport const HOVER_OUTLINE = \"#2563eb\";\nexport const ACTIVE_OUTLINE = \"#16a34a\";\n\n// Toolbar label/icon constants (kept in core for reuse)\nexport const LABEL_BOLD = \"<b>B</b>\";\nexport const LABEL_ITALIC = \"<i>I</i>\";\nexport const LABEL_UNDERLINE = \"<u>U</u>\";\nexport const LABEL_STRIKETHROUGH = \"<s>S</s>\";\nexport const LABEL_UNDO = \"↺\";\nexport const LABEL_REDO = \"↻\";\nexport const LABEL_LINK = \"🔗\";\nexport const LABEL_UNORDERED_LIST = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"3\" cy=\"4\" r=\"1\" fill=\"currentColor\" />\n <rect x=\"6\" y=\"3\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n <circle cx=\"3\" cy=\"8\" r=\"1\" fill=\"currentColor\" />\n <rect x=\"6\" y=\"7\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n <circle cx=\"3\" cy=\"12\" r=\"1\" fill=\"currentColor\" />\n <rect x=\"6\" y=\"11\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n </svg>\n`;\nexport const LABEL_ORDERED_LIST = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <text x=\"1\" y=\"4\" font-size=\"4\" fill=\"currentColor\">1.</text>\n <rect x=\"6\" y=\"3\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n <text x=\"1\" y=\"8\" font-size=\"4\" fill=\"currentColor\">2.</text>\n <rect x=\"6\" y=\"7\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n <text x=\"1\" y=\"12\" font-size=\"4\" fill=\"currentColor\">3.</text>\n <rect x=\"6\" y=\"11\" width=\"9\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n </svg>\n`;\nexport const LABEL_ALIGN_LEFT = `\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n\t\t<rect x=\"1\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t</svg>\n`;\nexport const LABEL_ALIGN_CENTER = `\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n\t\t<rect x=\"3\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"3\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t</svg>\n`;\nexport const LABEL_ALIGN_RIGHT = `\n\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n\t\t<rect x=\"5\" y=\"2\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"6\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"5\" y=\"10\" width=\"10\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t\t<rect x=\"1\" y=\"14\" width=\"14\" height=\"2\" rx=\"0.5\" fill=\"currentColor\" />\n\t</svg>\n`;\n\n// Font and size option lists used by the toolbar\nexport const FONT_OPTIONS: { label: string; value: string }[] = [\n { label: \"Arial\", value: \"Arial\" },\n { label: \"Helvetica\", value: \"Helvetica, Arial, sans-serif\" },\n { label: \"Verdana\", value: \"Verdana, Geneva, sans-serif\" },\n { label: \"Tahoma\", value: \"Tahoma, Geneva, sans-serif\" },\n { label: \"Trebuchet MS\", value: \"Trebuchet MS, Helvetica, sans-serif\" },\n { label: \"Georgia\", value: \"Georgia, serif\" },\n { label: \"Times New Roman\", value: \"Times New Roman, Times, serif\" },\n { label: \"Palatino\", value: \"Palatino, 'Palatino Linotype', serif\" },\n { label: \"Garamond\", value: \"Garamond, serif\" },\n { label: \"Book Antiqua\", value: \"'Book Antiqua', Palatino, serif\" },\n { label: \"Courier New\", value: \"'Courier New', Courier, monospace\" },\n { label: \"Lucida Console\", value: \"'Lucida Console', Monaco, monospace\" },\n { label: \"Impact\", value: \"Impact, Charcoal, sans-serif\" },\n { label: \"Comic Sans MS\", value: \"'Comic Sans MS', 'Comic Sans', cursive\" },\n { label: \"Segoe UI\", value: \"'Segoe UI', Tahoma, Geneva, sans-serif\" },\n {\n label: \"Roboto\",\n value: \"Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif\",\n },\n { label: \"Open Sans\", value: \"'Open Sans', Arial, sans-serif\" },\n { label: \"Lato\", value: \"Lato, 'Helvetica Neue', Arial, sans-serif\" },\n { label: \"Montserrat\", value: \"Montserrat, Arial, sans-serif\" },\n { label: \"Source Sans Pro\", value: \"'Source Sans Pro', Arial, sans-serif\" },\n { label: \"Fira Sans\", value: \"'Fira Sans', Arial, sans-serif\" },\n { label: \"Ubuntu\", value: \"Ubuntu, Arial, sans-serif\" },\n { label: \"Noto Sans\", value: \"'Noto Sans', Arial, sans-serif\" },\n { label: \"Droid Sans\", value: \"'Droid Sans', Arial, sans-serif\" },\n {\n label: \"Helvetica Neue\",\n value: \"'Helvetica Neue', Helvetica, Arial, sans-serif\",\n },\n {\n label: \"System UI\",\n value:\n \"system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif\",\n },\n];\n\nexport const SIZE_OPTIONS: { label: string; value: string }[] = [\n { label: \"8\", value: \"8\" },\n { label: \"9\", value: \"9\" },\n { label: \"10\", value: \"10\" },\n { label: \"11\", value: \"11\" },\n { label: \"12\", value: \"12\" },\n { label: \"14\", value: \"14\" },\n { label: \"16\", value: \"16\" },\n { label: \"18\", value: \"18\" },\n { label: \"20\", value: \"20\" },\n { label: \"22\", value: \"22\" },\n { label: \"24\", value: \"24\" },\n { label: \"26\", value: \"26\" },\n { label: \"28\", value: \"28\" },\n { label: \"36\", value: \"36\" },\n { label: \"48\", value: \"48\" },\n { label: \"72\", value: \"72\" },\n];\n\n// Block format options (Paragraph + Headings)\nexport const FORMAT_OPTIONS: { label: string; value: string }[] = [\n { label: \"Heading 1\", value: \"h1\" },\n { label: \"Heading 2\", value: \"h2\" },\n { label: \"Heading 3\", value: \"h3\" },\n { label: \"Heading 4\", value: \"h4\" },\n { label: \"Heading 5\", value: \"h5\" },\n { label: \"Heading 6\", value: \"h6\" },\n];\n\n// Clear formatting label\nexport const LABEL_CLEAR_FORMAT = \"🧹 Clear\";\n\n// Maximum allowed file size for inline image uploads (bytes)\nexport const MAX_FILE_SIZE = 1024 * 1024; // 1MB\n","import createDOMPurify from \"dompurify\";\n\n/**\n * Sanitize HTML using DOMPurify.\n *\n * Works in browser and in jsdom (tests) by accepting either a `Window`\n * or a `Document` (from which `defaultView` is used).\n */\nexport function sanitizeHtml(\n html: string,\n ctx?: Window | Document | null,\n): string {\n if (!html) return \"\";\n // Try to get a Window reference\n let win: Window | null = null;\n if (ctx && (ctx as Document).defaultView) {\n win = (ctx as Document).defaultView as Window;\n } else if (ctx && (ctx as Window).document) {\n win = ctx as Window;\n } else if (typeof window !== \"undefined\") {\n win = window as Window;\n }\n\n // If we have a window, use DOMPurify\n if (win) {\n try {\n const DOMPurify = createDOMPurify(win as any);\n // Preserve element ids and data-* attributes so restoring snapshots\n // during undo/redo does not break page scripts or CSS that rely on\n // those attributes. Still strip <script> and all inline event\n // handlers (on*) to avoid executing arbitrary script.\n // Use ADD_ATTR to allow `id`, and a hook to preserve any `data-` attrs.\n try {\n DOMPurify.addHook(\"uponSanitizeAttribute\", (node: any, data: any) => {\n try {\n if (data && data.attrName && data.attrName.startsWith(\"data-\")) {\n // Keep data-* attributes\n (data as any).keepAttr = true;\n }\n } catch (e) {\n /* ignore hook errors */\n }\n });\n } catch (e) {\n /* addHook may not be available in some environments; ignore */\n }\n\n return DOMPurify.sanitize(html, {\n // Use sensible defaults: allow common formatting tags but strip scripts\n ALLOWED_TAGS: [\n \"a\",\n \"b\",\n \"i\",\n \"em\",\n \"strong\",\n \"u\",\n \"p\",\n \"div\",\n \"span\",\n // Common semantic elements: preserve document structure so undo/redo\n // does not flatten header/section/nav into plain content.\n \"header\",\n \"nav\",\n \"section\",\n \"main\",\n \"footer\",\n \"article\",\n \"aside\",\n \"figure\",\n \"figcaption\",\n \"time\",\n // Interactive / form elements that may appear in content\n \"button\",\n \"input\",\n \"label\",\n \"select\",\n \"option\",\n \"textarea\",\n \"details\",\n \"summary\",\n // Allow <style> tags so user/content-provided CSS is preserved\n // when taking snapshots and during undo/redo operations.\n // DOMPurify will still sanitize the contents of style blocks.\n \"style\",\n // Preserve linked stylesheets so page/editor styling isn't lost\n \"link\",\n \"ul\",\n \"ol\",\n \"li\",\n \"br\",\n \"hr\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"img\",\n ],\n ALLOWED_ATTR: [\n \"href\",\n \"title\",\n \"alt\",\n \"src\",\n \"class\",\n \"style\",\n // Attributes used by <link> tags\n \"rel\",\n \"type\",\n \"media\",\n ],\n // Also allow `id` attributes so element ids survive sanitization.\n ADD_ATTR: [\"id\"],\n });\n } catch (e) {\n // If DOMPurify initialization fails, fall through to minimal stripping\n }\n }\n\n // Minimal fallback: remove <script> tags and on* attributes\n return html\n .replace(/<script[\\s\\S]*?>[\\s\\S]*?<\\/script>/gi, \"\")\n .replace(/on[a-z]+=\\\"[^\"]*\\\"/gi, \"\");\n}\n\nexport default sanitizeHtml;\n","import { editorEventEmitter } from \"./events\";\nimport {\n DEFAULT_MAX_STACK,\n TOOLBAR_ID,\n STYLE_ID,\n CLASS_EDITABLE,\n CLASS_ACTIVE,\n} from \"./constants\";\nimport { sanitizeHtml } from \"../utils/sanitize\";\n\nlet _doc: Document | null = null;\nlet _undoStack: string[] = [];\nlet _redoStack: string[] = [];\nlet _currentEditable: HTMLElement | null = null;\nlet _savedRange: Range | null = null;\nlet _maxStackSize = DEFAULT_MAX_STACK;\n\nexport function _setDoc(doc: Document | null) {\n _doc = doc;\n}\nexport function _getDoc() {\n return _doc;\n}\nexport function _setUndoStack(stack: string[]) {\n _undoStack = stack;\n editorEventEmitter.emit({\n type: \"undoStateChanged\",\n timestamp: Date.now(),\n data: { canUndo: _undoStack.length > 1 },\n });\n}\nexport function _getUndoStack() {\n return _undoStack;\n}\nexport function _setRedoStack(stack: string[]) {\n _redoStack = stack;\n editorEventEmitter.emit({\n type: \"redoStateChanged\",\n timestamp: Date.now(),\n data: { canRedo: _redoStack.length > 0 },\n });\n}\nexport function _getRedoStack() {\n return _redoStack;\n}\nexport function _setCurrentEditable(el: HTMLElement | null) {\n _currentEditable = el;\n editorEventEmitter.emit({\n type: \"selectionChanged\",\n timestamp: Date.now(),\n data: { element: el?.tagName },\n });\n}\nexport function _getCurrentEditable() {\n return _currentEditable;\n}\n\n// Selection save/restore helpers used by toolbar interactions\nexport function _saveSelection() {\n try {\n if (!_doc) return;\n const sel = _doc.getSelection();\n if (!sel) return;\n if (!sel.rangeCount) return;\n _savedRange = sel.getRangeAt(0).cloneRange();\n } catch (e) {\n /* ignore */\n }\n}\n\nexport function _restoreSelection() {\n try {\n if (!_doc) return;\n const sel = _doc.getSelection();\n if (!sel) return;\n if (_savedRange) {\n sel.removeAllRanges();\n sel.addRange(_savedRange);\n _savedRange = null;\n }\n } catch (e) {\n /* ignore */\n }\n}\nexport function pushStandaloneSnapshot(clearRedo = true) {\n if (!_doc) return;\n // Clone the documentElement and remove injected UI (toolbar/style)\n // so snapshots capture only the user's content.\n const clone = _doc.documentElement.cloneNode(true) as HTMLElement;\n const toolbarNode = clone.querySelector(`#${TOOLBAR_ID}`);\n if (toolbarNode && toolbarNode.parentNode)\n toolbarNode.parentNode.removeChild(toolbarNode);\n const styleNode = clone.querySelector(`#${STYLE_ID}`);\n if (styleNode && styleNode.parentNode)\n styleNode.parentNode.removeChild(styleNode);\n // Remove editor-specific attributes/classes so snapshots don't persist\n // transient editing state (contenteditable, toolbar classes, tabindex).\n try {\n const editableNodes = clone.querySelectorAll(\n \"[contenteditable], .\" + CLASS_EDITABLE + \", .\" + CLASS_ACTIVE,\n );\n editableNodes.forEach((el) => {\n try {\n if (el instanceof Element) {\n if (el.hasAttribute(\"contenteditable\"))\n el.removeAttribute(\"contenteditable\");\n if (el.hasAttribute(\"tabindex\")) el.removeAttribute(\"tabindex\");\n el.classList.remove(CLASS_EDITABLE, CLASS_ACTIVE);\n }\n } catch (e) {\n /* ignore */\n }\n });\n } catch (e) {\n /* ignore */\n }\n // Preserve scripts by replacing them with a harmless placeholder\n // that contains the encoded script source/attributes. This allows the\n // sanitizer to run (which strips <script> tags) while keeping the\n // script content available to re-insert and execute on restore.\n try {\n const scripts = Array.from(\n clone.querySelectorAll<HTMLScriptElement>(\"script\"),\n );\n scripts.forEach((s) => {\n try {\n const code = s.textContent || \"\";\n const attrs: Record<string, string> = {};\n Array.from(s.attributes).forEach((a) => (attrs[a.name] = a.value));\n const placeholder = clone.ownerDocument!.createElement(\"span\");\n // encode script body in base64 to survive sanitization\n try {\n // btoa may throw on Unicode; encodeURIComponent first\n const safe =\n typeof btoa !== \"undefined\"\n ? btoa(unescape(encodeURIComponent(code)))\n : encodeURIComponent(code);\n placeholder.setAttribute(\"data-rhe-script\", safe);\n } catch (e) {\n placeholder.setAttribute(\"data-rhe-script\", encodeURIComponent(code));\n }\n if (Object.keys(attrs).length) {\n placeholder.setAttribute(\n \"data-rhe-script-attrs\",\n encodeURIComponent(JSON.stringify(attrs)),\n );\n }\n // mark parent editable region if present so we can reinsert in-place\n const parentMarker = s.closest(\"[data-rhe-id]\") as HTMLElement | null;\n if (parentMarker && parentMarker.getAttribute(\"data-rhe-id\")) {\n placeholder.setAttribute(\n \"data-rhe-script-parent\",\n parentMarker.getAttribute(\"data-rhe-id\")!,\n );\n } else {\n placeholder.setAttribute(\"data-rhe-script-parent\", \"head\");\n }\n s.parentNode?.replaceChild(placeholder, s);\n } catch (e) {\n /* ignore per-script errors */\n }\n });\n } catch (e) {\n /* ignore script extraction errors */\n }\n const snapRaw = clone.outerHTML;\n const snap = sanitizeHtml(snapRaw, _doc);\n if (!_undoStack.length || _undoStack[_undoStack.length - 1] !== snap) {\n _undoStack.push(snap);\n if (_undoStack.length > _maxStackSize) _undoStack.shift();\n editorEventEmitter.emit({\n type: \"contentChanged\",\n timestamp: Date.now(),\n data: { htmlLength: snap.length },\n });\n }\n if (clearRedo) {\n _redoStack = [];\n editorEventEmitter.emit({\n type: \"redoStateChanged\",\n timestamp: Date.now(),\n data: { canRedo: false },\n });\n }\n}\nexport function setMaxStackSize(size: number) {\n _maxStackSize = Math.max(1, size);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.global.js
CHANGED
|
@@ -1551,6 +1551,40 @@ var RichHtmlEditor = (() => {
|
|
|
1551
1551
|
const styleId = STYLE_ID;
|
|
1552
1552
|
let styleEl = doc.getElementById(styleId);
|
|
1553
1553
|
const css = `
|
|
1554
|
+
/* Scoped conservative reset for editor UI root to prevent template styles leaking in */
|
|
1555
|
+
#rhe-editor-root {
|
|
1556
|
+
box-sizing: border-box;
|
|
1557
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
1558
|
+
font-size: 14px;
|
|
1559
|
+
line-height: 1.5;
|
|
1560
|
+
color: #0f172a;
|
|
1561
|
+
-webkit-font-smoothing: antialiased;
|
|
1562
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1563
|
+
}
|
|
1564
|
+
#rhe-editor-root *,
|
|
1565
|
+
#rhe-editor-root *::before,
|
|
1566
|
+
#rhe-editor-root *::after {
|
|
1567
|
+
box-sizing: inherit;
|
|
1568
|
+
}
|
|
1569
|
+
/* Restore user-agent defaults for native controls inside the root */
|
|
1570
|
+
#rhe-editor-root button,
|
|
1571
|
+
#rhe-editor-root input,
|
|
1572
|
+
#rhe-editor-root textarea,
|
|
1573
|
+
#rhe-editor-root select {
|
|
1574
|
+
all: revert;
|
|
1575
|
+
font: inherit;
|
|
1576
|
+
color: inherit;
|
|
1577
|
+
background: transparent;
|
|
1578
|
+
border: none;
|
|
1579
|
+
padding: 0;
|
|
1580
|
+
margin: 0;
|
|
1581
|
+
}
|
|
1582
|
+
/* Basic focus visibility for accessibility inside root */
|
|
1583
|
+
#rhe-editor-root :focus {
|
|
1584
|
+
outline: 2px solid Highlight;
|
|
1585
|
+
outline-offset: 2px;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1554
1588
|
.${CLASS_EDITABLE}{outline:2px dashed ${HOVER_OUTLINE};cursor:text}
|
|
1555
1589
|
.${CLASS_ACTIVE}{outline:2px solid ${ACTIVE_OUTLINE};cursor:text}
|
|
1556
1590
|
#${TOOLBAR_ID} img{cursor:auto}
|
|
@@ -1860,6 +1894,28 @@ var RichHtmlEditor = (() => {
|
|
|
1860
1894
|
// src/toolbar/render.ts
|
|
1861
1895
|
init_constants();
|
|
1862
1896
|
|
|
1897
|
+
// src/dom/root.ts
|
|
1898
|
+
var EDITOR_ROOT_ID = "rhe-editor-root";
|
|
1899
|
+
function getEditorRoot(doc) {
|
|
1900
|
+
let root = doc.getElementById(EDITOR_ROOT_ID);
|
|
1901
|
+
if (root) return root;
|
|
1902
|
+
root = doc.createElement("div");
|
|
1903
|
+
root.id = EDITOR_ROOT_ID;
|
|
1904
|
+
root.setAttribute("data-rhe-root", "true");
|
|
1905
|
+
try {
|
|
1906
|
+
if (doc.body && doc.body.firstChild)
|
|
1907
|
+
doc.body.insertBefore(root, doc.body.firstChild);
|
|
1908
|
+
else if (doc.body) doc.body.appendChild(root);
|
|
1909
|
+
else doc.documentElement.appendChild(root);
|
|
1910
|
+
} catch (e) {
|
|
1911
|
+
try {
|
|
1912
|
+
doc.documentElement.appendChild(root);
|
|
1913
|
+
} catch (err) {
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return root;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1863
1919
|
// src/toolbar/color.ts
|
|
1864
1920
|
function makeColorInput(doc, options, title, command, initialColor) {
|
|
1865
1921
|
const input = doc.createElement("input");
|
|
@@ -2004,7 +2060,11 @@ var RichHtmlEditor = (() => {
|
|
|
2004
2060
|
)
|
|
2005
2061
|
);
|
|
2006
2062
|
overflowMenu.appendChild(
|
|
2007
|
-
helpers.makeColorInput(
|
|
2063
|
+
helpers.makeColorInput(
|
|
2064
|
+
"Text color",
|
|
2065
|
+
"foreColor",
|
|
2066
|
+
format.foreColor
|
|
2067
|
+
)
|
|
2008
2068
|
);
|
|
2009
2069
|
overflowMenu.appendChild(
|
|
2010
2070
|
helpers.makeColorInput(
|
|
@@ -2089,7 +2149,9 @@ var RichHtmlEditor = (() => {
|
|
|
2089
2149
|
function setupNavigation(toolbar) {
|
|
2090
2150
|
toolbar.addEventListener("keydown", (e) => {
|
|
2091
2151
|
const focusable = Array.from(
|
|
2092
|
-
toolbar.querySelectorAll(
|
|
2152
|
+
toolbar.querySelectorAll(
|
|
2153
|
+
"button, select, input, [tabindex]"
|
|
2154
|
+
)
|
|
2093
2155
|
).filter((el) => !el.hasAttribute("disabled"));
|
|
2094
2156
|
if (!focusable.length) return;
|
|
2095
2157
|
const idx = focusable.indexOf(document.activeElement);
|
|
@@ -2264,7 +2326,12 @@ var RichHtmlEditor = (() => {
|
|
|
2264
2326
|
makeGroup: makeGroup2
|
|
2265
2327
|
});
|
|
2266
2328
|
setupNavigation(toolbar);
|
|
2267
|
-
|
|
2329
|
+
try {
|
|
2330
|
+
const root = getEditorRoot(doc);
|
|
2331
|
+
root.insertBefore(toolbar, root.firstChild);
|
|
2332
|
+
} catch (e) {
|
|
2333
|
+
doc.body.insertBefore(toolbar, doc.body.firstChild);
|
|
2334
|
+
}
|
|
2268
2335
|
}
|
|
2269
2336
|
|
|
2270
2337
|
// src/dom/handlers.ts
|
|
@@ -2317,6 +2384,8 @@ var RichHtmlEditor = (() => {
|
|
|
2317
2384
|
const fileInput = doc.createElement("input");
|
|
2318
2385
|
fileInput.type = "file";
|
|
2319
2386
|
fileInput.accept = "image/*";
|
|
2387
|
+
fileInput.style.width = "100%";
|
|
2388
|
+
fileInput.style.boxSizing = "border-box";
|
|
2320
2389
|
uploadPane.appendChild(fileInput);
|
|
2321
2390
|
const uploadMsg = doc.createElement("div");
|
|
2322
2391
|
uploadMsg.className = "rhe-img-msg";
|
|
@@ -2327,6 +2396,8 @@ var RichHtmlEditor = (() => {
|
|
|
2327
2396
|
const urlInput = doc.createElement("input");
|
|
2328
2397
|
urlInput.type = "url";
|
|
2329
2398
|
urlInput.placeholder = "https://example.com/image.jpg";
|
|
2399
|
+
urlInput.style.width = "100%";
|
|
2400
|
+
urlInput.style.boxSizing = "border-box";
|
|
2330
2401
|
urlPane.appendChild(urlInput);
|
|
2331
2402
|
const urlMsg = doc.createElement("div");
|
|
2332
2403
|
urlMsg.className = "rhe-img-msg";
|
|
@@ -2461,7 +2532,12 @@ var RichHtmlEditor = (() => {
|
|
|
2461
2532
|
overlay.addEventListener("keydown", (e) => {
|
|
2462
2533
|
if (e.key === "Escape") close();
|
|
2463
2534
|
});
|
|
2464
|
-
|
|
2535
|
+
try {
|
|
2536
|
+
const root = getEditorRoot(doc);
|
|
2537
|
+
root.appendChild(overlay);
|
|
2538
|
+
} catch (e) {
|
|
2539
|
+
doc.body.appendChild(overlay);
|
|
2540
|
+
}
|
|
2465
2541
|
fileInput.focus();
|
|
2466
2542
|
}
|
|
2467
2543
|
|