@remyxjs/react 1.0.0-beta → 1.0.4-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 (64) hide show
  1. package/dist/{AttachmentModal-D-uxbXvb.cjs → AttachmentModal-BDFFiZJt.cjs} +2 -2
  2. package/dist/{AttachmentModal-D-uxbXvb.cjs.map → AttachmentModal-BDFFiZJt.cjs.map} +1 -1
  3. package/dist/{AttachmentModal--6T-vYJt.js → AttachmentModal-Di5V6QX-.js} +2 -2
  4. package/dist/{AttachmentModal--6T-vYJt.js.map → AttachmentModal-Di5V6QX-.js.map} +1 -1
  5. package/dist/{ContextMenu-B4K3Zbfp.cjs → ContextMenu-BafbP2Uh.cjs} +2 -2
  6. package/dist/{ContextMenu-B4K3Zbfp.cjs.map → ContextMenu-BafbP2Uh.cjs.map} +1 -1
  7. package/dist/{ContextMenu-D8wNSMc3.js → ContextMenu-DsA0YsY7.js} +2 -2
  8. package/dist/{ContextMenu-D8wNSMc3.js.map → ContextMenu-DsA0YsY7.js.map} +1 -1
  9. package/dist/{EmbedModal-Bh2Tcow9.cjs → EmbedModal-C73lODIT.cjs} +2 -2
  10. package/dist/{EmbedModal-Bh2Tcow9.cjs.map → EmbedModal-C73lODIT.cjs.map} +1 -1
  11. package/dist/{EmbedModal-cxE4maD2.js → EmbedModal-alpHQiAP.js} +2 -2
  12. package/dist/{EmbedModal-cxE4maD2.js.map → EmbedModal-alpHQiAP.js.map} +1 -1
  13. package/dist/{ExportModal-Cf1uE4r_.js → ExportModal-BN8LUaSR.js} +2 -2
  14. package/dist/{ExportModal-Cf1uE4r_.js.map → ExportModal-BN8LUaSR.js.map} +1 -1
  15. package/dist/{ExportModal-DwQVsrZE.cjs → ExportModal-Cu-cCwa6.cjs} +2 -2
  16. package/dist/{ExportModal-DwQVsrZE.cjs.map → ExportModal-Cu-cCwa6.cjs.map} +1 -1
  17. package/dist/{FindReplaceModal-Dgt_MrWb.js → FindReplaceModal-BqNcotUr.js} +2 -2
  18. package/dist/{FindReplaceModal-Dgt_MrWb.js.map → FindReplaceModal-BqNcotUr.js.map} +1 -1
  19. package/dist/{FindReplaceModal-DYL_2z8U.cjs → FindReplaceModal-DgysYT04.cjs} +2 -2
  20. package/dist/{FindReplaceModal-DYL_2z8U.cjs.map → FindReplaceModal-DgysYT04.cjs.map} +1 -1
  21. package/dist/{ImageModal-D39ywxqI.cjs → ImageModal-BzgYXY9y.cjs} +2 -2
  22. package/dist/{ImageModal-D39ywxqI.cjs.map → ImageModal-BzgYXY9y.cjs.map} +1 -1
  23. package/dist/{ImageModal-DqScpPrc.js → ImageModal-bCzSSTmd.js} +2 -2
  24. package/dist/{ImageModal-DqScpPrc.js.map → ImageModal-bCzSSTmd.js.map} +1 -1
  25. package/dist/{ImportDocumentModal-BKqMxO3z.js → ImportDocumentModal-BypTEn2i.js} +4 -4
  26. package/dist/{ImportDocumentModal-BKqMxO3z.js.map → ImportDocumentModal-BypTEn2i.js.map} +1 -1
  27. package/dist/{ImportDocumentModal-Bev9hp_J.cjs → ImportDocumentModal-gCFept9p.cjs} +2 -2
  28. package/dist/{ImportDocumentModal-Bev9hp_J.cjs.map → ImportDocumentModal-gCFept9p.cjs.map} +1 -1
  29. package/dist/{LinkModal-k9IeDtAb.js → LinkModal-CmkK4m-k.js} +3 -3
  30. package/dist/{LinkModal-k9IeDtAb.js.map → LinkModal-CmkK4m-k.js.map} +1 -1
  31. package/dist/{LinkModal-B-igSa-g.cjs → LinkModal-DPxg7YCE.cjs} +2 -2
  32. package/dist/{LinkModal-B-igSa-g.cjs.map → LinkModal-DPxg7YCE.cjs.map} +1 -1
  33. package/dist/{MenuBar-DWxJNHmb.js → MenuBar-DOv7JPwR.js} +2 -2
  34. package/dist/{MenuBar-DWxJNHmb.js.map → MenuBar-DOv7JPwR.js.map} +1 -1
  35. package/dist/{MenuBar-B-ZAX9rH.cjs → MenuBar-Lw_5Jp8u.cjs} +2 -2
  36. package/dist/{MenuBar-B-ZAX9rH.cjs.map → MenuBar-Lw_5Jp8u.cjs.map} +1 -1
  37. package/dist/{ModalOverlay-BDsGgv3_.cjs → ModalOverlay-Dh5quv7X.cjs} +2 -2
  38. package/dist/{ModalOverlay-BDsGgv3_.cjs.map → ModalOverlay-Dh5quv7X.cjs.map} +1 -1
  39. package/dist/{ModalOverlay-CLvRNHmp.js → ModalOverlay-Dt0JiW3M.js} +2 -2
  40. package/dist/{ModalOverlay-CLvRNHmp.js.map → ModalOverlay-Dt0JiW3M.js.map} +1 -1
  41. package/dist/{SourceModal-BNI_i4iW.cjs → SourceModal-CHKC5xcv.cjs} +2 -2
  42. package/dist/{SourceModal-BNI_i4iW.cjs.map → SourceModal-CHKC5xcv.cjs.map} +1 -1
  43. package/dist/{SourceModal-MdTGK3Uf.js → SourceModal-ChhNVz7y.js} +2 -2
  44. package/dist/{SourceModal-MdTGK3Uf.js.map → SourceModal-ChhNVz7y.js.map} +1 -1
  45. package/dist/{TablePickerModal-DYODWEA1.js → TablePickerModal-BnZHyMsk.js} +2 -2
  46. package/dist/{TablePickerModal-DYODWEA1.js.map → TablePickerModal-BnZHyMsk.js.map} +1 -1
  47. package/dist/{TablePickerModal-Do1QyoyM.cjs → TablePickerModal-Dr0wUx_h.cjs} +2 -2
  48. package/dist/{TablePickerModal-Do1QyoyM.cjs.map → TablePickerModal-Dr0wUx_h.cjs.map} +1 -1
  49. package/dist/index-Bg_uAWlM.js +3 -0
  50. package/dist/index-Bg_uAWlM.js.map +1 -0
  51. package/dist/index-DL-qBZZU.js +357 -0
  52. package/dist/index-DL-qBZZU.js.map +1 -0
  53. package/dist/index-OfJxpaev.cjs +2 -0
  54. package/dist/index-OfJxpaev.cjs.map +1 -0
  55. package/dist/index-qh1Yzh-l.cjs +2 -0
  56. package/dist/index-qh1Yzh-l.cjs.map +1 -0
  57. package/dist/remyx-react.cjs +1 -1
  58. package/dist/remyx-react.css +1 -1
  59. package/dist/remyx-react.js +1 -1
  60. package/package.json +3 -3
  61. package/dist/index-C720tbJA.js +0 -359
  62. package/dist/index-C720tbJA.js.map +0 -1
  63. package/dist/index-Dc63uIP0.cjs +0 -2
  64. package/dist/index-Dc63uIP0.cjs.map +0 -1
@@ -1 +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":"0KAKO,SAASA,GAAiBC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,IAChD,MAAOC,EAAUC,GAAeC,EAAAA,SAAS,KAClCC,EAAaC,GAAkBF,EAAAA,SAAS,KACxCG,EAAeC,GAAoBJ,EAAAA,UAAS,IAC5CK,EAASC,GAAcN,EAAAA,SAAS,CAAEO,MAAO,EAAGC,QAAS,KACrDC,EAAeC,GAAoBV,EAAAA,SAAS,IAC7CW,EAAeC,EAAAA,OAAO,MAE5BC,EAAAA,WAAU,KACR,IAAKhB,EAAQ,OAEb,OADcA,EAAOiB,SAASC,GAAG,eAAgBT,EAC1C,GACN,CAACT,IAEJgB,EAAAA,WAAU,KACHlB,IACHE,GAAQmB,eAAe,aACvBjB,EAAY,IACZG,EAAe,IACfI,EAAW,CAAEC,MAAO,EAAGC,QAAS,IAChCE,EAAiB,IACnB,GACC,CAACf,EAAME,IAEV,MAAMoB,EAAa,KACjB,GAAKnB,EAASoB,OACd,IACErB,EAAOmB,eAAe,OAAQ,CAAEG,KAAMrB,EAAUK,iBAClD,OAASiB,GAET,GAIIC,EAAmBC,EAAAA,aAAY,KACnC,MAAMC,EAAQlB,EAAQE,MACtBV,EAAOmB,eAAe,aAAc,CAAEf,gBACtCS,EAAiB,YAAYa,eAA6B,IAAVA,EAAc,IAAM,MACpEC,YAAW,IAAMd,EAAiB,KAAK,IAAI,GAC1C,CAACb,EAAQI,EAAaI,EAAQE,QAG3BkB,EAAgBH,eAAaI,IACjC,GAAc,WAAVA,EAAEC,IAGJ,OAFAD,EAAEE,sBACFhC,IAKF,GAAc,QAAV8B,EAAEC,KAAiBhB,EAAaH,QAAS,CAC3C,MAAMqB,EAAYC,MAAMC,KAAKpB,EAAaH,QAAQwB,iBArD7B,mEAsDrB,GAAyB,IAArBH,EAAUI,OAAoC,YAApBP,EAAEE,iBAEhC,MAAMM,EAAQL,EAAU,GAClBM,EAAON,EAAUA,EAAUI,OAAS,GAEtCP,EAAEU,SACAC,SAASC,gBAAkBJ,IAC7BR,EAAEE,iBACFO,EAAKI,SAGHF,SAASC,gBAAkBH,IAC7BT,EAAEE,iBACFM,EAAMK,QAGZ,IACC,CAAC3C,IAEJ,OAAKD,EAGH6C,EAAAA,KAAC,MAAA,CACCC,UAAU,yBACVC,YAAchB,GAAMA,EAAEiB,kBACtBC,UAAWnB,EACXoB,IAAKlC,EACLmC,KAAK,SACL,aAAW,mBAEXC,SAAA,GAAAP,KAAC,MAAA,CAAIC,UAAU,eACbM,SAAA,CAAAC,MAAC,QAAA,CAAMC,QAAQ,iBAAiBR,UAAU,cAAcM,SAAA,cACxDC,EAAAA,IAAC,QAAA,CACCE,GAAG,iBACHC,KAAK,OACLV,UAAU,gCACVW,MAAOtD,EACPuD,SAAW3B,GAAM3B,EAAY2B,EAAE4B,OAAOF,OACtCR,UAAYlB,IACI,UAAVA,EAAEC,MACJD,EAAEE,iBACEvB,EAAQE,MAAQ,EAClBV,EAAOmB,eAAe,YAEtBC,IAEJ,EAEFsC,YAAY,UACZC,WAAS,MAEXR,IAAC,OAAA,CAAKP,UAAU,iBACbM,WAAQxC,MAAQ,EAAI,GAAGF,EAAQG,WAAWH,EAAQE,QAAU,eAE/DyC,EAAAA,IAAC,UAAOG,KAAK,SAASV,UAAU,qBAAqBgB,QAASxC,EAAY8B,SAAA,WAC1EC,IAAC,SAAA,CAAOG,KAAK,SAASV,UAAU,qBAAqBgB,QAAS,IAAM5D,EAAOmB,eAAe,YAAa,aAAW,iBAAiB+B,SAAA,QACnIC,IAAC,SAAA,CAAOG,KAAK,SAASV,UAAU,qBAAqBgB,QAAS,IAAM5D,EAAOmB,eAAe,YAAa,aAAW,aAAa+B,SAAA,QAC/HP,KAAC,QAAA,CAAMC,UAAU,kCACfM,SAAA,CAAAC,EAAAA,IAAC,QAAA,CACCG,KAAK,WACLO,QAASvD,EACTkD,SAAW3B,GAAMtB,EAAiBsB,EAAE4B,OAAOI,SAC3C,aAAW,iCACX,QAGJV,EAAAA,IAAC,SAAA,CAAOG,KAAK,SAASV,UAAU,qBAAqBgB,QAAS7D,EAAS,aAAW,yBAAyBmD,SAAA,WAE7GP,KAAC,MAAA,CAAIC,UAAU,eACbM,SAAA,CAAAC,MAAC,QAAA,CAAMC,QAAQ,oBAAoBR,UAAU,cAAcM,SAAA,iBAC3DC,EAAAA,IAAC,QAAA,CACCE,GAAG,oBACHC,KAAK,OACLV,UAAU,gCACVW,MAAOnD,EACPoD,SAAW3B,GAAMxB,EAAewB,EAAE4B,OAAOF,OACzCR,UAAYlB,IACI,UAAVA,EAAEC,MACJD,EAAEE,iBACF/B,EAAOmB,eAAe,UAAW,CAAEf,gBACrC,EAEFsD,YAAY,iBAEdP,IAAC,SAAA,CAAOG,KAAK,SAASV,UAAU,qBAAqBgB,QAAS,IAAM5D,EAAOmB,eAAe,UAAW,CAAEf,gBAAgB8C,SAAA,YACvHC,EAAAA,IAAC,UAAOG,KAAK,SAASV,UAAU,qBAAqBgB,QAASpC,EAAkB0B,SAAA,QAC/EtC,GAAiBuC,EAAAA,IAAC,OAAA,CAAKP,UAAU,iBAAiBK,KAAK,SAAUC,SAAAtC,UAnEtD,IAuEpB,CAEAf,EAAiBiE,UAAY,CAC3BhE,KAAMiE,EAAAA,UAAUC,KAAKC,WACrBlE,QAASgE,EAAAA,UAAUG,KAAKD,WACxBjE,OAAQ+D,EAAAA,UAAUI"}
1
+ {"version":3,"file":"FindReplaceModal-DgysYT04.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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"}
@@ -1,2 +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
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),a=require("./index-qh1Yzh-l.cjs"),t=require("./ModalOverlay-Dh5quv7X.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-BzgYXY9y.cjs.map
@@ -1 +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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"}
1
+ {"version":3,"file":"ImageModal-BzgYXY9y.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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"}
@@ -1,4 +1,4 @@
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__ */
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-DL-qBZZU.js";import{M as o}from"./ModalOverlay-Dt0JiW3M.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
2
  return e(o,{title:"Insert Image",open:m,onClose:d,children:[
3
3
  /* @__PURE__ */e("div",{className:"rmx-tabs",children:[
4
4
  /* @__PURE__ */r("button",{type:"button",className:"rmx-tab "+("url"===p?"rmx-active":""),onClick:()=>u("url"),children:"URL"}),
@@ -20,4 +20,4 @@ return e(o,{title:"Insert Image",open:m,onClose:d,children:[
20
20
  /* @__PURE__ */r("button",{type:"button",className:"rmx-btn",onClick:d,children:"Cancel"}),
21
21
  /* @__PURE__ */r("button",{type:"submit",className:"rmx-btn rmx-btn-primary",disabled:!x.trim()||C,children:C?/* @__PURE__ */e(a,{children:[
22
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
23
+ //# sourceMappingURL=ImageModal-bCzSSTmd.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"ImageModal-bCzSSTmd.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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"}
@@ -1,10 +1,10 @@
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__ */
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-DL-qBZZU.js";import{M as t}from"./ModalOverlay-Dt0JiW3M.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,f]=a(""),[N,g]=a(""),[v,y]=a("insert"),C=l(null),j=()=>{m(),u(null),x(!1),f(""),g(""),y("insert"),C.current&&(C.current.value="")},k=o().join(", ");/* @__PURE__ */
2
2
  return e(t,{title:"Import Document",open:i,onClose:j,children:[
3
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"}),
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),f("");try{const e=await c(r);f(e)}catch(n){g(n.message||"Failed to convert document"),f("")}finally{x(!1)}}},className:"rmx-sr-only"}),
5
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
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:[
7
+ /* @__PURE__ */e("p",{className:"rmx-upload-hint",children:["Supported: ",k]}),p&&!h&&!N&&/* @__PURE__ */r("p",{className:"rmx-upload-hint rmx-upload-filename",children:p.name})]}),N&&/* @__PURE__ */r("div",{className:"rmx-form-group rmx-form-error",children:N}),b&&/* @__PURE__ */e(n,{children:[
8
8
  /* @__PURE__ */e("div",{className:"rmx-form-group",children:[
9
9
  /* @__PURE__ */r("label",{className:"rmx-form-label",children:"Preview"}),
10
10
  /* @__PURE__ */r("div",{className:"rmx-import-preview",dangerouslySetInnerHTML:{__html:d?.sanitizer?d.sanitizer.sanitize(b):""}})]}),
@@ -19,4 +19,4 @@ return e(t,{title:"Import Document",open:i,onClose:j,children:[
19
19
  /* @__PURE__ */r("button",{type:"button",className:"rmx-btn",onClick:j,children:"Cancel"}),
20
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
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
22
+ //# sourceMappingURL=ImportDocumentModal-BypTEn2i.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"ImportDocumentModal-BypTEn2i.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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"}
@@ -1,2 +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
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),s=require("./index-qh1Yzh-l.cjs"),n=require("./ModalOverlay-Dh5quv7X.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-gCFept9p.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImportDocumentModal-Bev9hp_J.cjs","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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"}
1
+ {"version":3,"file":"ImportDocumentModal-gCFept9p.cjs","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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"}
@@ -1,4 +1,4 @@
1
- import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as r,useEffect as n}from"react";import{P as a}from"./index-C720tbJA.js";import{M as o}from"./ModalOverlay-CLvRNHmp.js";const l=/^\s*(https?|mailto|tel|ftp|ftps):\/?\/?/i,m=/^\s*(\/|#|\?|\.)/;function i({open:a,onClose:i,engine:c,data:s}){const[d,p]=r(""),[x,u]=r(""),[h,b]=r("_blank");n((()=>{a&&s&&(p(s.href||""),u(s.text||""),b(s.target||"_blank"))}),[a,s]);/* @__PURE__ */
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as r,useEffect as n}from"react";import{P as a}from"./index-DL-qBZZU.js";import{M as o}from"./ModalOverlay-Dt0JiW3M.js";const l=/^\s*(https?|mailto|tel|ftp|ftps):\/?\/?/i,m=/^\s*(\/|#|\?|\.)/;function i({open:a,onClose:i,engine:c,data:s}){const[d,p]=r(""),[x,u]=r(""),[h,f]=r("_blank");n((()=>{a&&s&&(p(s.href||""),u(s.text||""),f(s.target||"_blank"))}),[a,s]);/* @__PURE__ */
2
2
  return e(o,{title:s?.href?"Edit Link":"Insert Link",open:a,onClose:i,children:/* @__PURE__ */t("form",{onSubmit:e=>{if(e.preventDefault(),!d.trim())return;let t;try{t=decodeURIComponent(d.trim())}catch{t=d.trim()}(l.test(t)||m.test(t))&&(s?.href?c.executeCommand("editLink",{href:d,text:x,target:h}):c.executeCommand("insertLink",{href:d,text:x,target:h}),i())},className:"rmx-modal-form",children:[
3
3
  /* @__PURE__ */t("div",{className:"rmx-form-group",children:[
4
4
  /* @__PURE__ */e("label",{className:"rmx-form-label",htmlFor:"rmx-link-url",children:"URL"}),
@@ -7,9 +7,9 @@ return e(o,{title:s?.href?"Edit Link":"Insert Link",open:a,onClose:i,children:/*
7
7
  /* @__PURE__ */e("label",{className:"rmx-form-label",htmlFor:"rmx-link-text",children:"Display Text"}),
8
8
  /* @__PURE__ */e("input",{id:"rmx-link-text",type:"text",className:"rmx-form-input",value:x,onChange:e=>u(e.target.value),placeholder:"Link text (optional)"})]}),
9
9
  /* @__PURE__ */e("div",{className:"rmx-form-group",children:/* @__PURE__ */t("label",{className:"rmx-form-checkbox",children:[
10
- /* @__PURE__ */e("input",{type:"checkbox",checked:"_blank"===h,onChange:e=>b(e.target.checked?"_blank":"_self")}),"Open in new tab"]})}),
10
+ /* @__PURE__ */e("input",{type:"checkbox",checked:"_blank"===h,onChange:e=>f(e.target.checked?"_blank":"_self")}),"Open in new tab"]})}),
11
11
  /* @__PURE__ */t("div",{className:"rmx-modal-actions",children:[s?.href&&/* @__PURE__ */e("button",{type:"button",className:"rmx-btn rmx-btn-danger",onClick:()=>{c.executeCommand("removeLink"),i()},children:"Remove Link"}),
12
12
  /* @__PURE__ */t("div",{className:"rmx-modal-actions-right",children:[
13
13
  /* @__PURE__ */e("button",{type:"button",className:"rmx-btn",onClick:i,children:"Cancel"}),
14
14
  /* @__PURE__ */e("button",{type:"submit",className:"rmx-btn rmx-btn-primary",children:s?.href?"Update":"Insert"})]})]})]})})}i.propTypes={open:a.bool.isRequired,onClose:a.func.isRequired,engine:a.object,data:a.object};export{i as LinkModal};
15
- //# sourceMappingURL=LinkModal-k9IeDtAb.js.map
15
+ //# sourceMappingURL=LinkModal-CmkK4m-k.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LinkModal-k9IeDtAb.js","sources":["../src/components/Modals/LinkModal.jsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\n// Allowlist-based protocol validation — safer than a blacklist because new\n// dangerous protocols (e.g. mhtml:, ms-*:) are blocked by default.\nconst SAFE_PROTOCOL = /^\\s*(https?|mailto|tel|ftp|ftps):\\/?\\/?/i\nconst RELATIVE_URL = /^\\s*(\\/|#|\\?|\\.)/\n\nexport function LinkModal({ open, onClose, engine, data }) {\n const [href, setHref] = useState('')\n const [text, setText] = useState('')\n const [target, setTarget] = useState('_blank')\n\n useEffect(() => {\n if (open && data) {\n setHref(data.href || '')\n setText(data.text || '')\n setTarget(data.target || '_blank')\n }\n }, [open, data])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!href.trim()) return\n // Decode percent-encoding before validation to prevent bypasses like java%73cript:\n let trimmedHref\n try {\n trimmedHref = decodeURIComponent(href.trim())\n } catch {\n trimmedHref = href.trim()\n }\n // Block any protocol not in the allowlist (blocks javascript:, vbscript:, data:, mhtml:, etc.)\n if (!SAFE_PROTOCOL.test(trimmedHref) && !RELATIVE_URL.test(trimmedHref)) return\n\n if (data?.href) {\n engine.executeCommand('editLink', { href, text, target })\n } else {\n engine.executeCommand('insertLink', { href, text, target })\n }\n onClose()\n }\n\n const handleRemove = () => {\n engine.executeCommand('removeLink')\n onClose()\n }\n\n return (\n <ModalOverlay title={data?.href ? 'Edit Link' : 'Insert Link'} 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-link-url\">URL</label>\n <input\n id=\"rmx-link-url\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={href}\n onChange={(e) => setHref(e.target.value)}\n placeholder=\"https://example.com or /about or #section\"\n required\n autoFocus\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-link-text\">Display Text</label>\n <input\n id=\"rmx-link-text\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Link text (optional)\"\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-checkbox\">\n <input\n type=\"checkbox\"\n checked={target === '_blank'}\n onChange={(e) => setTarget(e.target.checked ? '_blank' : '_self')}\n />\n Open in new tab\n </label>\n </div>\n <div className=\"rmx-modal-actions\">\n {data?.href && (\n <button type=\"button\" className=\"rmx-btn rmx-btn-danger\" onClick={handleRemove}>\n Remove Link\n </button>\n )}\n <div className=\"rmx-modal-actions-right\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\">\n {data?.href ? 'Update' : 'Insert'}\n </button>\n </div>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nLinkModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n data: PropTypes.object,\n}\n"],"names":["SAFE_PROTOCOL","RELATIVE_URL","LinkModal","open","onClose","engine","data","href","setHref","useState","text","setText","target","setTarget","useEffect","ModalOverlay","title","children","onSubmit","e","preventDefault","trim","trimmedHref","decodeURIComponent","test","executeCommand","className","jsxs","jsx","htmlFor","id","type","value","onChange","placeholder","required","autoFocus","checked","onClick","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"yLAMA,MAAMA,EAAgB,2CAChBC,EAAe,mBAEd,SAASC,GAAUC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,EAAAC,KAAQA,IACjD,MAAOC,EAAMC,GAAWC,EAAS,KAC1BC,EAAMC,GAAWF,EAAS,KAC1BG,EAAQC,GAAaJ,EAAS,UAErCK,GAAU,KACJX,GAAQG,IACVE,EAAQF,EAAKC,MAAQ,IACrBI,EAAQL,EAAKI,MAAQ,IACrBG,EAAUP,EAAKM,QAAU,UAC3B,GACC,CAACT,EAAMG;AA4BV,SACGS,EAAA,CAAaC,MAAOV,GAAMC,KAAO,YAAc,cAAeJ,OAAYC,UACzEa,0BAAC,OAAA,CAAKC,SA5BYC,IAEpB,GADAA,EAAEC,kBACGb,EAAKc,OAAQ,OAElB,IAAIC,EACJ,IACEA,EAAcC,mBAAmBhB,EAAKc,OACxC,CAAA,MACEC,EAAcf,EAAKc,MACrB,EAEKrB,EAAcwB,KAAKF,IAAiBrB,EAAauB,KAAKF,MAEvDhB,GAAMC,KACRF,EAAOoB,eAAe,WAAY,CAAElB,OAAMG,OAAME,WAEhDP,EAAOoB,eAAe,aAAc,CAAElB,OAAMG,OAAME,WAEpDR,IAAA,EAUgCsB,UAAU,iBACtCT,SAAA;eAAAU,EAAC,MAAA,CAAID,UAAU,iBACbT,SAAA;eAAAW,EAAC,QAAA,CAAMF,UAAU,iBAAiBG,QAAQ,eAAeZ,SAAA;eACzDW,EAAC,QAAA,CACCE,GAAG,eACHC,KAAK,OACLL,UAAU,iBACVM,MAAOzB,EACP0B,SAAWd,GAAMX,EAAQW,EAAEP,OAAOoB,OAClCE,YAAY,4CACZC,UAAQ,EACRC,WAAS;eAGbT,EAAC,MAAA,CAAID,UAAU,iBACbT,SAAA;eAAAW,EAAC,QAAA,CAAMF,UAAU,iBAAiBG,QAAQ,gBAAgBZ,SAAA;eAC1DW,EAAC,QAAA,CACCE,GAAG,gBACHC,KAAK,OACLL,UAAU,iBACVM,MAAOtB,EACPuB,SAAWd,GAAMR,EAAQQ,EAAEP,OAAOoB,OAClCE,YAAY;iBAGf,MAAA,CAAIR,UAAU,iBACbT,wBAAAU,EAAC,QAAA,CAAMD,UAAU,oBACfT,SAAA;eAAAW,EAAC,QAAA,CACCG,KAAK,WACLM,QAAoB,WAAXzB,EACTqB,SAAWd,GAAMN,EAAUM,EAAEP,OAAOyB,QAAU,SAAW,WACzD;eAINV,EAAC,MAAA,CAAID,UAAU,oBACZT,SAAA,CAAAX,GAAMC,uBACJ,SAAA,CAAOwB,KAAK,SAASL,UAAU,yBAAyBY,QA5C9C,KACnBjC,EAAOoB,eAAe,cACtBrB,GAAA,EA0CwFa,SAAA;eAIlFU,EAAC,MAAA,CAAID,UAAU,0BACbT,SAAA;eAAAW,EAAC,UAAOG,KAAK,SAASL,UAAU,UAAUY,QAASlC,EAASa,SAAA;eAC5DW,EAAC,UAAOG,KAAK,SAASL,UAAU,0BAC7BT,SAAAX,GAAMC,KAAO,SAAW,qBAOvC,CAEAL,EAAUqC,UAAY,CACpBpC,KAAMqC,EAAUC,KAAKC,WACrBtC,QAASoC,EAAUG,KAAKD,WACxBrC,OAAQmC,EAAUI,OAClBtC,KAAMkC,EAAUI"}
1
+ {"version":3,"file":"LinkModal-CmkK4m-k.js","sources":["../src/components/Modals/LinkModal.jsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\n// Allowlist-based protocol validation — safer than a blacklist because new\n// dangerous protocols (e.g. mhtml:, ms-*:) are blocked by default.\nconst SAFE_PROTOCOL = /^\\s*(https?|mailto|tel|ftp|ftps):\\/?\\/?/i\nconst RELATIVE_URL = /^\\s*(\\/|#|\\?|\\.)/\n\nexport function LinkModal({ open, onClose, engine, data }) {\n const [href, setHref] = useState('')\n const [text, setText] = useState('')\n const [target, setTarget] = useState('_blank')\n\n useEffect(() => {\n if (open && data) {\n setHref(data.href || '')\n setText(data.text || '')\n setTarget(data.target || '_blank')\n }\n }, [open, data])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!href.trim()) return\n // Decode percent-encoding before validation to prevent bypasses like java%73cript:\n let trimmedHref\n try {\n trimmedHref = decodeURIComponent(href.trim())\n } catch {\n trimmedHref = href.trim()\n }\n // Block any protocol not in the allowlist (blocks javascript:, vbscript:, data:, mhtml:, etc.)\n if (!SAFE_PROTOCOL.test(trimmedHref) && !RELATIVE_URL.test(trimmedHref)) return\n\n if (data?.href) {\n engine.executeCommand('editLink', { href, text, target })\n } else {\n engine.executeCommand('insertLink', { href, text, target })\n }\n onClose()\n }\n\n const handleRemove = () => {\n engine.executeCommand('removeLink')\n onClose()\n }\n\n return (\n <ModalOverlay title={data?.href ? 'Edit Link' : 'Insert Link'} 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-link-url\">URL</label>\n <input\n id=\"rmx-link-url\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={href}\n onChange={(e) => setHref(e.target.value)}\n placeholder=\"https://example.com or /about or #section\"\n required\n autoFocus\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-link-text\">Display Text</label>\n <input\n id=\"rmx-link-text\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Link text (optional)\"\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-checkbox\">\n <input\n type=\"checkbox\"\n checked={target === '_blank'}\n onChange={(e) => setTarget(e.target.checked ? '_blank' : '_self')}\n />\n Open in new tab\n </label>\n </div>\n <div className=\"rmx-modal-actions\">\n {data?.href && (\n <button type=\"button\" className=\"rmx-btn rmx-btn-danger\" onClick={handleRemove}>\n Remove Link\n </button>\n )}\n <div className=\"rmx-modal-actions-right\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\">\n {data?.href ? 'Update' : 'Insert'}\n </button>\n </div>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nLinkModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n data: PropTypes.object,\n}\n"],"names":["SAFE_PROTOCOL","RELATIVE_URL","LinkModal","open","onClose","engine","data","href","setHref","useState","text","setText","target","setTarget","useEffect","ModalOverlay","title","children","onSubmit","e","preventDefault","trim","trimmedHref","decodeURIComponent","test","executeCommand","className","jsxs","jsx","htmlFor","id","type","value","onChange","placeholder","required","autoFocus","checked","onClick","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"yLAMA,MAAMA,EAAgB,2CAChBC,EAAe,mBAEd,SAASC,GAAUC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,EAAAC,KAAQA,IACjD,MAAOC,EAAMC,GAAWC,EAAS,KAC1BC,EAAMC,GAAWF,EAAS,KAC1BG,EAAQC,GAAaJ,EAAS,UAErCK,GAAU,KACJX,GAAQG,IACVE,EAAQF,EAAKC,MAAQ,IACrBI,EAAQL,EAAKI,MAAQ,IACrBG,EAAUP,EAAKM,QAAU,UAC3B,GACC,CAACT,EAAMG;AA4BV,SACGS,EAAA,CAAaC,MAAOV,GAAMC,KAAO,YAAc,cAAeJ,OAAYC,UACzEa,0BAAC,OAAA,CAAKC,SA5BYC,IAEpB,GADAA,EAAEC,kBACGb,EAAKc,OAAQ,OAElB,IAAIC,EACJ,IACEA,EAAcC,mBAAmBhB,EAAKc,OACxC,CAAA,MACEC,EAAcf,EAAKc,MACrB,EAEKrB,EAAcwB,KAAKF,IAAiBrB,EAAauB,KAAKF,MAEvDhB,GAAMC,KACRF,EAAOoB,eAAe,WAAY,CAAElB,OAAMG,OAAME,WAEhDP,EAAOoB,eAAe,aAAc,CAAElB,OAAMG,OAAME,WAEpDR,IAAA,EAUgCsB,UAAU,iBACtCT,SAAA;eAAAU,EAAC,MAAA,CAAID,UAAU,iBACbT,SAAA;eAAAW,EAAC,QAAA,CAAMF,UAAU,iBAAiBG,QAAQ,eAAeZ,SAAA;eACzDW,EAAC,QAAA,CACCE,GAAG,eACHC,KAAK,OACLL,UAAU,iBACVM,MAAOzB,EACP0B,SAAWd,GAAMX,EAAQW,EAAEP,OAAOoB,OAClCE,YAAY,4CACZC,UAAQ,EACRC,WAAS;eAGbT,EAAC,MAAA,CAAID,UAAU,iBACbT,SAAA;eAAAW,EAAC,QAAA,CAAMF,UAAU,iBAAiBG,QAAQ,gBAAgBZ,SAAA;eAC1DW,EAAC,QAAA,CACCE,GAAG,gBACHC,KAAK,OACLL,UAAU,iBACVM,MAAOtB,EACPuB,SAAWd,GAAMR,EAAQQ,EAAEP,OAAOoB,OAClCE,YAAY;iBAGf,MAAA,CAAIR,UAAU,iBACbT,wBAAAU,EAAC,QAAA,CAAMD,UAAU,oBACfT,SAAA;eAAAW,EAAC,QAAA,CACCG,KAAK,WACLM,QAAoB,WAAXzB,EACTqB,SAAWd,GAAMN,EAAUM,EAAEP,OAAOyB,QAAU,SAAW,WACzD;eAINV,EAAC,MAAA,CAAID,UAAU,oBACZT,SAAA,CAAAX,GAAMC,uBACJ,SAAA,CAAOwB,KAAK,SAASL,UAAU,yBAAyBY,QA5C9C,KACnBjC,EAAOoB,eAAe,cACtBrB,GAAA,EA0CwFa,SAAA;eAIlFU,EAAC,MAAA,CAAID,UAAU,0BACbT,SAAA;eAAAW,EAAC,UAAOG,KAAK,SAASL,UAAU,UAAUY,QAASlC,EAASa,SAAA;eAC5DW,EAAC,UAAOG,KAAK,SAASL,UAAU,0BAC7BT,SAAAX,GAAMC,KAAO,SAAW,qBAOvC,CAEAL,EAAUqC,UAAY,CACpBpC,KAAMqC,EAAUC,KAAKC,WACrBtC,QAASoC,EAAUG,KAAKD,WACxBrC,OAAQmC,EAAUI,OAClBtC,KAAMkC,EAAUI"}
@@ -1,2 +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"),n=require("./ModalOverlay-BDsGgv3_.cjs"),s=/^\s*(https?|mailto|tel|ftp|ftps):\/?\/?/i,a=/^\s*(\/|#|\?|\.)/;function l({open:r,onClose:l,engine:o,data:i}){const[m,c]=t.useState(""),[x,d]=t.useState(""),[u,p]=t.useState("_blank");t.useEffect((()=>{r&&i&&(c(i.href||""),d(i.text||""),p(i.target||"_blank"))}),[r,i]);return e.jsx(n.ModalOverlay,{title:i?.href?"Edit Link":"Insert Link",open:r,onClose:l,children:e.jsxs("form",{onSubmit:e=>{if(e.preventDefault(),!m.trim())return;let t;try{t=decodeURIComponent(m.trim())}catch{t=m.trim()}(s.test(t)||a.test(t))&&(i?.href?o.executeCommand("editLink",{href:m,text:x,target:u}):o.executeCommand("insertLink",{href:m,text:x,target:u}),l())},className:"rmx-modal-form",children:[e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-link-url",children:"URL"}),e.jsx("input",{id:"rmx-link-url",type:"text",className:"rmx-form-input",value:m,onChange:e=>c(e.target.value),placeholder:"https://example.com or /about or #section",required:!0,autoFocus:!0})]}),e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-link-text",children:"Display Text"}),e.jsx("input",{id:"rmx-link-text",type:"text",className:"rmx-form-input",value:x,onChange:e=>d(e.target.value),placeholder:"Link text (optional)"})]}),e.jsx("div",{className:"rmx-form-group",children:e.jsxs("label",{className:"rmx-form-checkbox",children:[e.jsx("input",{type:"checkbox",checked:"_blank"===u,onChange:e=>p(e.target.checked?"_blank":"_self")}),"Open in new tab"]})}),e.jsxs("div",{className:"rmx-modal-actions",children:[i?.href&&e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-danger",onClick:()=>{o.executeCommand("removeLink"),l()},children:"Remove Link"}),e.jsxs("div",{className:"rmx-modal-actions-right",children:[e.jsx("button",{type:"button",className:"rmx-btn",onClick:l,children:"Cancel"}),e.jsx("button",{type:"submit",className:"rmx-btn rmx-btn-primary",children:i?.href?"Update":"Insert"})]})]})]})})}l.propTypes={open:r.PropTypes.bool.isRequired,onClose:r.PropTypes.func.isRequired,engine:r.PropTypes.object,data:r.PropTypes.object},exports.LinkModal=l;
2
- //# sourceMappingURL=LinkModal-B-igSa-g.cjs.map
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("react"),r=require("./index-qh1Yzh-l.cjs"),n=require("./ModalOverlay-Dh5quv7X.cjs"),s=/^\s*(https?|mailto|tel|ftp|ftps):\/?\/?/i,a=/^\s*(\/|#|\?|\.)/;function l({open:r,onClose:l,engine:o,data:i}){const[m,c]=t.useState(""),[x,d]=t.useState(""),[u,p]=t.useState("_blank");t.useEffect((()=>{r&&i&&(c(i.href||""),d(i.text||""),p(i.target||"_blank"))}),[r,i]);return e.jsx(n.ModalOverlay,{title:i?.href?"Edit Link":"Insert Link",open:r,onClose:l,children:e.jsxs("form",{onSubmit:e=>{if(e.preventDefault(),!m.trim())return;let t;try{t=decodeURIComponent(m.trim())}catch{t=m.trim()}(s.test(t)||a.test(t))&&(i?.href?o.executeCommand("editLink",{href:m,text:x,target:u}):o.executeCommand("insertLink",{href:m,text:x,target:u}),l())},className:"rmx-modal-form",children:[e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-link-url",children:"URL"}),e.jsx("input",{id:"rmx-link-url",type:"text",className:"rmx-form-input",value:m,onChange:e=>c(e.target.value),placeholder:"https://example.com or /about or #section",required:!0,autoFocus:!0})]}),e.jsxs("div",{className:"rmx-form-group",children:[e.jsx("label",{className:"rmx-form-label",htmlFor:"rmx-link-text",children:"Display Text"}),e.jsx("input",{id:"rmx-link-text",type:"text",className:"rmx-form-input",value:x,onChange:e=>d(e.target.value),placeholder:"Link text (optional)"})]}),e.jsx("div",{className:"rmx-form-group",children:e.jsxs("label",{className:"rmx-form-checkbox",children:[e.jsx("input",{type:"checkbox",checked:"_blank"===u,onChange:e=>p(e.target.checked?"_blank":"_self")}),"Open in new tab"]})}),e.jsxs("div",{className:"rmx-modal-actions",children:[i?.href&&e.jsx("button",{type:"button",className:"rmx-btn rmx-btn-danger",onClick:()=>{o.executeCommand("removeLink"),l()},children:"Remove Link"}),e.jsxs("div",{className:"rmx-modal-actions-right",children:[e.jsx("button",{type:"button",className:"rmx-btn",onClick:l,children:"Cancel"}),e.jsx("button",{type:"submit",className:"rmx-btn rmx-btn-primary",children:i?.href?"Update":"Insert"})]})]})]})})}l.propTypes={open:r.PropTypes.bool.isRequired,onClose:r.PropTypes.func.isRequired,engine:r.PropTypes.object,data:r.PropTypes.object},exports.LinkModal=l;
2
+ //# sourceMappingURL=LinkModal-DPxg7YCE.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LinkModal-B-igSa-g.cjs","sources":["../src/components/Modals/LinkModal.jsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\n// Allowlist-based protocol validation — safer than a blacklist because new\n// dangerous protocols (e.g. mhtml:, ms-*:) are blocked by default.\nconst SAFE_PROTOCOL = /^\\s*(https?|mailto|tel|ftp|ftps):\\/?\\/?/i\nconst RELATIVE_URL = /^\\s*(\\/|#|\\?|\\.)/\n\nexport function LinkModal({ open, onClose, engine, data }) {\n const [href, setHref] = useState('')\n const [text, setText] = useState('')\n const [target, setTarget] = useState('_blank')\n\n useEffect(() => {\n if (open && data) {\n setHref(data.href || '')\n setText(data.text || '')\n setTarget(data.target || '_blank')\n }\n }, [open, data])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!href.trim()) return\n // Decode percent-encoding before validation to prevent bypasses like java%73cript:\n let trimmedHref\n try {\n trimmedHref = decodeURIComponent(href.trim())\n } catch {\n trimmedHref = href.trim()\n }\n // Block any protocol not in the allowlist (blocks javascript:, vbscript:, data:, mhtml:, etc.)\n if (!SAFE_PROTOCOL.test(trimmedHref) && !RELATIVE_URL.test(trimmedHref)) return\n\n if (data?.href) {\n engine.executeCommand('editLink', { href, text, target })\n } else {\n engine.executeCommand('insertLink', { href, text, target })\n }\n onClose()\n }\n\n const handleRemove = () => {\n engine.executeCommand('removeLink')\n onClose()\n }\n\n return (\n <ModalOverlay title={data?.href ? 'Edit Link' : 'Insert Link'} 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-link-url\">URL</label>\n <input\n id=\"rmx-link-url\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={href}\n onChange={(e) => setHref(e.target.value)}\n placeholder=\"https://example.com or /about or #section\"\n required\n autoFocus\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-link-text\">Display Text</label>\n <input\n id=\"rmx-link-text\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Link text (optional)\"\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-checkbox\">\n <input\n type=\"checkbox\"\n checked={target === '_blank'}\n onChange={(e) => setTarget(e.target.checked ? '_blank' : '_self')}\n />\n Open in new tab\n </label>\n </div>\n <div className=\"rmx-modal-actions\">\n {data?.href && (\n <button type=\"button\" className=\"rmx-btn rmx-btn-danger\" onClick={handleRemove}>\n Remove Link\n </button>\n )}\n <div className=\"rmx-modal-actions-right\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\">\n {data?.href ? 'Update' : 'Insert'}\n </button>\n </div>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nLinkModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n data: PropTypes.object,\n}\n"],"names":["SAFE_PROTOCOL","RELATIVE_URL","LinkModal","open","onClose","engine","data","href","setHref","useState","text","setText","target","setTarget","useEffect","jsx","ModalOverlay","title","children","jsxs","onSubmit","e","preventDefault","trim","trimmedHref","decodeURIComponent","test","executeCommand","className","htmlFor","id","type","value","onChange","placeholder","required","autoFocus","checked","onClick","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"mNAMMA,EAAgB,2CAChBC,EAAe,mBAEd,SAASC,GAAUC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,EAAAC,KAAQA,IACjD,MAAOC,EAAMC,GAAWC,EAAAA,SAAS,KAC1BC,EAAMC,GAAWF,EAAAA,SAAS,KAC1BG,EAAQC,GAAaJ,EAAAA,SAAS,UAErCK,EAAAA,WAAU,KACJX,GAAQG,IACVE,EAAQF,EAAKC,MAAQ,IACrBI,EAAQL,EAAKI,MAAQ,IACrBG,EAAUP,EAAKM,QAAU,UAC3B,GACC,CAACT,EAAMG,IA4BV,OACES,EAAAA,IAACC,EAAAA,aAAA,CAAaC,MAAOX,GAAMC,KAAO,YAAc,cAAeJ,OAAYC,UACzEc,WAAAC,KAAC,OAAA,CAAKC,SA5BYC,IAEpB,GADAA,EAAEC,kBACGf,EAAKgB,OAAQ,OAElB,IAAIC,EACJ,IACEA,EAAcC,mBAAmBlB,EAAKgB,OACxC,CAAA,MACEC,EAAcjB,EAAKgB,MACrB,EAEKvB,EAAc0B,KAAKF,IAAiBvB,EAAayB,KAAKF,MAEvDlB,GAAMC,KACRF,EAAOsB,eAAe,WAAY,CAAEpB,OAAMG,OAAME,WAEhDP,EAAOsB,eAAe,aAAc,CAAEpB,OAAMG,OAAME,WAEpDR,IAAA,EAUgCwB,UAAU,iBACtCV,SAAA,GAAAC,KAAC,MAAA,CAAIS,UAAU,iBACbV,SAAA,CAAAH,MAAC,QAAA,CAAMa,UAAU,iBAAiBC,QAAQ,eAAeX,SAAA,QACzDH,EAAAA,IAAC,QAAA,CACCe,GAAG,eACHC,KAAK,OACLH,UAAU,iBACVI,MAAOzB,EACP0B,SAAWZ,GAAMb,EAAQa,EAAET,OAAOoB,OAClCE,YAAY,4CACZC,UAAQ,EACRC,WAAS,SAGbjB,KAAC,MAAA,CAAIS,UAAU,iBACbV,SAAA,CAAAH,MAAC,QAAA,CAAMa,UAAU,iBAAiBC,QAAQ,gBAAgBX,SAAA,iBAC1DH,EAAAA,IAAC,QAAA,CACCe,GAAG,gBACHC,KAAK,OACLH,UAAU,iBACVI,MAAOtB,EACPuB,SAAWZ,GAAMV,EAAQU,EAAET,OAAOoB,OAClCE,YAAY,kCAGf,MAAA,CAAIN,UAAU,iBACbV,SAAAC,EAAAA,KAAC,QAAA,CAAMS,UAAU,oBACfV,SAAA,CAAAH,EAAAA,IAAC,QAAA,CACCgB,KAAK,WACLM,QAAoB,WAAXzB,EACTqB,SAAWZ,GAAMR,EAAUQ,EAAET,OAAOyB,QAAU,SAAW,WACzD,yBAINlB,KAAC,MAAA,CAAIS,UAAU,oBACZV,SAAA,CAAAZ,GAAMC,YACJ,SAAA,CAAOwB,KAAK,SAASH,UAAU,yBAAyBU,QA5C9C,KACnBjC,EAAOsB,eAAe,cACtBvB,GAAA,EA0CwFc,SAAA,kBAIlFC,KAAC,MAAA,CAAIS,UAAU,0BACbV,SAAA,CAAAH,EAAAA,IAAC,UAAOgB,KAAK,SAASH,UAAU,UAAUU,QAASlC,EAASc,SAAA,WAC5DH,EAAAA,IAAC,UAAOgB,KAAK,SAASH,UAAU,0BAC7BV,SAAAZ,GAAMC,KAAO,SAAW,qBAOvC,CAEAL,EAAUqC,UAAY,CACpBpC,KAAMqC,EAAAA,UAAUC,KAAKC,WACrBtC,QAASoC,EAAAA,UAAUG,KAAKD,WACxBrC,OAAQmC,EAAAA,UAAUI,OAClBtC,KAAMkC,EAAAA,UAAUI"}
1
+ {"version":3,"file":"LinkModal-DPxg7YCE.cjs","sources":["../src/components/Modals/LinkModal.jsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport { ModalOverlay } from './ModalOverlay.jsx'\n\n// Allowlist-based protocol validation — safer than a blacklist because new\n// dangerous protocols (e.g. mhtml:, ms-*:) are blocked by default.\nconst SAFE_PROTOCOL = /^\\s*(https?|mailto|tel|ftp|ftps):\\/?\\/?/i\nconst RELATIVE_URL = /^\\s*(\\/|#|\\?|\\.)/\n\nexport function LinkModal({ open, onClose, engine, data }) {\n const [href, setHref] = useState('')\n const [text, setText] = useState('')\n const [target, setTarget] = useState('_blank')\n\n useEffect(() => {\n if (open && data) {\n setHref(data.href || '')\n setText(data.text || '')\n setTarget(data.target || '_blank')\n }\n }, [open, data])\n\n const handleSubmit = (e) => {\n e.preventDefault()\n if (!href.trim()) return\n // Decode percent-encoding before validation to prevent bypasses like java%73cript:\n let trimmedHref\n try {\n trimmedHref = decodeURIComponent(href.trim())\n } catch {\n trimmedHref = href.trim()\n }\n // Block any protocol not in the allowlist (blocks javascript:, vbscript:, data:, mhtml:, etc.)\n if (!SAFE_PROTOCOL.test(trimmedHref) && !RELATIVE_URL.test(trimmedHref)) return\n\n if (data?.href) {\n engine.executeCommand('editLink', { href, text, target })\n } else {\n engine.executeCommand('insertLink', { href, text, target })\n }\n onClose()\n }\n\n const handleRemove = () => {\n engine.executeCommand('removeLink')\n onClose()\n }\n\n return (\n <ModalOverlay title={data?.href ? 'Edit Link' : 'Insert Link'} 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-link-url\">URL</label>\n <input\n id=\"rmx-link-url\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={href}\n onChange={(e) => setHref(e.target.value)}\n placeholder=\"https://example.com or /about or #section\"\n required\n autoFocus\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-label\" htmlFor=\"rmx-link-text\">Display Text</label>\n <input\n id=\"rmx-link-text\"\n type=\"text\"\n className=\"rmx-form-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Link text (optional)\"\n />\n </div>\n <div className=\"rmx-form-group\">\n <label className=\"rmx-form-checkbox\">\n <input\n type=\"checkbox\"\n checked={target === '_blank'}\n onChange={(e) => setTarget(e.target.checked ? '_blank' : '_self')}\n />\n Open in new tab\n </label>\n </div>\n <div className=\"rmx-modal-actions\">\n {data?.href && (\n <button type=\"button\" className=\"rmx-btn rmx-btn-danger\" onClick={handleRemove}>\n Remove Link\n </button>\n )}\n <div className=\"rmx-modal-actions-right\">\n <button type=\"button\" className=\"rmx-btn\" onClick={onClose}>Cancel</button>\n <button type=\"submit\" className=\"rmx-btn rmx-btn-primary\">\n {data?.href ? 'Update' : 'Insert'}\n </button>\n </div>\n </div>\n </form>\n </ModalOverlay>\n )\n}\n\nLinkModal.propTypes = {\n open: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n engine: PropTypes.object,\n data: PropTypes.object,\n}\n"],"names":["SAFE_PROTOCOL","RELATIVE_URL","LinkModal","open","onClose","engine","data","href","setHref","useState","text","setText","target","setTarget","useEffect","jsx","ModalOverlay","title","children","jsxs","onSubmit","e","preventDefault","trim","trimmedHref","decodeURIComponent","test","executeCommand","className","htmlFor","id","type","value","onChange","placeholder","required","autoFocus","checked","onClick","propTypes","PropTypes","bool","isRequired","func","object"],"mappings":"mNAMMA,EAAgB,2CAChBC,EAAe,mBAEd,SAASC,GAAUC,KAAEA,EAAAC,QAAMA,EAAAC,OAASA,EAAAC,KAAQA,IACjD,MAAOC,EAAMC,GAAWC,EAAAA,SAAS,KAC1BC,EAAMC,GAAWF,EAAAA,SAAS,KAC1BG,EAAQC,GAAaJ,EAAAA,SAAS,UAErCK,EAAAA,WAAU,KACJX,GAAQG,IACVE,EAAQF,EAAKC,MAAQ,IACrBI,EAAQL,EAAKI,MAAQ,IACrBG,EAAUP,EAAKM,QAAU,UAC3B,GACC,CAACT,EAAMG,IA4BV,OACES,EAAAA,IAACC,EAAAA,aAAA,CAAaC,MAAOX,GAAMC,KAAO,YAAc,cAAeJ,OAAYC,UACzEc,WAAAC,KAAC,OAAA,CAAKC,SA5BYC,IAEpB,GADAA,EAAEC,kBACGf,EAAKgB,OAAQ,OAElB,IAAIC,EACJ,IACEA,EAAcC,mBAAmBlB,EAAKgB,OACxC,CAAA,MACEC,EAAcjB,EAAKgB,MACrB,EAEKvB,EAAc0B,KAAKF,IAAiBvB,EAAayB,KAAKF,MAEvDlB,GAAMC,KACRF,EAAOsB,eAAe,WAAY,CAAEpB,OAAMG,OAAME,WAEhDP,EAAOsB,eAAe,aAAc,CAAEpB,OAAMG,OAAME,WAEpDR,IAAA,EAUgCwB,UAAU,iBACtCV,SAAA,GAAAC,KAAC,MAAA,CAAIS,UAAU,iBACbV,SAAA,CAAAH,MAAC,QAAA,CAAMa,UAAU,iBAAiBC,QAAQ,eAAeX,SAAA,QACzDH,EAAAA,IAAC,QAAA,CACCe,GAAG,eACHC,KAAK,OACLH,UAAU,iBACVI,MAAOzB,EACP0B,SAAWZ,GAAMb,EAAQa,EAAET,OAAOoB,OAClCE,YAAY,4CACZC,UAAQ,EACRC,WAAS,SAGbjB,KAAC,MAAA,CAAIS,UAAU,iBACbV,SAAA,CAAAH,MAAC,QAAA,CAAMa,UAAU,iBAAiBC,QAAQ,gBAAgBX,SAAA,iBAC1DH,EAAAA,IAAC,QAAA,CACCe,GAAG,gBACHC,KAAK,OACLH,UAAU,iBACVI,MAAOtB,EACPuB,SAAWZ,GAAMV,EAAQU,EAAET,OAAOoB,OAClCE,YAAY,kCAGf,MAAA,CAAIN,UAAU,iBACbV,SAAAC,EAAAA,KAAC,QAAA,CAAMS,UAAU,oBACfV,SAAA,CAAAH,EAAAA,IAAC,QAAA,CACCgB,KAAK,WACLM,QAAoB,WAAXzB,EACTqB,SAAWZ,GAAMR,EAAUQ,EAAET,OAAOyB,QAAU,SAAW,WACzD,yBAINlB,KAAC,MAAA,CAAIS,UAAU,oBACZV,SAAA,CAAAZ,GAAMC,YACJ,SAAA,CAAOwB,KAAK,SAASH,UAAU,yBAAyBU,QA5C9C,KACnBjC,EAAOsB,eAAe,cACtBvB,GAAA,EA0CwFc,SAAA,kBAIlFC,KAAC,MAAA,CAAIS,UAAU,0BACbV,SAAA,CAAAH,EAAAA,IAAC,UAAOgB,KAAK,SAASH,UAAU,UAAUU,QAASlC,EAASc,SAAA,WAC5DH,EAAAA,IAAC,UAAOgB,KAAK,SAASH,UAAU,0BAC7BV,SAAAZ,GAAMC,KAAO,SAAW,qBAOvC,CAEAL,EAAUqC,UAAY,CACpBpC,KAAMqC,EAAAA,UAAUC,KAAKC,WACrBtC,QAASoC,EAAAA,UAAUG,KAAKD,WACxBrC,OAAQmC,EAAAA,UAAUI,OAClBtC,KAAMkC,EAAAA,UAAUI"}
@@ -1,4 +1,4 @@
1
- import{jsx as e,jsxs as n}from"react/jsx-runtime";import r,{useState as t,useRef as a,useCallback as o,useEffect as l}from"react";import{u,I as m,C as s}from"./index-C720tbJA.js";import{TOOLTIP_MAP as c,getShortcutLabel as i,getCommandActiveState as p,MODAL_COMMANDS as b}from"@remyxjs/core";function d(e,n){if(!e)return;const r=Array.from(e.querySelectorAll(':scope > [role="menuitem"], :scope > .rmx-menubar-submenu-wrapper > [role="menuitem"]')),t=document.activeElement,a=r.indexOf(t);if(-1===a)return;let o;"down"===n?o=a<r.length-1?a+1:0:"up"===n?o=a>0?a-1:r.length-1:"home"===n?o=0:"end"===n&&(o=r.length-1),r[o]?.focus()}const f=r.memo((function({item:r,engine:t,onOpenModal:a,onClose:o}){const l=u();if("---"===r)/* @__PURE__ */
1
+ import{jsx as e,jsxs as n}from"react/jsx-runtime";import r,{useState as t,useRef as a,useCallback as o,useEffect as l}from"react";import{u,I as m,C as s}from"./index-DL-qBZZU.js";import{TOOLTIP_MAP as c,getShortcutLabel as i,getCommandActiveState as p,MODAL_COMMANDS as b}from"@remyxjs/core";function d(e,n){if(!e)return;const r=Array.from(e.querySelectorAll(':scope > [role="menuitem"], :scope > .rmx-menubar-submenu-wrapper > [role="menuitem"]')),t=document.activeElement,a=r.indexOf(t);if(-1===a)return;let o;"down"===n?o=a<r.length-1?a+1:0:"up"===n?o=a>0?a-1:r.length-1:"home"===n?o=0:"end"===n&&(o=r.length-1),r[o]?.focus()}const f=r.memo((function({item:r,engine:t,onOpenModal:a,onClose:o}){const l=u();if("---"===r)/* @__PURE__ */
2
2
  return e("div",{className:"rmx-menubar-separator",role:"separator"});if("object"==typeof r&&r.label&&r.items)/* @__PURE__ */
3
3
  return e(x,{label:r.label,items:r.items,engine:t,onOpenModal:a,onClose:o});const s="string"==typeof r?r:r.command,f=c[s]||s,v=i(s),w=p(s,l,t),h=m[s],D=e=>{e.preventDefault();const n=b[s];n?"link"===s?l.link?a?.("link",l.link):a?.("link",{text:t.selection.getSelectedText()}):a?.(n):t.executeCommand(s),o()};/* @__PURE__ */
4
4
  return n("button",{className:"rmx-menubar-item "+(w?"rmx-active":""),onClick:D,onKeyDown:e=>{const n=e.currentTarget.closest(".rmx-menubar-menu");switch(e.key){case"ArrowDown":e.preventDefault(),d(n,"down");break;case"ArrowUp":e.preventDefault(),d(n,"up");break;case"Home":e.preventDefault(),d(n,"home");break;case"End":e.preventDefault(),d(n,"end");break;case"Enter":case" ":e.preventDefault(),D(e)}},onMouseDown:e=>e.preventDefault(),type:"button",role:"menuitem",tabIndex:-1,children:[
@@ -9,4 +9,4 @@ return n("button",{className:"rmx-menubar-item "+(w?"rmx-active":""),onClick:D,o
9
9
  /* @__PURE__ */e("span",{className:"rmx-menubar-item-label",children:r}),
10
10
  /* @__PURE__ */e("span",{className:"rmx-menubar-submenu-arrow",children:/* @__PURE__ */e(s,{size:14})})]}),p&&/* @__PURE__ */e("div",{className:"rmx-menubar-menu rmx-menubar-submenu",role:"menu","aria-label":r,children:u.map(((n,r)=>/* @__PURE__ */e(f,{item:n,engine:m,onOpenModal:c,onClose:i},"string"==typeof n?`${n}-${r}`:n.label||r)))})]})}const v=r.memo((function({config:r,engine:m,onOpenModal:s}){u();const[c,i]=t(null),[p,b]=t(!1),d=a(null),x=a([]);l((()=>{if(null===c)return;const e=e=>{d.current&&!d.current.contains(e.target)&&(i(null),b(!1))},n=e=>{"Escape"===e.key&&(i(null),b(!1),null!==c&&x.current[c]?.focus())};return document.addEventListener("mousedown",e),document.addEventListener("keydown",n),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("keydown",n)}}),[c]);const v=o((e=>{c===e?(i(null),b(!1)):(i(e),b(!0))}),[c]),w=o((e=>{p&&null!==c&&i(e)}),[p,c]),h=o((()=>{i(null),b(!1)}),[]),D=o((e=>{const n=x.current.filter(Boolean),r=n.indexOf(document.activeElement);switch(e.key){case"ArrowRight":{e.preventDefault();const t=r<n.length-1?r+1:0;n[t]?.focus(),null!==c&&i(t);break}case"ArrowLeft":{e.preventDefault();const t=r>0?r-1:n.length-1;n[t]?.focus(),null!==c&&i(t);break}case"ArrowDown":{e.preventDefault(),null===c&&r>=0&&(i(r),b(!0));const n=d.current?.querySelectorAll(".rmx-menubar-menu")?.[0];if(n){const e=n.querySelector('[role="menuitem"]');e?.focus()}break}case"Home":e.preventDefault(),n[0]?.focus(),null!==c&&i(0);break;case"End":e.preventDefault(),n[n.length-1]?.focus(),null!==c&&i(n.length-1)}}),[c]);return m&&r?/* @__PURE__ */e("div",{className:"rmx-menubar",ref:d,role:"menubar","aria-label":"Editor menu",onKeyDown:D,children:r.map(((r,t)=>/* @__PURE__ */n("div",{className:"rmx-menubar-dropdown",children:[
11
11
  /* @__PURE__ */e("button",{ref:e=>{x.current[t]=e},className:"rmx-menubar-trigger "+(c===t?"rmx-open":""),onClick:e=>{e.preventDefault(),v(t)},onMouseEnter:()=>w(t),onMouseDown:e=>e.preventDefault(),type:"button",role:"menuitem","aria-haspopup":"true","aria-expanded":c===t,tabIndex:0===t?0:-1,children:r.label}),c===t&&/* @__PURE__ */e("div",{className:"rmx-menubar-menu",role:"menu","aria-label":r.label,children:r.items.map(((n,r)=>/* @__PURE__ */e(f,{item:n,engine:m,onOpenModal:s,onClose:h},"string"==typeof n?`${n}-${r}`:n.label||n.command||r)))})]},r.label)))}):null}));export{v as MenuBar};
12
- //# sourceMappingURL=MenuBar-DWxJNHmb.js.map
12
+ //# sourceMappingURL=MenuBar-DOv7JPwR.js.map