@editora/core 1.0.1 → 1.0.2
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 +9 -0
- package/dist/A11yCheckerPlugin.native-CZKpi3uF.mjs +475 -0
- package/dist/A11yCheckerPlugin.native-CZKpi3uF.mjs.map +1 -0
- package/dist/AnchorPlugin.native-7es9PVZ9.mjs +340 -0
- package/dist/AnchorPlugin.native-7es9PVZ9.mjs.map +1 -0
- package/dist/BackgroundColorPlugin.native-Dip5uqTg.mjs +449 -0
- package/dist/BackgroundColorPlugin.native-Dip5uqTg.mjs.map +1 -0
- package/dist/BlockquotePlugin.native-JFmOLsxN.mjs +48 -0
- package/dist/BlockquotePlugin.native-JFmOLsxN.mjs.map +1 -0
- package/dist/BoldPlugin.native-BAzzoqU5.mjs +45 -0
- package/dist/BoldPlugin.native-BAzzoqU5.mjs.map +1 -0
- package/dist/CapitalizationPlugin.native-DOMsh5R7.mjs +79 -0
- package/dist/CapitalizationPlugin.native-DOMsh5R7.mjs.map +1 -0
- package/dist/ChecklistPlugin.native-Dccs3nLe.mjs +153 -0
- package/dist/ChecklistPlugin.native-Dccs3nLe.mjs.map +1 -0
- package/dist/ClearFormattingPlugin.native-BZPDHswo.mjs +27 -0
- package/dist/ClearFormattingPlugin.native-BZPDHswo.mjs.map +1 -0
- package/dist/CodePlugin.native-DD9xFIid.mjs +1679 -0
- package/dist/CodePlugin.native-DD9xFIid.mjs.map +1 -0
- package/dist/CodeSamplePlugin.native-DMbEdO9j.mjs +326 -0
- package/dist/CodeSamplePlugin.native-DMbEdO9j.mjs.map +1 -0
- package/dist/CommentsPlugin.native-2zQV8Ia4.mjs +473 -0
- package/dist/CommentsPlugin.native-2zQV8Ia4.mjs.map +1 -0
- package/dist/DirectionPlugin.native-Be7wCzkI.mjs +59 -0
- package/dist/DirectionPlugin.native-Be7wCzkI.mjs.map +1 -0
- package/dist/DocumentManagerPlugin.native-BvZL5CSG.mjs +116 -0
- package/dist/DocumentManagerPlugin.native-BvZL5CSG.mjs.map +1 -0
- package/dist/EmbedIframePlugin.native-ifr9KLdN.mjs +461 -0
- package/dist/EmbedIframePlugin.native-ifr9KLdN.mjs.map +1 -0
- package/dist/EmojisPlugin.native-D6mJSnSR.mjs +1033 -0
- package/dist/EmojisPlugin.native-D6mJSnSR.mjs.map +1 -0
- package/dist/FontFamilyPlugin.native-BzS_9qbM.mjs +106 -0
- package/dist/FontFamilyPlugin.native-BzS_9qbM.mjs.map +1 -0
- package/dist/FontSizePlugin.native-DkLMLPue.mjs +186 -0
- package/dist/FontSizePlugin.native-DkLMLPue.mjs.map +1 -0
- package/dist/FootnotePlugin.native-BciVc9W6.mjs +128 -0
- package/dist/FootnotePlugin.native-BciVc9W6.mjs.map +1 -0
- package/dist/FullscreenPlugin.native-ChXyxeNw.mjs +77 -0
- package/dist/FullscreenPlugin.native-ChXyxeNw.mjs.map +1 -0
- package/dist/HeadingPlugin.native-DrLYwQnQ.mjs +64 -0
- package/dist/HeadingPlugin.native-DrLYwQnQ.mjs.map +1 -0
- package/dist/HistoryPlugin.native-DoDRifCf.mjs +89 -0
- package/dist/HistoryPlugin.native-DoDRifCf.mjs.map +1 -0
- package/dist/IndentPlugin.native-CbFugPoi.mjs +133 -0
- package/dist/IndentPlugin.native-CbFugPoi.mjs.map +1 -0
- package/dist/ItalicPlugin.native-CQjjDyUL.mjs +43 -0
- package/dist/ItalicPlugin.native-CQjjDyUL.mjs.map +1 -0
- package/dist/LineHeightPlugin.native-CWQT2FIa.mjs +73 -0
- package/dist/LineHeightPlugin.native-CWQT2FIa.mjs.map +1 -0
- package/dist/LinkPlugin.native-BdAOV-iu.mjs +206 -0
- package/dist/LinkPlugin.native-BdAOV-iu.mjs.map +1 -0
- package/dist/ListPlugin.native-CLFU5AUQ.mjs +59 -0
- package/dist/ListPlugin.native-CLFU5AUQ.mjs.map +1 -0
- package/dist/MathPlugin.native-DE_ii-LA.mjs +182 -0
- package/dist/MathPlugin.native-DE_ii-LA.mjs.map +1 -0
- package/dist/MediaManagerPlugin.native-DaYFDzNM.mjs +533 -0
- package/dist/MediaManagerPlugin.native-DaYFDzNM.mjs.map +1 -0
- package/dist/MergeTagPlugin.native-CrxyThyn.mjs +178 -0
- package/dist/MergeTagPlugin.native-CrxyThyn.mjs.map +1 -0
- package/dist/PageBreakPlugin.native-DDjcDyRW.mjs +172 -0
- package/dist/PageBreakPlugin.native-DDjcDyRW.mjs.map +1 -0
- package/dist/PreviewPlugin.native-DBvfpmIv.mjs +322 -0
- package/dist/PreviewPlugin.native-DBvfpmIv.mjs.map +1 -0
- package/dist/PrintPlugin.native-BUpm52VJ.mjs +311 -0
- package/dist/PrintPlugin.native-BUpm52VJ.mjs.map +1 -0
- package/dist/SpecialCharactersPlugin.native-x7a2SWXc.mjs +731 -0
- package/dist/SpecialCharactersPlugin.native-x7a2SWXc.mjs.map +1 -0
- package/dist/SpellCheckPlugin.native-B7yTh0iE.mjs +465 -0
- package/dist/SpellCheckPlugin.native-B7yTh0iE.mjs.map +1 -0
- package/dist/StrikethroughPlugin.native-ChaZLaXw.mjs +43 -0
- package/dist/StrikethroughPlugin.native-ChaZLaXw.mjs.map +1 -0
- package/dist/TablePlugin.native-EEWXn1-s.mjs +491 -0
- package/dist/TablePlugin.native-EEWXn1-s.mjs.map +1 -0
- package/dist/TemplatePlugin.native-BlSn1c9h.mjs +564 -0
- package/dist/TemplatePlugin.native-BlSn1c9h.mjs.map +1 -0
- package/dist/TextAlignmentPlugin.native-CQIs1m7R.mjs +97 -0
- package/dist/TextAlignmentPlugin.native-CQIs1m7R.mjs.map +1 -0
- package/dist/TextColorPlugin.native-D6SmTglm.mjs +432 -0
- package/dist/TextColorPlugin.native-D6SmTglm.mjs.map +1 -0
- package/dist/UnderlinePlugin.native-QpIcK4L2.mjs +35 -0
- package/dist/UnderlinePlugin.native-QpIcK4L2.mjs.map +1 -0
- package/dist/documentManager-irzj9n3V.mjs +37627 -0
- package/dist/documentManager-irzj9n3V.mjs.map +1 -0
- package/dist/editorContainerHelpers-C7kdWnS0.mjs +27 -0
- package/dist/editorContainerHelpers-C7kdWnS0.mjs.map +1 -0
- package/dist/editora.min.js +14 -12
- package/dist/editora.min.js.map +1 -1
- package/dist/editora.umd.js +14 -12
- package/dist/editora.umd.js.map +1 -1
- package/dist/index-BF5RBhL9.js +4 -0
- package/dist/index-BF5RBhL9.js.map +1 -0
- package/dist/{index-BS4zT-KN.mjs → index-BPsf460l.mjs} +286 -162
- package/dist/index-BPsf460l.mjs.map +1 -0
- package/dist/index.cjs.js +3 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es-CuicffkQ.mjs +6665 -0
- package/dist/index.es-CuicffkQ.mjs.map +1 -0
- package/dist/index.esm.js +117 -112
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin-loader.js +55 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/purify.es-CKpwg8Tk.mjs +471 -0
- package/dist/purify.es-CKpwg8Tk.mjs.map +1 -0
- package/dist/webcomponent-core.js +1243 -0
- package/dist/webcomponent-core.js.map +1 -0
- package/dist/webcomponent-core.min.css +1 -0
- package/dist/webcomponent-core.min.js +597 -0
- package/dist/webcomponent-core.min.js.map +1 -0
- package/dist/webcomponent.cjs.js +1 -1
- package/dist/webcomponent.esm.js +3 -3
- package/dist/webcomponent.js +1286 -0
- package/dist/webcomponent.js.map +1 -0
- package/dist/webcomponent.min.css +1 -0
- package/dist/webcomponent.min.js +337 -334
- package/dist/webcomponent.min.js.map +1 -1
- package/package.json +16 -4
- package/dist/index-BK2lHfHK.js +0 -2
- package/dist/index-BK2lHfHK.js.map +0 -1
- package/dist/index-BS4zT-KN.mjs.map +0 -1
- package/dist/webcomponent.umd.js +0 -4073
- package/dist/webcomponent.umd.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaManagerPlugin.native-DaYFDzNM.mjs","sources":["../../plugins/media-manager/src/MediaManagerPlugin.native.ts"],"sourcesContent":["import { Plugin } from '@editora/core';\n\n/**\n * Media Manager Plugin - Native Implementation\n * \n * Features:\n * - Insert images and videos with full dialog UI\n * - Drag & drop file upload\n * - URL input with dimensions\n * - Inline resize handles (drag corners to resize)\n * - Floating toolbar on image selection\n * - Link images\n */\n\nlet savedSelection: Range | null = null;\nlet selectedMedia: HTMLImageElement | HTMLVideoElement | null = null;\nlet floatingToolbar: HTMLDivElement | null = null;\nlet resizeHandles: HTMLDivElement[] = [];\nlet isResizing = false;\nlet currentHandle: string | null = null;\nlet startX = 0;\nlet startY = 0;\nlet startWidth = 0;\nlet startHeight = 0;\nlet aspectRatio = 1;\nlet editorElement: HTMLElement | null = null; // Editor instance context\n\ndeclare global {\n interface Window {\n __mediaManagerInitialized?: boolean;\n }\n}\n\nconst showMediaDialog = (type: 'image' | 'video') => {\n const selection = window.getSelection();\n if (selection && selection.rangeCount > 0) {\n savedSelection = selection.getRangeAt(0).cloneRange();\n }\n\n const overlay = document.createElement('div');\n overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 99999;';\n\n const dialog = document.createElement('div');\n dialog.style.cssText = 'background: white; border-radius: 8px; width: 90%; max-width: 600px; max-height: 90vh; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);';\n\n let activeTab: 'upload' | 'url' = 'upload';\n let urlValue = '';\n let widthValue = '';\n let heightValue = '';\n let altValue = '';\n\n const renderDialog = () => {\n dialog.innerHTML = `\n <div style=\"display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid #e1e5e9; background: #f8f9fa;\">\n <h2 style=\"margin: 0; font-size: 18px; font-weight: 600;\">Insert ${type === 'image' ? 'Image' : 'Video'}</h2>\n <button class=\"close-btn\" style=\"background: none; border: none; font-size: 28px; cursor: pointer; color: #6c757d; padding: 0; width: 30px; height: 30px; line-height: 1;\">×</button>\n </div>\n\n <div class=\"tabs\" style=\"display: flex; border-bottom: 1px solid #e1e5e9;\">\n <button class=\"tab-upload\" style=\"flex: 1; padding: 12px; border: none; background: ${activeTab === 'upload' ? '#007bff' : '#f8f9fa'}; color: ${activeTab === 'upload' ? 'white' : '#495057'}; cursor: pointer; font-weight: 500;\">📤 Upload</button>\n <button class=\"tab-url\" style=\"flex: 1; padding: 12px; border: none; background: ${activeTab === 'url' ? '#007bff' : '#f8f9fa'}; color: ${activeTab === 'url' ? 'white' : '#495057'}; cursor: pointer; font-weight: 500;\">🔗 URL</button>\n </div>\n\n <div style=\"padding: 20px; overflow-y: auto; flex: 1;\">\n ${activeTab === 'upload' ? `\n <div id=\"upload-section\">\n <div class=\"dropzone\" style=\"border: 2px dashed #ced4da; border-radius: 8px; padding: 40px; text-align: center; cursor: pointer; transition: all 0.3s;\">\n <div style=\"font-size: 48px; margin-bottom: 10px;\">📁</div>\n <p style=\"margin: 0 0 8px 0; font-weight: 600; font-size: 16px;\">Drag and drop your ${type} here</p>\n <p style=\"margin: 0 0 8px 0; color: #6c757d; font-size: 14px;\">or click to browse</p>\n <p style=\"margin: 0; color: #6c757d; font-size: 12px;\">Max file size: 50MB</p>\n </div>\n <input type=\"file\" id=\"file-input\" accept=\"${type === 'image' ? 'image/*' : 'video/*'}\" style=\"display: none;\">\n <div id=\"upload-progress\" style=\"display: none; margin-top: 20px;\">\n <div style=\"background: #e9ecef; border-radius: 4px; height: 8px; overflow: hidden;\">\n <div id=\"progress-bar\" style=\"background: #007bff; height: 100%; width: 0%; transition: width 0.3s;\"></div>\n </div>\n <p id=\"progress-text\" style=\"margin-top: 8px; text-align: center; color: #6c757d; font-size: 14px;\">Uploading...</p>\n </div>\n </div>\n ` : `\n <div id=\"url-section\">\n <div style=\"margin-bottom: 20px;\">\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">URL:</label>\n <input type=\"text\" id=\"url-input\" placeholder=\"https://example.com/${type}.${type === 'image' ? 'jpg' : 'mp4'}\" value=\"${urlValue}\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\">\n </div>\n ${type === 'image' ? `\n <div style=\"margin-bottom: 20px;\">\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">Alt Text (for accessibility):</label>\n <input type=\"text\" id=\"alt-input\" placeholder=\"Describe the image\" value=\"${altValue}\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\">\n </div>\n ` : ''}\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px;\">\n <div>\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">Width (px):</label>\n <input type=\"number\" id=\"width-input\" placeholder=\"Auto\" value=\"${widthValue}\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\">\n </div>\n <div>\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">Height (px):</label>\n <input type=\"number\" id=\"height-input\" placeholder=\"Auto\" value=\"${heightValue}\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\">\n </div>\n </div>\n ${urlValue ? `\n <div style=\"margin-bottom: 20px;\">\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">Preview:</label>\n <div style=\"border: 1px solid #dee2e6; border-radius: 4px; padding: 10px; background: #f8f9fa; text-align: center;\">\n ${type === 'image' ? `<img src=\"${urlValue}\" alt=\"Preview\" style=\"max-width: 100%; max-height: 200px;\">` : `<video src=\"${urlValue}\" controls style=\"max-width: 100%; max-height: 200px;\"></video>`}\n </div>\n </div>\n ` : ''}\n </div>\n `}\n </div>\n\n <div style=\"display: flex; justify-content: flex-end; gap: 10px; padding: 16px 20px; border-top: 1px solid #e1e5e9; background: #f8f9fa;\">\n <button class=\"cancel-btn\" style=\"padding: 10px 20px; background: #fff; border: 1px solid #ced4da; border-radius: 4px; cursor: pointer; font-size: 14px;\">Cancel</button>\n <button id=\"insert-btn\" style=\"padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\" ${!urlValue && activeTab === 'url' ? 'disabled' : ''}>Insert</button>\n </div>\n `;\n };\n\n renderDialog();\n overlay.appendChild(dialog);\n document.body.appendChild(overlay);\n\n const closeDialog = () => document.body.removeChild(overlay);\n\n const insertMedia = () => {\n if (!urlValue) return;\n\n const mediaEl = type === 'image' ? document.createElement('img') : document.createElement('video');\n mediaEl.src = urlValue;\n mediaEl.setAttribute('data-media-type', type);\n \n if (type === 'image' && altValue) {\n (mediaEl as HTMLImageElement).alt = altValue;\n }\n if (widthValue) {\n mediaEl.style.width = `${widthValue}px`;\n mediaEl.setAttribute('width', widthValue);\n }\n if (heightValue) {\n mediaEl.style.height = `${heightValue}px`;\n mediaEl.setAttribute('height', heightValue);\n }\n \n if (type === 'video') {\n (mediaEl as HTMLVideoElement).controls = true;\n }\n\n if (!widthValue && !heightValue) {\n mediaEl.style.cssText = 'max-width: 100%; height: auto; display: block; margin: 1em 0; cursor: pointer;';\n } else {\n mediaEl.style.cssText = `display: block; margin: 1em 0; cursor: pointer; ${widthValue ? `width: ${widthValue}px;` : 'max-width: 100%;'} ${heightValue ? `height: ${heightValue}px;` : 'height: auto;'}`;\n }\n \n if (savedSelection) {\n savedSelection.deleteContents();\n savedSelection.insertNode(mediaEl);\n }\n\n closeDialog();\n };\n\n const handleFileUpload = async (file: File) => {\n const progressDiv = dialog.querySelector('#upload-progress') as HTMLDivElement;\n const progressBar = dialog.querySelector('#progress-bar') as HTMLDivElement;\n const progressText = dialog.querySelector('#progress-text') as HTMLParagraphElement;\n\n if (progressDiv && progressBar && progressText) {\n progressDiv.style.display = 'block';\n\n let progress = 0;\n const interval = setInterval(() => {\n progress += Math.random() * 30;\n if (progress > 90) progress = 90;\n progressBar.style.width = `${progress}%`;\n }, 200);\n\n try {\n const reader = new FileReader();\n reader.onload = () => {\n clearInterval(interval);\n progressBar.style.width = '100%';\n progressText.textContent = '✅ Upload complete';\n \n setTimeout(() => {\n urlValue = reader.result as string;\n activeTab = 'url';\n renderDialog();\n attachEventHandlers();\n }, 500);\n };\n reader.readAsDataURL(file);\n } catch (error) {\n clearInterval(interval);\n progressText.textContent = '❌ Upload failed';\n }\n }\n };\n\n const attachEventHandlers = () => {\n const closeBtn = dialog.querySelector('.close-btn') as HTMLButtonElement;\n const cancelBtn = dialog.querySelector('.cancel-btn') as HTMLButtonElement;\n const insertBtn = dialog.querySelector('#insert-btn') as HTMLButtonElement;\n const tabUpload = dialog.querySelector('.tab-upload') as HTMLButtonElement;\n const tabUrl = dialog.querySelector('.tab-url') as HTMLButtonElement;\n\n closeBtn?.addEventListener('click', closeDialog);\n cancelBtn?.addEventListener('click', closeDialog);\n insertBtn?.addEventListener('click', insertMedia);\n\n tabUpload?.addEventListener('click', () => {\n activeTab = 'upload';\n renderDialog();\n attachEventHandlers();\n });\n\n tabUrl?.addEventListener('click', () => {\n activeTab = 'url';\n renderDialog();\n attachEventHandlers();\n });\n\n if (activeTab === 'upload') {\n const dropzone = dialog.querySelector('.dropzone') as HTMLDivElement;\n const fileInput = dialog.querySelector('#file-input') as HTMLInputElement;\n\n dropzone?.addEventListener('click', () => fileInput?.click());\n \n dropzone?.addEventListener('dragover', (e) => {\n e.preventDefault();\n dropzone.style.borderColor = '#007bff';\n dropzone.style.background = '#f0f8ff';\n });\n\n dropzone?.addEventListener('dragleave', () => {\n dropzone.style.borderColor = '#ced4da';\n dropzone.style.background = 'transparent';\n });\n\n dropzone?.addEventListener('drop', (e) => {\n e.preventDefault();\n dropzone.style.borderColor = '#ced4da';\n dropzone.style.background = 'transparent';\n const file = e.dataTransfer?.files[0];\n if (file) handleFileUpload(file);\n });\n\n fileInput?.addEventListener('change', (e) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (file) handleFileUpload(file);\n });\n }\n\n if (activeTab === 'url') {\n const urlInput = dialog.querySelector('#url-input') as HTMLInputElement;\n const altInput = dialog.querySelector('#alt-input') as HTMLInputElement;\n const widthInput = dialog.querySelector('#width-input') as HTMLInputElement;\n const heightInput = dialog.querySelector('#height-input') as HTMLInputElement;\n\n urlInput?.addEventListener('input', () => {\n urlValue = urlInput.value;\n renderDialog();\n attachEventHandlers();\n });\n\n altInput?.addEventListener('input', () => {\n altValue = altInput.value;\n });\n\n widthInput?.addEventListener('input', () => {\n widthValue = widthInput.value;\n });\n\n heightInput?.addEventListener('input', () => {\n heightValue = heightInput.value;\n });\n }\n };\n\n attachEventHandlers();\n overlay.addEventListener('click', (e) => { if (e.target === overlay) closeDialog(); });\n};\n\n// Create resize handles\nconst createResizeHandles = () => {\n const positions = ['nw', 'ne', 'sw', 'se'];\n positions.forEach(pos => {\n const handle = document.createElement('div');\n handle.className = `media-resize-handle-${pos}`;\n handle.style.cssText = `\n position: fixed;\n width: 10px;\n height: 10px;\n background: #007bff;\n border: 2px solid white;\n border-radius: 50%;\n cursor: ${pos}-resize;\n z-index: 10001;\n display: none;\n box-shadow: 0 1px 3px rgba(0,0,0,0.3);\n `;\n handle.setAttribute('data-position', pos);\n document.body.appendChild(handle);\n resizeHandles.push(handle);\n });\n};\n\n// Update resize handle positions\nconst updateResizeHandles = () => {\n if (!selectedMedia) {\n resizeHandles.forEach(h => h.style.display = 'none');\n return;\n }\n\n const rect = selectedMedia.getBoundingClientRect();\n const positions = {\n nw: { x: rect.left - 5, y: rect.top - 5 },\n ne: { x: rect.right - 5, y: rect.top - 5 },\n sw: { x: rect.left - 5, y: rect.bottom - 5 },\n se: { x: rect.right - 5, y: rect.bottom - 5 }\n };\n\n resizeHandles.forEach(handle => {\n const pos = handle.getAttribute('data-position') as keyof typeof positions;\n const coord = positions[pos];\n handle.style.left = `${coord.x}px`;\n handle.style.top = `${coord.y}px`;\n handle.style.display = 'block';\n });\n};\n\n// Show Alt Text dialog\nconst showAltTextDialog = (img: HTMLImageElement) => {\n const overlay = document.createElement('div');\n overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 10001;';\n\n const dialog = document.createElement('div');\n dialog.style.cssText = 'background: white; border-radius: 8px; width: 90%; max-width: 500px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);';\n\n dialog.innerHTML = `\n <div style=\"display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid #e1e5e9; background: #f8f9fa;\">\n <h2 style=\"margin: 0; font-size: 18px; font-weight: 600;\">Edit Alt Text</h2>\n <button class=\"close-btn\" style=\"background: none; border: none; font-size: 28px; cursor: pointer; color: #6c757d; padding: 0; width: 30px; height: 30px; line-height: 1;\">×</button>\n </div>\n <div style=\"padding: 20px;\">\n <label style=\"display: block; margin-bottom: 8px; font-weight: 500;\">Alternative Text (for accessibility):</label>\n <textarea id=\"alt-text-input\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px; min-height: 80px; font-family: inherit; resize: vertical;\" placeholder=\"Describe the image for screen readers...\">${img.alt || ''}</textarea>\n <p style=\"margin-top: 8px; margin-bottom: 0; font-size: 12px; color: #6c757d;\">Good alt text is descriptive and concise. It helps users with visual impairments understand your content.</p>\n </div>\n <div style=\"display: flex; justify-content: flex-end; gap: 10px; padding: 16px 20px; border-top: 1px solid #e1e5e9; background: #f8f9fa;\">\n <button class=\"cancel-btn\" style=\"padding: 10px 20px; background: #fff; border: 1px solid #ced4da; border-radius: 4px; cursor: pointer; font-size: 14px;\">Cancel</button>\n <button class=\"save-btn\" style=\"padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\">Save</button>\n </div>\n `;\n\n overlay.appendChild(dialog);\n document.body.appendChild(overlay);\n\n const input = dialog.querySelector('#alt-text-input') as HTMLTextAreaElement;\n const closeBtn = dialog.querySelector('.close-btn') as HTMLButtonElement;\n const cancelBtn = dialog.querySelector('.cancel-btn') as HTMLButtonElement;\n const saveBtn = dialog.querySelector('.save-btn') as HTMLButtonElement;\n\n const closeDialog = () => document.body.removeChild(overlay);\n\n closeBtn.addEventListener('click', closeDialog);\n cancelBtn.addEventListener('click', closeDialog);\n overlay.addEventListener('click', (e) => { if (e.target === overlay) closeDialog(); });\n\n saveBtn.addEventListener('click', () => {\n img.alt = input.value;\n closeDialog();\n });\n\n input.focus();\n input.select();\n};\n\n// Show Link dialog for media\nconst showLinkDialogForMedia = (media: HTMLImageElement | HTMLVideoElement) => {\n const existingLink = (media as HTMLElement).closest('a');\n const currentHref = existingLink?.getAttribute('href') || '';\n const currentTarget = existingLink?.getAttribute('target') || '_self';\n const currentTitle = existingLink?.getAttribute('title') || '';\n\n const overlay = document.createElement('div');\n overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 10001;';\n\n const dialog = document.createElement('div');\n dialog.style.cssText = 'background: white; border-radius: 8px; width: 90%; max-width: 500px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);';\n\n dialog.innerHTML = `\n <div style=\"display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid #e1e5e9; background: #f8f9fa;\">\n <h2 style=\"margin: 0; font-size: 18px; font-weight: 600;\">${existingLink ? 'Edit Link' : 'Add Link'}</h2>\n <button class=\"close-btn\" style=\"background: none; border: none; font-size: 28px; cursor: pointer; color: #6c757d; padding: 0; width: 30px; height: 30px; line-height: 1;\">×</button>\n </div>\n <div style=\"padding: 20px;\">\n <div style=\"margin-bottom: 16px;\">\n <label style=\"display: block; margin-bottom: 6px; font-weight: 500;\">URL:</label>\n <input id=\"link-url\" type=\"url\" value=\"${currentHref}\" placeholder=\"https://example.com\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\" />\n </div>\n <div style=\"margin-bottom: 16px;\">\n <label style=\"display: block; margin-bottom: 6px; font-weight: 500;\">Title (tooltip):</label>\n <input id=\"link-title\" type=\"text\" value=\"${currentTitle}\" placeholder=\"Optional tooltip text\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\" />\n </div>\n <div>\n <label style=\"display: flex; align-items: center; cursor: pointer;\">\n <input id=\"link-target\" type=\"checkbox\" ${currentTarget === '_blank' ? 'checked' : ''} style=\"margin-right: 8px;\" />\n Open in new window/tab\n </label>\n </div>\n </div>\n <div style=\"display: flex; justify-content: space-between; padding: 16px 20px; border-top: 1px solid #e1e5e9; background: #f8f9fa;\">\n ${existingLink ? '<button class=\"remove-link-btn\" style=\"padding: 10px 20px; background: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\">Remove Link</button>' : '<span></span>'}\n <div style=\"display: flex; gap: 10px;\">\n <button class=\"cancel-btn\" style=\"padding: 10px 20px; background: #fff; border: 1px solid #ced4da; border-radius: 4px; cursor: pointer; font-size: 14px;\">Cancel</button>\n <button class=\"save-btn\" style=\"padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\">Save</button>\n </div>\n </div>\n `;\n\n overlay.appendChild(dialog);\n document.body.appendChild(overlay);\n\n const urlInput = dialog.querySelector('#link-url') as HTMLInputElement;\n const titleInput = dialog.querySelector('#link-title') as HTMLInputElement;\n const targetCheckbox = dialog.querySelector('#link-target') as HTMLInputElement;\n const closeBtn = dialog.querySelector('.close-btn') as HTMLButtonElement;\n const cancelBtn = dialog.querySelector('.cancel-btn') as HTMLButtonElement;\n const saveBtn = dialog.querySelector('.save-btn') as HTMLButtonElement;\n const removeLinkBtn = dialog.querySelector('.remove-link-btn') as HTMLButtonElement;\n\n const closeDialog = () => document.body.removeChild(overlay);\n\n closeBtn.addEventListener('click', closeDialog);\n cancelBtn.addEventListener('click', closeDialog);\n overlay.addEventListener('click', (e) => { if (e.target === overlay) closeDialog(); });\n\n saveBtn.addEventListener('click', () => {\n const url = urlInput.value.trim();\n if (url) {\n const normalized = url.startsWith('http') ? url : `https://${url}`;\n \n if (existingLink) {\n existingLink.setAttribute('href', normalized);\n existingLink.setAttribute('target', targetCheckbox.checked ? '_blank' : '_self');\n if (targetCheckbox.checked) {\n existingLink.setAttribute('rel', 'noopener noreferrer');\n } else {\n existingLink.removeAttribute('rel');\n }\n if (titleInput.value.trim()) {\n existingLink.setAttribute('title', titleInput.value.trim());\n } else {\n existingLink.removeAttribute('title');\n }\n } else {\n const link = document.createElement('a');\n link.href = normalized;\n link.target = targetCheckbox.checked ? '_blank' : '_self';\n if (targetCheckbox.checked) {\n link.rel = 'noopener noreferrer';\n }\n if (titleInput.value.trim()) {\n link.title = titleInput.value.trim();\n }\n media.replaceWith(link);\n link.appendChild(media);\n }\n \n closeDialog();\n \n // Update toolbar to reflect link state\n if (floatingToolbar && selectedMedia) {\n showFloatingToolbar(selectedMedia);\n }\n }\n });\n\n removeLinkBtn?.addEventListener('click', () => {\n if (existingLink && confirm('Remove link from this media?')) {\n existingLink.replaceWith(media);\n closeDialog();\n \n // Update toolbar\n if (floatingToolbar && selectedMedia) {\n showFloatingToolbar(selectedMedia);\n }\n }\n });\n\n urlInput.focus();\n};\n\n// Show Replace Image dialog\nconst showReplaceImageDialog = (img: HTMLImageElement) => {\n const overlay = document.createElement('div');\n overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; z-index: 10001;';\n\n const dialog = document.createElement('div');\n dialog.style.cssText = 'background: white; border-radius: 8px; width: 90%; max-width: 600px; max-height: 90vh; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);';\n\n let activeTab: 'upload' | 'url' = 'url';\n let urlValue = img.src;\n\n const renderDialog = () => {\n dialog.innerHTML = `\n <div style=\"display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid #e1e5e9; background: #f8f9fa;\">\n <h2 style=\"margin: 0; font-size: 18px; font-weight: 600;\">Replace Image</h2>\n <button class=\"close-btn\" style=\"background: none; border: none; font-size: 28px; cursor: pointer; color: #6c757d; padding: 0; width: 30px; height: 30px; line-height: 1;\">×</button>\n </div>\n\n <div class=\"tabs\" style=\"display: flex; border-bottom: 1px solid #e1e5e9;\">\n <button class=\"tab-upload\" style=\"flex: 1; padding: 12px; border: none; background: ${activeTab === 'upload' ? '#007bff' : '#f8f9fa'}; color: ${activeTab === 'upload' ? 'white' : '#495057'}; cursor: pointer; font-weight: 500;\">📤 Upload</button>\n <button class=\"tab-url\" style=\"flex: 1; padding: 12px; border: none; background: ${activeTab === 'url' ? '#007bff' : '#f8f9fa'}; color: ${activeTab === 'url' ? 'white' : '#495057'}; cursor: pointer; font-weight: 500;\">🔗 URL</button>\n </div>\n\n <div style=\"padding: 20px; overflow-y: auto; flex: 1;\">\n ${activeTab === 'upload' ? `\n <div id=\"upload-section\">\n <div class=\"dropzone\" style=\"border: 2px dashed #ced4da; border-radius: 8px; padding: 40px; text-align: center; cursor: pointer; transition: all 0.3s;\">\n <div style=\"font-size: 48px; margin-bottom: 10px;\">📁</div>\n <p style=\"margin: 0 0 8px 0; font-weight: 600; font-size: 16px;\">Drag and drop your image here</p>\n <p style=\"margin: 0 0 8px 0; color: #6c757d; font-size: 14px;\">or click to browse</p>\n </div>\n <input type=\"file\" id=\"file-input\" accept=\"image/*\" style=\"display: none;\">\n <div id=\"upload-progress\" style=\"display: none; margin-top: 20px;\">\n <div style=\"background: #e9ecef; border-radius: 4px; height: 8px; overflow: hidden;\">\n <div id=\"progress-bar\" style=\"background: #007bff; height: 100%; width: 0%; transition: width 0.3s;\"></div>\n </div>\n <p id=\"progress-text\" style=\"margin-top: 8px; text-align: center; color: #6c757d; font-size: 14px;\">Uploading...</p>\n </div>\n </div>\n ` : `\n <div id=\"url-section\">\n <div style=\"margin-bottom: 20px;\">\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">Image URL:</label>\n <input type=\"text\" id=\"url-input\" placeholder=\"https://example.com/image.jpg\" value=\"${urlValue}\" style=\"width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #ced4da; border-radius: 4px; font-size: 14px;\">\n </div>\n ${urlValue ? `\n <div style=\"margin-bottom: 20px;\">\n <label style=\"display: block; font-weight: 600; margin-bottom: 8px; font-size: 14px;\">Preview:</label>\n <div style=\"border: 1px solid #dee2e6; border-radius: 4px; padding: 10px; background: #f8f9fa; text-align: center;\">\n <img src=\"${urlValue}\" alt=\"Preview\" style=\"max-width: 100%; max-height: 300px;\" onerror=\"this.parentElement.innerHTML='<p style="color: #dc3545;">Failed to load image</p>'\">\n </div>\n </div>\n ` : ''}\n </div>\n `}\n </div>\n\n <div style=\"display: flex; justify-content: flex-end; gap: 10px; padding: 16px 20px; border-top: 1px solid #e1e5e9; background: #f8f9fa;\">\n <button class=\"cancel-btn\" style=\"padding: 10px 20px; background: #fff; border: 1px solid #ced4da; border-radius: 4px; cursor: pointer; font-size: 14px;\">Cancel</button>\n <button id=\"replace-btn\" style=\"padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;\" ${!urlValue && activeTab === 'url' ? 'disabled' : ''}>Replace</button>\n </div>\n `;\n };\n\n renderDialog();\n overlay.appendChild(dialog);\n document.body.appendChild(overlay);\n\n const closeDialog = () => document.body.removeChild(overlay);\n\n const replaceImage = () => {\n if (urlValue) {\n img.src = urlValue;\n closeDialog();\n }\n };\n\n const handleFileUpload = async (file: File) => {\n const progressDiv = dialog.querySelector('#upload-progress') as HTMLDivElement;\n const progressBar = dialog.querySelector('#progress-bar') as HTMLDivElement;\n const progressText = dialog.querySelector('#progress-text') as HTMLParagraphElement;\n\n if (progressDiv && progressBar && progressText) {\n progressDiv.style.display = 'block';\n\n let progress = 0;\n const interval = setInterval(() => {\n progress += Math.random() * 30;\n if (progress > 90) progress = 90;\n progressBar.style.width = `${progress}%`;\n }, 200);\n\n try {\n const reader = new FileReader();\n reader.onload = () => {\n clearInterval(interval);\n progressBar.style.width = '100%';\n progressText.textContent = '✅ Upload complete';\n \n setTimeout(() => {\n urlValue = reader.result as string;\n activeTab = 'url';\n renderDialog();\n attachEventHandlers();\n }, 500);\n };\n reader.readAsDataURL(file);\n } catch (error) {\n clearInterval(interval);\n progressText.textContent = '❌ Upload failed';\n }\n }\n };\n\n const attachEventHandlers = () => {\n const closeBtn = dialog.querySelector('.close-btn') as HTMLButtonElement;\n const cancelBtn = dialog.querySelector('.cancel-btn') as HTMLButtonElement;\n const replaceBtn = dialog.querySelector('#replace-btn') as HTMLButtonElement;\n const tabUpload = dialog.querySelector('.tab-upload') as HTMLButtonElement;\n const tabUrl = dialog.querySelector('.tab-url') as HTMLButtonElement;\n\n closeBtn?.addEventListener('click', closeDialog);\n cancelBtn?.addEventListener('click', closeDialog);\n replaceBtn?.addEventListener('click', replaceImage);\n\n tabUpload?.addEventListener('click', () => {\n activeTab = 'upload';\n renderDialog();\n attachEventHandlers();\n });\n\n tabUrl?.addEventListener('click', () => {\n activeTab = 'url';\n renderDialog();\n attachEventHandlers();\n });\n\n if (activeTab === 'upload') {\n const dropzone = dialog.querySelector('.dropzone') as HTMLDivElement;\n const fileInput = dialog.querySelector('#file-input') as HTMLInputElement;\n\n dropzone?.addEventListener('click', () => fileInput?.click());\n \n dropzone?.addEventListener('dragover', (e) => {\n e.preventDefault();\n dropzone.style.borderColor = '#007bff';\n dropzone.style.background = '#f0f8ff';\n });\n\n dropzone?.addEventListener('dragleave', () => {\n dropzone.style.borderColor = '#ced4da';\n dropzone.style.background = 'transparent';\n });\n\n dropzone?.addEventListener('drop', (e) => {\n e.preventDefault();\n dropzone.style.borderColor = '#ced4da';\n dropzone.style.background = 'transparent';\n const file = e.dataTransfer?.files[0];\n if (file) handleFileUpload(file);\n });\n\n fileInput?.addEventListener('change', (e) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (file) handleFileUpload(file);\n });\n }\n\n if (activeTab === 'url') {\n const urlInput = dialog.querySelector('#url-input') as HTMLInputElement;\n\n urlInput?.addEventListener('input', () => {\n urlValue = urlInput.value;\n renderDialog();\n attachEventHandlers();\n });\n }\n };\n\n attachEventHandlers();\n overlay.addEventListener('click', (e) => { if (e.target === overlay) closeDialog(); });\n};\n\nconst updatePosition = () => {\n if (!floatingToolbar || !selectedMedia) return;\n\n const toolbarHeight = floatingToolbar.offsetHeight || 40;\n const mediaTop = selectedMedia.offsetTop;\n const mediaLeft = selectedMedia.offsetLeft;\n const mediaWidth = selectedMedia.offsetWidth;\n\n // Position above the image, centered\n const top = mediaTop - toolbarHeight - 8;\n const left = mediaLeft + (mediaWidth / 2) - ((floatingToolbar.offsetWidth || 120) / 2);\n\n floatingToolbar.style.top = `${top}px`;\n floatingToolbar.style.left = `${left}px`;\n setTimeout(() => { \n if (floatingToolbar) floatingToolbar.style.display = \"flex\";\n },100);\n};\n\n// Show floating toolbar\nconst showFloatingToolbar = (media: HTMLImageElement | HTMLVideoElement) => {\n // Clean up existing toolbar\n if (floatingToolbar) {\n if ((floatingToolbar as any)._cleanup) {\n (floatingToolbar as any)._cleanup();\n }\n floatingToolbar.remove();\n }\n\n // Ensure parent has relative positioning\n const mediaParent = media.parentElement;\n if (mediaParent) {\n const originalPosition = mediaParent.style.position;\n if (!originalPosition || originalPosition === 'static') {\n mediaParent.style.position = 'relative';\n (mediaParent as any)._originalPosition = originalPosition; // Store for cleanup\n }\n\n floatingToolbar = document.createElement('div');\n floatingToolbar.className = 'media-floating-toolbar';\n floatingToolbar.style.cssText = `\n position: absolute;\n background: white;\n border: 1px solid #ced4da;\n border-radius: 6px;\n box-shadow: 0 2px 12px rgba(0,0,0,0.15);\n gap: 2px;\n padding: 4px;\n z-index: 10000;\n pointer-events: auto;\n display: none;\n `;\n\n // Insert toolbar as first child of parent\n mediaParent.insertBefore(floatingToolbar, mediaParent.firstChild);\n\n // Position the toolbar\n updatePosition();\n }\n\n const buttonStyle = 'padding: 6px 8px; border: none; background: white; cursor: pointer; border-radius: 4px; transition: background 0.2s; display: flex; align-items: center; justify-content: center;';\n \n const isImage = media.tagName === 'IMG';\n const existingLink = (media as HTMLElement).closest('a');\n\n floatingToolbar.innerHTML = `\n <button class=\"btn-align-left\" title=\"Align Left\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"17\" y1=\"10\" x2=\"3\" y2=\"10\"></line><line x1=\"21\" y1=\"6\" x2=\"3\" y2=\"6\"></line><line x1=\"21\" y1=\"14\" x2=\"3\" y2=\"14\"></line><line x1=\"17\" y1=\"18\" x2=\"3\" y2=\"18\"></line></svg>\n </button>\n <button class=\"btn-align-center\" title=\"Align Center\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"18\" y1=\"10\" x2=\"6\" y2=\"10\"></line><line x1=\"21\" y1=\"6\" x2=\"3\" y2=\"6\"></line><line x1=\"21\" y1=\"14\" x2=\"3\" y2=\"14\"></line><line x1=\"18\" y1=\"18\" x2=\"6\" y2=\"18\"></line></svg>\n </button>\n <button class=\"btn-align-right\" title=\"Align Right\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"21\" y1=\"10\" x2=\"7\" y2=\"10\"></line><line x1=\"21\" y1=\"6\" x2=\"3\" y2=\"6\"></line><line x1=\"21\" y1=\"14\" x2=\"3\" y2=\"14\"></line><line x1=\"21\" y1=\"18\" x2=\"7\" y2=\"18\"></line></svg>\n </button>\n <div style=\"width: 1px; height: 20px; background: #dee2e6; margin: 0 2px;\"></div>\n ${isImage ? `\n <button class=\"btn-alt\" title=\"Edit Alt Text\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"></path></svg>\n </button>` : ''}\n <button class=\"btn-link\" title=\"${existingLink ? 'Edit/Remove Link' : 'Add Link'}\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"></path><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"></path></svg>\n </button>\n ${isImage ? `\n <button class=\"btn-replace\" title=\"Replace Image\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"></rect><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"></circle><polyline points=\"21 15 16 10 5 21\"></polyline></svg>\n </button>` : ''}\n <div style=\"width: 1px; height: 20px; background: #dee2e6; margin: 0 2px;\"></div>\n <button class=\"btn-remove\" title=\"Remove\" style=\"${buttonStyle}\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path></svg>\n </button>\n `;\n\n\n\n // Position the toolbar after it's rendered\n setTimeout(() => {\n updatePosition();\n }, 0);\n\n // Add event listeners for repositioning\n const handleReposition = () => updatePosition();\n \n // Listen for scroll events on parent elements and window\n let currentElement: Element | null = selectedMedia.parentElement;\n while (currentElement) {\n currentElement.addEventListener('scroll', handleReposition);\n currentElement = currentElement.parentElement;\n }\n \n window.addEventListener('scroll', handleReposition);\n window.addEventListener('resize', handleReposition);\n \n // Store cleanup function\n (floatingToolbar as any)._cleanup = () => {\n let element: Element | null = selectedMedia?.parentElement;\n while (element) {\n element.removeEventListener('scroll', handleReposition);\n element = element.parentElement;\n }\n window.removeEventListener('scroll', handleReposition);\n window.removeEventListener('resize', handleReposition);\n };\n\n floatingToolbar.querySelectorAll('button').forEach(btn => {\n btn.addEventListener('mouseenter', () => {\n (btn as HTMLElement).style.background = '#f8f9fa';\n });\n btn.addEventListener('mouseleave', () => {\n (btn as HTMLElement).style.background = 'white';\n });\n });\n\n // Align Left\n floatingToolbar.querySelector('.btn-align-left')?.addEventListener('click', () => {\n media.style.display = 'block';\n media.style.marginLeft = '0';\n media.style.marginRight = 'auto';\n updatePosition();\n });\n\n // Align Center\n floatingToolbar.querySelector('.btn-align-center')?.addEventListener('click', () => {\n media.style.display = 'block';\n media.style.marginLeft = 'auto';\n media.style.marginRight = 'auto';\n updatePosition();\n });\n\n // Align Right\n floatingToolbar.querySelector('.btn-align-right')?.addEventListener('click', () => {\n media.style.display = 'block';\n media.style.marginLeft = 'auto';\n media.style.marginRight = '0';\n updatePosition();\n });\n\n // Edit Alt Text (Images only)\n floatingToolbar.querySelector('.btn-alt')?.addEventListener('click', () => {\n if (media.tagName === 'IMG') {\n showAltTextDialog(media as HTMLImageElement);\n }\n });\n\n // Add/Edit Link\n floatingToolbar.querySelector('.btn-link')?.addEventListener('click', () => {\n showLinkDialogForMedia(media);\n });\n\n // Replace Image\n floatingToolbar.querySelector('.btn-replace')?.addEventListener('click', () => {\n if (media.tagName === 'IMG') {\n showReplaceImageDialog(media as HTMLImageElement);\n }\n });\n\n floatingToolbar.querySelector('.btn-resize')?.addEventListener('click', () => {\n const width = prompt('Enter width in pixels:', String(media.width || media.offsetWidth));\n if (width && !isNaN(parseInt(width))) {\n const w = parseInt(width);\n media.style.width = `${w}px`;\n media.setAttribute('width', String(w));\n updateResizeHandles();\n updatePosition();\n }\n });\n\n floatingToolbar.querySelector('.btn-remove')?.addEventListener('click', () => {\n if (confirm('Remove this media?')) {\n media.remove();\n if (floatingToolbar) {\n if ((floatingToolbar as any)._cleanup) {\n (floatingToolbar as any)._cleanup();\n }\n floatingToolbar.remove();\n floatingToolbar = null;\n }\n selectedMedia = null;\n updateResizeHandles();\n }\n });\n\n // Add scroll and resize event listeners (matching resize handles approach)\n // Store cleanup function for later removal\n (floatingToolbar as any)._cleanup = () => {\n window.removeEventListener('scroll', updatePosition);\n window.removeEventListener('resize', updatePosition);\n };\n};\n\n// Initialize media manager function (moved outside conditional for plugin access)\nconst initMediaManager = (editorEl?: HTMLElement) => {\n // Store the editor element for scoping\n editorElement = editorEl || null;\n\n createResizeHandles();\n\n // Add click listener - global but scoped to this editor instance\n document.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n\n if (target.tagName === 'IMG' || target.tagName === 'VIDEO') {\n const media = target as HTMLImageElement | HTMLVideoElement;\n\n // Check if media is within this editor's contenteditable area\n let isInThisEditor = false;\n if (editorElement) {\n // For web component, check if media is within this editor element\n isInThisEditor = editorElement.contains(media);\n } else {\n // Fallback to global behavior\n isInThisEditor = !!media.closest('[contenteditable=\"true\"]');\n }\n\n if (isInThisEditor) {\n e.preventDefault();\n e.stopPropagation();\n selectedMedia = media;\n selectedMedia.style.display = 'block'; // Ensure block display for alignment\n showFloatingToolbar(media);\n updateResizeHandles();\n return;\n }\n }\n\n if (!target.closest('.btn-link, .btn-resize, .btn-remove')) {\n if (floatingToolbar && !target.closest('button')) {\n if ((floatingToolbar as any)._cleanup) {\n (floatingToolbar as any)._cleanup();\n }\n floatingToolbar.remove();\n floatingToolbar = null;\n \n // Restore parent position\n if (selectedMedia && selectedMedia.parentElement) {\n const parent = selectedMedia.parentElement;\n if ((parent as any)._originalPosition !== undefined) {\n parent.style.position = (parent as any)._originalPosition;\n delete (parent as any)._originalPosition;\n }\n }\n \n selectedMedia = null;\n updateResizeHandles();\n }\n }\n });\n\n resizeHandles.forEach(handle => {\n handle.addEventListener('mousedown', (e) => {\n if (!selectedMedia) return;\n e.preventDefault();\n e.stopPropagation();\n\n isResizing = true;\n currentHandle = handle.getAttribute('data-position');\n startX = e.clientX;\n startY = e.clientY;\n\n const rect = selectedMedia.getBoundingClientRect();\n startWidth = rect.width;\n startHeight = rect.height;\n aspectRatio = startWidth / startHeight;\n\n document.body.style.userSelect = 'none';\n document.body.style.cursor = `${currentHandle}-resize`;\n });\n });\n\n document.addEventListener('mousemove', (e) => {\n if (!isResizing || !selectedMedia || !currentHandle) return;\n\n const deltaX = e.clientX - startX;\n const deltaY = e.clientY - startY;\n\n let newWidth = startWidth;\n let newHeight = startHeight;\n\n switch (currentHandle) {\n case 'se':\n newWidth = startWidth + deltaX;\n newHeight = startHeight + deltaY;\n break;\n case 'sw':\n newWidth = startWidth - deltaX;\n newHeight = startHeight + deltaY;\n break;\n case 'ne':\n newWidth = startWidth + deltaX;\n newHeight = startHeight - deltaY;\n break;\n case 'nw':\n newWidth = startWidth - deltaX;\n newHeight = startHeight - deltaY;\n break;\n }\n\n if (Math.abs(deltaX) > Math.abs(deltaY)) {\n newHeight = newWidth / aspectRatio;\n } else {\n newWidth = newHeight * aspectRatio;\n }\n\n newWidth = Math.max(50, newWidth);\n newHeight = Math.max(50, newHeight);\n\n selectedMedia.style.width = `${newWidth}px`;\n selectedMedia.style.height = `${newHeight}px`;\n selectedMedia.setAttribute('width', String(Math.round(newWidth)));\n selectedMedia.setAttribute('height', String(Math.round(newHeight)));\n\n updateResizeHandles();\n updatePosition();\n });\n\n document.addEventListener('mouseup', () => {\n if (isResizing) {\n isResizing = false;\n currentHandle = null;\n document.body.style.userSelect = '';\n document.body.style.cursor = '';\n }\n });\n\n window.addEventListener('scroll', updateResizeHandles);\n window.addEventListener('resize', updateResizeHandles);\n};\n\n// Initialize media manager\nif (typeof window !== 'undefined' && !window.__mediaManagerInitialized) {\n window.__mediaManagerInitialized = true;\n\n // Call initMediaManager without parameters for global initialization\n initMediaManager();\n}\n\nexport const MediaManagerPlugin = (): Plugin => ({\n name: 'image',\n \n initialize: (config?: any) => {\n // Get the editor element from config (passed by web component)\n const editorEl = config?.editorElement;\n initMediaManager(editorEl);\n },\n \n toolbar: [\n {\n label: 'Image',\n command: 'insertImage',\n icon: '<svg width=\"24px\" height=\"24px\" viewBox=\"0 0 32 32\" enable-background=\"new 0 0 32 32\"><g><rect fill=\"none\" height=\"22\" stroke=\"#000000\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" stroke-width=\"2\" width=\"30\" x=\"1\" y=\"5\"></rect><polygon fill=\"none\" points=\"31,27 21,17 11,27\" stroke=\"#000000\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" stroke-width=\"2\"></polygon><polygon fill=\"none\" points=\"18,20 9,11 1,19 1,27 11,27\" stroke=\"#000000\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" stroke-width=\"2\"></polygon><circle cx=\"19\" cy=\"11\" fill=\"none\" r=\"2\" stroke=\"#000000\" stroke-linejoin=\"round\" stroke-miterlimit=\"10\" stroke-width=\"2\"></circle></g></svg>'\n },\n {\n label: 'Video',\n command: 'insertVideo',\n icon: '<svg width=\"24\" height=\"24\" focusable=\"false\"><path d=\"M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm4.8 2.6 5.6 4a.5.5 0 0 1 0 .8l-5.6 4A.5.5 0 0 1 9 16V8a.5.5 0 0 1 .8-.4Z\" fill-rule=\"nonzero\"></path></svg>'\n }\n ],\n \n commands: {\n insertImage: () => {\n showMediaDialog('image');\n return true;\n },\n \n insertVideo: () => {\n showMediaDialog('video');\n return true;\n }\n },\n \n keymap: {\n 'Mod-Shift-i': 'insertImage'\n }\n});\n"],"names":["savedSelection","selectedMedia","floatingToolbar","resizeHandles","isResizing","currentHandle","startX","startY","startWidth","startHeight","aspectRatio","editorElement","showMediaDialog","type","selection","overlay","dialog","activeTab","urlValue","widthValue","heightValue","altValue","renderDialog","closeDialog","insertMedia","mediaEl","handleFileUpload","file","progressDiv","progressBar","progressText","progress","interval","reader","attachEventHandlers","error","closeBtn","cancelBtn","insertBtn","tabUpload","tabUrl","dropzone","fileInput","e","_a","urlInput","altInput","widthInput","heightInput","createResizeHandles","pos","handle","updateResizeHandles","h","rect","positions","coord","showAltTextDialog","img","input","saveBtn","showLinkDialogForMedia","media","existingLink","currentHref","currentTarget","currentTitle","titleInput","targetCheckbox","removeLinkBtn","url","normalized","link","showFloatingToolbar","showReplaceImageDialog","replaceImage","replaceBtn","updatePosition","toolbarHeight","mediaTop","mediaLeft","mediaWidth","top","left","_b","_c","_d","_e","_f","_g","_h","mediaParent","originalPosition","buttonStyle","isImage","handleReposition","currentElement","element","btn","width","w","initMediaManager","editorEl","target","isInThisEditor","parent","deltaX","deltaY","newWidth","newHeight","MediaManagerPlugin","config"],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const E = {
|
|
2
|
+
USER: {
|
|
3
|
+
name: "User",
|
|
4
|
+
tags: [
|
|
5
|
+
{ key: "first_name", label: "First Name", category: "User", preview: "John" },
|
|
6
|
+
{ key: "last_name", label: "Last Name", category: "User", preview: "Doe" },
|
|
7
|
+
{ key: "email", label: "Email", category: "User", preview: "john@example.com" },
|
|
8
|
+
{ key: "phone", label: "Phone", category: "User", preview: "+1-555-1234" },
|
|
9
|
+
{ key: "full_name", label: "Full Name", category: "User", preview: "John Doe" },
|
|
10
|
+
{ key: "username", label: "Username", category: "User", preview: "johndoe" }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
COMPANY: {
|
|
14
|
+
name: "Company",
|
|
15
|
+
tags: [
|
|
16
|
+
{ key: "company_name", label: "Company Name", category: "Company", preview: "Acme Corp" },
|
|
17
|
+
{ key: "company_address", label: "Company Address", category: "Company", preview: "123 Main St" },
|
|
18
|
+
{ key: "company_phone", label: "Company Phone", category: "Company", preview: "+1-555-0000" },
|
|
19
|
+
{ key: "company_email", label: "Company Email", category: "Company", preview: "info@acme.com" }
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
DATE: {
|
|
23
|
+
name: "Date",
|
|
24
|
+
tags: [
|
|
25
|
+
{ key: "today", label: "Today", category: "Date", preview: (/* @__PURE__ */ new Date()).toLocaleDateString() },
|
|
26
|
+
{ key: "tomorrow", label: "Tomorrow", category: "Date", preview: new Date(Date.now() + 864e5).toLocaleDateString() },
|
|
27
|
+
{ key: "next_week", label: "Next Week", category: "Date", preview: new Date(Date.now() + 6048e5).toLocaleDateString() }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
CUSTOM: {
|
|
31
|
+
name: "Custom",
|
|
32
|
+
tags: []
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
let L = null, r = null;
|
|
36
|
+
function T(c) {
|
|
37
|
+
const s = L;
|
|
38
|
+
if (!s) return;
|
|
39
|
+
const l = window.getSelection();
|
|
40
|
+
if (!l) return;
|
|
41
|
+
l.removeAllRanges(), l.addRange(s);
|
|
42
|
+
const C = `merge-tag-${c.key}-${Date.now()}`, t = document.createElement("span");
|
|
43
|
+
t.className = "rte-merge-tag", t.id = C, t.setAttribute("contenteditable", "false"), t.setAttribute("data-key", c.key), t.setAttribute("data-category", c.category), t.setAttribute("aria-label", `Merge tag: ${c.label}`), t.textContent = `{{ ${c.label} }}`, t.style.cssText = "background-color: #e3f2fd; border: 1px solid #bbdefb; border-radius: 3px; padding: 2px 6px; margin: 0 2px; display: inline-block; white-space: nowrap; font-weight: 500; color: #1976d2; cursor: pointer; user-select: none;";
|
|
44
|
+
try {
|
|
45
|
+
s.deleteContents(), s.insertNode(t), s.setStartAfter(t), s.setEndAfter(t), l.removeAllRanges(), l.addRange(s);
|
|
46
|
+
const i = t.closest('[contenteditable="true"]');
|
|
47
|
+
i && i.dispatchEvent(new Event("input", { bubbles: !0 }));
|
|
48
|
+
} catch (i) {
|
|
49
|
+
console.error("Failed to insert merge tag:", i);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function R() {
|
|
53
|
+
var A;
|
|
54
|
+
const c = window.getSelection();
|
|
55
|
+
c && c.rangeCount > 0 && (L = c.getRangeAt(0).cloneRange());
|
|
56
|
+
let s = "USER", l = "", C = E.USER.tags;
|
|
57
|
+
r = C[0] || null;
|
|
58
|
+
const t = document.createElement("div");
|
|
59
|
+
t.className = "rte-dialog-overlay", t.style.cssText = "position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 10000;";
|
|
60
|
+
const i = document.createElement("div");
|
|
61
|
+
i.className = "rte-dialog rte-merge-tag-dialog", i.style.cssText = "background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); width: 500px; max-width: 90vw; max-height: 600px; overflow: hidden; display: flex; flex-direction: column;";
|
|
62
|
+
const f = document.createElement("div");
|
|
63
|
+
f.className = "rte-dialog-header", f.style.cssText = "padding: 16px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;", f.innerHTML = `
|
|
64
|
+
<h2 style="margin: 0; font-size: 18px; font-weight: 600;">Insert Merge Tag</h2>
|
|
65
|
+
<button class="rte-dialog-close" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #999; padding: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;" aria-label="Close">✕</button>
|
|
66
|
+
`;
|
|
67
|
+
const y = document.createElement("div");
|
|
68
|
+
y.className = "rte-dialog-body", y.style.cssText = "padding: 16px; overflow-y: auto; flex: 1;";
|
|
69
|
+
const d = document.createElement("input");
|
|
70
|
+
d.type = "text", d.className = "rte-input", d.placeholder = "Search merge tags...", d.setAttribute("aria-label", "Search merge tags"), d.style.cssText = "width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; box-sizing: border-box; margin-bottom: 12px;", y.appendChild(d);
|
|
71
|
+
const v = document.createElement("div");
|
|
72
|
+
v.className = "rte-tabs", v.style.cssText = "display: flex; gap: 8px; margin-bottom: 12px; border-bottom: 2px solid #eee;";
|
|
73
|
+
const N = Object.entries(E).map(([a, e]) => ({ key: a, name: e.name }));
|
|
74
|
+
N.forEach((a) => {
|
|
75
|
+
const e = document.createElement("button");
|
|
76
|
+
e.className = "rte-tab", e.textContent = a.name, e.setAttribute("aria-label", `${a.name} category`), e.style.cssText = "padding: 8px 12px; background: none; border: none; cursor: pointer; border-bottom: 3px solid transparent; color: #666; font-weight: 500; transition: all 0.2s;", a.key === s && (e.classList.add("active"), e.style.color = "#1976d2", e.style.borderBottomColor = "#1976d2"), e.addEventListener("click", () => {
|
|
77
|
+
s = a.key, l = "", d.value = "", M(), k();
|
|
78
|
+
}), e.addEventListener("mouseenter", () => {
|
|
79
|
+
e.style.color = "#333";
|
|
80
|
+
}), e.addEventListener("mouseleave", () => {
|
|
81
|
+
e.classList.contains("active") || (e.style.color = "#666");
|
|
82
|
+
}), v.appendChild(e);
|
|
83
|
+
}), y.appendChild(v);
|
|
84
|
+
const u = document.createElement("div");
|
|
85
|
+
u.className = "rte-merge-tag-list", u.style.cssText = "border: 1px solid #ddd; border-radius: 4px; max-height: 300px; overflow-y: auto; margin-bottom: 12px;", y.appendChild(u);
|
|
86
|
+
const x = document.createElement("div");
|
|
87
|
+
x.className = "rte-merge-tag-preview", x.style.cssText = "padding: 8px; background-color: #f5f5f5; border-radius: 4px; font-family: monospace; font-size: 12px;", y.appendChild(x);
|
|
88
|
+
function M() {
|
|
89
|
+
v.querySelectorAll(".rte-tab").forEach((e, o) => {
|
|
90
|
+
N[o].key === s ? (e.classList.add("active"), e.style.color = "#1976d2", e.style.borderBottomColor = "#1976d2") : (e.classList.remove("active"), e.style.color = "#666", e.style.borderBottomColor = "transparent");
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function k() {
|
|
94
|
+
const a = E[s];
|
|
95
|
+
let e = a ? a.tags : [];
|
|
96
|
+
if (l.trim() && (e = e.filter(
|
|
97
|
+
(o) => o.label.toLowerCase().includes(l.toLowerCase()) || o.key.toLowerCase().includes(l.toLowerCase())
|
|
98
|
+
)), C = e, r = e[0] || null, u.innerHTML = "", e.length === 0) {
|
|
99
|
+
const o = document.createElement("div");
|
|
100
|
+
o.className = "rte-empty-state", o.textContent = "No merge tags found", o.style.cssText = "padding: 24px; text-align: center; color: #999;", u.appendChild(o);
|
|
101
|
+
} else
|
|
102
|
+
e.forEach((o, S) => {
|
|
103
|
+
const n = document.createElement("div");
|
|
104
|
+
n.className = "rte-merge-tag-item", n.setAttribute("data-tag-key", o.key), n.style.cssText = "padding: 8px 12px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background-color 0.2s;", S === 0 && (n.classList.add("selected"), n.style.backgroundColor = "#f5f5f5");
|
|
105
|
+
const w = document.createElement("div");
|
|
106
|
+
if (w.className = "tag-label", w.textContent = o.label, w.style.cssText = "font-weight: 600; color: #333;", n.appendChild(w), o.preview) {
|
|
107
|
+
const g = document.createElement("div");
|
|
108
|
+
g.className = "tag-preview", g.textContent = o.preview, g.style.cssText = "font-size: 12px; color: #999; margin-top: 2px;", n.appendChild(g);
|
|
109
|
+
}
|
|
110
|
+
if (o.description) {
|
|
111
|
+
const g = document.createElement("div");
|
|
112
|
+
g.className = "tag-description", g.textContent = o.description, g.style.cssText = "font-size: 12px; color: #aaa; margin-top: 2px;", n.appendChild(g);
|
|
113
|
+
}
|
|
114
|
+
n.addEventListener("click", () => {
|
|
115
|
+
r = o, U(), D();
|
|
116
|
+
}), n.addEventListener("dblclick", () => {
|
|
117
|
+
T(o), b();
|
|
118
|
+
}), n.addEventListener("mouseenter", () => {
|
|
119
|
+
n.style.backgroundColor = "#f5f5f5";
|
|
120
|
+
}), n.addEventListener("mouseleave", () => {
|
|
121
|
+
n.classList.contains("selected") || (n.style.backgroundColor = "");
|
|
122
|
+
}), u.appendChild(n);
|
|
123
|
+
});
|
|
124
|
+
D();
|
|
125
|
+
}
|
|
126
|
+
function U() {
|
|
127
|
+
u.querySelectorAll(".rte-merge-tag-item").forEach((e) => {
|
|
128
|
+
e.getAttribute("data-tag-key") === (r == null ? void 0 : r.key) ? (e.classList.add("selected"), e.style.backgroundColor = "#f5f5f5") : (e.classList.remove("selected"), e.style.backgroundColor = "");
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function D() {
|
|
132
|
+
r ? (x.innerHTML = `<strong>Preview:</strong> {{ ${r.label} }}`, x.style.display = "block") : x.style.display = "none";
|
|
133
|
+
}
|
|
134
|
+
d.addEventListener("input", (a) => {
|
|
135
|
+
l = a.target.value, k();
|
|
136
|
+
}), d.addEventListener("keydown", (a) => {
|
|
137
|
+
a.key === "Enter" && r ? (T(r), b()) : a.key === "Escape" && b();
|
|
138
|
+
});
|
|
139
|
+
const h = document.createElement("div");
|
|
140
|
+
h.className = "rte-dialog-footer", h.style.cssText = "padding: 12px 16px; border-top: 1px solid #eee; display: flex; gap: 8px; justify-content: flex-end;";
|
|
141
|
+
const p = document.createElement("button");
|
|
142
|
+
p.className = "rte-button-secondary", p.textContent = "Cancel", p.style.cssText = "padding: 8px 16px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; cursor: pointer; background-color: #f5f5f5; color: #333; transition: all 0.2s;", p.addEventListener("click", b), p.addEventListener("mouseenter", () => {
|
|
143
|
+
p.style.backgroundColor = "#eeeeee";
|
|
144
|
+
}), p.addEventListener("mouseleave", () => {
|
|
145
|
+
p.style.backgroundColor = "#f5f5f5";
|
|
146
|
+
});
|
|
147
|
+
const m = document.createElement("button");
|
|
148
|
+
m.className = "rte-button-primary", m.textContent = "Insert", m.style.cssText = "padding: 8px 16px; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; background-color: #1976d2; color: white; transition: all 0.2s;", m.addEventListener("click", () => {
|
|
149
|
+
r && (T(r), b());
|
|
150
|
+
}), m.addEventListener("mouseenter", () => {
|
|
151
|
+
m.style.backgroundColor = "#1565c0";
|
|
152
|
+
}), m.addEventListener("mouseleave", () => {
|
|
153
|
+
m.style.backgroundColor = "#1976d2";
|
|
154
|
+
}), h.appendChild(p), h.appendChild(m), i.appendChild(f), i.appendChild(y), i.appendChild(h), t.appendChild(i);
|
|
155
|
+
function b() {
|
|
156
|
+
t.remove(), L = null, r = null;
|
|
157
|
+
}
|
|
158
|
+
(A = f.querySelector(".rte-dialog-close")) == null || A.addEventListener("click", b), t.addEventListener("click", (a) => {
|
|
159
|
+
a.target === t && b();
|
|
160
|
+
}), document.body.appendChild(t), k(), setTimeout(() => d.focus(), 100);
|
|
161
|
+
}
|
|
162
|
+
const z = () => ({
|
|
163
|
+
name: "mergeTag",
|
|
164
|
+
toolbar: [
|
|
165
|
+
{
|
|
166
|
+
label: "Merge Tag",
|
|
167
|
+
command: "insertMergeTag",
|
|
168
|
+
icon: "{{ }}"
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
commands: {
|
|
172
|
+
insertMergeTag: () => (R(), !0)
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
export {
|
|
176
|
+
z as MergeTagPlugin
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=MergeTagPlugin.native-CrxyThyn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MergeTagPlugin.native-CrxyThyn.mjs","sources":["../../plugins/merge-tag/src/MergeTagPlugin.native.ts"],"sourcesContent":["import type { Plugin } from '@editora/core';\n\n/**\n * MergeTagPlugin - Native implementation\n * \n * Features:\n * - Category tabs (User, Company, Date, Custom)\n * - Search and filter\n * - Tag preview pane\n * - Keyboard navigation (Enter to insert, Escape to close)\n * - Double-click to insert\n * - Accessible dialog\n * - Non-editable inline tags\n * - Styled merge tags matching React version\n */\n\ninterface MergeTag {\n key: string;\n label: string;\n category: string;\n preview?: string;\n description?: string;\n}\n\nconst MERGE_TAG_CATEGORIES = {\n USER: {\n name: 'User',\n tags: [\n { key: 'first_name', label: 'First Name', category: 'User', preview: 'John' },\n { key: 'last_name', label: 'Last Name', category: 'User', preview: 'Doe' },\n { key: 'email', label: 'Email', category: 'User', preview: 'john@example.com' },\n { key: 'phone', label: 'Phone', category: 'User', preview: '+1-555-1234' },\n { key: 'full_name', label: 'Full Name', category: 'User', preview: 'John Doe' },\n { key: 'username', label: 'Username', category: 'User', preview: 'johndoe' }\n ]\n },\n COMPANY: {\n name: 'Company',\n tags: [\n { key: 'company_name', label: 'Company Name', category: 'Company', preview: 'Acme Corp' },\n { key: 'company_address', label: 'Company Address', category: 'Company', preview: '123 Main St' },\n { key: 'company_phone', label: 'Company Phone', category: 'Company', preview: '+1-555-0000' },\n { key: 'company_email', label: 'Company Email', category: 'Company', preview: 'info@acme.com' }\n ]\n },\n DATE: {\n name: 'Date',\n tags: [\n { key: 'today', label: 'Today', category: 'Date', preview: new Date().toLocaleDateString() },\n { key: 'tomorrow', label: 'Tomorrow', category: 'Date', preview: new Date(Date.now() + 86400000).toLocaleDateString() },\n { key: 'next_week', label: 'Next Week', category: 'Date', preview: new Date(Date.now() + 604800000).toLocaleDateString() }\n ]\n },\n CUSTOM: {\n name: 'Custom',\n tags: []\n }\n};\n\nlet savedSelection: Range | null = null;\nlet selectedTag: MergeTag | null = null;\n\nfunction insertMergeTag(tag: MergeTag) {\n const range = savedSelection;\n if (!range) return;\n\n const selection = window.getSelection();\n if (!selection) return;\n\n selection.removeAllRanges();\n selection.addRange(range);\n\n const tagId = `merge-tag-${tag.key}-${Date.now()}`;\n const span = document.createElement('span');\n span.className = 'rte-merge-tag';\n span.id = tagId;\n span.setAttribute('contenteditable', 'false');\n span.setAttribute('data-key', tag.key);\n span.setAttribute('data-category', tag.category);\n span.setAttribute('aria-label', `Merge tag: ${tag.label}`);\n span.textContent = `{{ ${tag.label} }}`;\n span.style.cssText = 'background-color: #e3f2fd; border: 1px solid #bbdefb; border-radius: 3px; padding: 2px 6px; margin: 0 2px; display: inline-block; white-space: nowrap; font-weight: 500; color: #1976d2; cursor: pointer; user-select: none;';\n\n try {\n range.deleteContents();\n range.insertNode(span);\n \n // Move cursor after the tag\n range.setStartAfter(span);\n range.setEndAfter(span);\n selection.removeAllRanges();\n selection.addRange(range);\n\n // Trigger input event\n const editor = span.closest('[contenteditable=\"true\"]');\n if (editor) {\n editor.dispatchEvent(new Event('input', { bubbles: true }));\n }\n } catch (error) {\n console.error('Failed to insert merge tag:', error);\n }\n}\n\nfunction showMergeTagDialog() {\n // Save current selection\n const selection = window.getSelection();\n if (selection && selection.rangeCount > 0) {\n savedSelection = selection.getRangeAt(0).cloneRange();\n }\n\n let currentCategory = 'USER';\n let searchTerm = '';\n let filteredTags: MergeTag[] = MERGE_TAG_CATEGORIES.USER.tags;\n selectedTag = filteredTags[0] || null;\n\n // Create overlay\n const overlay = document.createElement('div');\n overlay.className = 'rte-dialog-overlay';\n overlay.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 10000;';\n\n // Create dialog\n const dialog = document.createElement('div');\n dialog.className = 'rte-dialog rte-merge-tag-dialog';\n dialog.style.cssText = 'background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); width: 500px; max-width: 90vw; max-height: 600px; overflow: hidden; display: flex; flex-direction: column;';\n\n // Header\n const header = document.createElement('div');\n header.className = 'rte-dialog-header';\n header.style.cssText = 'padding: 16px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center;';\n header.innerHTML = `\n <h2 style=\"margin: 0; font-size: 18px; font-weight: 600;\">Insert Merge Tag</h2>\n <button class=\"rte-dialog-close\" style=\"background: none; border: none; font-size: 24px; cursor: pointer; color: #999; padding: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;\" aria-label=\"Close\">✕</button>\n `;\n\n // Body\n const body = document.createElement('div');\n body.className = 'rte-dialog-body';\n body.style.cssText = 'padding: 16px; overflow-y: auto; flex: 1;';\n\n // Search input\n const searchInput = document.createElement('input');\n searchInput.type = 'text';\n searchInput.className = 'rte-input';\n searchInput.placeholder = 'Search merge tags...';\n searchInput.setAttribute('aria-label', 'Search merge tags');\n searchInput.style.cssText = 'width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; box-sizing: border-box; margin-bottom: 12px;';\n body.appendChild(searchInput);\n\n // Category tabs\n const tabsContainer = document.createElement('div');\n tabsContainer.className = 'rte-tabs';\n tabsContainer.style.cssText = 'display: flex; gap: 8px; margin-bottom: 12px; border-bottom: 2px solid #eee;';\n\n const categories = Object.entries(MERGE_TAG_CATEGORIES).map(([key, value]) => ({ key, name: value.name }));\n categories.forEach(cat => {\n const tab = document.createElement('button');\n tab.className = 'rte-tab';\n tab.textContent = cat.name;\n tab.setAttribute('aria-label', `${cat.name} category`);\n tab.style.cssText = 'padding: 8px 12px; background: none; border: none; cursor: pointer; border-bottom: 3px solid transparent; color: #666; font-weight: 500; transition: all 0.2s;';\n \n if (cat.key === currentCategory) {\n tab.classList.add('active');\n tab.style.color = '#1976d2';\n tab.style.borderBottomColor = '#1976d2';\n }\n\n tab.addEventListener('click', () => {\n currentCategory = cat.key;\n searchTerm = '';\n searchInput.value = '';\n updateTabs();\n updateTagList();\n });\n\n tab.addEventListener('mouseenter', () => {\n tab.style.color = '#333';\n });\n\n tab.addEventListener('mouseleave', () => {\n if (!tab.classList.contains('active')) {\n tab.style.color = '#666';\n }\n });\n\n tabsContainer.appendChild(tab);\n });\n body.appendChild(tabsContainer);\n\n // Tag list\n const tagList = document.createElement('div');\n tagList.className = 'rte-merge-tag-list';\n tagList.style.cssText = 'border: 1px solid #ddd; border-radius: 4px; max-height: 300px; overflow-y: auto; margin-bottom: 12px;';\n body.appendChild(tagList);\n\n // Preview pane\n const previewPane = document.createElement('div');\n previewPane.className = 'rte-merge-tag-preview';\n previewPane.style.cssText = 'padding: 8px; background-color: #f5f5f5; border-radius: 4px; font-family: monospace; font-size: 12px;';\n body.appendChild(previewPane);\n\n function updateTabs() {\n const tabs = tabsContainer.querySelectorAll('.rte-tab');\n tabs.forEach((tab, index) => {\n const cat = categories[index];\n if (cat.key === currentCategory) {\n tab.classList.add('active');\n (tab as HTMLElement).style.color = '#1976d2';\n (tab as HTMLElement).style.borderBottomColor = '#1976d2';\n } else {\n tab.classList.remove('active');\n (tab as HTMLElement).style.color = '#666';\n (tab as HTMLElement).style.borderBottomColor = 'transparent';\n }\n });\n }\n\n function updateTagList() {\n const categoryData = MERGE_TAG_CATEGORIES[currentCategory as keyof typeof MERGE_TAG_CATEGORIES];\n let tags = categoryData ? categoryData.tags : [];\n\n // Filter by search\n if (searchTerm.trim()) {\n tags = tags.filter(tag =>\n tag.label.toLowerCase().includes(searchTerm.toLowerCase()) ||\n tag.key.toLowerCase().includes(searchTerm.toLowerCase())\n );\n }\n\n filteredTags = tags;\n selectedTag = tags[0] || null;\n\n // Clear and rebuild tag list\n tagList.innerHTML = '';\n\n if (tags.length === 0) {\n const emptyState = document.createElement('div');\n emptyState.className = 'rte-empty-state';\n emptyState.textContent = 'No merge tags found';\n emptyState.style.cssText = 'padding: 24px; text-align: center; color: #999;';\n tagList.appendChild(emptyState);\n } else {\n tags.forEach((tag, index) => {\n const tagItem = document.createElement('div');\n tagItem.className = 'rte-merge-tag-item';\n tagItem.setAttribute('data-tag-key', tag.key);\n tagItem.style.cssText = 'padding: 8px 12px; border-bottom: 1px solid #f0f0f0; cursor: pointer; transition: background-color 0.2s;';\n\n if (index === 0) {\n tagItem.classList.add('selected');\n tagItem.style.backgroundColor = '#f5f5f5';\n }\n\n const labelDiv = document.createElement('div');\n labelDiv.className = 'tag-label';\n labelDiv.textContent = tag.label;\n labelDiv.style.cssText = 'font-weight: 600; color: #333;';\n tagItem.appendChild(labelDiv);\n\n if (tag.preview) {\n const previewDiv = document.createElement('div');\n previewDiv.className = 'tag-preview';\n previewDiv.textContent = tag.preview;\n previewDiv.style.cssText = 'font-size: 12px; color: #999; margin-top: 2px;';\n tagItem.appendChild(previewDiv);\n }\n\n if (tag.description) {\n const descDiv = document.createElement('div');\n descDiv.className = 'tag-description';\n descDiv.textContent = tag.description;\n descDiv.style.cssText = 'font-size: 12px; color: #aaa; margin-top: 2px;';\n tagItem.appendChild(descDiv);\n }\n\n tagItem.addEventListener('click', () => {\n selectedTag = tag;\n updateTagSelection();\n updatePreview();\n });\n\n tagItem.addEventListener('dblclick', () => {\n insertMergeTag(tag);\n closeDialog();\n });\n\n tagItem.addEventListener('mouseenter', () => {\n tagItem.style.backgroundColor = '#f5f5f5';\n });\n\n tagItem.addEventListener('mouseleave', () => {\n if (!tagItem.classList.contains('selected')) {\n tagItem.style.backgroundColor = '';\n }\n });\n\n tagList.appendChild(tagItem);\n });\n }\n\n updatePreview();\n }\n\n function updateTagSelection() {\n const items = tagList.querySelectorAll('.rte-merge-tag-item');\n items.forEach(item => {\n const key = item.getAttribute('data-tag-key');\n if (key === selectedTag?.key) {\n item.classList.add('selected');\n (item as HTMLElement).style.backgroundColor = '#f5f5f5';\n } else {\n item.classList.remove('selected');\n (item as HTMLElement).style.backgroundColor = '';\n }\n });\n }\n\n function updatePreview() {\n if (selectedTag) {\n previewPane.innerHTML = `<strong>Preview:</strong> {{ ${selectedTag.label} }}`;\n previewPane.style.display = 'block';\n } else {\n previewPane.style.display = 'none';\n }\n }\n\n // Search functionality\n searchInput.addEventListener('input', (e) => {\n searchTerm = (e.target as HTMLInputElement).value;\n updateTagList();\n });\n\n // Keyboard navigation\n searchInput.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && selectedTag) {\n insertMergeTag(selectedTag);\n closeDialog();\n } else if (e.key === 'Escape') {\n closeDialog();\n }\n });\n\n // Footer\n const footer = document.createElement('div');\n footer.className = 'rte-dialog-footer';\n footer.style.cssText = 'padding: 12px 16px; border-top: 1px solid #eee; display: flex; gap: 8px; justify-content: flex-end;';\n\n const cancelBtn = document.createElement('button');\n cancelBtn.className = 'rte-button-secondary';\n cancelBtn.textContent = 'Cancel';\n cancelBtn.style.cssText = 'padding: 8px 16px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; cursor: pointer; background-color: #f5f5f5; color: #333; transition: all 0.2s;';\n cancelBtn.addEventListener('click', closeDialog);\n cancelBtn.addEventListener('mouseenter', () => {\n cancelBtn.style.backgroundColor = '#eeeeee';\n });\n cancelBtn.addEventListener('mouseleave', () => {\n cancelBtn.style.backgroundColor = '#f5f5f5';\n });\n\n const insertBtn = document.createElement('button');\n insertBtn.className = 'rte-button-primary';\n insertBtn.textContent = 'Insert';\n insertBtn.style.cssText = 'padding: 8px 16px; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; background-color: #1976d2; color: white; transition: all 0.2s;';\n insertBtn.addEventListener('click', () => {\n if (selectedTag) {\n insertMergeTag(selectedTag);\n closeDialog();\n }\n });\n insertBtn.addEventListener('mouseenter', () => {\n insertBtn.style.backgroundColor = '#1565c0';\n });\n insertBtn.addEventListener('mouseleave', () => {\n insertBtn.style.backgroundColor = '#1976d2';\n });\n\n footer.appendChild(cancelBtn);\n footer.appendChild(insertBtn);\n\n // Assemble dialog\n dialog.appendChild(header);\n dialog.appendChild(body);\n dialog.appendChild(footer);\n overlay.appendChild(dialog);\n\n // Close handlers\n function closeDialog() {\n overlay.remove();\n savedSelection = null;\n selectedTag = null;\n }\n\n header.querySelector('.rte-dialog-close')?.addEventListener('click', closeDialog);\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) closeDialog();\n });\n\n // Add to document\n document.body.appendChild(overlay);\n\n // Initialize\n updateTagList();\n\n // Focus search\n setTimeout(() => searchInput.focus(), 100);\n}\n\nexport const MergeTagPlugin = (): Plugin => ({\n name: 'mergeTag',\n \n toolbar: [\n {\n label: 'Merge Tag',\n command: 'insertMergeTag',\n icon: '{{ }}',\n }\n ],\n \n commands: {\n insertMergeTag: () => {\n showMergeTagDialog();\n return true;\n }\n }\n});\n"],"names":["MERGE_TAG_CATEGORIES","savedSelection","selectedTag","insertMergeTag","tag","range","selection","tagId","span","editor","error","showMergeTagDialog","_a","currentCategory","searchTerm","filteredTags","overlay","dialog","header","body","searchInput","tabsContainer","categories","key","value","cat","tab","updateTabs","updateTagList","tagList","previewPane","index","categoryData","tags","emptyState","tagItem","labelDiv","previewDiv","descDiv","updateTagSelection","updatePreview","closeDialog","item","e","footer","cancelBtn","insertBtn","MergeTagPlugin"],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
let c = null;
|
|
2
|
+
const b = () => {
|
|
3
|
+
c || (c = document.createElement("style"), c.textContent = `
|
|
4
|
+
.rte-page-break {
|
|
5
|
+
display: block;
|
|
6
|
+
position: relative;
|
|
7
|
+
height: 12px;
|
|
8
|
+
margin: 8px 0;
|
|
9
|
+
background: linear-gradient(90deg, #ccc 0%, transparent 100%);
|
|
10
|
+
border-top: 2px dashed #999;
|
|
11
|
+
border-bottom: none;
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
user-select: none;
|
|
14
|
+
transition: all 0.2s ease;
|
|
15
|
+
outline: none;
|
|
16
|
+
-webkit-user-select: none;
|
|
17
|
+
-moz-user-select: none;
|
|
18
|
+
-ms-user-select: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.rte-page-break::before {
|
|
22
|
+
content: '⎙ PAGE BREAK';
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: -12px;
|
|
25
|
+
left: 0;
|
|
26
|
+
font-size: 10px;
|
|
27
|
+
font-weight: bold;
|
|
28
|
+
color: #666;
|
|
29
|
+
background: white;
|
|
30
|
+
padding: 2px 6px;
|
|
31
|
+
letter-spacing: 0.5px;
|
|
32
|
+
opacity: 0.7;
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.rte-page-break:hover {
|
|
37
|
+
background: linear-gradient(90deg, #999 0%, transparent 100%);
|
|
38
|
+
border-top-color: #666;
|
|
39
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.rte-page-break:hover::before {
|
|
43
|
+
opacity: 1;
|
|
44
|
+
color: #333;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.rte-page-break:focus,
|
|
48
|
+
.rte-page-break:focus-visible,
|
|
49
|
+
.rte-page-break-selected {
|
|
50
|
+
outline: 2px solid #0066cc;
|
|
51
|
+
outline-offset: -2px;
|
|
52
|
+
border-top-color: #0066cc;
|
|
53
|
+
background: linear-gradient(90deg, #0066cc 0%, transparent 100%);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.rte-page-break * {
|
|
57
|
+
user-select: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media print {
|
|
61
|
+
.rte-page-break {
|
|
62
|
+
display: block;
|
|
63
|
+
height: 0;
|
|
64
|
+
margin: 0;
|
|
65
|
+
background: none;
|
|
66
|
+
border: none;
|
|
67
|
+
page-break-after: always;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.rte-page-break::before {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
`, document.head.appendChild(c));
|
|
75
|
+
}, u = (e) => {
|
|
76
|
+
const i = ["SPAN", "A", "STRONG", "EM", "B", "I", "U", "SUP", "SUB", "CODE"];
|
|
77
|
+
let n = e.nodeType === Node.TEXT_NODE ? e.parentElement : e;
|
|
78
|
+
for (; n && !n.hasAttribute("contenteditable"); ) {
|
|
79
|
+
if (i.includes(n.tagName))
|
|
80
|
+
return !0;
|
|
81
|
+
n = n.parentElement;
|
|
82
|
+
}
|
|
83
|
+
return !1;
|
|
84
|
+
}, m = (e) => {
|
|
85
|
+
const i = ["DIV", "P", "BLOCKQUOTE", "PRE", "H1", "H2", "H3", "H4", "H5", "H6", "LI", "TD", "TH"];
|
|
86
|
+
let n = e;
|
|
87
|
+
for (; n; ) {
|
|
88
|
+
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
89
|
+
const s = n;
|
|
90
|
+
if (i.includes(s.tagName))
|
|
91
|
+
return s;
|
|
92
|
+
}
|
|
93
|
+
n = n.parentNode;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}, k = (e) => {
|
|
97
|
+
let i = e.nextElementSibling;
|
|
98
|
+
for (; i && i.classList.contains("rte-page-break"); )
|
|
99
|
+
i.remove(), i = e.nextElementSibling;
|
|
100
|
+
let n = e.previousElementSibling;
|
|
101
|
+
for (; n && n.classList.contains("rte-page-break"); ) {
|
|
102
|
+
const s = n;
|
|
103
|
+
n = n.previousElementSibling, s.remove();
|
|
104
|
+
}
|
|
105
|
+
}, f = () => {
|
|
106
|
+
var p, d;
|
|
107
|
+
const e = window.getSelection();
|
|
108
|
+
if (!e || e.rangeCount === 0) return !1;
|
|
109
|
+
const n = e.getRangeAt(0).commonAncestorContainer;
|
|
110
|
+
if (u(n))
|
|
111
|
+
return console.warn("Page breaks cannot be inserted in inline contexts"), !1;
|
|
112
|
+
const s = m(n);
|
|
113
|
+
if (!s)
|
|
114
|
+
return console.warn("No block element found for page break insertion"), !1;
|
|
115
|
+
const t = document.createElement("div");
|
|
116
|
+
t.className = "rte-page-break", t.setAttribute("data-page-break", "true"), t.setAttribute("data-type", "page-break"), t.setAttribute("contenteditable", "false"), t.setAttribute("tabindex", "0"), t.setAttribute("role", "separator"), t.setAttribute("aria-label", "Page break"), t.addEventListener("click", (r) => {
|
|
117
|
+
r.preventDefault(), r.stopPropagation(), t.focus();
|
|
118
|
+
const a = document.createRange();
|
|
119
|
+
a.selectNode(t), e.removeAllRanges(), e.addRange(a);
|
|
120
|
+
}), t.addEventListener("keydown", (r) => {
|
|
121
|
+
if (r.key === "Delete" || r.key === "Backspace") {
|
|
122
|
+
r.preventDefault();
|
|
123
|
+
const a = t.nextElementSibling || t.previousElementSibling;
|
|
124
|
+
if (t.remove(), a) {
|
|
125
|
+
const o = document.createRange();
|
|
126
|
+
o.selectNode(a), e.removeAllRanges(), e.addRange(o);
|
|
127
|
+
}
|
|
128
|
+
} else if (r.key === "ArrowDown" || r.key === "ArrowRight") {
|
|
129
|
+
r.preventDefault();
|
|
130
|
+
const a = t.nextElementSibling;
|
|
131
|
+
if (a) {
|
|
132
|
+
const o = document.createRange();
|
|
133
|
+
o.setStart(a, 0), o.collapse(!0), e.removeAllRanges(), e.addRange(o);
|
|
134
|
+
}
|
|
135
|
+
} else if (r.key === "ArrowUp" || r.key === "ArrowLeft") {
|
|
136
|
+
r.preventDefault();
|
|
137
|
+
const a = t.previousElementSibling;
|
|
138
|
+
if (a) {
|
|
139
|
+
const o = document.createRange();
|
|
140
|
+
o.selectNodeContents(a), o.collapse(!1), e.removeAllRanges(), e.addRange(o);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}), (p = s.parentNode) == null || p.insertBefore(t, s.nextSibling), k(t);
|
|
144
|
+
const l = document.createRange(), g = t.nextSibling;
|
|
145
|
+
if (g)
|
|
146
|
+
l.setStart(g, 0);
|
|
147
|
+
else {
|
|
148
|
+
const r = document.createElement("p");
|
|
149
|
+
r.innerHTML = "<br>", (d = t.parentNode) == null || d.insertBefore(r, t.nextSibling), l.setStart(r, 0);
|
|
150
|
+
}
|
|
151
|
+
return l.collapse(!0), e.removeAllRanges(), e.addRange(l), !0;
|
|
152
|
+
}, C = () => (b(), {
|
|
153
|
+
name: "pageBreak",
|
|
154
|
+
toolbar: [
|
|
155
|
+
{
|
|
156
|
+
label: "Page Break",
|
|
157
|
+
command: "insertPageBreak",
|
|
158
|
+
icon: '<svg fill="#000000" width="24px" height="24px" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M1,6 L3,6 C3.55228475,6 4,6.44771525 4,7 C4,7.55228475 3.55228475,8 3,8 L1,8 C0.44771525,8 0,7.55228475 0,7 C0,6.44771525 0.44771525,6 1,6 Z M11,6 L13,6 C13.5522847,6 14,6.44771525 14,7 C14,7.55228475 13.5522847,8 13,8 L11,8 C10.4477153,8 10,7.55228475 10,7 C10,6.44771525 10.4477153,6 11,6 Z M6,6 L8,6 C8.55228475,6 9,6.44771525 9,7 C9,7.55228475 8.55228475,8 8,8 L6,8 C5.44771525,8 5,7.55228475 5,7 C5,6.44771525 5.44771525,6 6,6 Z M0,1 C0,0.44771525 0.44771525,-2.26485497e-13 1,-2.26485497e-13 C1.55228475,-2.26485497e-13 2,0.44771525 2,1 L2,3.0142458 L12,3.0142458 L12,1 C12,0.44771525 12.4477153,-2.26485497e-13 13,-2.26485497e-13 C13.5522847,-2.26485497e-13 14,0.44771525 14,1 L14,3.0142458 C14,4.1188153 13.1045695,5.0142458 12,5.0142458 L2,5.0142458 C0.8954305,5.0142458 0,4.1188153 0,3.0142458 L0,1 Z M0,13.0142458 L0,11 C0,9.8954305 0.8954305,9 2,9 L12,9 C13.1045695,9 14,9.8954305 14,11 L14,13.0142458 C14,13.5665305 13.5522847,14.0142458 13,14.0142458 C12.4477153,14.0142458 12,13.5665305 12,13.0142458 L12,11 L2,11 L2,13.0142458 C2,13.5665305 1.55228475,14.0142458 1,14.0142458 C0.44771525,14.0142458 0,13.5665305 0,13.0142458 Z"></path></g></svg>',
|
|
159
|
+
shortcut: "Mod-Enter"
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
commands: {
|
|
163
|
+
insertPageBreak: f
|
|
164
|
+
},
|
|
165
|
+
keymap: {
|
|
166
|
+
"Mod-Enter": "insertPageBreak"
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
export {
|
|
170
|
+
C as PageBreakPlugin
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=PageBreakPlugin.native-DDjcDyRW.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PageBreakPlugin.native-DDjcDyRW.mjs","sources":["../../plugins/page-break/src/PageBreakPlugin.native.ts"],"sourcesContent":["import { Plugin } from '@editora/core';\n\n/**\n * Page Break Plugin - Native Implementation\n * \n * Features:\n * - Visual page break markers with dashed border\n * - Print integration (page-break-after: always)\n * - Block-level enforcement (no inline contexts)\n * - Click to select page break\n * - Keyboard navigation support\n * - Collapse adjacent page breaks\n * - Delete key removes page break\n * - Hover effects for better UX\n * - Accessibility compliant (ARIA labels, focus states)\n */\n\nlet pageBreakStyles: HTMLStyleElement | null = null;\n\nconst injectStyles = () => {\n if (pageBreakStyles) return;\n\n pageBreakStyles = document.createElement('style');\n pageBreakStyles.textContent = `\n .rte-page-break {\n display: block;\n position: relative;\n height: 12px;\n margin: 8px 0;\n background: linear-gradient(90deg, #ccc 0%, transparent 100%);\n border-top: 2px dashed #999;\n border-bottom: none;\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n outline: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n }\n\n .rte-page-break::before {\n content: '⎙ PAGE BREAK';\n position: absolute;\n top: -12px;\n left: 0;\n font-size: 10px;\n font-weight: bold;\n color: #666;\n background: white;\n padding: 2px 6px;\n letter-spacing: 0.5px;\n opacity: 0.7;\n pointer-events: none;\n }\n\n .rte-page-break:hover {\n background: linear-gradient(90deg, #999 0%, transparent 100%);\n border-top-color: #666;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);\n }\n\n .rte-page-break:hover::before {\n opacity: 1;\n color: #333;\n }\n\n .rte-page-break:focus,\n .rte-page-break:focus-visible,\n .rte-page-break-selected {\n outline: 2px solid #0066cc;\n outline-offset: -2px;\n border-top-color: #0066cc;\n background: linear-gradient(90deg, #0066cc 0%, transparent 100%);\n }\n\n .rte-page-break * {\n user-select: none;\n }\n\n @media print {\n .rte-page-break {\n display: block;\n height: 0;\n margin: 0;\n background: none;\n border: none;\n page-break-after: always;\n }\n\n .rte-page-break::before {\n display: none;\n }\n }\n `;\n document.head.appendChild(pageBreakStyles);\n};\n\nconst isInlineContext = (node: Node): boolean => {\n const inlineElements = ['SPAN', 'A', 'STRONG', 'EM', 'B', 'I', 'U', 'SUP', 'SUB', 'CODE'];\n \n let current: HTMLElement | null = node.nodeType === Node.TEXT_NODE \n ? (node.parentElement as HTMLElement) \n : (node as HTMLElement);\n\n while (current && !current.hasAttribute('contenteditable')) {\n if (inlineElements.includes(current.tagName)) {\n return true;\n }\n current = current.parentElement;\n }\n\n return false;\n};\n\nconst getContainingBlock = (node: Node): HTMLElement | null => {\n const blockElements = ['DIV', 'P', 'BLOCKQUOTE', 'PRE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'TD', 'TH'];\n let current: Node | null = node;\n\n while (current) {\n if (current.nodeType === Node.ELEMENT_NODE) {\n const element = current as HTMLElement;\n if (blockElements.includes(element.tagName)) {\n return element;\n }\n }\n current = current.parentNode;\n }\n\n return null;\n};\n\nconst collapseAdjacentPageBreaks = (pageBreak: HTMLElement) => {\n let next = pageBreak.nextElementSibling;\n \n while (next) {\n if (next.classList.contains('rte-page-break')) {\n next.remove();\n next = pageBreak.nextElementSibling;\n } else {\n break;\n }\n }\n\n let prev = pageBreak.previousElementSibling;\n while (prev) {\n if (prev.classList.contains('rte-page-break')) {\n const toRemove = prev;\n prev = prev.previousElementSibling;\n toRemove.remove();\n } else {\n break;\n }\n }\n};\n\nconst insertPageBreak = () => {\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0) return false;\n\n const range = selection.getRangeAt(0);\n const container = range.commonAncestorContainer as HTMLElement;\n\n // Check if we're in an inline context\n if (isInlineContext(container)) {\n console.warn('Page breaks cannot be inserted in inline contexts');\n return false;\n }\n\n // Get the block element containing the selection\n const blockElement = getContainingBlock(container);\n if (!blockElement) {\n console.warn('No block element found for page break insertion');\n return false;\n }\n\n // Create page break element\n const pageBreak = document.createElement('div');\n pageBreak.className = 'rte-page-break';\n pageBreak.setAttribute('data-page-break', 'true');\n pageBreak.setAttribute('data-type', 'page-break');\n pageBreak.setAttribute('contenteditable', 'false');\n pageBreak.setAttribute('tabindex', '0');\n pageBreak.setAttribute('role', 'separator');\n pageBreak.setAttribute('aria-label', 'Page break');\n\n // Handle click to select the page break\n pageBreak.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n pageBreak.focus();\n \n const newRange = document.createRange();\n newRange.selectNode(pageBreak);\n selection.removeAllRanges();\n selection.addRange(newRange);\n });\n\n // Handle keyboard navigation\n pageBreak.addEventListener('keydown', (e) => {\n if (e.key === 'Delete' || e.key === 'Backspace') {\n e.preventDefault();\n const next = pageBreak.nextElementSibling || pageBreak.previousElementSibling;\n pageBreak.remove();\n \n if (next) {\n const newRange = document.createRange();\n newRange.selectNode(next);\n selection.removeAllRanges();\n selection.addRange(newRange);\n }\n } else if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {\n e.preventDefault();\n const next = pageBreak.nextElementSibling;\n if (next) {\n const newRange = document.createRange();\n newRange.setStart(next, 0);\n newRange.collapse(true);\n selection.removeAllRanges();\n selection.addRange(newRange);\n }\n } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {\n e.preventDefault();\n const prev = pageBreak.previousElementSibling;\n if (prev) {\n const newRange = document.createRange();\n newRange.selectNodeContents(prev);\n newRange.collapse(false);\n selection.removeAllRanges();\n selection.addRange(newRange);\n }\n }\n });\n\n // Insert after the block element\n blockElement.parentNode?.insertBefore(pageBreak, blockElement.nextSibling);\n\n // Collapse adjacent page breaks\n collapseAdjacentPageBreaks(pageBreak);\n\n // Move cursor after the page break\n const newRange = document.createRange();\n const nextNode = pageBreak.nextSibling;\n if (nextNode) {\n newRange.setStart(nextNode, 0);\n } else {\n // Create a new paragraph after the page break if nothing exists\n const p = document.createElement('p');\n p.innerHTML = '<br>';\n pageBreak.parentNode?.insertBefore(p, pageBreak.nextSibling);\n newRange.setStart(p, 0);\n }\n newRange.collapse(true);\n selection.removeAllRanges();\n selection.addRange(newRange);\n\n return true;\n};\n\nexport const PageBreakPlugin = (): Plugin => {\n // Inject styles on plugin initialization\n injectStyles();\n\n return {\n name: 'pageBreak',\n \n toolbar: [\n {\n label: 'Page Break',\n command: 'insertPageBreak',\n icon: '<svg fill=\"#000000\" width=\"24px\" height=\"24px\" viewBox=\"0 0 14 14\" xmlns=\"http://www.w3.org/2000/svg\"><g id=\"SVGRepo_bgCarrier\" stroke-width=\"0\"></g><g id=\"SVGRepo_tracerCarrier\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></g><g id=\"SVGRepo_iconCarrier\"><path d=\"M1,6 L3,6 C3.55228475,6 4,6.44771525 4,7 C4,7.55228475 3.55228475,8 3,8 L1,8 C0.44771525,8 0,7.55228475 0,7 C0,6.44771525 0.44771525,6 1,6 Z M11,6 L13,6 C13.5522847,6 14,6.44771525 14,7 C14,7.55228475 13.5522847,8 13,8 L11,8 C10.4477153,8 10,7.55228475 10,7 C10,6.44771525 10.4477153,6 11,6 Z M6,6 L8,6 C8.55228475,6 9,6.44771525 9,7 C9,7.55228475 8.55228475,8 8,8 L6,8 C5.44771525,8 5,7.55228475 5,7 C5,6.44771525 5.44771525,6 6,6 Z M0,1 C0,0.44771525 0.44771525,-2.26485497e-13 1,-2.26485497e-13 C1.55228475,-2.26485497e-13 2,0.44771525 2,1 L2,3.0142458 L12,3.0142458 L12,1 C12,0.44771525 12.4477153,-2.26485497e-13 13,-2.26485497e-13 C13.5522847,-2.26485497e-13 14,0.44771525 14,1 L14,3.0142458 C14,4.1188153 13.1045695,5.0142458 12,5.0142458 L2,5.0142458 C0.8954305,5.0142458 0,4.1188153 0,3.0142458 L0,1 Z M0,13.0142458 L0,11 C0,9.8954305 0.8954305,9 2,9 L12,9 C13.1045695,9 14,9.8954305 14,11 L14,13.0142458 C14,13.5665305 13.5522847,14.0142458 13,14.0142458 C12.4477153,14.0142458 12,13.5665305 12,13.0142458 L12,11 L2,11 L2,13.0142458 C2,13.5665305 1.55228475,14.0142458 1,14.0142458 C0.44771525,14.0142458 0,13.5665305 0,13.0142458 Z\"></path></g></svg>',\n shortcut: 'Mod-Enter'\n }\n ],\n \n commands: {\n insertPageBreak\n },\n \n keymap: {\n 'Mod-Enter': 'insertPageBreak'\n }\n };\n};\n"],"names":["pageBreakStyles","injectStyles","isInlineContext","node","inlineElements","current","getContainingBlock","blockElements","element","collapseAdjacentPageBreaks","pageBreak","next","prev","toRemove","insertPageBreak","_a","_b","selection","container","blockElement","e","newRange","nextNode","p","PageBreakPlugin"],"mappings":"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"}
|