@remyxjs/react 1.0.0-beta

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.
Files changed (74) hide show
  1. package/README.md +2158 -0
  2. package/create/index.js +367 -0
  3. package/create/templates/jsx/index.html +12 -0
  4. package/create/templates/jsx/src/App.jsx +25 -0
  5. package/create/templates/jsx/src/main.jsx +9 -0
  6. package/create/templates/jsx/vite.config.js +6 -0
  7. package/create/templates/typescript/index.html +12 -0
  8. package/create/templates/typescript/src/App.tsx +25 -0
  9. package/create/templates/typescript/src/main.tsx +9 -0
  10. package/create/templates/typescript/src/vite-env.d.ts +1 -0
  11. package/create/templates/typescript/tsconfig.json +21 -0
  12. package/create/templates/typescript/vite.config.ts +6 -0
  13. package/dist/AttachmentModal--6T-vYJt.js +21 -0
  14. package/dist/AttachmentModal--6T-vYJt.js.map +1 -0
  15. package/dist/AttachmentModal-D-uxbXvb.cjs +2 -0
  16. package/dist/AttachmentModal-D-uxbXvb.cjs.map +1 -0
  17. package/dist/CommandPalette-DXWyTGOU.js +19 -0
  18. package/dist/CommandPalette-DXWyTGOU.js.map +1 -0
  19. package/dist/CommandPalette-t9J8JGNJ.cjs +2 -0
  20. package/dist/CommandPalette-t9J8JGNJ.cjs.map +1 -0
  21. package/dist/ContextMenu-B4K3Zbfp.cjs +2 -0
  22. package/dist/ContextMenu-B4K3Zbfp.cjs.map +1 -0
  23. package/dist/ContextMenu-D8wNSMc3.js +2 -0
  24. package/dist/ContextMenu-D8wNSMc3.js.map +1 -0
  25. package/dist/EmbedModal-Bh2Tcow9.cjs +2 -0
  26. package/dist/EmbedModal-Bh2Tcow9.cjs.map +1 -0
  27. package/dist/EmbedModal-cxE4maD2.js +11 -0
  28. package/dist/EmbedModal-cxE4maD2.js.map +1 -0
  29. package/dist/ExportModal-Cf1uE4r_.js +13 -0
  30. package/dist/ExportModal-Cf1uE4r_.js.map +1 -0
  31. package/dist/ExportModal-DwQVsrZE.cjs +2 -0
  32. package/dist/ExportModal-DwQVsrZE.cjs.map +1 -0
  33. package/dist/FindReplaceModal-DYL_2z8U.cjs +2 -0
  34. package/dist/FindReplaceModal-DYL_2z8U.cjs.map +1 -0
  35. package/dist/FindReplaceModal-Dgt_MrWb.js +17 -0
  36. package/dist/FindReplaceModal-Dgt_MrWb.js.map +1 -0
  37. package/dist/ImageModal-D39ywxqI.cjs +2 -0
  38. package/dist/ImageModal-D39ywxqI.cjs.map +1 -0
  39. package/dist/ImageModal-DqScpPrc.js +23 -0
  40. package/dist/ImageModal-DqScpPrc.js.map +1 -0
  41. package/dist/ImportDocumentModal-BKqMxO3z.js +22 -0
  42. package/dist/ImportDocumentModal-BKqMxO3z.js.map +1 -0
  43. package/dist/ImportDocumentModal-Bev9hp_J.cjs +2 -0
  44. package/dist/ImportDocumentModal-Bev9hp_J.cjs.map +1 -0
  45. package/dist/LinkModal-B-igSa-g.cjs +2 -0
  46. package/dist/LinkModal-B-igSa-g.cjs.map +1 -0
  47. package/dist/LinkModal-k9IeDtAb.js +15 -0
  48. package/dist/LinkModal-k9IeDtAb.js.map +1 -0
  49. package/dist/MenuBar-B-ZAX9rH.cjs +2 -0
  50. package/dist/MenuBar-B-ZAX9rH.cjs.map +1 -0
  51. package/dist/MenuBar-DWxJNHmb.js +12 -0
  52. package/dist/MenuBar-DWxJNHmb.js.map +1 -0
  53. package/dist/ModalOverlay-BDsGgv3_.cjs +2 -0
  54. package/dist/ModalOverlay-BDsGgv3_.cjs.map +1 -0
  55. package/dist/ModalOverlay-CLvRNHmp.js +6 -0
  56. package/dist/ModalOverlay-CLvRNHmp.js.map +1 -0
  57. package/dist/SourceModal-BNI_i4iW.cjs +2 -0
  58. package/dist/SourceModal-BNI_i4iW.cjs.map +1 -0
  59. package/dist/SourceModal-MdTGK3Uf.js +12 -0
  60. package/dist/SourceModal-MdTGK3Uf.js.map +1 -0
  61. package/dist/TablePickerModal-DYODWEA1.js +17 -0
  62. package/dist/TablePickerModal-DYODWEA1.js.map +1 -0
  63. package/dist/TablePickerModal-Do1QyoyM.cjs +2 -0
  64. package/dist/TablePickerModal-Do1QyoyM.cjs.map +1 -0
  65. package/dist/index-C720tbJA.js +359 -0
  66. package/dist/index-C720tbJA.js.map +1 -0
  67. package/dist/index-Dc63uIP0.cjs +2 -0
  68. package/dist/index-Dc63uIP0.cjs.map +1 -0
  69. package/dist/remyx-react.cjs +2 -0
  70. package/dist/remyx-react.cjs.map +1 -0
  71. package/dist/remyx-react.css +1 -0
  72. package/dist/remyx-react.js +2 -0
  73. package/dist/remyx-react.js.map +1 -0
  74. package/package.json +69 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette-DXWyTGOU.js","sources":["../src/components/CommandPalette/CommandPalette.jsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'\nimport {\n SLASH_COMMAND_ITEMS,\n filterSlashItems,\n recordRecentCommand,\n getCustomCommandItems,\n TOOLTIP_MAP,\n SHORTCUT_MAP,\n MODAL_COMMANDS,\n getShortcutLabel,\n} from '@remyxjs/core'\n\n/**\n * Build command items from engine's registered commands + slash items + custom items.\n * Deduplicates by id and adds shortcut labels.\n */\nfunction buildCommandList(engine, customSlashItems) {\n const slashItems = customSlashItems || SLASH_COMMAND_ITEMS\n // Merge in globally registered custom items\n const customGlobal = getCustomCommandItems()\n const merged = [...slashItems]\n for (const ci of customGlobal) {\n if (!merged.some(m => m.id === ci.id)) {\n merged.push(ci)\n }\n }\n const seen = new Set(merged.map(i => i.id))\n const engineItems = []\n\n if (engine) {\n const allCommands = engine.commands.getAll()\n for (const name of allCommands) {\n if (seen.has(name)) continue\n // Skip internal/meta commands\n if (name === 'undo' || name === 'redo' || name === 'removeFormat') continue\n\n const tooltip = TOOLTIP_MAP[name]\n if (!tooltip) continue // Skip commands without a user-facing name\n\n const shortcut = SHORTCUT_MAP[name]\n const isModal = name in MODAL_COMMANDS\n\n engineItems.push({\n id: name,\n label: tooltip,\n description: '',\n icon: shortcut ? getShortcutLabel(name).charAt(0) : '\\u2022',\n keywords: [name],\n category: 'Commands',\n shortcutLabel: shortcut ? getShortcutLabel(name) : null,\n action: isModal\n ? (_eng, openModal) => openModal?.(MODAL_COMMANDS[name])\n : (eng) => eng.executeCommand(name),\n })\n seen.add(name)\n }\n }\n\n // Add shortcut labels to slash items\n const enrichedSlash = merged.map(item => ({\n ...item,\n shortcutLabel: SHORTCUT_MAP[item.id] ? getShortcutLabel(item.id) : null,\n }))\n\n return [...enrichedSlash, ...engineItems]\n}\n\nfunction CommandPaletteInner({ open, onClose, engine, onOpenModal, slashCommandItems }) {\n const inputRef = useRef(null)\n const listRef = useRef(null)\n const [query, setQuery] = useState('')\n const [selectedIndex, setSelectedIndex] = useState(0)\n\n const allItems = useMemo(\n () => buildCommandList(engine, slashCommandItems),\n [engine, slashCommandItems]\n )\n\n const filteredItems = useMemo(\n () => filterSlashItems(allItems, query),\n [allItems, query]\n )\n\n // Reset on open\n useEffect(() => {\n if (open) {\n setQuery('')\n setSelectedIndex(0)\n // Focus input after render\n requestAnimationFrame(() => inputRef.current?.focus())\n }\n }, [open])\n\n // Reset index on filter change\n useEffect(() => {\n setSelectedIndex(0)\n }, [query])\n\n // Scroll active item into view\n useEffect(() => {\n if (!listRef.current) return\n const active = listRef.current.querySelector('.rmx-command-palette-item.rmx-active')\n if (active) {\n active.scrollIntoView({ block: 'nearest' })\n }\n }, [selectedIndex])\n\n const executeItem = useCallback((item) => {\n onClose()\n recordRecentCommand(item.id)\n // Defer execution so the palette is closed first and focus returns to editor\n requestAnimationFrame(() => {\n item.action(engine, onOpenModal)\n })\n }, [engine, onClose, onOpenModal])\n\n const handleKeyDown = useCallback((e) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n onClose()\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setSelectedIndex(i => filteredItems.length === 0 ? 0 : (i + 1) % filteredItems.length)\n return\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n setSelectedIndex(i => filteredItems.length === 0 ? 0 : (i - 1 + filteredItems.length) % filteredItems.length)\n return\n }\n if (e.key === 'Enter') {\n e.preventDefault()\n if (filteredItems.length > 0 && selectedIndex < filteredItems.length) {\n executeItem(filteredItems[selectedIndex])\n }\n }\n }, [filteredItems, selectedIndex, executeItem, onClose])\n\n // Focus trap — prevent tab from escaping\n const handleOverlayKeyDown = useCallback((e) => {\n if (e.key === 'Tab') {\n e.preventDefault()\n }\n }, [])\n\n if (!open) return null\n\n // Group items by category\n const groups = []\n let lastCategory = null\n for (let i = 0; i < filteredItems.length; i++) {\n const item = filteredItems[i]\n if (item.category !== lastCategory) {\n groups.push({ category: item.category, items: [] })\n lastCategory = item.category\n }\n groups[groups.length - 1].items.push({ item, index: i })\n }\n\n return (\n <div\n className=\"rmx-command-palette-overlay\"\n onMouseDown={(e) => {\n if (e.target === e.currentTarget) {\n e.preventDefault()\n onClose()\n }\n }}\n onKeyDown={handleOverlayKeyDown}\n >\n <div className=\"rmx-command-palette\" role=\"dialog\" aria-label=\"Command palette\">\n <div className=\"rmx-command-palette-input-wrap\">\n <span className=\"rmx-command-palette-search-icon\" aria-hidden=\"true\">/</span>\n <input\n ref={inputRef}\n className=\"rmx-command-palette-input\"\n type=\"text\"\n placeholder=\"Type a command...\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n aria-activedescendant={\n filteredItems[selectedIndex]\n ? `rmx-cmd-item-${filteredItems[selectedIndex].id}`\n : undefined\n }\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls=\"rmx-command-palette-list\"\n aria-autocomplete=\"list\"\n />\n </div>\n\n <div\n ref={listRef}\n className=\"rmx-command-palette-list\"\n id=\"rmx-command-palette-list\"\n role=\"listbox\"\n >\n {filteredItems.length === 0 ? (\n <div className=\"rmx-command-palette-empty\">No matching commands</div>\n ) : (\n groups.map((group) => (\n <div key={group.category}>\n <div className=\"rmx-command-palette-category\">{group.category}</div>\n {group.items.map(({ item, index }) => (\n <div\n key={item.id}\n id={`rmx-cmd-item-${item.id}`}\n className={`rmx-command-palette-item${index === selectedIndex ? ' rmx-active' : ''}`}\n role=\"option\"\n aria-selected={index === selectedIndex}\n onMouseEnter={() => setSelectedIndex(index)}\n onClick={() => executeItem(item)}\n >\n <div className=\"rmx-command-palette-item-icon\">{item.icon}</div>\n <div className=\"rmx-command-palette-item-text\">\n <div className=\"rmx-command-palette-item-label\">{item.label}</div>\n {item.description && (\n <div className=\"rmx-command-palette-item-desc\">{item.description}</div>\n )}\n </div>\n {item.shortcutLabel && (\n <span className=\"rmx-command-palette-shortcut\">{item.shortcutLabel}</span>\n )}\n </div>\n ))}\n </div>\n ))\n )}\n </div>\n\n <div className=\"rmx-command-palette-hint\">\n <span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>\n <span><kbd>↵</kbd> select</span>\n <span><kbd>esc</kbd> close</span>\n </div>\n </div>\n </div>\n )\n}\n\nexport const CommandPalette = React.memo(CommandPaletteInner)\n"],"names":["CommandPalette","React","memo","open","onClose","engine","onOpenModal","slashCommandItems","inputRef","useRef","listRef","query","setQuery","useState","selectedIndex","setSelectedIndex","allItems","useMemo","customSlashItems","slashItems","SLASH_COMMAND_ITEMS","customGlobal","getCustomCommandItems","merged","ci","some","m","id","push","seen","Set","map","i","engineItems","allCommands","commands","getAll","name","has","tooltip","TOOLTIP_MAP","shortcut","SHORTCUT_MAP","isModal","MODAL_COMMANDS","label","description","icon","getShortcutLabel","charAt","keywords","category","shortcutLabel","action","_eng","openModal","eng","executeCommand","add","item","buildCommandList","filteredItems","filterSlashItems","useEffect","requestAnimationFrame","current","focus","active","querySelector","scrollIntoView","block","executeItem","useCallback","recordRecentCommand","handleKeyDown","e","key","preventDefault","length","handleOverlayKeyDown","groups","lastCategory","items","index","jsx","className","onMouseDown","target","currentTarget","onKeyDown","children","role","jsxs","ref","type","placeholder","value","onChange","group","onMouseEnter","onClick"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("react"),a=require("@remyxjs/core");const n=t.memo((function({open:n,onClose:r,engine:s,onOpenModal:o,slashCommandItems:c}){const l=t.useRef(null),m=t.useRef(null),[i,d]=t.useState(""),[u,p]=t.useState(0),x=t.useMemo((()=>function(e,t){const n=t||a.SLASH_COMMAND_ITEMS,r=a.getCustomCommandItems(),s=[...n];for(const a of r)s.some((e=>e.id===a.id))||s.push(a);const o=new Set(s.map((e=>e.id))),c=[];if(e){const t=e.commands.getAll();for(const e of t){if(o.has(e))continue;if("undo"===e||"redo"===e||"removeFormat"===e)continue;const t=a.TOOLTIP_MAP[e];if(!t)continue;const n=a.SHORTCUT_MAP[e],r=e in a.MODAL_COMMANDS;c.push({id:e,label:t,description:"",icon:n?a.getShortcutLabel(e).charAt(0):"•",keywords:[e],category:"Commands",shortcutLabel:n?a.getShortcutLabel(e):null,action:r?(t,n)=>n?.(a.MODAL_COMMANDS[e]):t=>t.executeCommand(e)}),o.add(e)}}return[...s.map((e=>({...e,shortcutLabel:a.SHORTCUT_MAP[e.id]?a.getShortcutLabel(e.id):null}))),...c]}(s,c)),[s,c]),h=t.useMemo((()=>a.filterSlashItems(x,i)),[x,i]);t.useEffect((()=>{n&&(d(""),p(0),requestAnimationFrame((()=>l.current?.focus())))}),[n]),t.useEffect((()=>{p(0)}),[i]),t.useEffect((()=>{if(!m.current)return;const e=m.current.querySelector(".rmx-command-palette-item.rmx-active");e&&e.scrollIntoView({block:"nearest"})}),[u]);const g=t.useCallback((e=>{r(),a.recordRecentCommand(e.id),requestAnimationFrame((()=>{e.action(s,o)}))}),[s,r,o]),v=t.useCallback((e=>"Escape"===e.key?(e.preventDefault(),void r()):"ArrowDown"===e.key?(e.preventDefault(),void p((e=>0===h.length?0:(e+1)%h.length))):"ArrowUp"===e.key?(e.preventDefault(),void p((e=>0===h.length?0:(e-1+h.length)%h.length))):void("Enter"===e.key&&(e.preventDefault(),h.length>0&&u<h.length&&g(h[u])))),[h,u,g,r]),f=t.useCallback((e=>{"Tab"===e.key&&e.preventDefault()}),[]);if(!n)return null;const j=[];let b=null;for(let e=0;e<h.length;e++){const t=h[e];t.category!==b&&(j.push({category:t.category,items:[]}),b=t.category),j[j.length-1].items.push({item:t,index:e})}return e.jsx("div",{className:"rmx-command-palette-overlay",onMouseDown:e=>{e.target===e.currentTarget&&(e.preventDefault(),r())},onKeyDown:f,children:e.jsxs("div",{className:"rmx-command-palette",role:"dialog","aria-label":"Command palette",children:[e.jsxs("div",{className:"rmx-command-palette-input-wrap",children:[e.jsx("span",{className:"rmx-command-palette-search-icon","aria-hidden":"true",children:"/"}),e.jsx("input",{ref:l,className:"rmx-command-palette-input",type:"text",placeholder:"Type a command...",value:i,onChange:e=>d(e.target.value),onKeyDown:v,"aria-activedescendant":h[u]?`rmx-cmd-item-${h[u].id}`:void 0,role:"combobox","aria-expanded":"true","aria-controls":"rmx-command-palette-list","aria-autocomplete":"list"})]}),e.jsx("div",{ref:m,className:"rmx-command-palette-list",id:"rmx-command-palette-list",role:"listbox",children:0===h.length?e.jsx("div",{className:"rmx-command-palette-empty",children:"No matching commands"}):j.map((t=>e.jsxs("div",{children:[e.jsx("div",{className:"rmx-command-palette-category",children:t.category}),t.items.map((({item:t,index:a})=>e.jsxs("div",{id:`rmx-cmd-item-${t.id}`,className:"rmx-command-palette-item"+(a===u?" rmx-active":""),role:"option","aria-selected":a===u,onMouseEnter:()=>p(a),onClick:()=>g(t),children:[e.jsx("div",{className:"rmx-command-palette-item-icon",children:t.icon}),e.jsxs("div",{className:"rmx-command-palette-item-text",children:[e.jsx("div",{className:"rmx-command-palette-item-label",children:t.label}),t.description&&e.jsx("div",{className:"rmx-command-palette-item-desc",children:t.description})]}),t.shortcutLabel&&e.jsx("span",{className:"rmx-command-palette-shortcut",children:t.shortcutLabel})]},t.id)))]},t.category)))}),e.jsxs("div",{className:"rmx-command-palette-hint",children:[e.jsxs("span",{children:[e.jsx("kbd",{children:"↑"}),e.jsx("kbd",{children:"↓"})," navigate"]}),e.jsxs("span",{children:[e.jsx("kbd",{children:"↵"})," select"]}),e.jsxs("span",{children:[e.jsx("kbd",{children:"esc"})," close"]})]})]})})}));exports.CommandPalette=n;
2
+ //# sourceMappingURL=CommandPalette-t9J8JGNJ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette-t9J8JGNJ.cjs","sources":["../src/components/CommandPalette/CommandPalette.jsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'\nimport {\n SLASH_COMMAND_ITEMS,\n filterSlashItems,\n recordRecentCommand,\n getCustomCommandItems,\n TOOLTIP_MAP,\n SHORTCUT_MAP,\n MODAL_COMMANDS,\n getShortcutLabel,\n} from '@remyxjs/core'\n\n/**\n * Build command items from engine's registered commands + slash items + custom items.\n * Deduplicates by id and adds shortcut labels.\n */\nfunction buildCommandList(engine, customSlashItems) {\n const slashItems = customSlashItems || SLASH_COMMAND_ITEMS\n // Merge in globally registered custom items\n const customGlobal = getCustomCommandItems()\n const merged = [...slashItems]\n for (const ci of customGlobal) {\n if (!merged.some(m => m.id === ci.id)) {\n merged.push(ci)\n }\n }\n const seen = new Set(merged.map(i => i.id))\n const engineItems = []\n\n if (engine) {\n const allCommands = engine.commands.getAll()\n for (const name of allCommands) {\n if (seen.has(name)) continue\n // Skip internal/meta commands\n if (name === 'undo' || name === 'redo' || name === 'removeFormat') continue\n\n const tooltip = TOOLTIP_MAP[name]\n if (!tooltip) continue // Skip commands without a user-facing name\n\n const shortcut = SHORTCUT_MAP[name]\n const isModal = name in MODAL_COMMANDS\n\n engineItems.push({\n id: name,\n label: tooltip,\n description: '',\n icon: shortcut ? getShortcutLabel(name).charAt(0) : '\\u2022',\n keywords: [name],\n category: 'Commands',\n shortcutLabel: shortcut ? getShortcutLabel(name) : null,\n action: isModal\n ? (_eng, openModal) => openModal?.(MODAL_COMMANDS[name])\n : (eng) => eng.executeCommand(name),\n })\n seen.add(name)\n }\n }\n\n // Add shortcut labels to slash items\n const enrichedSlash = merged.map(item => ({\n ...item,\n shortcutLabel: SHORTCUT_MAP[item.id] ? getShortcutLabel(item.id) : null,\n }))\n\n return [...enrichedSlash, ...engineItems]\n}\n\nfunction CommandPaletteInner({ open, onClose, engine, onOpenModal, slashCommandItems }) {\n const inputRef = useRef(null)\n const listRef = useRef(null)\n const [query, setQuery] = useState('')\n const [selectedIndex, setSelectedIndex] = useState(0)\n\n const allItems = useMemo(\n () => buildCommandList(engine, slashCommandItems),\n [engine, slashCommandItems]\n )\n\n const filteredItems = useMemo(\n () => filterSlashItems(allItems, query),\n [allItems, query]\n )\n\n // Reset on open\n useEffect(() => {\n if (open) {\n setQuery('')\n setSelectedIndex(0)\n // Focus input after render\n requestAnimationFrame(() => inputRef.current?.focus())\n }\n }, [open])\n\n // Reset index on filter change\n useEffect(() => {\n setSelectedIndex(0)\n }, [query])\n\n // Scroll active item into view\n useEffect(() => {\n if (!listRef.current) return\n const active = listRef.current.querySelector('.rmx-command-palette-item.rmx-active')\n if (active) {\n active.scrollIntoView({ block: 'nearest' })\n }\n }, [selectedIndex])\n\n const executeItem = useCallback((item) => {\n onClose()\n recordRecentCommand(item.id)\n // Defer execution so the palette is closed first and focus returns to editor\n requestAnimationFrame(() => {\n item.action(engine, onOpenModal)\n })\n }, [engine, onClose, onOpenModal])\n\n const handleKeyDown = useCallback((e) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n onClose()\n return\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setSelectedIndex(i => filteredItems.length === 0 ? 0 : (i + 1) % filteredItems.length)\n return\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n setSelectedIndex(i => filteredItems.length === 0 ? 0 : (i - 1 + filteredItems.length) % filteredItems.length)\n return\n }\n if (e.key === 'Enter') {\n e.preventDefault()\n if (filteredItems.length > 0 && selectedIndex < filteredItems.length) {\n executeItem(filteredItems[selectedIndex])\n }\n }\n }, [filteredItems, selectedIndex, executeItem, onClose])\n\n // Focus trap — prevent tab from escaping\n const handleOverlayKeyDown = useCallback((e) => {\n if (e.key === 'Tab') {\n e.preventDefault()\n }\n }, [])\n\n if (!open) return null\n\n // Group items by category\n const groups = []\n let lastCategory = null\n for (let i = 0; i < filteredItems.length; i++) {\n const item = filteredItems[i]\n if (item.category !== lastCategory) {\n groups.push({ category: item.category, items: [] })\n lastCategory = item.category\n }\n groups[groups.length - 1].items.push({ item, index: i })\n }\n\n return (\n <div\n className=\"rmx-command-palette-overlay\"\n onMouseDown={(e) => {\n if (e.target === e.currentTarget) {\n e.preventDefault()\n onClose()\n }\n }}\n onKeyDown={handleOverlayKeyDown}\n >\n <div className=\"rmx-command-palette\" role=\"dialog\" aria-label=\"Command palette\">\n <div className=\"rmx-command-palette-input-wrap\">\n <span className=\"rmx-command-palette-search-icon\" aria-hidden=\"true\">/</span>\n <input\n ref={inputRef}\n className=\"rmx-command-palette-input\"\n type=\"text\"\n placeholder=\"Type a command...\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n aria-activedescendant={\n filteredItems[selectedIndex]\n ? `rmx-cmd-item-${filteredItems[selectedIndex].id}`\n : undefined\n }\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls=\"rmx-command-palette-list\"\n aria-autocomplete=\"list\"\n />\n </div>\n\n <div\n ref={listRef}\n className=\"rmx-command-palette-list\"\n id=\"rmx-command-palette-list\"\n role=\"listbox\"\n >\n {filteredItems.length === 0 ? (\n <div className=\"rmx-command-palette-empty\">No matching commands</div>\n ) : (\n groups.map((group) => (\n <div key={group.category}>\n <div className=\"rmx-command-palette-category\">{group.category}</div>\n {group.items.map(({ item, index }) => (\n <div\n key={item.id}\n id={`rmx-cmd-item-${item.id}`}\n className={`rmx-command-palette-item${index === selectedIndex ? ' rmx-active' : ''}`}\n role=\"option\"\n aria-selected={index === selectedIndex}\n onMouseEnter={() => setSelectedIndex(index)}\n onClick={() => executeItem(item)}\n >\n <div className=\"rmx-command-palette-item-icon\">{item.icon}</div>\n <div className=\"rmx-command-palette-item-text\">\n <div className=\"rmx-command-palette-item-label\">{item.label}</div>\n {item.description && (\n <div className=\"rmx-command-palette-item-desc\">{item.description}</div>\n )}\n </div>\n {item.shortcutLabel && (\n <span className=\"rmx-command-palette-shortcut\">{item.shortcutLabel}</span>\n )}\n </div>\n ))}\n </div>\n ))\n )}\n </div>\n\n <div className=\"rmx-command-palette-hint\">\n <span><kbd>↑</kbd><kbd>↓</kbd> navigate</span>\n <span><kbd>↵</kbd> select</span>\n <span><kbd>esc</kbd> close</span>\n </div>\n </div>\n </div>\n )\n}\n\nexport const CommandPalette = React.memo(CommandPaletteInner)\n"],"names":["CommandPalette","React","memo","open","onClose","engine","onOpenModal","slashCommandItems","inputRef","useRef","listRef","query","setQuery","useState","selectedIndex","setSelectedIndex","allItems","useMemo","customSlashItems","slashItems","SLASH_COMMAND_ITEMS","customGlobal","getCustomCommandItems","merged","ci","some","m","id","push","seen","Set","map","i","engineItems","allCommands","commands","getAll","name","has","tooltip","TOOLTIP_MAP","shortcut","SHORTCUT_MAP","isModal","MODAL_COMMANDS","label","description","icon","getShortcutLabel","charAt","keywords","category","shortcutLabel","action","_eng","openModal","eng","executeCommand","add","item","buildCommandList","filteredItems","filterSlashItems","useEffect","requestAnimationFrame","current","focus","active","querySelector","scrollIntoView","block","executeItem","useCallback","recordRecentCommand","handleKeyDown","e","key","preventDefault","length","handleOverlayKeyDown","groups","lastCategory","items","index","jsx","className","onMouseDown","target","currentTarget","onKeyDown","children","role","jsxs","ref","type","placeholder","value","onChange","group","onMouseEnter","onClick"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("react"),r=require("./index-Dc63uIP0.cjs");const n=t.memo((function({contextMenu:r,onHide:n,onOpenModal:s}){const[o,a]=t.useState(-1),u=t.useRef(null),c=t.useRef([]),i=t.useMemo((()=>r.visible&&r.items?r.items.map(((e,t)=>e.separator?null:t)).filter((e=>null!==e)):[]),[r.visible,r.items]);t.useEffect((()=>{r.visible&&u.current&&(u.current.focus(),a(-1))}),[r.visible]),t.useEffect((()=>{o>=0&&c.current[o]&&c.current[o].focus()}),[o]);const l=t.useCallback((e=>{if("function"==typeof e.command)try{e.command()}catch(t){}else"editLinkModal"===e.command&&s?.("link",{href:e.data?.href,text:e.data?.textContent,target:e.data?.target});n()}),[n,s]),p=t.useCallback((e=>{if(!i.length)return;const t=i.indexOf(o);switch(e.key){case"ArrowDown":{e.preventDefault();const r=t<i.length-1?t+1:0;a(i[r]);break}case"ArrowUp":{e.preventDefault();const r=t>0?t-1:i.length-1;a(i[r]);break}case"Home":e.preventDefault(),a(i[0]);break;case"End":e.preventDefault(),a(i[i.length-1]);break;case"Enter":e.preventDefault(),o>=0&&r.items[o]&&l(r.items[o]);break;case"Escape":case"Tab":e.preventDefault(),n()}}),[i,o,r.items,l,n]);return r.visible?e.jsx("div",{ref:u,className:"rmx-context-menu",style:{top:r.y,left:r.x},role:"menu",tabIndex:-1,onKeyDown:p,children:r.items.map(((t,r)=>t.separator?e.jsx("div",{className:"rmx-context-menu-separator",role:"separator"},r):e.jsx("button",{ref:e=>{c.current[r]=e},className:"rmx-context-menu-item"+(o===r?" rmx-focused":""),onClick:()=>l(t),onMouseEnter:()=>a(r),type:"button",role:"menuitem",tabIndex:-1,children:t.label},r)))}):null}));n.propTypes={contextMenu:r.PropTypes.shape({visible:r.PropTypes.bool,x:r.PropTypes.number,y:r.PropTypes.number,items:r.PropTypes.array}).isRequired,onHide:r.PropTypes.func.isRequired,onOpenModal:r.PropTypes.func},exports.ContextMenu=n;
2
+ //# sourceMappingURL=ContextMenu-B4K3Zbfp.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextMenu-B4K3Zbfp.cjs","sources":["../src/components/ContextMenu/ContextMenu.jsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'\nimport PropTypes from 'prop-types'\n\nfunction ContextMenuInner({ contextMenu, onHide, onOpenModal }) {\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const menuRef = useRef(null)\n const itemRefs = useRef([])\n\n // Compute actionable (non-separator) item indices\n const actionableIndices = useMemo(() => {\n if (!contextMenu.visible || !contextMenu.items) return []\n return contextMenu.items\n .map((item, i) => (item.separator ? null : i))\n .filter((i) => i !== null)\n }, [contextMenu.visible, contextMenu.items])\n\n // Focus the menu container when it appears\n useEffect(() => {\n if (contextMenu.visible && menuRef.current) {\n menuRef.current.focus()\n setFocusedIndex(-1)\n }\n }, [contextMenu.visible])\n\n // Keep focused item in sync with DOM focus\n useEffect(() => {\n if (focusedIndex >= 0 && itemRefs.current[focusedIndex]) {\n itemRefs.current[focusedIndex].focus()\n }\n }, [focusedIndex])\n\n const executeItem = useCallback((item) => {\n if (typeof item.command === 'function') {\n try {\n item.command()\n } catch (err) {\n console.error('[Remyx] Context menu command failed:', err)\n }\n } else if (item.command === 'editLinkModal') {\n onOpenModal?.('link', {\n href: item.data?.href,\n text: item.data?.textContent,\n target: item.data?.target,\n })\n }\n onHide()\n }, [onHide, onOpenModal])\n\n const handleKeyDown = useCallback((e) => {\n if (!actionableIndices.length) return\n\n const currentPos = actionableIndices.indexOf(focusedIndex)\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const next = currentPos < actionableIndices.length - 1 ? currentPos + 1 : 0\n setFocusedIndex(actionableIndices[next])\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prev = currentPos > 0 ? currentPos - 1 : actionableIndices.length - 1\n setFocusedIndex(actionableIndices[prev])\n break\n }\n case 'Home': {\n e.preventDefault()\n setFocusedIndex(actionableIndices[0])\n break\n }\n case 'End': {\n e.preventDefault()\n setFocusedIndex(actionableIndices[actionableIndices.length - 1])\n break\n }\n case 'Enter': {\n e.preventDefault()\n if (focusedIndex >= 0 && contextMenu.items[focusedIndex]) {\n executeItem(contextMenu.items[focusedIndex])\n }\n break\n }\n case 'Escape': {\n e.preventDefault()\n onHide()\n break\n }\n case 'Tab': {\n e.preventDefault()\n onHide()\n break\n }\n default:\n break\n }\n }, [actionableIndices, focusedIndex, contextMenu.items, executeItem, onHide])\n\n if (!contextMenu.visible) return null\n\n return (\n <div\n ref={menuRef}\n className=\"rmx-context-menu\"\n style={{ top: contextMenu.y, left: contextMenu.x }}\n role=\"menu\"\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n {contextMenu.items.map((item, i) => {\n if (item.separator) {\n return <div key={i} className=\"rmx-context-menu-separator\" role=\"separator\" />\n }\n return (\n <button\n key={i}\n ref={(el) => { itemRefs.current[i] = el }}\n className={`rmx-context-menu-item${focusedIndex === i ? ' rmx-focused' : ''}`}\n onClick={() => executeItem(item)}\n onMouseEnter={() => setFocusedIndex(i)}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n >\n {item.label}\n </button>\n )\n })}\n </div>\n )\n}\n\nexport const ContextMenu = React.memo(ContextMenuInner)\n\nContextMenu.propTypes = {\n contextMenu: PropTypes.shape({\n visible: PropTypes.bool,\n x: PropTypes.number,\n y: PropTypes.number,\n items: PropTypes.array,\n }).isRequired,\n onHide: PropTypes.func.isRequired,\n onOpenModal: PropTypes.func,\n}\n"],"names":["ContextMenu","React","memo","contextMenu","onHide","onOpenModal","focusedIndex","setFocusedIndex","useState","menuRef","useRef","itemRefs","actionableIndices","useMemo","visible","items","map","item","i","separator","filter","useEffect","current","focus","executeItem","useCallback","command","err","href","data","text","textContent","target","handleKeyDown","e","length","currentPos","indexOf","key","preventDefault","next","prev","jsx","ref","className","style","top","y","left","x","role","tabIndex","onKeyDown","children","el","onClick","onMouseEnter","type","label","propTypes","PropTypes","shape","bool","number","array","isRequired","func"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{jsx as e}from"react/jsx-runtime";import t,{useState as n,useRef as r,useMemo as a,useEffect as o,useCallback as s}from"react";import{P as i}from"./index-C720tbJA.js";const c=t.memo((function({contextMenu:t,onHide:i,onOpenModal:c}){const[l,m]=n(-1),u=r(null),p=r([]),f=a((()=>t.visible&&t.items?t.items.map(((e,t)=>e.separator?null:t)).filter((e=>null!==e)):[]),[t.visible,t.items]);o((()=>{t.visible&&u.current&&(u.current.focus(),m(-1))}),[t.visible]),o((()=>{l>=0&&p.current[l]&&p.current[l].focus()}),[l]);const d=s((e=>{if("function"==typeof e.command)try{e.command()}catch(t){}else"editLinkModal"===e.command&&c?.("link",{href:e.data?.href,text:e.data?.textContent,target:e.data?.target});i()}),[i,c]),b=s((e=>{if(!f.length)return;const n=f.indexOf(l);switch(e.key){case"ArrowDown":{e.preventDefault();const t=n<f.length-1?n+1:0;m(f[t]);break}case"ArrowUp":{e.preventDefault();const t=n>0?n-1:f.length-1;m(f[t]);break}case"Home":e.preventDefault(),m(f[0]);break;case"End":e.preventDefault(),m(f[f.length-1]);break;case"Enter":e.preventDefault(),l>=0&&t.items[l]&&d(t.items[l]);break;case"Escape":case"Tab":e.preventDefault(),i()}}),[f,l,t.items,d,i]);return t.visible?/* @__PURE__ */e("div",{ref:u,className:"rmx-context-menu",style:{top:t.y,left:t.x},role:"menu",tabIndex:-1,onKeyDown:b,children:t.items.map(((t,n)=>t.separator?/* @__PURE__ */e("div",{className:"rmx-context-menu-separator",role:"separator"},n):/* @__PURE__ */e("button",{ref:e=>{p.current[n]=e},className:"rmx-context-menu-item"+(l===n?" rmx-focused":""),onClick:()=>d(t),onMouseEnter:()=>m(n),type:"button",role:"menuitem",tabIndex:-1,children:t.label},n)))}):null}));c.propTypes={contextMenu:i.shape({visible:i.bool,x:i.number,y:i.number,items:i.array}).isRequired,onHide:i.func.isRequired,onOpenModal:i.func};export{c as ContextMenu};
2
+ //# sourceMappingURL=ContextMenu-D8wNSMc3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextMenu-D8wNSMc3.js","sources":["../src/components/ContextMenu/ContextMenu.jsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'\nimport PropTypes from 'prop-types'\n\nfunction ContextMenuInner({ contextMenu, onHide, onOpenModal }) {\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const menuRef = useRef(null)\n const itemRefs = useRef([])\n\n // Compute actionable (non-separator) item indices\n const actionableIndices = useMemo(() => {\n if (!contextMenu.visible || !contextMenu.items) return []\n return contextMenu.items\n .map((item, i) => (item.separator ? null : i))\n .filter((i) => i !== null)\n }, [contextMenu.visible, contextMenu.items])\n\n // Focus the menu container when it appears\n useEffect(() => {\n if (contextMenu.visible && menuRef.current) {\n menuRef.current.focus()\n setFocusedIndex(-1)\n }\n }, [contextMenu.visible])\n\n // Keep focused item in sync with DOM focus\n useEffect(() => {\n if (focusedIndex >= 0 && itemRefs.current[focusedIndex]) {\n itemRefs.current[focusedIndex].focus()\n }\n }, [focusedIndex])\n\n const executeItem = useCallback((item) => {\n if (typeof item.command === 'function') {\n try {\n item.command()\n } catch (err) {\n console.error('[Remyx] Context menu command failed:', err)\n }\n } else if (item.command === 'editLinkModal') {\n onOpenModal?.('link', {\n href: item.data?.href,\n text: item.data?.textContent,\n target: item.data?.target,\n })\n }\n onHide()\n }, [onHide, onOpenModal])\n\n const handleKeyDown = useCallback((e) => {\n if (!actionableIndices.length) return\n\n const currentPos = actionableIndices.indexOf(focusedIndex)\n\n switch (e.key) {\n case 'ArrowDown': {\n e.preventDefault()\n const next = currentPos < actionableIndices.length - 1 ? currentPos + 1 : 0\n setFocusedIndex(actionableIndices[next])\n break\n }\n case 'ArrowUp': {\n e.preventDefault()\n const prev = currentPos > 0 ? currentPos - 1 : actionableIndices.length - 1\n setFocusedIndex(actionableIndices[prev])\n break\n }\n case 'Home': {\n e.preventDefault()\n setFocusedIndex(actionableIndices[0])\n break\n }\n case 'End': {\n e.preventDefault()\n setFocusedIndex(actionableIndices[actionableIndices.length - 1])\n break\n }\n case 'Enter': {\n e.preventDefault()\n if (focusedIndex >= 0 && contextMenu.items[focusedIndex]) {\n executeItem(contextMenu.items[focusedIndex])\n }\n break\n }\n case 'Escape': {\n e.preventDefault()\n onHide()\n break\n }\n case 'Tab': {\n e.preventDefault()\n onHide()\n break\n }\n default:\n break\n }\n }, [actionableIndices, focusedIndex, contextMenu.items, executeItem, onHide])\n\n if (!contextMenu.visible) return null\n\n return (\n <div\n ref={menuRef}\n className=\"rmx-context-menu\"\n style={{ top: contextMenu.y, left: contextMenu.x }}\n role=\"menu\"\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n {contextMenu.items.map((item, i) => {\n if (item.separator) {\n return <div key={i} className=\"rmx-context-menu-separator\" role=\"separator\" />\n }\n return (\n <button\n key={i}\n ref={(el) => { itemRefs.current[i] = el }}\n className={`rmx-context-menu-item${focusedIndex === i ? ' rmx-focused' : ''}`}\n onClick={() => executeItem(item)}\n onMouseEnter={() => setFocusedIndex(i)}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n >\n {item.label}\n </button>\n )\n })}\n </div>\n )\n}\n\nexport const ContextMenu = React.memo(ContextMenuInner)\n\nContextMenu.propTypes = {\n contextMenu: PropTypes.shape({\n visible: PropTypes.bool,\n x: PropTypes.number,\n y: PropTypes.number,\n items: PropTypes.array,\n }).isRequired,\n onHide: PropTypes.func.isRequired,\n onOpenModal: PropTypes.func,\n}\n"],"names":["ContextMenu","React","memo","contextMenu","onHide","onOpenModal","focusedIndex","setFocusedIndex","useState","menuRef","useRef","itemRefs","actionableIndices","useMemo","visible","items","map","item","i","separator","filter","useEffect","current","focus","executeItem","useCallback","command","err","href","data","text","textContent","target","handleKeyDown","e","length","currentPos","indexOf","key","preventDefault","next","prev","jsx","ref","className","style","top","y","left","x","role","tabIndex","onKeyDown","children","el","onClick","onMouseEnter","type","label","propTypes","PropTypes","shape","bool","number","array","isRequired","func"],"mappings":"6KAoIO,MAAMA,EAAcC,EAAMC,MAjIjC,UAA0BC,YAAEA,EAAAC,OAAaA,EAAAC,YAAQA,IAC/C,MAAOC,EAAcC,GAAmBC,GAAS,GAC3CC,EAAUC,EAAO,MACjBC,EAAWD,EAAO,IAGlBE,EAAoBC,GAAQ,IAC3BV,EAAYW,SAAYX,EAAYY,MAClCZ,EAAYY,MAChBC,KAAI,CAACC,EAAMC,IAAOD,EAAKE,UAAY,KAAOD,IAC1CE,QAAQF,GAAY,OAANA,IAHsC,IAItD,CAACf,EAAYW,QAASX,EAAYY,QAGrCM,GAAU,KACJlB,EAAYW,SAAWL,EAAQa,UACjCb,EAAQa,QAAQC,QAChBhB,GAAgB,GAClB,GACC,CAACJ,EAAYW,UAGhBO,GAAU,KACJf,GAAgB,GAAKK,EAASW,QAAQhB,IACxCK,EAASW,QAAQhB,GAAciB,OACjC,GACC,CAACjB,IAEJ,MAAMkB,EAAcC,GAAaR,IAC/B,GAA4B,mBAAjBA,EAAKS,QACd,IACET,EAAKS,SACP,OAASC,GAET,KAC0B,kBAAjBV,EAAKS,SACdrB,IAAc,OAAQ,CACpBuB,KAAMX,EAAKY,MAAMD,KACjBE,KAAMb,EAAKY,MAAME,YACjBC,OAAQf,EAAKY,MAAMG,SAGvB5B,GAAA,GACC,CAACA,EAAQC,IAEN4B,EAAgBR,GAAaS,IACjC,IAAKtB,EAAkBuB,OAAQ,OAE/B,MAAMC,EAAaxB,EAAkByB,QAAQ/B,GAE7C,OAAQ4B,EAAEI,KACR,IAAK,YAAa,CAChBJ,EAAEK,iBACF,MAAMC,EAAOJ,EAAaxB,EAAkBuB,OAAS,EAAIC,EAAa,EAAI,EAC1E7B,EAAgBK,EAAkB4B,IAClC,KACF,CACA,IAAK,UAAW,CACdN,EAAEK,iBACF,MAAME,EAAOL,EAAa,EAAIA,EAAa,EAAIxB,EAAkBuB,OAAS,EAC1E5B,EAAgBK,EAAkB6B,IAClC,KACF,CACA,IAAK,OACHP,EAAEK,iBACFhC,EAAgBK,EAAkB,IAClC,MAEF,IAAK,MACHsB,EAAEK,iBACFhC,EAAgBK,EAAkBA,EAAkBuB,OAAS,IAC7D,MAEF,IAAK,QACHD,EAAEK,iBACEjC,GAAgB,GAAKH,EAAYY,MAAMT,IACzCkB,EAAYrB,EAAYY,MAAMT,IAEhC,MAEF,IAAK,SAKL,IAAK,MACH4B,EAAEK,iBACFnC,IAIA,GAEH,CAACQ,EAAmBN,EAAcH,EAAYY,MAAOS,EAAapB,IAErE,OAAKD,EAAYW,uBAGf4B,EAAC,MAAA,CACCC,IAAKlC,EACLmC,UAAU,mBACVC,MAAO,CAAEC,IAAK3C,EAAY4C,EAAGC,KAAM7C,EAAY8C,GAC/CC,KAAK,OACLC,UAAU,EACVC,UAAWnB,EAEVoB,SAAAlD,EAAYY,MAAMC,KAAI,CAACC,EAAMC,IACxBD,EAAKE,2BACC,MAAA,CAAYyB,UAAU,6BAA6BM,KAAK,aAA/ChC,kBAGjBwB,EAAC,SAAA,CAECC,IAAMW,IAAS3C,EAASW,QAAQJ,GAAKoC,CAAA,EACrCV,UAAW,yBAAwBtC,IAAiBY,EAAI,eAAiB,IACzEqC,QAAS,IAAM/B,EAAYP,GAC3BuC,aAAc,IAAMjD,EAAgBW,GACpCuC,KAAK,SACLP,KAAK,WACLC,UAAU,EAETE,SAAApC,EAAKyC,OATDxC,OAjBkB,IAgCnC,IAIAlB,EAAY2D,UAAY,CACtBxD,YAAayD,EAAUC,MAAM,CAC3B/C,QAAS8C,EAAUE,KACnBb,EAAGW,EAAUG,OACbhB,EAAGa,EAAUG,OACbhD,MAAO6C,EAAUI,QAChBC,WACH7D,OAAQwD,EAAUM,KAAKD,WACvB5D,YAAauD,EAAUM"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("react"),r=require("./index-Dc63uIP0.cjs"),o=require("./ModalOverlay-BDsGgv3_.cjs"),i=/^\s*(javascript|vbscript|data\s*:\s*text\/html)\s*:/i,a=[{pattern:/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]+)/,toEmbed:e=>`https://www.youtube.com/embed/${e}`},{pattern:/vimeo\.com\/(\d+)/,toEmbed:e=>`https://player.vimeo.com/video/${e}`},{pattern:/dailymotion\.com\/video\/([a-zA-Z0-9]+)/,toEmbed:e=>`https://www.dailymotion.com/embed/video/${e}`}];function l({open:r,onClose:l,engine:s}){const[n,m]=t.useState(""),[c,d]=t.useState(null),[u,p]=t.useState(!1),b=t.useRef(null);t.useEffect((()=>{if(b.current&&clearTimeout(b.current),n.trim())return b.current=setTimeout((()=>{d(function(e){if(!e)return null;for(const t of a){const r=e.match(t.pattern);if(r)return t.toEmbed(r[1])}return null}(n.trim())),p(!1)}),500),()=>clearTimeout(b.current);d(null)}),[n]),t.useEffect((()=>{r||(m(""),d(null))}),[r]);return e.jsx(o.ModalOverlay,{title:"Embed Media",open:r,onClose:l,children:e.jsxs("form",{onSubmit:e=>{if(e.preventDefault(),!n.trim())return;let t;try{t=decodeURIComponent(n.trim())}catch{t=n.trim()}i.test(t)||(s.executeCommand("embedMedia",{url:n}),l(),m(""))},className:"rmx-modal-form",children:[e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-embed-url",children:"Video URL"}),e.jsx("input",{id:"rmx-embed-url",type:"url",className:"rmx-form-input",value:n,onChange:e=>m(e.target.value),placeholder:"https://www.youtube.com/watch?v=...",required:!0,autoFocus:!0})]}),e.jsx("p",{className:"rmx-form-hint",children:"Supports YouTube, Vimeo, and Dailymotion URLs"}),n.trim()&&e.jsx("div",{className:"rmx-embed-preview",children:c?e.jsxs("div",{className:"rmx-embed-loading-container",children:[!u&&e.jsx("div",{className:"rmx-embed-loading-text",children:"Loading preview..."}),e.jsx("iframe",{src:c,title:"Embed preview",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0,sandbox:"allow-scripts allow-same-origin allow-presentation",onLoad:()=>p(!0)})]}):e.jsx("div",{className:"rmx-embed-preview-placeholder",children:"Enter a valid YouTube, Vimeo, or Dailymotion URL to see a preview"})}),e.jsxs("div",{className:"rmx-modal-actions",children:[e.jsx("button",{type:"button",className:"rmx-btn",onClick:l,children:"Cancel"}),e.jsx("button",{type:"submit",className:"rmx-btn rmx-btn-primary",disabled:!n.trim(),children:"Embed"})]})]})})}l.propTypes={open:r.PropTypes.bool.isRequired,onClose:r.PropTypes.func.isRequired,engine:r.PropTypes.object},exports.EmbedModal=l;
2
+ //# sourceMappingURL=EmbedModal-Bh2Tcow9.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbedModal-Bh2Tcow9.cjs","sources":["../src/components/Modals/EmbedModal.jsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\nconst DANGEROUS_PROTOCOL = /^\\s*(javascript|vbscript|data\\s*:\\s*text\\/html)\\s*:/i\n\nconst ALLOWED_EMBED_DOMAINS = [\n { pattern: /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([a-zA-Z0-9_-]+)/, toEmbed: (id) => `https://www.youtube.com/embed/${id}` },\n { pattern: /vimeo\\.com\\/(\\d+)/, toEmbed: (id) => `https://player.vimeo.com/video/${id}` },\n { pattern: /dailymotion\\.com\\/video\\/([a-zA-Z0-9]+)/, toEmbed: (id) => `https://www.dailymotion.com/embed/video/${id}` },\n]\n\nfunction getEmbedUrl(url) {\n if (!url) return null\n for (const domain of ALLOWED_EMBED_DOMAINS) {\n const match = url.match(domain.pattern)\n if (match) return domain.toEmbed(match[1])\n }\n return null\n}\n\nexport function EmbedModal({ open, onClose, engine }) {\n const [url, setUrl] = useState('')\n const [previewUrl, setPreviewUrl] = useState(null)\n const [iframeLoaded, setIframeLoaded] = useState(false)\n const debounceRef = useRef(null)\n\n // Debounce the preview URL update\n useEffect(() => {\n if (debounceRef.current) clearTimeout(debounceRef.current)\n if (!url.trim()) {\n setPreviewUrl(null)\n return\n }\n debounceRef.current = setTimeout(() => {\n setPreviewUrl(getEmbedUrl(url.trim()))\n setIframeLoaded(false)\n }, 500)\n return () => clearTimeout(debounceRef.current)\n }, [url])\n\n // Reset state when modal closes\n useEffect(() => {\n if (!open) {\n setUrl('')\n setPreviewUrl(null)\n }\n }, [open])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!url.trim()) return\n // Decode percent-encoding before validation to prevent bypasses like java%73cript:\n let decoded\n try {\n decoded = decodeURIComponent(url.trim())\n } catch {\n decoded = url.trim()\n }\n if (DANGEROUS_PROTOCOL.test(decoded)) return\n engine.executeCommand('embedMedia', { url })\n onClose()\n setUrl('')\n }\n\n return (\n <ModalOverlay title=\"Embed Media\" open={open} onClose={onClose}>\n <form onSubmit={handleSubmit} className=\"rmx-modal-form\">\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-embed-url\">Video URL</label>\n <input\n id=\"rmx-embed-url\"\n type=\"url\"\n className=\"rmx-form-input\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder=\"https://www.youtube.com/watch?v=...\"\n required\n autoFocus\n />\n </div>\n <p className=\"rmx-form-hint\">Supports YouTube, Vimeo, and Dailymotion URLs</p>\n\n {url.trim() && (\n <div className=\"rmx-embed-preview\">\n {previewUrl ? (\n <div className=\"rmx-embed-loading-container\">\n {!iframeLoaded && <div className=\"rmx-embed-loading-text\">Loading preview...</div>}\n <iframe\n src={previewUrl}\n title=\"Embed preview\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n sandbox=\"allow-scripts allow-same-origin allow-presentation\"\n onLoad={() => setIframeLoaded(true)}\n />\n </div>\n ) : (\n <div className=\"rmx-embed-preview-placeholder\">\n Enter a valid YouTube, Vimeo, or Dailymotion URL to see a preview\n </div>\n )}\n </div>\n )}\n\n <div className=\"rmx-modal-actions\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\" disabled={!url.trim()}>Embed</button>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nEmbedModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["DANGEROUS_PROTOCOL","ALLOWED_EMBED_DOMAINS","pattern","toEmbed","id","EmbedModal","open","onClose","engine","url","setUrl","useState","previewUrl","setPreviewUrl","iframeLoaded","setIframeLoaded","debounceRef","useRef","useEffect","current","clearTimeout","trim","setTimeout","domain","match","getEmbedUrl","jsx","ModalOverlay","title","children","jsxs","onSubmit","e","preventDefault","decoded","decodeURIComponent","test","executeCommand","className","htmlFor","type","value","onChange","target","placeholder","required","autoFocus","src","allow","allowFullScreen","sandbox","onLoad","onClick","disabled","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"mNAIMA,EAAqB,uDAErBC,EAAwB,CAC5B,CAAEC,QAAS,0DAA2DC,QAAUC,GAAO,iCAAiCA,KACxH,CAAEF,QAAS,oBAAqBC,QAAUC,GAAO,kCAAkCA,KACnF,CAAEF,QAAS,0CAA2CC,QAAUC,GAAO,2CAA2CA,MAY7G,SAASC,GAAWC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,IAC1C,MAAOC,EAAKC,GAAUC,EAAAA,SAAS,KACxBC,EAAYC,GAAiBF,EAAAA,SAAS,OACtCG,EAAcC,GAAmBJ,EAAAA,UAAS,GAC3CK,EAAcC,EAAAA,OAAO,MAG3BC,EAAAA,WAAU,KAER,GADIF,EAAYG,SAASC,aAAaJ,EAAYG,SAC7CV,EAAIY,OAQT,OAJAL,EAAYG,QAAUG,YAAW,KAC/BT,EAvBN,SAAqBJ,GACnB,IAAKA,EAAK,OAAO,KACjB,IAAA,MAAWc,KAAUtB,EAAuB,CAC1C,MAAMuB,EAAQf,EAAIe,MAAMD,EAAOrB,SAC/B,GAAIsB,EAAO,OAAOD,EAAOpB,QAAQqB,EAAM,GACzC,CACA,OAAO,IACT,CAgBoBC,CAAYhB,EAAIY,SAC9BN,GAAgB,EAAK,GACpB,KACI,IAAMK,aAAaJ,EAAYG,SAPpCN,EAAc,KAO6B,GAC5C,CAACJ,IAGJS,EAAAA,WAAU,KACHZ,IACHI,EAAO,IACPG,EAAc,MAChB,GACC,CAACP,IAkBJ,SACEoB,IAACC,EAAAA,aAAA,CAAaC,MAAM,cAActB,OAAYC,UAC5CsB,SAAAC,EAAAA,KAAC,OAAA,CAAKC,SAlBYC,IAEpB,GADAA,EAAEC,kBACGxB,EAAIY,OAAQ,OAEjB,IAAIa,EACJ,IACEA,EAAUC,mBAAmB1B,EAAIY,OACnC,CAAA,MACEa,EAAUzB,EAAIY,MAChB,CACIrB,EAAmBoC,KAAKF,KAC5B1B,EAAO6B,eAAe,aAAc,CAAE5B,QACtCF,IACAG,EAAO,IAAE,EAKuB4B,UAAU,iBACtCT,SAAA,GAAAC,KAAC,MAAA,CAAIQ,UAAU,iBACbT,SAAA,CAAAH,MAAC,QAAA,CAAMY,UAAU,iBAAiBC,QAAQ,gBAAgBV,SAAA,cAC1DH,EAAAA,IAAC,QAAA,CACCtB,GAAG,gBACHoC,KAAK,MACLF,UAAU,iBACVG,MAAOhC,EACPiC,SAAWV,GAAMtB,EAAOsB,EAAEW,OAAOF,OACjCG,YAAY,sCACZC,UAAQ,EACRC,WAAS,OAGbpB,EAAAA,IAAC,IAAA,CAAEY,UAAU,gBAAgBT,SAAA,kDAE5BpB,EAAIY,QACHK,EAAAA,IAAC,MAAA,CAAIY,UAAU,oBACZT,SAAAjB,EACCkB,EAAAA,KAAC,MAAA,CAAIQ,UAAU,8BACZT,SAAA,EAACf,GAAgBY,EAAAA,IAAC,MAAA,CAAIY,UAAU,yBAAyBT,SAAA,uBAC1DH,EAAAA,IAAC,SAAA,CACCqB,IAAKnC,EACLgB,MAAM,gBACNoB,MAAM,2FACNC,iBAAe,EACfC,QAAQ,qDACRC,OAAQ,IAAMpC,GAAgB,QAIlCW,MAAC,MAAA,CAAIY,UAAU,gCAAgCT,mFAOrDC,KAAC,MAAA,CAAIQ,UAAU,oBACbT,SAAA,CAAAH,EAAAA,IAAC,UAAOc,KAAK,SAASF,UAAU,UAAUc,QAAS7C,EAASsB,SAAA,WAC5DH,EAAAA,IAAC,SAAA,CAAOc,KAAK,SAASF,UAAU,0BAA0Be,UAAW5C,EAAIY,OAAQQ,SAAA,iBAK3F,CAEAxB,EAAWiD,UAAY,CACrBhD,KAAMiD,EAAAA,UAAUC,KAAKC,WACrBlD,QAASgD,EAAAA,UAAUG,KAAKD,WACxBjD,OAAQ+C,EAAAA,UAAUI"}
@@ -0,0 +1,11 @@
1
+ import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useState as t,useRef as o,useEffect as i}from"react";import{P as m}from"./index-C720tbJA.js";import{M as a}from"./ModalOverlay-CLvRNHmp.js";const l=/^\s*(javascript|vbscript|data\s*:\s*text\/html)\s*:/i,n=[{pattern:/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]+)/,toEmbed:e=>`https://www.youtube.com/embed/${e}`},{pattern:/vimeo\.com\/(\d+)/,toEmbed:e=>`https://player.vimeo.com/video/${e}`},{pattern:/dailymotion\.com\/video\/([a-zA-Z0-9]+)/,toEmbed:e=>`https://www.dailymotion.com/embed/video/${e}`}];function c({open:m,onClose:c,engine:d}){const[s,u]=t(""),[p,b]=t(null),[h,v]=t(!1),w=o(null);i((()=>{if(w.current&&clearTimeout(w.current),s.trim())return w.current=setTimeout((()=>{b(function(e){if(!e)return null;for(const r of n){const t=e.match(r.pattern);if(t)return r.toEmbed(t[1])}return null}(s.trim())),v(!1)}),500),()=>clearTimeout(w.current);b(null)}),[s]),i((()=>{m||(u(""),b(null))}),[m]);/* @__PURE__ */
2
+ return e(a,{title:"Embed Media",open:m,onClose:c,children:/* @__PURE__ */r("form",{onSubmit:e=>{if(e.preventDefault(),!s.trim())return;let r;try{r=decodeURIComponent(s.trim())}catch{r=s.trim()}l.test(r)||(d.executeCommand("embedMedia",{url:s}),c(),u(""))},className:"rmx-modal-form",children:[
3
+ /* @__PURE__ */r("div",{className:"rmx-form-group",children:[
4
+ /* @__PURE__ */e("label",{className:"rmx-form-label",htmlFor:"rmx-embed-url",children:"Video URL"}),
5
+ /* @__PURE__ */e("input",{id:"rmx-embed-url",type:"url",className:"rmx-form-input",value:s,onChange:e=>u(e.target.value),placeholder:"https://www.youtube.com/watch?v=...",required:!0,autoFocus:!0})]}),
6
+ /* @__PURE__ */e("p",{className:"rmx-form-hint",children:"Supports YouTube, Vimeo, and Dailymotion URLs"}),s.trim()&&/* @__PURE__ */e("div",{className:"rmx-embed-preview",children:p?/* @__PURE__ */r("div",{className:"rmx-embed-loading-container",children:[!h&&/* @__PURE__ */e("div",{className:"rmx-embed-loading-text",children:"Loading preview..."}),
7
+ /* @__PURE__ */e("iframe",{src:p,title:"Embed preview",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0,sandbox:"allow-scripts allow-same-origin allow-presentation",onLoad:()=>v(!0)})]}):/* @__PURE__ */e("div",{className:"rmx-embed-preview-placeholder",children:"Enter a valid YouTube, Vimeo, or Dailymotion URL to see a preview"})}),
8
+ /* @__PURE__ */r("div",{className:"rmx-modal-actions",children:[
9
+ /* @__PURE__ */e("button",{type:"button",className:"rmx-btn",onClick:c,children:"Cancel"}),
10
+ /* @__PURE__ */e("button",{type:"submit",className:"rmx-btn rmx-btn-primary",disabled:!s.trim(),children:"Embed"})]})]})})}c.propTypes={open:m.bool.isRequired,onClose:m.func.isRequired,engine:m.object};export{c as EmbedModal};
11
+ //# sourceMappingURL=EmbedModal-cxE4maD2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmbedModal-cxE4maD2.js","sources":["../src/components/Modals/EmbedModal.jsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\nconst DANGEROUS_PROTOCOL = /^\\s*(javascript|vbscript|data\\s*:\\s*text\\/html)\\s*:/i\n\nconst ALLOWED_EMBED_DOMAINS = [\n { pattern: /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([a-zA-Z0-9_-]+)/, toEmbed: (id) => `https://www.youtube.com/embed/${id}` },\n { pattern: /vimeo\\.com\\/(\\d+)/, toEmbed: (id) => `https://player.vimeo.com/video/${id}` },\n { pattern: /dailymotion\\.com\\/video\\/([a-zA-Z0-9]+)/, toEmbed: (id) => `https://www.dailymotion.com/embed/video/${id}` },\n]\n\nfunction getEmbedUrl(url) {\n if (!url) return null\n for (const domain of ALLOWED_EMBED_DOMAINS) {\n const match = url.match(domain.pattern)\n if (match) return domain.toEmbed(match[1])\n }\n return null\n}\n\nexport function EmbedModal({ open, onClose, engine }) {\n const [url, setUrl] = useState('')\n const [previewUrl, setPreviewUrl] = useState(null)\n const [iframeLoaded, setIframeLoaded] = useState(false)\n const debounceRef = useRef(null)\n\n // Debounce the preview URL update\n useEffect(() => {\n if (debounceRef.current) clearTimeout(debounceRef.current)\n if (!url.trim()) {\n setPreviewUrl(null)\n return\n }\n debounceRef.current = setTimeout(() => {\n setPreviewUrl(getEmbedUrl(url.trim()))\n setIframeLoaded(false)\n }, 500)\n return () => clearTimeout(debounceRef.current)\n }, [url])\n\n // Reset state when modal closes\n useEffect(() => {\n if (!open) {\n setUrl('')\n setPreviewUrl(null)\n }\n }, [open])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!url.trim()) return\n // Decode percent-encoding before validation to prevent bypasses like java%73cript:\n let decoded\n try {\n decoded = decodeURIComponent(url.trim())\n } catch {\n decoded = url.trim()\n }\n if (DANGEROUS_PROTOCOL.test(decoded)) return\n engine.executeCommand('embedMedia', { url })\n onClose()\n setUrl('')\n }\n\n return (\n <ModalOverlay title=\"Embed Media\" open={open} onClose={onClose}>\n <form onSubmit={handleSubmit} className=\"rmx-modal-form\">\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-embed-url\">Video URL</label>\n <input\n id=\"rmx-embed-url\"\n type=\"url\"\n className=\"rmx-form-input\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder=\"https://www.youtube.com/watch?v=...\"\n required\n autoFocus\n />\n </div>\n <p className=\"rmx-form-hint\">Supports YouTube, Vimeo, and Dailymotion URLs</p>\n\n {url.trim() && (\n <div className=\"rmx-embed-preview\">\n {previewUrl ? (\n <div className=\"rmx-embed-loading-container\">\n {!iframeLoaded && <div className=\"rmx-embed-loading-text\">Loading preview...</div>}\n <iframe\n src={previewUrl}\n title=\"Embed preview\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n sandbox=\"allow-scripts allow-same-origin allow-presentation\"\n onLoad={() => setIframeLoaded(true)}\n />\n </div>\n ) : (\n <div className=\"rmx-embed-preview-placeholder\">\n Enter a valid YouTube, Vimeo, or Dailymotion URL to see a preview\n </div>\n )}\n </div>\n )}\n\n <div className=\"rmx-modal-actions\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\" disabled={!url.trim()}>Embed</button>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nEmbedModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["DANGEROUS_PROTOCOL","ALLOWED_EMBED_DOMAINS","pattern","toEmbed","id","EmbedModal","open","onClose","engine","url","setUrl","useState","previewUrl","setPreviewUrl","iframeLoaded","setIframeLoaded","debounceRef","useRef","useEffect","current","clearTimeout","trim","setTimeout","domain","match","getEmbedUrl","jsx","ModalOverlay","title","children","jsxs","onSubmit","e","preventDefault","decoded","decodeURIComponent","test","executeCommand","className","htmlFor","type","value","onChange","target","placeholder","required","autoFocus","src","allow","allowFullScreen","sandbox","onLoad","onClick","disabled","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"qMAIA,MAAMA,EAAqB,uDAErBC,EAAwB,CAC5B,CAAEC,QAAS,0DAA2DC,QAAUC,GAAO,iCAAiCA,KACxH,CAAEF,QAAS,oBAAqBC,QAAUC,GAAO,kCAAkCA,KACnF,CAAEF,QAAS,0CAA2CC,QAAUC,GAAO,2CAA2CA,MAY7G,SAASC,GAAWC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,IAC1C,MAAOC,EAAKC,GAAUC,EAAS,KACxBC,EAAYC,GAAiBF,EAAS,OACtCG,EAAcC,GAAmBJ,GAAS,GAC3CK,EAAcC,EAAO,MAG3BC,GAAU,KAER,GADIF,EAAYG,SAASC,aAAaJ,EAAYG,SAC7CV,EAAIY,OAQT,OAJAL,EAAYG,QAAUG,YAAW,KAC/BT,EAvBN,SAAqBJ,GACnB,IAAKA,EAAK,OAAO,KACjB,IAAA,MAAWc,KAAUtB,EAAuB,CAC1C,MAAMuB,EAAQf,EAAIe,MAAMD,EAAOrB,SAC/B,GAAIsB,EAAO,OAAOD,EAAOpB,QAAQqB,EAAM,GACzC,CACA,OAAO,IACT,CAgBoBC,CAAYhB,EAAIY,SAC9BN,GAAgB,EAAK,GACpB,KACI,IAAMK,aAAaJ,EAAYG,SAPpCN,EAAc,KAO6B,GAC5C,CAACJ,IAGJS,GAAU,KACHZ,IACHI,EAAO,IACPG,EAAc,MAChB,GACC,CAACP;AAkBJ,OACEoB,EAACC,EAAA,CAAaC,MAAM,cAActB,OAAYC,UAC5CsB,wBAAAC,EAAC,OAAA,CAAKC,SAlBYC,IAEpB,GADAA,EAAEC,kBACGxB,EAAIY,OAAQ,OAEjB,IAAIa,EACJ,IACEA,EAAUC,mBAAmB1B,EAAIY,OACnC,CAAA,MACEa,EAAUzB,EAAIY,MAChB,CACIrB,EAAmBoC,KAAKF,KAC5B1B,EAAO6B,eAAe,aAAc,CAAE5B,QACtCF,IACAG,EAAO,IAAE,EAKuB4B,UAAU,iBACtCT,SAAA;eAAAC,EAAC,MAAA,CAAIQ,UAAU,iBACbT,SAAA;eAAAH,EAAC,QAAA,CAAMY,UAAU,iBAAiBC,QAAQ,gBAAgBV,SAAA;eAC1DH,EAAC,QAAA,CACCtB,GAAG,gBACHoC,KAAK,MACLF,UAAU,iBACVG,MAAOhC,EACPiC,SAAWV,GAAMtB,EAAOsB,EAAEW,OAAOF,OACjCG,YAAY,sCACZC,UAAQ,EACRC,WAAS;iBAGZ,IAAA,CAAER,UAAU,gBAAgBT,SAAA,kDAE5BpB,EAAIY,uBACHK,EAAC,MAAA,CAAIY,UAAU,oBACZT,SAAAjB,iBACCkB,EAAC,MAAA,CAAIQ,UAAU,8BACZT,SAAA,EAACf,kBAAgBY,EAAC,MAAA,CAAIY,UAAU,yBAAyBT,SAAA;eAC1DH,EAAC,SAAA,CACCqB,IAAKnC,EACLgB,MAAM,gBACNoB,MAAM,2FACNC,iBAAe,EACfC,QAAQ,qDACRC,OAAQ,IAAMpC,GAAgB,uBAIlCW,EAAC,MAAA,CAAIY,UAAU,gCAAgCT;eAOrDC,EAAC,MAAA,CAAIQ,UAAU,oBACbT,SAAA;eAAAH,EAAC,UAAOc,KAAK,SAASF,UAAU,UAAUc,QAAS7C,EAASsB,SAAA;eAC5DH,EAAC,SAAA,CAAOc,KAAK,SAASF,UAAU,0BAA0Be,UAAW5C,EAAIY,OAAQQ,SAAA,iBAK3F,CAEAxB,EAAWiD,UAAY,CACrBhD,KAAMiD,EAAUC,KAAKC,WACrBlD,QAASgD,EAAUG,KAAKD,WACxBjD,OAAQ+C,EAAUI"}
@@ -0,0 +1,13 @@
1
+ import{jsxs as e,jsx as o}from"react/jsx-runtime";import{useState as r}from"react";import{P as n}from"./index-C720tbJA.js";import{M as t}from"./ModalOverlay-CLvRNHmp.js";import{exportAsPDF as a,exportAsMarkdown as l,exportAsDocx as s}from"@remyxjs/core";function i({open:n,onClose:i,engine:m}){const[p,c]=r(""),d=e=>{try{c(""),e(m.getHTML()),i()}catch(o){c(o.message||"Export failed. Please try again.")}};/* @__PURE__ */
2
+ return e(t,{title:"Export Document",open:n,onClose:()=>{c(""),i()},width:360,children:[p&&/* @__PURE__ */o("div",{className:"rmx-form-group rmx-form-error",children:p}),
3
+ /* @__PURE__ */e("div",{className:"rmx-export-options",children:[
4
+ /* @__PURE__ */e("button",{type:"button",className:"rmx-export-btn",onClick:()=>d(a),"aria-label":"Export as PDF — Opens print dialog",children:[
5
+ /* @__PURE__ */o("span",{className:"rmx-export-btn-label",children:"PDF"}),
6
+ /* @__PURE__ */o("span",{className:"rmx-export-btn-hint",children:"Opens print dialog to save as PDF"})]}),
7
+ /* @__PURE__ */e("button",{type:"button",className:"rmx-export-btn",onClick:()=>d(l),"aria-label":"Export as Markdown — Downloads .md file",children:[
8
+ /* @__PURE__ */o("span",{className:"rmx-export-btn-label",children:"Markdown"}),
9
+ /* @__PURE__ */o("span",{className:"rmx-export-btn-hint",children:"Downloads .md file"})]}),
10
+ /* @__PURE__ */e("button",{type:"button",className:"rmx-export-btn",onClick:()=>d(s),"aria-label":"Export as Word Document — Downloads .doc file",children:[
11
+ /* @__PURE__ */o("span",{className:"rmx-export-btn-label",children:"Word Document"}),
12
+ /* @__PURE__ */o("span",{className:"rmx-export-btn-hint",children:"Downloads .doc file"})]})]})]})}i.propTypes={open:n.bool.isRequired,onClose:n.func.isRequired,engine:n.object};export{i as ExportModal};
13
+ //# sourceMappingURL=ExportModal-Cf1uE4r_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExportModal-Cf1uE4r_.js","sources":["../src/components/Modals/ExportModal.jsx"],"sourcesContent":["import React, { useState } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\nimport { exportAsMarkdown, exportAsPDF, exportAsDocx } from '@remyxjs/core'\n\nexport function ExportModal({ open, onClose, engine }) {\n const [error, setError] = useState('')\n\n const handleExport = (exportFn) => {\n try {\n setError('')\n exportFn(engine.getHTML())\n onClose()\n } catch (err) {\n console.error('Export failed:', err)\n setError(err.message || 'Export failed. Please try again.')\n }\n }\n\n const handleClose = () => {\n setError('')\n onClose()\n }\n\n return (\n <ModalOverlay title=\"Export Document\" open={open} onClose={handleClose} width={360}>\n {error && (\n <div className=\"rmx-form-group rmx-form-error\">\n {error}\n </div>\n )}\n <div className=\"rmx-export-options\">\n <button\n type=\"button\"\n className=\"rmx-export-btn\"\n onClick={() => handleExport(exportAsPDF)}\n aria-label=\"Export as PDF — Opens print dialog\"\n >\n <span className=\"rmx-export-btn-label\">PDF</span>\n <span className=\"rmx-export-btn-hint\">Opens print dialog to save as PDF</span>\n </button>\n <button\n type=\"button\"\n className=\"rmx-export-btn\"\n onClick={() => handleExport(exportAsMarkdown)}\n aria-label=\"Export as Markdown — Downloads .md file\"\n >\n <span className=\"rmx-export-btn-label\">Markdown</span>\n <span className=\"rmx-export-btn-hint\">Downloads .md file</span>\n </button>\n <button\n type=\"button\"\n className=\"rmx-export-btn\"\n onClick={() => handleExport(exportAsDocx)}\n aria-label=\"Export as Word Document — Downloads .doc file\"\n >\n <span className=\"rmx-export-btn-label\">Word Document</span>\n <span className=\"rmx-export-btn-hint\">Downloads .doc file</span>\n </button>\n </div>\n </ModalOverlay>\n )\n}\n\nExportModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["ExportModal","open","onClose","engine","error","setError","useState","handleExport","exportFn","getHTML","err","message","jsxs","ModalOverlay","title","width","children","className","type","onClick","exportAsPDF","exportAsMarkdown","exportAsDocx","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"8PAKO,SAASA,GAAYC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,IAC3C,MAAOC,EAAOC,GAAYC,EAAS,IAE7BC,EAAgBC,IACpB,IACEH,EAAS,IACTG,EAASL,EAAOM,WAChBP,GACF,OAASQ,GAEPL,EAASK,EAAIC,SAAW,mCAC1B;AAQF,OACEC,EAACC,GAAaC,MAAM,kBAAkBb,OAAYC,QANhC,KAClBG,EAAS,IACTH,GAAA,EAIwEa,MAAO,IAC5EC,SAAA,CAAAZ,oBACE,MAAA,CAAIa,UAAU,gCACZD,SAAAZ;eAGLQ,EAAC,MAAA,CAAIK,UAAU,qBACbD,SAAA;eAAAJ,EAAC,SAAA,CACCM,KAAK,SACLD,UAAU,iBACVE,QAAS,IAAMZ,EAAaa,GAC5B,aAAW,qCAEXJ,SAAA;iBAAC,OAAA,CAAKC,UAAU,uBAAuBD,SAAA;iBACtC,OAAA,CAAKC,UAAU,sBAAsBD,SAAA;eAExCJ,EAAC,SAAA,CACCM,KAAK,SACLD,UAAU,iBACVE,QAAS,IAAMZ,EAAac,GAC5B,aAAW,0CAEXL,SAAA;iBAAC,OAAA,CAAKC,UAAU,uBAAuBD,SAAA;iBACtC,OAAA,CAAKC,UAAU,sBAAsBD,SAAA;eAExCJ,EAAC,SAAA,CACCM,KAAK,SACLD,UAAU,iBACVE,QAAS,IAAMZ,EAAae,GAC5B,aAAW,gDAEXN,SAAA;iBAAC,OAAA,CAAKC,UAAU,uBAAuBD,SAAA;iBACtC,OAAA,CAAKC,UAAU,sBAAsBD,SAAA,gCAKhD,CAEAhB,EAAYuB,UAAY,CACtBtB,KAAMuB,EAAUC,KAAKC,WACrBxB,QAASsB,EAAUG,KAAKD,WACxBvB,OAAQqB,EAAUI"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),o=require("./index-Dc63uIP0.cjs"),s=require("./ModalOverlay-BDsGgv3_.cjs"),t=require("@remyxjs/core");function n({open:o,onClose:n,engine:a}){const[l,i]=r.useState(""),p=e=>{try{i(""),e(a.getHTML()),n()}catch(r){i(r.message||"Export failed. Please try again.")}};return e.jsxs(s.ModalOverlay,{title:"Export Document",open:o,onClose:()=>{i(""),n()},width:360,children:[l&&e.jsx("div",{className:"rmx-form-group rmx-form-error",children:l}),e.jsxs("div",{className:"rmx-export-options",children:[e.jsxs("button",{type:"button",className:"rmx-export-btn",onClick:()=>p(t.exportAsPDF),"aria-label":"Export as PDF — Opens print dialog",children:[e.jsx("span",{className:"rmx-export-btn-label",children:"PDF"}),e.jsx("span",{className:"rmx-export-btn-hint",children:"Opens print dialog to save as PDF"})]}),e.jsxs("button",{type:"button",className:"rmx-export-btn",onClick:()=>p(t.exportAsMarkdown),"aria-label":"Export as Markdown — Downloads .md file",children:[e.jsx("span",{className:"rmx-export-btn-label",children:"Markdown"}),e.jsx("span",{className:"rmx-export-btn-hint",children:"Downloads .md file"})]}),e.jsxs("button",{type:"button",className:"rmx-export-btn",onClick:()=>p(t.exportAsDocx),"aria-label":"Export as Word Document — Downloads .doc file",children:[e.jsx("span",{className:"rmx-export-btn-label",children:"Word Document"}),e.jsx("span",{className:"rmx-export-btn-hint",children:"Downloads .doc file"})]})]})]})}n.propTypes={open:o.PropTypes.bool.isRequired,onClose:o.PropTypes.func.isRequired,engine:o.PropTypes.object},exports.ExportModal=n;
2
+ //# sourceMappingURL=ExportModal-DwQVsrZE.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExportModal-DwQVsrZE.cjs","sources":["../src/components/Modals/ExportModal.jsx"],"sourcesContent":["import React, { useState } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\nimport { exportAsMarkdown, exportAsPDF, exportAsDocx } from '@remyxjs/core'\n\nexport function ExportModal({ open, onClose, engine }) {\n const [error, setError] = useState('')\n\n const handleExport = (exportFn) => {\n try {\n setError('')\n exportFn(engine.getHTML())\n onClose()\n } catch (err) {\n console.error('Export failed:', err)\n setError(err.message || 'Export failed. Please try again.')\n }\n }\n\n const handleClose = () => {\n setError('')\n onClose()\n }\n\n return (\n <ModalOverlay title=\"Export Document\" open={open} onClose={handleClose} width={360}>\n {error && (\n <div className=\"rmx-form-group rmx-form-error\">\n {error}\n </div>\n )}\n <div className=\"rmx-export-options\">\n <button\n type=\"button\"\n className=\"rmx-export-btn\"\n onClick={() => handleExport(exportAsPDF)}\n aria-label=\"Export as PDF — Opens print dialog\"\n >\n <span className=\"rmx-export-btn-label\">PDF</span>\n <span className=\"rmx-export-btn-hint\">Opens print dialog to save as PDF</span>\n </button>\n <button\n type=\"button\"\n className=\"rmx-export-btn\"\n onClick={() => handleExport(exportAsMarkdown)}\n aria-label=\"Export as Markdown — Downloads .md file\"\n >\n <span className=\"rmx-export-btn-label\">Markdown</span>\n <span className=\"rmx-export-btn-hint\">Downloads .md file</span>\n </button>\n <button\n type=\"button\"\n className=\"rmx-export-btn\"\n onClick={() => handleExport(exportAsDocx)}\n aria-label=\"Export as Word Document — Downloads .doc file\"\n >\n <span className=\"rmx-export-btn-label\">Word Document</span>\n <span className=\"rmx-export-btn-hint\">Downloads .doc file</span>\n </button>\n </div>\n </ModalOverlay>\n )\n}\n\nExportModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["ExportModal","open","onClose","engine","error","setError","useState","handleExport","exportFn","getHTML","err","message","jsxs","ModalOverlay","title","width","children","jsx","className","type","onClick","exportAsPDF","exportAsMarkdown","exportAsDocx","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"8OAKO,SAASA,GAAYC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,IAC3C,MAAOC,EAAOC,GAAYC,EAAAA,SAAS,IAE7BC,EAAgBC,IACpB,IACEH,EAAS,IACTG,EAASL,EAAOM,WAChBP,GACF,OAASQ,GAEPL,EAASK,EAAIC,SAAW,mCAC1B,GAQF,OACEC,OAACC,EAAAA,cAAaC,MAAM,kBAAkBb,OAAYC,QANhC,KAClBG,EAAS,IACTH,GAAA,EAIwEa,MAAO,IAC5EC,SAAA,CAAAZ,KACCa,IAAC,MAAA,CAAIC,UAAU,gCACZF,SAAAZ,MAGLQ,KAAC,MAAA,CAAIM,UAAU,qBACbF,SAAA,CAAAJ,EAAAA,KAAC,SAAA,CACCO,KAAK,SACLD,UAAU,iBACVE,QAAS,IAAMb,EAAac,eAC5B,aAAW,qCAEXL,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAKC,UAAU,uBAAuBF,SAAA,QACvCC,EAAAA,IAAC,OAAA,CAAKC,UAAU,sBAAsBF,SAAA,yCAExCJ,EAAAA,KAAC,SAAA,CACCO,KAAK,SACLD,UAAU,iBACVE,QAAS,IAAMb,EAAae,oBAC5B,aAAW,0CAEXN,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAKC,UAAU,uBAAuBF,SAAA,aACvCC,EAAAA,IAAC,OAAA,CAAKC,UAAU,sBAAsBF,SAAA,0BAExCJ,EAAAA,KAAC,SAAA,CACCO,KAAK,SACLD,UAAU,iBACVE,QAAS,IAAMb,EAAagB,gBAC5B,aAAW,gDAEXP,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAKC,UAAU,uBAAuBF,SAAA,kBACvCC,EAAAA,IAAC,OAAA,CAAKC,UAAU,sBAAsBF,SAAA,gCAKhD,CAEAhB,EAAYwB,UAAY,CACtBvB,KAAMwB,EAAAA,UAAUC,KAAKC,WACrBzB,QAASuB,EAAAA,UAAUG,KAAKD,WACxBxB,OAAQsB,EAAAA,UAAUI"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("react"),n=require("./index-Dc63uIP0.cjs");function r({open:n,onClose:r,engine:a}){const[l,s]=t.useState(""),[c,o]=t.useState(""),[i,u]=t.useState(!1),[m,x]=t.useState({total:0,current:0}),[d,p]=t.useState(""),b=t.useRef(null);t.useEffect((()=>{if(!a)return;return a.eventBus.on("find:results",x)}),[a]),t.useEffect((()=>{n||(a?.executeCommand("clearFind"),s(""),o(""),x({total:0,current:0}),p(""))}),[n,a]);const f=()=>{if(l.trim())try{a.executeCommand("find",{text:l,caseSensitive:i})}catch(e){}},h=t.useCallback((()=>{const e=m.total;a.executeCommand("replaceAll",{replaceText:c}),p(`Replaced ${e} occurrence${1!==e?"s":""}`),setTimeout((()=>p("")),3e3)}),[a,c,m.total]),y=t.useCallback((e=>{if("Escape"===e.key)return e.preventDefault(),void r();if("Tab"===e.key&&b.current){const t=Array.from(b.current.querySelectorAll('input, button:not([disabled]), [tabindex]:not([tabindex="-1"])'));if(0===t.length)return void e.preventDefault();const n=t[0],r=t[t.length-1];e.shiftKey?document.activeElement===n&&(e.preventDefault(),r.focus()):document.activeElement===r&&(e.preventDefault(),n.focus())}}),[r]);return n?e.jsxs("div",{className:"rmx-find-replace-panel",onMouseDown:e=>e.stopPropagation(),onKeyDown:y,ref:b,role:"search","aria-label":"Find and replace",children:[e.jsxs("div",{className:"rmx-find-row",children:[e.jsx("label",{htmlFor:"rmx-find-input",className:"rmx-sr-only",children:"Find text"}),e.jsx("input",{id:"rmx-find-input",type:"text",className:"rmx-form-input rmx-find-input",value:l,onChange:e=>s(e.target.value),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),m.total>0?a.executeCommand("findNext"):f())},placeholder:"Find...",autoFocus:!0}),e.jsx("span",{className:"rmx-find-count",children:m.total>0?`${m.current}/${m.total}`:"No results"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:f,children:"Find"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:()=>a.executeCommand("findPrev"),"aria-label":"Previous match",children:"▲"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:()=>a.executeCommand("findNext"),"aria-label":"Next match",children:"▼"}),e.jsxs("label",{className:"rmx-form-checkbox rmx-find-case",children:[e.jsx("input",{type:"checkbox",checked:i,onChange:e=>u(e.target.checked),"aria-label":"Toggle case sensitive search"}),"Aa"]}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:r,"aria-label":"Close find and replace",children:"×"})]}),e.jsxs("div",{className:"rmx-find-row",children:[e.jsx("label",{htmlFor:"rmx-replace-input",className:"rmx-sr-only",children:"Replace text"}),e.jsx("input",{id:"rmx-replace-input",type:"text",className:"rmx-form-input rmx-find-input",value:c,onChange:e=>o(e.target.value),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),a.executeCommand("replace",{replaceText:c}))},placeholder:"Replace..."}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:()=>a.executeCommand("replace",{replaceText:c}),children:"Replace"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:h,children:"All"}),d&&e.jsx("span",{className:"rmx-find-count",role:"status",children:d})]})]}):null}r.propTypes={open:n.PropTypes.bool.isRequired,onClose:n.PropTypes.func.isRequired,engine:n.PropTypes.object},exports.FindReplacePanel=r;
2
+ //# sourceMappingURL=FindReplaceModal-DYL_2z8U.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FindReplaceModal-DYL_2z8U.cjs","sources":["../src/components/Modals/FindReplaceModal.jsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react'\nimport PropTypes from 'prop-types'\n\nconst FOCUSABLE_SELECTOR = 'input, button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nexport function FindReplacePanel({ open, onClose, engine }) {\n const [findText, setFindText] = useState('')\n const [replaceText, setReplaceText] = useState('')\n const [caseSensitive, setCaseSensitive] = useState(false)\n const [results, setResults] = useState({ total: 0, current: 0 })\n const [replaceAllMsg, setReplaceAllMsg] = useState('')\n const containerRef = useRef(null)\n\n useEffect(() => {\n if (!engine) return\n const unsub = engine.eventBus.on('find:results', setResults)\n return unsub\n }, [engine])\n\n useEffect(() => {\n if (!open) {\n engine?.executeCommand('clearFind')\n setFindText('')\n setReplaceText('')\n setResults({ total: 0, current: 0 })\n setReplaceAllMsg('')\n }\n }, [open, engine])\n\n const handleFind = () => {\n if (!findText.trim()) return\n try {\n engine.executeCommand('find', { text: findText, caseSensitive })\n } catch (err) {\n console.error('Find command failed:', err)\n }\n }\n\n // #32: Replace All with count feedback\n const handleReplaceAll = useCallback(() => {\n const count = results.total\n engine.executeCommand('replaceAll', { replaceText })\n setReplaceAllMsg(`Replaced ${count} occurrence${count !== 1 ? 's' : ''}`)\n setTimeout(() => setReplaceAllMsg(''), 3000)\n }, [engine, replaceText, results.total])\n\n // #31: Escape key handler + focus trap\n const handleKeyDown = useCallback((e) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n onClose()\n return\n }\n\n // Focus trap\n if (e.key === 'Tab' && containerRef.current) {\n const focusable = Array.from(containerRef.current.querySelectorAll(FOCUSABLE_SELECTOR))\n if (focusable.length === 0) { e.preventDefault(); return }\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n }, [onClose])\n\n if (!open) return null\n\n return (\n <div\n className=\"rmx-find-replace-panel\"\n onMouseDown={(e) => e.stopPropagation()}\n onKeyDown={handleKeyDown}\n ref={containerRef}\n role=\"search\"\n aria-label=\"Find and replace\"\n >\n <div className=\"rmx-find-row\">\n <label htmlFor=\"rmx-find-input\" className=\"rmx-sr-only\">Find text</label>\n <input\n id=\"rmx-find-input\"\n type=\"text\"\n className=\"rmx-form-input rmx-find-input\"\n value={findText}\n onChange={(e) => setFindText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n if (results.total > 0) {\n engine.executeCommand('findNext')\n } else {\n handleFind()\n }\n }\n }}\n placeholder=\"Find...\"\n autoFocus\n />\n <span className=\"rmx-find-count\">\n {results.total > 0 ? `${results.current}/${results.total}` : 'No results'}\n </span>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={handleFind}>Find</button>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={() => engine.executeCommand('findPrev')} aria-label=\"Previous match\">&#9650;</button>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={() => engine.executeCommand('findNext')} aria-label=\"Next match\">&#9660;</button>\n <label className=\"rmx-form-checkbox rmx-find-case\">\n <input\n type=\"checkbox\"\n checked={caseSensitive}\n onChange={(e) => setCaseSensitive(e.target.checked)}\n aria-label=\"Toggle case sensitive search\"\n />\n Aa\n </label>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={onClose} aria-label=\"Close find and replace\">&times;</button>\n </div>\n <div className=\"rmx-find-row\">\n <label htmlFor=\"rmx-replace-input\" className=\"rmx-sr-only\">Replace text</label>\n <input\n id=\"rmx-replace-input\"\n type=\"text\"\n className=\"rmx-form-input rmx-find-input\"\n value={replaceText}\n onChange={(e) => setReplaceText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n engine.executeCommand('replace', { replaceText })\n }\n }}\n placeholder=\"Replace...\"\n />\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={() => engine.executeCommand('replace', { replaceText })}>Replace</button>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={handleReplaceAll}>All</button>\n {replaceAllMsg && <span className=\"rmx-find-count\" role=\"status\">{replaceAllMsg}</span>}\n </div>\n </div>\n )\n}\n\nFindReplacePanel.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["FindReplacePanel","open","onClose","engine","findText","setFindText","useState","replaceText","setReplaceText","caseSensitive","setCaseSensitive","results","setResults","total","current","replaceAllMsg","setReplaceAllMsg","containerRef","useRef","useEffect","eventBus","on","executeCommand","handleFind","trim","text","err","handleReplaceAll","useCallback","count","setTimeout","handleKeyDown","e","key","preventDefault","focusable","Array","from","querySelectorAll","length","first","last","shiftKey","document","activeElement","focus","jsxs","className","onMouseDown","stopPropagation","onKeyDown","ref","role","children","jsx","htmlFor","id","type","value","onChange","target","placeholder","autoFocus","onClick","checked","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import{jsxs as e,jsx as t}from"react/jsx-runtime";import{useState as n,useRef as r,useEffect as a,useCallback as l}from"react";import{P as c}from"./index-C720tbJA.js";function o({open:c,onClose:o,engine:i}){const[m,s]=n(""),[u,d]=n(""),[p,x]=n(!1),[b,f]=n({total:0,current:0}),[h,y]=n(""),v=r(null);a((()=>{if(!i)return;return i.eventBus.on("find:results",f)}),[i]),a((()=>{c||(i?.executeCommand("clearFind"),s(""),d(""),f({total:0,current:0}),y(""))}),[c,i]);const C=()=>{if(m.trim())try{i.executeCommand("find",{text:m,caseSensitive:p})}catch(e){}},N=l((()=>{const e=b.total;i.executeCommand("replaceAll",{replaceText:u}),y(`Replaced ${e} occurrence${1!==e?"s":""}`),setTimeout((()=>y("")),3e3)}),[i,u,b.total]),k=l((e=>{if("Escape"===e.key)return e.preventDefault(),void o();if("Tab"===e.key&&v.current){const t=Array.from(v.current.querySelectorAll('input, button:not([disabled]), [tabindex]:not([tabindex="-1"])'));if(0===t.length)return void e.preventDefault();const n=t[0],r=t[t.length-1];e.shiftKey?document.activeElement===n&&(e.preventDefault(),r.focus()):document.activeElement===r&&(e.preventDefault(),n.focus())}}),[o]);return c?/* @__PURE__ */e("div",{className:"rmx-find-replace-panel",onMouseDown:e=>e.stopPropagation(),onKeyDown:k,ref:v,role:"search","aria-label":"Find and replace",children:[
2
+ /* @__PURE__ */e("div",{className:"rmx-find-row",children:[
3
+ /* @__PURE__ */t("label",{htmlFor:"rmx-find-input",className:"rmx-sr-only",children:"Find text"}),
4
+ /* @__PURE__ */t("input",{id:"rmx-find-input",type:"text",className:"rmx-form-input rmx-find-input",value:m,onChange:e=>s(e.target.value),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),b.total>0?i.executeCommand("findNext"):C())},placeholder:"Find...",autoFocus:!0}),
5
+ /* @__PURE__ */t("span",{className:"rmx-find-count",children:b.total>0?`${b.current}/${b.total}`:"No results"}),
6
+ /* @__PURE__ */t("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:C,children:"Find"}),
7
+ /* @__PURE__ */t("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:()=>i.executeCommand("findPrev"),"aria-label":"Previous match",children:"▲"}),
8
+ /* @__PURE__ */t("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:()=>i.executeCommand("findNext"),"aria-label":"Next match",children:"▼"}),
9
+ /* @__PURE__ */e("label",{className:"rmx-form-checkbox rmx-find-case",children:[
10
+ /* @__PURE__ */t("input",{type:"checkbox",checked:p,onChange:e=>x(e.target.checked),"aria-label":"Toggle case sensitive search"}),"Aa"]}),
11
+ /* @__PURE__ */t("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:o,"aria-label":"Close find and replace",children:"×"})]}),
12
+ /* @__PURE__ */e("div",{className:"rmx-find-row",children:[
13
+ /* @__PURE__ */t("label",{htmlFor:"rmx-replace-input",className:"rmx-sr-only",children:"Replace text"}),
14
+ /* @__PURE__ */t("input",{id:"rmx-replace-input",type:"text",className:"rmx-form-input rmx-find-input",value:u,onChange:e=>d(e.target.value),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),i.executeCommand("replace",{replaceText:u}))},placeholder:"Replace..."}),
15
+ /* @__PURE__ */t("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:()=>i.executeCommand("replace",{replaceText:u}),children:"Replace"}),
16
+ /* @__PURE__ */t("button",{type:"button",className:"rmx-btn rmx-btn-sm",onClick:N,children:"All"}),h&&/* @__PURE__ */t("span",{className:"rmx-find-count",role:"status",children:h})]})]}):null}o.propTypes={open:c.bool.isRequired,onClose:c.func.isRequired,engine:c.object};export{o as FindReplacePanel};
17
+ //# sourceMappingURL=FindReplaceModal-Dgt_MrWb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FindReplaceModal-Dgt_MrWb.js","sources":["../src/components/Modals/FindReplaceModal.jsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react'\nimport PropTypes from 'prop-types'\n\nconst FOCUSABLE_SELECTOR = 'input, button:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nexport function FindReplacePanel({ open, onClose, engine }) {\n const [findText, setFindText] = useState('')\n const [replaceText, setReplaceText] = useState('')\n const [caseSensitive, setCaseSensitive] = useState(false)\n const [results, setResults] = useState({ total: 0, current: 0 })\n const [replaceAllMsg, setReplaceAllMsg] = useState('')\n const containerRef = useRef(null)\n\n useEffect(() => {\n if (!engine) return\n const unsub = engine.eventBus.on('find:results', setResults)\n return unsub\n }, [engine])\n\n useEffect(() => {\n if (!open) {\n engine?.executeCommand('clearFind')\n setFindText('')\n setReplaceText('')\n setResults({ total: 0, current: 0 })\n setReplaceAllMsg('')\n }\n }, [open, engine])\n\n const handleFind = () => {\n if (!findText.trim()) return\n try {\n engine.executeCommand('find', { text: findText, caseSensitive })\n } catch (err) {\n console.error('Find command failed:', err)\n }\n }\n\n // #32: Replace All with count feedback\n const handleReplaceAll = useCallback(() => {\n const count = results.total\n engine.executeCommand('replaceAll', { replaceText })\n setReplaceAllMsg(`Replaced ${count} occurrence${count !== 1 ? 's' : ''}`)\n setTimeout(() => setReplaceAllMsg(''), 3000)\n }, [engine, replaceText, results.total])\n\n // #31: Escape key handler + focus trap\n const handleKeyDown = useCallback((e) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n onClose()\n return\n }\n\n // Focus trap\n if (e.key === 'Tab' && containerRef.current) {\n const focusable = Array.from(containerRef.current.querySelectorAll(FOCUSABLE_SELECTOR))\n if (focusable.length === 0) { e.preventDefault(); return }\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n }, [onClose])\n\n if (!open) return null\n\n return (\n <div\n className=\"rmx-find-replace-panel\"\n onMouseDown={(e) => e.stopPropagation()}\n onKeyDown={handleKeyDown}\n ref={containerRef}\n role=\"search\"\n aria-label=\"Find and replace\"\n >\n <div className=\"rmx-find-row\">\n <label htmlFor=\"rmx-find-input\" className=\"rmx-sr-only\">Find text</label>\n <input\n id=\"rmx-find-input\"\n type=\"text\"\n className=\"rmx-form-input rmx-find-input\"\n value={findText}\n onChange={(e) => setFindText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n if (results.total > 0) {\n engine.executeCommand('findNext')\n } else {\n handleFind()\n }\n }\n }}\n placeholder=\"Find...\"\n autoFocus\n />\n <span className=\"rmx-find-count\">\n {results.total > 0 ? `${results.current}/${results.total}` : 'No results'}\n </span>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={handleFind}>Find</button>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={() => engine.executeCommand('findPrev')} aria-label=\"Previous match\">&#9650;</button>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={() => engine.executeCommand('findNext')} aria-label=\"Next match\">&#9660;</button>\n <label className=\"rmx-form-checkbox rmx-find-case\">\n <input\n type=\"checkbox\"\n checked={caseSensitive}\n onChange={(e) => setCaseSensitive(e.target.checked)}\n aria-label=\"Toggle case sensitive search\"\n />\n Aa\n </label>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={onClose} aria-label=\"Close find and replace\">&times;</button>\n </div>\n <div className=\"rmx-find-row\">\n <label htmlFor=\"rmx-replace-input\" className=\"rmx-sr-only\">Replace text</label>\n <input\n id=\"rmx-replace-input\"\n type=\"text\"\n className=\"rmx-form-input rmx-find-input\"\n value={replaceText}\n onChange={(e) => setReplaceText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n engine.executeCommand('replace', { replaceText })\n }\n }}\n placeholder=\"Replace...\"\n />\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={() => engine.executeCommand('replace', { replaceText })}>Replace</button>\n <button type=\"button\" className=\"rmx-btn rmx-btn-sm\" onClick={handleReplaceAll}>All</button>\n {replaceAllMsg && <span className=\"rmx-find-count\" role=\"status\">{replaceAllMsg}</span>}\n </div>\n </div>\n )\n}\n\nFindReplacePanel.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["FindReplacePanel","open","onClose","engine","findText","setFindText","useState","replaceText","setReplaceText","caseSensitive","setCaseSensitive","results","setResults","total","current","replaceAllMsg","setReplaceAllMsg","containerRef","useRef","useEffect","eventBus","on","executeCommand","handleFind","trim","text","err","handleReplaceAll","useCallback","count","setTimeout","handleKeyDown","e","key","preventDefault","focusable","Array","from","querySelectorAll","length","first","last","shiftKey","document","activeElement","focus","jsxs","className","onMouseDown","stopPropagation","onKeyDown","ref","role","children","jsx","htmlFor","id","type","value","onChange","target","placeholder","autoFocus","onClick","checked","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),a=require("./index-Dc63uIP0.cjs"),t=require("./ModalOverlay-BDsGgv3_.cjs"),s=/^\s*(https?:\/\/|\/|\.)/i,l=/^\s*data:image\/(png|jpe?g|gif|webp|avif|bmp|ico)(;base64)?,/i;function i({open:a,onClose:i,engine:n}){const[o,m]=r.useState("url"),[d,c]=r.useState(""),[u,x]=r.useState(""),[p,g]=r.useState(""),[h,b]=r.useState(""),[j,f]=r.useState(!1),[v,N]=r.useState("idle"),y=r.useRef(null);r.useEffect((()=>{a||(c(""),x(""),g(""),b(""),f(!1),m("url"),N("idle"))}),[a]),r.useEffect((()=>{if(!d.trim()||"url"!==o)return void N("idle");const e=d.trim();s.test(e)||l.test(e)?N("loading"):N("idle")}),[d,o]);return e.jsxs(t.ModalOverlay,{title:"Insert Image",open:a,onClose:i,children:[e.jsxs("div",{className:"rmx-tabs",children:[e.jsx("button",{type:"button",className:"rmx-tab "+("url"===o?"rmx-active":""),onClick:()=>m("url"),children:"URL"}),e.jsx("button",{type:"button",className:"rmx-tab "+("upload"===o?"rmx-active":""),onClick:()=>m("upload"),children:"Upload"})]}),"upload"===o&&e.jsxs("div",{className:"rmx-upload-area",children:[e.jsx("input",{ref:y,type:"file",accept:"image/*",onChange:e=>{const r=e.target.files[0];if(r)if(b(""),n.options.uploadHandler)f(!0),n.options.uploadHandler(r).then((e=>{c(e),m("url"),f(!1)})).catch((e=>{b(e.message||"Image upload failed. Please try again."),f(!1)}));else{f(!0);const e=new FileReader;e.onload=e=>{c(e.target.result),m("url"),f(!1)},e.onerror=()=>{b("Failed to read file."),f(!1)},e.readAsDataURL(r)}},className:"rmx-sr-only"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-upload",onClick:()=>y.current?.click(),disabled:j,children:j?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"rmx-spinner","aria-hidden":"true"})," Uploading…"]}):"Choose Image File"}),e.jsx("p",{className:"rmx-upload-hint",children:"or drag and drop an image into the editor"})]}),h&&e.jsx("div",{className:"rmx-form-group rmx-form-error",children:h}),e.jsxs("form",{onSubmit:e=>{if(e.preventDefault(),!d.trim()||j)return;const r=d.trim();(s.test(r)||l.test(r))&&(n.executeCommand("insertImage",{src:d,alt:u,width:p?`${p}px`:void 0}),i())},className:"rmx-modal-form",children:["url"===o&&e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-image-url",children:"Image URL"}),e.jsx("input",{id:"rmx-image-url",type:"text",className:"rmx-form-input",value:d,onChange:e=>c(e.target.value),placeholder:"https://example.com/image.jpg",required:!0,autoFocus:!0}),d.trim()&&"idle"!==v&&e.jsx("div",{className:"rmx-image-url-preview",children:"error"===v?e.jsx("span",{className:"rmx-image-url-preview-error",children:"Image could not be loaded"}):e.jsx("img",{src:d.trim(),alt:"Preview",onLoad:()=>N("loaded"),onError:()=>N("error")})})]}),e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-image-alt",children:"Alt Text"}),e.jsx("input",{id:"rmx-image-alt",type:"text",className:"rmx-form-input",value:u,onChange:e=>x(e.target.value),placeholder:"Image description"})]}),e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-image-width",children:"Width (px)"}),e.jsx("input",{id:"rmx-image-width",type:"number",className:"rmx-form-input",value:p,onChange:e=>g(e.target.value),placeholder:"Auto",min:"50"})]}),e.jsxs("div",{className:"rmx-modal-actions",children:[e.jsx("button",{type:"button",className:"rmx-btn",onClick:i,children:"Cancel"}),e.jsx("button",{type:"submit",className:"rmx-btn rmx-btn-primary",disabled:!d.trim()||j,children:j?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"rmx-spinner","aria-hidden":"true"})," Uploading…"]}):"Insert"})]})]})]})}i.propTypes={open:a.PropTypes.bool.isRequired,onClose:a.PropTypes.func.isRequired,engine:a.PropTypes.object},exports.ImageModal=i;
2
+ //# sourceMappingURL=ImageModal-D39ywxqI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageModal-D39ywxqI.cjs","sources":["../src/components/Modals/ImageModal.jsx"],"sourcesContent":["import { useState, useRef, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\n// Allowlist-based URL validation for image sources.\n// Allows http(s), relative URLs, and safe data URIs (raster images only — SVG blocked\n// because data:image/svg+xml can contain embedded <script> and event handlers).\nconst SAFE_IMAGE_URL = /^\\s*(https?:\\/\\/|\\/|\\.)/i\nconst SAFE_DATA_IMAGE = /^\\s*data:image\\/(png|jpe?g|gif|webp|avif|bmp|ico)(;base64)?,/i\n\nexport function ImageModal({ open, onClose, engine }) {\n const [tab, setTab] = useState('url')\n const [src, setSrc] = useState('')\n const [alt, setAlt] = useState('')\n const [width, setWidth] = useState('')\n const [error, setError] = useState('')\n const [loading, setLoading] = useState(false)\n const [previewStatus, setPreviewStatus] = useState('idle') // 'idle' | 'loading' | 'loaded' | 'error'\n const fileInputRef = useRef(null)\n\n // #40: Reset state when modal closes\n useEffect(() => {\n if (!open) {\n setSrc('')\n setAlt('')\n setWidth('')\n setError('')\n setLoading(false)\n setTab('url')\n setPreviewStatus('idle')\n }\n }, [open])\n\n // #57: Image URL preview\n useEffect(() => {\n if (!src.trim() || tab !== 'url') {\n setPreviewStatus('idle')\n return\n }\n const trimmed = src.trim()\n if (!SAFE_IMAGE_URL.test(trimmed) && !SAFE_DATA_IMAGE.test(trimmed)) {\n setPreviewStatus('idle')\n return\n }\n setPreviewStatus('loading')\n }, [src, tab])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!src.trim() || loading) return\n const trimmedSrc = src.trim()\n // Block anything not matching the allowlist (blocks javascript:, vbscript:, data:image/svg+xml, etc.)\n if (!SAFE_IMAGE_URL.test(trimmedSrc) && !SAFE_DATA_IMAGE.test(trimmedSrc)) return\n engine.executeCommand('insertImage', {\n src,\n alt,\n width: width ? `${width}px` : undefined,\n })\n onClose()\n }\n\n const handleFileChange = (e) => {\n const file = e.target.files[0]\n if (!file) return\n\n setError('')\n if (engine.options.uploadHandler) {\n setLoading(true)\n engine.options.uploadHandler(file).then((url) => {\n setSrc(url)\n setTab('url')\n setLoading(false)\n }).catch((err) => {\n console.error('Image upload failed:', err)\n setError(err.message || 'Image upload failed. Please try again.')\n setLoading(false)\n })\n } else {\n setLoading(true)\n const reader = new FileReader()\n reader.onload = (ev) => {\n setSrc(ev.target.result)\n setTab('url')\n setLoading(false)\n }\n reader.onerror = () => {\n setError('Failed to read file.')\n setLoading(false)\n }\n reader.readAsDataURL(file)\n }\n }\n\n return (\n <ModalOverlay title=\"Insert Image\" open={open} onClose={onClose}>\n <div className=\"rmx-tabs\">\n <button\n type=\"button\"\n className={`rmx-tab ${tab === 'url' ? 'rmx-active' : ''}`}\n onClick={() => setTab('url')}\n >\n URL\n </button>\n <button\n type=\"button\"\n className={`rmx-tab ${tab === 'upload' ? 'rmx-active' : ''}`}\n onClick={() => setTab('upload')}\n >\n Upload\n </button>\n </div>\n\n {tab === 'upload' && (\n <div className=\"rmx-upload-area\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileChange}\n className=\"rmx-sr-only\"\n />\n <button\n type=\"button\"\n className=\"rmx-btn rmx-btn-upload\"\n onClick={() => fileInputRef.current?.click()}\n disabled={loading}\n >\n {loading ? (\n <>\n <span className=\"rmx-spinner\" aria-hidden=\"true\" />\n {' Uploading\\u2026'}\n </>\n ) : (\n 'Choose Image File'\n )}\n </button>\n <p className=\"rmx-upload-hint\">or drag and drop an image into the editor</p>\n </div>\n )}\n\n {error && (\n <div className=\"rmx-form-group rmx-form-error\">\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"rmx-modal-form\">\n {tab === 'url' && (\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-image-url\">Image URL</label>\n <input\n id=\"rmx-image-url\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={src}\n onChange={(e) => setSrc(e.target.value)}\n placeholder=\"https://example.com/image.jpg\"\n required\n autoFocus\n />\n {/* #57: Image URL preview */}\n {src.trim() && previewStatus !== 'idle' && (\n <div className=\"rmx-image-url-preview\">\n {previewStatus === 'error' ? (\n <span className=\"rmx-image-url-preview-error\">Image could not be loaded</span>\n ) : (\n <img\n src={src.trim()}\n alt=\"Preview\"\n onLoad={() => setPreviewStatus('loaded')}\n onError={() => setPreviewStatus('error')}\n />\n )}\n </div>\n )}\n </div>\n )}\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-image-alt\">Alt Text</label>\n <input\n id=\"rmx-image-alt\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={alt}\n onChange={(e) => setAlt(e.target.value)}\n placeholder=\"Image description\"\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-image-width\">Width (px)</label>\n <input\n id=\"rmx-image-width\"\n type=\"number\"\n className=\"rmx-form-input\"\n value={width}\n onChange={(e) => setWidth(e.target.value)}\n placeholder=\"Auto\"\n min=\"50\"\n />\n </div>\n <div className=\"rmx-modal-actions\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\" disabled={!src.trim() || loading}>\n {loading ? (\n <>\n <span className=\"rmx-spinner\" aria-hidden=\"true\" />\n {' Uploading\\u2026'}\n </>\n ) : (\n 'Insert'\n )}\n </button>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nImageModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["SAFE_IMAGE_URL","SAFE_DATA_IMAGE","ImageModal","open","onClose","engine","tab","setTab","useState","src","setSrc","alt","setAlt","width","setWidth","error","setError","loading","setLoading","previewStatus","setPreviewStatus","fileInputRef","useRef","useEffect","trim","trimmed","test","jsxs","ModalOverlay","title","children","className","jsx","type","onClick","ref","accept","onChange","e","file","target","files","options","uploadHandler","then","url","catch","err","message","reader","FileReader","onload","ev","result","onerror","readAsDataURL","current","click","disabled","Fragment","onSubmit","preventDefault","trimmedSrc","executeCommand","htmlFor","id","value","placeholder","required","autoFocus","onLoad","onError","min","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import{jsxs as e,jsx as r,Fragment as a}from"react/jsx-runtime";import{useState as l,useRef as t,useEffect as i}from"react";import{P as m}from"./index-C720tbJA.js";import{M as o}from"./ModalOverlay-CLvRNHmp.js";const n=/^\s*(https?:\/\/|\/|\.)/i,s=/^\s*data:image\/(png|jpe?g|gif|webp|avif|bmp|ico)(;base64)?,/i;function d({open:m,onClose:d,engine:c}){const[p,u]=l("url"),[x,g]=l(""),[h,b]=l(""),[f,N]=l(""),[v,y]=l(""),[C,I]=l(!1),[w,F]=l("idle"),j=t(null);i((()=>{m||(g(""),b(""),N(""),y(""),I(!1),u("url"),F("idle"))}),[m]),i((()=>{if(!x.trim()||"url"!==p)return void F("idle");const e=x.trim();n.test(e)||s.test(e)?F("loading"):F("idle")}),[x,p]);/* @__PURE__ */
2
+ return e(o,{title:"Insert Image",open:m,onClose:d,children:[
3
+ /* @__PURE__ */e("div",{className:"rmx-tabs",children:[
4
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-tab "+("url"===p?"rmx-active":""),onClick:()=>u("url"),children:"URL"}),
5
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-tab "+("upload"===p?"rmx-active":""),onClick:()=>u("upload"),children:"Upload"})]}),"upload"===p&&/* @__PURE__ */e("div",{className:"rmx-upload-area",children:[
6
+ /* @__PURE__ */r("input",{ref:j,type:"file",accept:"image/*",onChange:e=>{const r=e.target.files[0];if(r)if(y(""),c.options.uploadHandler)I(!0),c.options.uploadHandler(r).then((e=>{g(e),u("url"),I(!1)})).catch((e=>{y(e.message||"Image upload failed. Please try again."),I(!1)}));else{I(!0);const e=new FileReader;e.onload=e=>{g(e.target.result),u("url"),I(!1)},e.onerror=()=>{y("Failed to read file."),I(!1)},e.readAsDataURL(r)}},className:"rmx-sr-only"}),
7
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-btn rmx-btn-upload",onClick:()=>j.current?.click(),disabled:C,children:C?/* @__PURE__ */e(a,{children:[
8
+ /* @__PURE__ */r("span",{className:"rmx-spinner","aria-hidden":"true"})," Uploading…"]}):"Choose Image File"}),
9
+ /* @__PURE__ */r("p",{className:"rmx-upload-hint",children:"or drag and drop an image into the editor"})]}),v&&/* @__PURE__ */r("div",{className:"rmx-form-group rmx-form-error",children:v}),
10
+ /* @__PURE__ */e("form",{onSubmit:e=>{if(e.preventDefault(),!x.trim()||C)return;const r=x.trim();(n.test(r)||s.test(r))&&(c.executeCommand("insertImage",{src:x,alt:h,width:f?`${f}px`:void 0}),d())},className:"rmx-modal-form",children:["url"===p&&/* @__PURE__ */e("div",{className:"rmx-form-group",children:[
11
+ /* @__PURE__ */r("label",{className:"rmx-form-label",htmlFor:"rmx-image-url",children:"Image URL"}),
12
+ /* @__PURE__ */r("input",{id:"rmx-image-url",type:"text",className:"rmx-form-input",value:x,onChange:e=>g(e.target.value),placeholder:"https://example.com/image.jpg",required:!0,autoFocus:!0}),x.trim()&&"idle"!==w&&/* @__PURE__ */r("div",{className:"rmx-image-url-preview",children:"error"===w?/* @__PURE__ */r("span",{className:"rmx-image-url-preview-error",children:"Image could not be loaded"}):/* @__PURE__ */r("img",{src:x.trim(),alt:"Preview",onLoad:()=>F("loaded"),onError:()=>F("error")})})]}),
13
+ /* @__PURE__ */e("div",{className:"rmx-form-group",children:[
14
+ /* @__PURE__ */r("label",{className:"rmx-form-label",htmlFor:"rmx-image-alt",children:"Alt Text"}),
15
+ /* @__PURE__ */r("input",{id:"rmx-image-alt",type:"text",className:"rmx-form-input",value:h,onChange:e=>b(e.target.value),placeholder:"Image description"})]}),
16
+ /* @__PURE__ */e("div",{className:"rmx-form-group",children:[
17
+ /* @__PURE__ */r("label",{className:"rmx-form-label",htmlFor:"rmx-image-width",children:"Width (px)"}),
18
+ /* @__PURE__ */r("input",{id:"rmx-image-width",type:"number",className:"rmx-form-input",value:f,onChange:e=>N(e.target.value),placeholder:"Auto",min:"50"})]}),
19
+ /* @__PURE__ */e("div",{className:"rmx-modal-actions",children:[
20
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-btn",onClick:d,children:"Cancel"}),
21
+ /* @__PURE__ */r("button",{type:"submit",className:"rmx-btn rmx-btn-primary",disabled:!x.trim()||C,children:C?/* @__PURE__ */e(a,{children:[
22
+ /* @__PURE__ */r("span",{className:"rmx-spinner","aria-hidden":"true"})," Uploading…"]}):"Insert"})]})]})]})}d.propTypes={open:m.bool.isRequired,onClose:m.func.isRequired,engine:m.object};export{d as ImageModal};
23
+ //# sourceMappingURL=ImageModal-DqScpPrc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageModal-DqScpPrc.js","sources":["../src/components/Modals/ImageModal.jsx"],"sourcesContent":["import { useState, useRef, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\n// Allowlist-based URL validation for image sources.\n// Allows http(s), relative URLs, and safe data URIs (raster images only — SVG blocked\n// because data:image/svg+xml can contain embedded <script> and event handlers).\nconst SAFE_IMAGE_URL = /^\\s*(https?:\\/\\/|\\/|\\.)/i\nconst SAFE_DATA_IMAGE = /^\\s*data:image\\/(png|jpe?g|gif|webp|avif|bmp|ico)(;base64)?,/i\n\nexport function ImageModal({ open, onClose, engine }) {\n const [tab, setTab] = useState('url')\n const [src, setSrc] = useState('')\n const [alt, setAlt] = useState('')\n const [width, setWidth] = useState('')\n const [error, setError] = useState('')\n const [loading, setLoading] = useState(false)\n const [previewStatus, setPreviewStatus] = useState('idle') // 'idle' | 'loading' | 'loaded' | 'error'\n const fileInputRef = useRef(null)\n\n // #40: Reset state when modal closes\n useEffect(() => {\n if (!open) {\n setSrc('')\n setAlt('')\n setWidth('')\n setError('')\n setLoading(false)\n setTab('url')\n setPreviewStatus('idle')\n }\n }, [open])\n\n // #57: Image URL preview\n useEffect(() => {\n if (!src.trim() || tab !== 'url') {\n setPreviewStatus('idle')\n return\n }\n const trimmed = src.trim()\n if (!SAFE_IMAGE_URL.test(trimmed) && !SAFE_DATA_IMAGE.test(trimmed)) {\n setPreviewStatus('idle')\n return\n }\n setPreviewStatus('loading')\n }, [src, tab])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!src.trim() || loading) return\n const trimmedSrc = src.trim()\n // Block anything not matching the allowlist (blocks javascript:, vbscript:, data:image/svg+xml, etc.)\n if (!SAFE_IMAGE_URL.test(trimmedSrc) && !SAFE_DATA_IMAGE.test(trimmedSrc)) return\n engine.executeCommand('insertImage', {\n src,\n alt,\n width: width ? `${width}px` : undefined,\n })\n onClose()\n }\n\n const handleFileChange = (e) => {\n const file = e.target.files[0]\n if (!file) return\n\n setError('')\n if (engine.options.uploadHandler) {\n setLoading(true)\n engine.options.uploadHandler(file).then((url) => {\n setSrc(url)\n setTab('url')\n setLoading(false)\n }).catch((err) => {\n console.error('Image upload failed:', err)\n setError(err.message || 'Image upload failed. Please try again.')\n setLoading(false)\n })\n } else {\n setLoading(true)\n const reader = new FileReader()\n reader.onload = (ev) => {\n setSrc(ev.target.result)\n setTab('url')\n setLoading(false)\n }\n reader.onerror = () => {\n setError('Failed to read file.')\n setLoading(false)\n }\n reader.readAsDataURL(file)\n }\n }\n\n return (\n <ModalOverlay title=\"Insert Image\" open={open} onClose={onClose}>\n <div className=\"rmx-tabs\">\n <button\n type=\"button\"\n className={`rmx-tab ${tab === 'url' ? 'rmx-active' : ''}`}\n onClick={() => setTab('url')}\n >\n URL\n </button>\n <button\n type=\"button\"\n className={`rmx-tab ${tab === 'upload' ? 'rmx-active' : ''}`}\n onClick={() => setTab('upload')}\n >\n Upload\n </button>\n </div>\n\n {tab === 'upload' && (\n <div className=\"rmx-upload-area\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileChange}\n className=\"rmx-sr-only\"\n />\n <button\n type=\"button\"\n className=\"rmx-btn rmx-btn-upload\"\n onClick={() => fileInputRef.current?.click()}\n disabled={loading}\n >\n {loading ? (\n <>\n <span className=\"rmx-spinner\" aria-hidden=\"true\" />\n {' Uploading\\u2026'}\n </>\n ) : (\n 'Choose Image File'\n )}\n </button>\n <p className=\"rmx-upload-hint\">or drag and drop an image into the editor</p>\n </div>\n )}\n\n {error && (\n <div className=\"rmx-form-group rmx-form-error\">\n {error}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"rmx-modal-form\">\n {tab === 'url' && (\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-image-url\">Image URL</label>\n <input\n id=\"rmx-image-url\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={src}\n onChange={(e) => setSrc(e.target.value)}\n placeholder=\"https://example.com/image.jpg\"\n required\n autoFocus\n />\n {/* #57: Image URL preview */}\n {src.trim() && previewStatus !== 'idle' && (\n <div className=\"rmx-image-url-preview\">\n {previewStatus === 'error' ? (\n <span className=\"rmx-image-url-preview-error\">Image could not be loaded</span>\n ) : (\n <img\n src={src.trim()}\n alt=\"Preview\"\n onLoad={() => setPreviewStatus('loaded')}\n onError={() => setPreviewStatus('error')}\n />\n )}\n </div>\n )}\n </div>\n )}\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-image-alt\">Alt Text</label>\n <input\n id=\"rmx-image-alt\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={alt}\n onChange={(e) => setAlt(e.target.value)}\n placeholder=\"Image description\"\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-image-width\">Width (px)</label>\n <input\n id=\"rmx-image-width\"\n type=\"number\"\n className=\"rmx-form-input\"\n value={width}\n onChange={(e) => setWidth(e.target.value)}\n placeholder=\"Auto\"\n min=\"50\"\n />\n </div>\n <div className=\"rmx-modal-actions\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\" disabled={!src.trim() || loading}>\n {loading ? (\n <>\n <span className=\"rmx-spinner\" aria-hidden=\"true\" />\n {' Uploading\\u2026'}\n </>\n ) : (\n 'Insert'\n )}\n </button>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nImageModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["SAFE_IMAGE_URL","SAFE_DATA_IMAGE","ImageModal","open","onClose","engine","tab","setTab","useState","src","setSrc","alt","setAlt","width","setWidth","error","setError","loading","setLoading","previewStatus","setPreviewStatus","fileInputRef","useRef","useEffect","trim","trimmed","test","ModalOverlay","title","children","jsxs","className","jsx","type","onClick","ref","accept","onChange","e","file","target","files","options","uploadHandler","then","url","catch","err","message","reader","FileReader","onload","ev","result","onerror","readAsDataURL","current","click","disabled","Fragment","onSubmit","preventDefault","trimmedSrc","executeCommand","htmlFor","id","value","placeholder","required","autoFocus","onLoad","onError","min","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import{jsxs as e,jsx as r,Fragment as n}from"react/jsx-runtime";import{useState as a,useRef as l}from"react";import{P as i}from"./index-C720tbJA.js";import{M as t}from"./ModalOverlay-CLvRNHmp.js";import{getSupportedFormatNames as o,getSupportedExtensions as s,convertDocument as c}from"@remyxjs/core";function m({open:i,onClose:m,engine:d}){const[p,u]=a(null),[h,x]=a(!1),[b,N]=a(""),[f,g]=a(""),[v,y]=a("insert"),C=l(null),j=()=>{m(),u(null),x(!1),N(""),g(""),y("insert"),C.current&&(C.current.value="")},k=o().join(", ");/* @__PURE__ */
2
+ return e(t,{title:"Import Document",open:i,onClose:j,children:[
3
+ /* @__PURE__ */e("div",{className:"rmx-upload-area",children:[
4
+ /* @__PURE__ */r("input",{ref:C,type:"file",accept:s(),onChange:async e=>{const r=e.target.files[0];if(r){u(r),g(""),x(!0),N("");try{const e=await c(r);N(e)}catch(n){g(n.message||"Failed to convert document"),N("")}finally{x(!1)}}},className:"rmx-sr-only"}),
5
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-btn rmx-btn-upload",onClick:()=>C.current?.click(),disabled:h,children:h?/* @__PURE__ */e(n,{children:[
6
+ /* @__PURE__ */r("span",{className:"rmx-spinner","aria-hidden":"true"})," Converting…"]}):"Choose Document"}),
7
+ /* @__PURE__ */e("p",{className:"rmx-upload-hint",children:["Supported: ",k]}),p&&!h&&!f&&/* @__PURE__ */r("p",{className:"rmx-upload-hint rmx-upload-filename",children:p.name})]}),f&&/* @__PURE__ */r("div",{className:"rmx-form-group rmx-form-error",children:f}),b&&/* @__PURE__ */e(n,{children:[
8
+ /* @__PURE__ */e("div",{className:"rmx-form-group",children:[
9
+ /* @__PURE__ */r("label",{className:"rmx-form-label",children:"Preview"}),
10
+ /* @__PURE__ */r("div",{className:"rmx-import-preview",dangerouslySetInnerHTML:{__html:d?.sanitizer?d.sanitizer.sanitize(b):""}})]}),
11
+ /* @__PURE__ */e("div",{className:"rmx-form-group",children:[
12
+ /* @__PURE__ */r("label",{className:"rmx-form-label",children:"Insert Mode"}),
13
+ /* @__PURE__ */e("div",{className:"rmx-radio-group",children:[
14
+ /* @__PURE__ */e("label",{className:"rmx-radio-label",children:[
15
+ /* @__PURE__ */r("input",{type:"radio",name:"insertMode",value:"insert",checked:"insert"===v,onChange:()=>y("insert")}),"Insert at cursor"]}),
16
+ /* @__PURE__ */e("label",{className:"rmx-radio-label",children:[
17
+ /* @__PURE__ */r("input",{type:"radio",name:"insertMode",value:"replace",checked:"replace"===v,onChange:()=>y("replace")}),"Replace all content"]})]}),"replace"===v&&/* @__PURE__ */r("p",{className:"rmx-replace-warning",children:"Warning: This will replace all existing content in the editor."})]})]}),
18
+ /* @__PURE__ */e("div",{className:"rmx-modal-actions",children:[
19
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-btn",onClick:j,children:"Cancel"}),
20
+ /* @__PURE__ */r("button",{type:"button",className:"rmx-btn rmx-btn-primary",disabled:!b||h,onClick:()=>{b&&(d.executeCommand("importDocument",{html:b,mode:v}),j())},children:h?/* @__PURE__ */e(n,{children:[
21
+ /* @__PURE__ */r("span",{className:"rmx-spinner","aria-hidden":"true"})," Converting…"]}):"Insert"})]})]})}m.propTypes={open:i.bool.isRequired,onClose:i.func.isRequired,engine:i.object};export{m as ImportDocumentModal};
22
+ //# sourceMappingURL=ImportDocumentModal-BKqMxO3z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImportDocumentModal-BKqMxO3z.js","sources":["../src/components/Modals/ImportDocumentModal.jsx"],"sourcesContent":["import { useState, useRef } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\nimport { convertDocument, getSupportedExtensions, getSupportedFormatNames } from '@remyxjs/core'\n\nexport function ImportDocumentModal({ open, onClose, engine }) {\n const [file, setFile] = useState(null)\n const [converting, setConverting] = useState(false)\n const [preview, setPreview] = useState('')\n const [error, setError] = useState('')\n const [insertMode, setInsertMode] = useState('insert')\n const fileInputRef = useRef(null)\n\n const handleFileChange = async (e) => {\n const selected = e.target.files[0]\n if (!selected) return\n\n setFile(selected)\n setError('')\n setConverting(true)\n setPreview('')\n\n try {\n const html = await convertDocument(selected)\n setPreview(html)\n } catch (err) {\n setError(err.message || 'Failed to convert document')\n setPreview('')\n } finally {\n setConverting(false)\n }\n }\n\n const handleInsert = () => {\n if (!preview) return\n engine.executeCommand('importDocument', {\n html: preview,\n mode: insertMode,\n })\n handleClose()\n }\n\n const handleClose = () => {\n onClose()\n setFile(null)\n setConverting(false)\n setPreview('')\n setError('')\n setInsertMode('insert')\n if (fileInputRef.current) fileInputRef.current.value = ''\n }\n\n const formats = getSupportedFormatNames().join(', ')\n\n return (\n <ModalOverlay title=\"Import Document\" open={open} onClose={handleClose}>\n <div className=\"rmx-upload-area\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={getSupportedExtensions()}\n onChange={handleFileChange}\n className=\"rmx-sr-only\"\n />\n <button\n type=\"button\"\n className=\"rmx-btn rmx-btn-upload\"\n onClick={() => fileInputRef.current?.click()}\n disabled={converting}\n >\n {converting ? (\n <>\n <span className=\"rmx-spinner\" aria-hidden=\"true\" />\n {' Converting\\u2026'}\n </>\n ) : 'Choose Document'}\n </button>\n <p className=\"rmx-upload-hint\">\n Supported: {formats}\n </p>\n {file && !converting && !error && (\n <p className=\"rmx-upload-hint rmx-upload-filename\">\n {file.name}\n </p>\n )}\n </div>\n\n {error && (\n <div className=\"rmx-form-group rmx-form-error\">\n {error}\n </div>\n )}\n\n {preview && (\n <>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\">Preview</label>\n <div\n className=\"rmx-import-preview\"\n dangerouslySetInnerHTML={{ __html: engine?.sanitizer ? engine.sanitizer.sanitize(preview) : '' }}\n />\n </div>\n\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\">Insert Mode</label>\n <div className=\"rmx-radio-group\">\n <label className=\"rmx-radio-label\">\n <input\n type=\"radio\"\n name=\"insertMode\"\n value=\"insert\"\n checked={insertMode === 'insert'}\n onChange={() => setInsertMode('insert')}\n />\n Insert at cursor\n </label>\n <label className=\"rmx-radio-label\">\n <input\n type=\"radio\"\n name=\"insertMode\"\n value=\"replace\"\n checked={insertMode === 'replace'}\n onChange={() => setInsertMode('replace')}\n />\n Replace all content\n </label>\n </div>\n {insertMode === 'replace' && (\n <p className=\"rmx-replace-warning\">Warning: This will replace all existing content in the editor.</p>\n )}\n </div>\n </>\n )}\n\n <div className=\"rmx-modal-actions\">\n <button type=\"button\" className=\"rmx-btn\" onClick={handleClose}>Cancel</button>\n <button\n type=\"button\"\n className=\"rmx-btn rmx-btn-primary\"\n disabled={!preview || converting}\n onClick={handleInsert}\n >\n {converting ? (\n <>\n <span className=\"rmx-spinner\" aria-hidden=\"true\" />\n {' Converting\\u2026'}\n </>\n ) : 'Insert'}\n </button>\n </div>\n </ModalOverlay>\n )\n}\n\nImportDocumentModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n}\n"],"names":["ImportDocumentModal","open","onClose","engine","file","setFile","useState","converting","setConverting","preview","setPreview","error","setError","insertMode","setInsertMode","fileInputRef","useRef","handleClose","current","value","formats","getSupportedFormatNames","join","ModalOverlay","title","children","jsxs","className","jsx","ref","type","accept","getSupportedExtensions","onChange","async","e","selected","target","files","html","convertDocument","err","message","onClick","click","disabled","Fragment","name","dangerouslySetInnerHTML","__html","sanitizer","sanitize","checked","executeCommand","mode","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),s=require("./index-Dc63uIP0.cjs"),n=require("./ModalOverlay-BDsGgv3_.cjs"),a=require("@remyxjs/core");function t({open:s,onClose:t,engine:l}){const[i,o]=r.useState(null),[c,m]=r.useState(!1),[d,u]=r.useState(""),[p,x]=r.useState(""),[h,j]=r.useState("insert"),b=r.useRef(null),g=()=>{t(),o(null),m(!1),u(""),x(""),j("insert"),b.current&&(b.current.value="")},y=a.getSupportedFormatNames().join(", ");return e.jsxs(n.ModalOverlay,{title:"Import Document",open:s,onClose:g,children:[e.jsxs("div",{className:"rmx-upload-area",children:[e.jsx("input",{ref:b,type:"file",accept:a.getSupportedExtensions(),onChange:async e=>{const r=e.target.files[0];if(r){o(r),x(""),m(!0),u("");try{const e=await a.convertDocument(r);u(e)}catch(s){x(s.message||"Failed to convert document"),u("")}finally{m(!1)}}},className:"rmx-sr-only"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-upload",onClick:()=>b.current?.click(),disabled:c,children:c?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"rmx-spinner","aria-hidden":"true"})," Converting…"]}):"Choose Document"}),e.jsxs("p",{className:"rmx-upload-hint",children:["Supported: ",y]}),i&&!c&&!p&&e.jsx("p",{className:"rmx-upload-hint rmx-upload-filename",children:i.name})]}),p&&e.jsx("div",{className:"rmx-form-group rmx-form-error",children:p}),d&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",children:"Preview"}),e.jsx("div",{className:"rmx-import-preview",dangerouslySetInnerHTML:{__html:l?.sanitizer?l.sanitizer.sanitize(d):""}})]}),e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",children:"Insert Mode"}),e.jsxs("div",{className:"rmx-radio-group",children:[e.jsxs("label",{className:"rmx-radio-label",children:[e.jsx("input",{type:"radio",name:"insertMode",value:"insert",checked:"insert"===h,onChange:()=>j("insert")}),"Insert at cursor"]}),e.jsxs("label",{className:"rmx-radio-label",children:[e.jsx("input",{type:"radio",name:"insertMode",value:"replace",checked:"replace"===h,onChange:()=>j("replace")}),"Replace all content"]})]}),"replace"===h&&e.jsx("p",{className:"rmx-replace-warning",children:"Warning: This will replace all existing content in the editor."})]})]}),e.jsxs("div",{className:"rmx-modal-actions",children:[e.jsx("button",{type:"button",className:"rmx-btn",onClick:g,children:"Cancel"}),e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-primary",disabled:!d||c,onClick:()=>{d&&(l.executeCommand("importDocument",{html:d,mode:h}),g())},children:c?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"rmx-spinner","aria-hidden":"true"})," Converting…"]}):"Insert"})]})]})}t.propTypes={open:s.PropTypes.bool.isRequired,onClose:s.PropTypes.func.isRequired,engine:s.PropTypes.object},exports.ImportDocumentModal=t;
2
+ //# sourceMappingURL=ImportDocumentModal-Bev9hp_J.cjs.map