minisiwyg-editor 0.2.0 → 0.2.1

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.
@@ -1,6 +1,6 @@
1
- "use strict";var z=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var G=(e,r)=>{for(var o in r)z(e,o,{get:r[o],enumerable:!0})},W=(e,r,o,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of Y(r))!$.call(e,i)&&i!==o&&z(e,i,{get:()=>r[i],enumerable:!(a=j(r,i))||a.enumerable});return e};var K=e=>W(z({},"__esModule",{value:!0}),e);var X={};G(X,{Minisiwyg:()=>V});module.exports=K(X);var A=require("react");var R={tags:{p:[],br:[],strong:[],em:[],a:["href","title","target"],h1:[],h2:[],h3:[],ul:[],ol:[],li:[],blockquote:[],pre:[],code:[]},strip:!0,maxDepth:10,maxLength:1e5,protocols:["https","http","mailto"]};Object.freeze(R);Object.freeze(R.protocols);for(let e of Object.values(R.tags))Object.freeze(e);Object.freeze(R.tags);var T=R;var S={b:"strong",i:"em"},k=new Set(["href","src","action","formaction"]),Z=new Set(["javascript","data"]);function Q(e){let r=e.trim();r=r.replace(/&#x([0-9a-f]+);?/gi,(a,i)=>String.fromCharCode(parseInt(i,16))),r=r.replace(/&#(\d+);?/g,(a,i)=>String.fromCharCode(parseInt(i,10)));try{r=decodeURIComponent(r)}catch{}r=r.replace(/[\s\x00-\x1f\u00A0\u1680\u2000-\u200B\u2028\u2029\u202F\u205F\u3000\uFEFF]+/g,"");let o=r.match(/^([a-z][a-z0-9+\-.]*)\s*:/i);return o?o[1].toLowerCase():null}function P(e,r){let o=Q(e);return o===null?!0:Z.has(o)?!1:r.includes(o)}function D(e,r,o){let a=Array.from(e.childNodes);for(let i of a){if(i.nodeType===3)continue;if(i.nodeType!==1){e.removeChild(i);continue}let n=i,u=n.tagName.toLowerCase(),l=S[u];if(l&&(u=l),o>=r.maxDepth){e.removeChild(n);continue}let s=r.tags[u];if(s===void 0){if(r.strip)e.removeChild(n);else{for(D(n,r,o);n.firstChild;)e.insertBefore(n.firstChild,n);e.removeChild(n)}continue}let f=n;if(l&&n.tagName.toLowerCase()!==l){let p=n.ownerDocument.createElement(l);for(;n.firstChild;)p.appendChild(n.firstChild);e.replaceChild(p,n),f=p}let b=Array.from(f.attributes);for(let C of b){let p=C.name.toLowerCase();if(p.startsWith("on")){f.removeAttribute(C.name);continue}if(!s.includes(p)){f.removeAttribute(C.name);continue}k.has(p)&&(P(C.value,r.protocols)||f.removeAttribute(C.name))}D(f,r,o+1)}}function O(e,r){let o=document.createElement("template");if(!e)return o.content;o.innerHTML=e;let a=o.content;return D(a,r,0),r.maxLength>0&&(a.textContent?.length??0)>r.maxLength&&H(a,r.maxLength),a}function H(e,r){let o=r,a=Array.from(e.childNodes);for(let i of a){if(o<=0){e.removeChild(i);continue}if(i.nodeType===3){let n=i.textContent??"";n.length>o?(i.textContent=n.slice(0,o),o=0):o-=n.length}else i.nodeType===1?o=H(i,o):e.removeChild(i)}return o}function J(e,r){let o=0,a=e.parentNode;for(;a&&a!==r;)a.nodeType===1&&o++,a=a.parentNode;return o}function U(e,r,o){let a=e.tagName.toLowerCase(),i=S[a];if(i&&(a=i),J(e,o)>=r.maxDepth)return e.parentNode?.removeChild(e),!0;let u=r.tags[a];if(u===void 0){if(r.strip)e.parentNode?.removeChild(e);else{let s=e.parentNode;if(s){for(;e.firstChild;)s.insertBefore(e.firstChild,e);s.removeChild(e)}}return!0}let l=e;if(i&&e.tagName.toLowerCase()!==i){let s=e.ownerDocument.createElement(i);for(;e.firstChild;)s.appendChild(e.firstChild);for(let f of Array.from(e.attributes))s.setAttribute(f.name,f.value);e.parentNode?.replaceChild(s,e),l=s}for(let s of Array.from(l.attributes)){let f=s.name.toLowerCase();if(f.startsWith("on")){l.removeAttribute(s.name);continue}if(!u.includes(f)){l.removeAttribute(s.name);continue}k.has(f)&&(P(s.value,r.protocols)||l.removeAttribute(s.name))}return!1}function I(e,r,o){let a=Array.from(e.childNodes);for(let i of a){if(i.nodeType!==1){i.nodeType!==3&&e.removeChild(i);continue}U(i,r,o)||I(i,r,o)}}function _(e,r){if(!r||!r.tags)throw new TypeError('Policy must have a "tags" property');let o=!1,a=[];function i(u){for(let l of a)l(u)}let n=new MutationObserver(u=>{if(!o){o=!0;try{for(let l of u)if(l.type==="childList")for(let s of Array.from(l.addedNodes)){if(s.nodeType===3)continue;if(s.nodeType!==1){s.parentNode?.removeChild(s);continue}U(s,r,e)||I(s,r,e)}else if(l.type==="attributes"){let s=l.target;if(s.nodeType!==1)continue;let f=l.attributeName;if(!f)continue;let b=s.tagName.toLowerCase(),C=S[b]||b,p=r.tags[C];if(!p)continue;let w=f.toLowerCase();if(w.startsWith("on")){s.removeAttribute(f);continue}if(!p.includes(w)){s.removeAttribute(f);continue}if(k.has(w)){let y=s.getAttribute(f);y&&!P(y,r.protocols)&&s.removeAttribute(f)}}}catch(l){i(l instanceof Error?l:new Error(String(l)))}finally{o=!1}}});return n.observe(e,{childList:!0,attributes:!0,subtree:!0}),{destroy(){n.disconnect()},on(u,l){u==="error"&&a.push(l)}}}var F=new Set(["bold","italic","heading","blockquote","unorderedList","orderedList","link","unlink","codeBlock"]);function B(e,r){if(!e)throw new TypeError("createEditor requires an HTMLElement");if(!e.ownerDocument||!e.parentNode)throw new TypeError("createEditor requires an element attached to the DOM");let o=r?.policy??T,a={tags:Object.fromEntries(Object.entries(o.tags).map(([d,c])=>[d,[...c]])),strip:o.strip,maxDepth:o.maxDepth,maxLength:o.maxLength,protocols:[...o.protocols]},i={},n=e.ownerDocument;function u(d,...c){for(let t of i[d]??[])t(...c)}function l(){let d=e.innerHTML;u("change",d),r?.onChange?.(d)}e.contentEditable="true";let s=_(e,a);s.on("error",d=>u("error",d));function f(d){d.preventDefault();let c=d.clipboardData;if(!c)return;let t=n.getSelection();if(t&&t.rangeCount>0&&t.anchorNode&&p(t.anchorNode,"PRE")){let g=c.getData("text/plain");if(!g)return;a.maxLength>0&&(e.textContent?.length??0)+g.length>a.maxLength&&u("overflow",a.maxLength);let x=t.getRangeAt(0);x.deleteContents();let M=n.createTextNode(g);x.insertNode(M),x.setStartAfter(M),x.collapse(!0),t.removeAllRanges(),t.addRange(x),u("paste",e.innerHTML),l();return}let L=c.getData("text/html");if(!L){let m=c.getData("text/plain");if(!m)return;L=m.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/\n/g,"<br>")}let h=O(L,a),v=n.getSelection();if(!v||v.rangeCount===0)return;let E=v.getRangeAt(0);if(E.deleteContents(),a.maxLength>0){let m=h.textContent?.length??0;(e.textContent?.length??0)+m>a.maxLength&&u("overflow",a.maxLength)}let N=h.lastChild;if(E.insertNode(h),N){let m=n.createRange();m.setStartAfter(N),m.collapse(!0),v.removeAllRanges(),v.addRange(m)}u("paste",e.innerHTML),l()}function b(){l()}function C(d){let c=n.getSelection();if(!c||c.rangeCount===0)return;let t=c.anchorNode;if(!t)return;let L=p(t,"PRE");if(d.key==="Enter"&&L){d.preventDefault();let h=c.getRangeAt(0);h.deleteContents();let v=n.createTextNode(`
2
- `);h.insertNode(v),h.setStartAfter(v),h.collapse(!0),c.removeAllRanges(),c.addRange(h),l()}if(d.key==="Backspace"&&L){let h=L.textContent||"";if(c.anchorOffset===0&&(h===""||h===`
3
- `)){d.preventDefault();let N=n.createElement("p");N.appendChild(n.createElement("br")),L.parentNode?.replaceChild(N,L);let m=n.createRange();m.selectNodeContents(N),m.collapse(!0),c.removeAllRanges(),c.addRange(m),l()}}}e.addEventListener("keydown",C),e.addEventListener("paste",f),e.addEventListener("input",b);function p(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return t;t=t.parentNode}return null}function w(d){let c=n.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t)return!1;let L=p(t,d);if(!L)return!1;let h=p(t,"LI"),v=L.parentNode;if(!v)return!1;let E=[],N=null;for(let g of Array.from(L.childNodes)){if(g.nodeType!==1||g.tagName!=="LI")continue;let x=n.createElement("p");for(;g.firstChild;)x.appendChild(g.firstChild);x.firstChild||x.appendChild(n.createElement("br")),E.push(x),g===h&&(N=x)}for(let g of E)v.insertBefore(g,L);v.removeChild(L);let m=N??E[0];if(m){let g=n.createRange();g.selectNodeContents(m),g.collapse(!1),c.removeAllRanges(),c.addRange(g)}return l(),!0}function y(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return!0;t=t.parentNode}return!1}return{exec(d,c){if(!F.has(d))throw new Error(`Unknown editor command: "${d}"`);switch(e.focus(),d){case"bold":n.execCommand("bold",!1);break;case"italic":n.execCommand("italic",!1);break;case"heading":{let t=c??"1";if(!["1","2","3"].includes(t))throw new Error(`Invalid heading level: "${t}". Use 1, 2, or 3`);n.execCommand("formatBlock",!1,`<h${t}>`);break}case"blockquote":n.execCommand("formatBlock",!1,"<blockquote>");break;case"unorderedList":w("UL")||n.execCommand("insertUnorderedList",!1);break;case"orderedList":w("OL")||n.execCommand("insertOrderedList",!1);break;case"link":{if(!c)throw new Error("Link command requires a URL value");let t=c.trim();if(!P(t,a.protocols)){u("error",new Error(`Protocol not allowed: ${t}`));return}n.execCommand("createLink",!1,t);break}case"unlink":n.execCommand("unlink",!1);break;case"codeBlock":{let t=n.getSelection();if(!t||t.rangeCount===0)break;let L=t.anchorNode,h=L?p(L,"PRE"):null;if(h){let v=n.createElement("p");v.textContent=h.textContent||"",h.parentNode?.replaceChild(v,h);let E=n.createRange();E.selectNodeContents(v),E.collapse(!1),t.removeAllRanges(),t.addRange(E)}else{let E=t.getRangeAt(0).startContainer;for(;E.parentNode&&E.parentNode!==e;)E=E.parentNode;let N=n.createElement("pre"),m=n.createElement("code"),g=E.textContent||"";m.textContent=g.endsWith(`
4
- `)?g:g+`
5
- `,N.appendChild(m),E.parentNode===e?e.replaceChild(N,E):e.appendChild(N);let x=n.createRange();x.selectNodeContents(m),x.collapse(!1),t.removeAllRanges(),t.addRange(x)}l();break}}},queryState(d){if(!F.has(d))throw new Error(`Unknown editor command: "${d}"`);let c=n.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t||!e.contains(t))return!1;switch(d){case"bold":return y(t,"STRONG")||y(t,"B");case"italic":return y(t,"EM")||y(t,"I");case"heading":return y(t,"H1")||y(t,"H2")||y(t,"H3");case"blockquote":return y(t,"BLOCKQUOTE");case"unorderedList":return y(t,"UL");case"orderedList":return y(t,"OL");case"link":return y(t,"A");case"unlink":return!1;case"codeBlock":return y(t,"PRE");default:return!1}},getHTML(){return e.innerHTML},getText(){return e.textContent??""},destroy(){e.removeEventListener("keydown",C),e.removeEventListener("paste",f),e.removeEventListener("input",b),s.destroy(),e.contentEditable="false"},on(d,c){i[d]||(i[d]=[]),i[d].push(c)}}}var q=require("react/jsx-runtime");function V(e){let{initialHTML:r,value:o,onChange:a,policy:i,className:n,editorRef:u}=e,l=(0,A.useRef)(null),s=(0,A.useRef)(null),f=(0,A.useRef)(a),b=(0,A.useRef)(i??T);return(0,A.useEffect)(()=>{f.current=a},[a]),(0,A.useEffect)(()=>{let C=l.current;if(!C)return;let p=i??T;b.current=p,C.replaceChildren(O(o??r??"",p));let w=B(C,{policy:p,onChange:y=>f.current?.(y)});return s.current=w,u?.(w),()=>{w.destroy(),s.current=null,u?.(null)}},[]),(0,A.useEffect)(()=>{let C=s.current,p=l.current;!C||!p||o===void 0||C.getHTML()!==o&&p.replaceChildren(O(o,b.current))},[o]),(0,q.jsx)("div",{ref:l,className:n})}
1
+ "use strict";var D=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var G=Object.prototype.hasOwnProperty;var W=(e,r)=>{for(var o in r)D(e,o,{get:r[o],enumerable:!0})},K=(e,r,o,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of $(r))!G.call(e,i)&&i!==o&&D(e,i,{get:()=>r[i],enumerable:!(a=Y(r,i))||a.enumerable});return e};var Z=e=>K(D({},"__esModule",{value:!0}),e);var ee={};W(ee,{Minisiwyg:()=>X});module.exports=Z(ee);var A=require("react");var R={tags:{p:[],br:[],strong:[],em:[],a:["href","title","target"],h1:[],h2:[],h3:[],ul:[],ol:[],li:[],blockquote:[],pre:[],code:[]},strip:!0,maxDepth:10,maxLength:1e5,protocols:["https","http","mailto"]};Object.freeze(R);Object.freeze(R.protocols);for(let e of Object.values(R.tags))Object.freeze(e);Object.freeze(R.tags);var T=R;var S={b:"strong",i:"em"},k=new Set(["href","src","action","formaction"]),Q=new Set(["javascript","data"]);function J(e){let r=e.trim();r=r.replace(/&#x([0-9a-f]+);?/gi,(a,i)=>String.fromCharCode(parseInt(i,16))),r=r.replace(/&#(\d+);?/g,(a,i)=>String.fromCharCode(parseInt(i,10)));try{r=decodeURIComponent(r)}catch{}r=r.replace(/[\s\x00-\x1f\u00A0\u1680\u2000-\u200B\u2028\u2029\u202F\u205F\u3000\uFEFF]+/g,"");let o=r.match(/^([a-z][a-z0-9+\-.]*)\s*:/i);return o?o[1].toLowerCase():null}function P(e,r){let o=J(e);return o===null?!0:Q.has(o)?!1:r.includes(o)}function M(e,r,o){let a=Array.from(e.childNodes);for(let i of a){if(i.nodeType===3)continue;if(i.nodeType!==1){e.removeChild(i);continue}let n=i,u=n.tagName.toLowerCase(),l=S[u];if(l&&(u=l),o>=r.maxDepth){e.removeChild(n);continue}let s=r.tags[u];if(s===void 0){if(r.strip)e.removeChild(n);else{for(M(n,r,o);n.firstChild;)e.insertBefore(n.firstChild,n);e.removeChild(n)}continue}let f=n;if(l&&n.tagName.toLowerCase()!==l){let p=n.ownerDocument.createElement(l);for(;n.firstChild;)p.appendChild(n.firstChild);e.replaceChild(p,n),f=p}let b=Array.from(f.attributes);for(let L of b){let p=L.name.toLowerCase();if(p.startsWith("on")){f.removeAttribute(L.name);continue}if(!s.includes(p)){f.removeAttribute(L.name);continue}k.has(p)&&(P(L.value,r.protocols)||f.removeAttribute(L.name))}M(f,r,o+1)}}function O(e,r){let o=document.createElement("template");if(!e)return o.content;o.innerHTML=e;let a=o.content;return M(a,r,0),r.maxLength>0&&(a.textContent?.length??0)>r.maxLength&&U(a,r.maxLength),a}function U(e,r){let o=r,a=Array.from(e.childNodes);for(let i of a){if(o<=0){e.removeChild(i);continue}if(i.nodeType===3){let n=i.textContent??"";n.length>o?(i.textContent=n.slice(0,o),o=0):o-=n.length}else i.nodeType===1?o=U(i,o):e.removeChild(i)}return o}function V(e,r){let o=0,a=e.parentNode;for(;a&&a!==r;)a.nodeType===1&&o++,a=a.parentNode;return o}function I(e,r,o){let a=e.tagName.toLowerCase(),i=S[a];if(i&&(a=i),V(e,o)>=r.maxDepth)return e.parentNode?.removeChild(e),!0;let u=r.tags[a];if(u===void 0){if(r.strip)e.parentNode?.removeChild(e);else{let s=e.parentNode;if(s){for(;e.firstChild;)s.insertBefore(e.firstChild,e);s.removeChild(e)}}return!0}let l=e;if(i&&e.tagName.toLowerCase()!==i){let s=e.ownerDocument.createElement(i);for(;e.firstChild;)s.appendChild(e.firstChild);for(let f of Array.from(e.attributes))s.setAttribute(f.name,f.value);e.parentNode?.replaceChild(s,e),l=s}for(let s of Array.from(l.attributes)){let f=s.name.toLowerCase();if(f.startsWith("on")){l.removeAttribute(s.name);continue}if(!u.includes(f)){l.removeAttribute(s.name);continue}k.has(f)&&(P(s.value,r.protocols)||l.removeAttribute(s.name))}return!1}function _(e,r,o){let a=Array.from(e.childNodes);for(let i of a){if(i.nodeType!==1){i.nodeType!==3&&e.removeChild(i);continue}I(i,r,o)||_(i,r,o)}}function F(e,r){if(!r||!r.tags)throw new TypeError('Policy must have a "tags" property');let o=!1,a=[];function i(u){for(let l of a)l(u)}let n=new MutationObserver(u=>{if(!o){o=!0;try{for(let l of u)if(l.type==="childList")for(let s of Array.from(l.addedNodes)){if(s.nodeType===3)continue;if(s.nodeType!==1){s.parentNode?.removeChild(s);continue}I(s,r,e)||_(s,r,e)}else if(l.type==="attributes"){let s=l.target;if(s.nodeType!==1)continue;let f=l.attributeName;if(!f)continue;let b=s.tagName.toLowerCase(),L=S[b]||b,p=r.tags[L];if(!p)continue;let w=f.toLowerCase();if(w.startsWith("on")){s.removeAttribute(f);continue}if(!p.includes(w)){s.removeAttribute(f);continue}if(k.has(w)){let y=s.getAttribute(f);y&&!P(y,r.protocols)&&s.removeAttribute(f)}}}catch(l){i(l instanceof Error?l:new Error(String(l)))}finally{o=!1}}});return n.observe(e,{childList:!0,attributes:!0,subtree:!0}),{destroy(){n.disconnect()},on(u,l){u==="error"&&a.push(l)}}}var B=new Set(["bold","italic","heading","blockquote","unorderedList","orderedList","link","unlink","codeBlock"]);function q(e,r){if(!e)throw new TypeError("createEditor requires an HTMLElement");if(!e.ownerDocument||!e.parentNode)throw new TypeError("createEditor requires an element attached to the DOM");let o=r?.policy??T,a={tags:Object.fromEntries(Object.entries(o.tags).map(([d,c])=>[d,[...c]])),strip:o.strip,maxDepth:o.maxDepth,maxLength:o.maxLength,protocols:[...o.protocols]},i={},n=e.ownerDocument;function u(d,...c){for(let t of i[d]??[])t(...c)}function l(){let d=e.innerHTML;u("change",d),r?.onChange?.(d)}e.contentEditable="true";let s=F(e,a);s.on("error",d=>u("error",d));function f(d){d.preventDefault();let c=d.clipboardData;if(!c)return;let t=n.getSelection();if(t&&t.rangeCount>0&&t.anchorNode&&p(t.anchorNode,"PRE")){let E=c.getData("text/plain");if(!E)return;a.maxLength>0&&(e.textContent?.length??0)+E.length>a.maxLength&&u("overflow",a.maxLength);let x=t.getRangeAt(0);x.deleteContents();let H=n.createTextNode(E);x.insertNode(H),x.setStartAfter(H),x.collapse(!0),t.removeAllRanges(),t.addRange(x),u("paste",e.innerHTML),l();return}let g=c.getData("text/html");if(!g){let h=c.getData("text/plain");if(!h)return;g=h.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/\n/g,"<br>")}let m=O(g,a),v=n.getSelection();if(!v||v.rangeCount===0)return;let C=v.getRangeAt(0);if(C.deleteContents(),a.maxLength>0){let h=m.textContent?.length??0;(e.textContent?.length??0)+h>a.maxLength&&u("overflow",a.maxLength)}let N=m.lastChild;if(C.insertNode(m),N){let h=n.createRange();h.setStartAfter(N),h.collapse(!0),v.removeAllRanges(),v.addRange(h)}u("paste",e.innerHTML),l()}function b(){l()}function L(d){let c=n.getSelection();if(!c||c.rangeCount===0)return;let t=c.anchorNode;if(!t)return;let g=p(t,"PRE");if(d.key==="Enter"&&g){d.preventDefault();let m=c.getRangeAt(0);m.deleteContents();let v=n.createTextNode(`
2
+ `);m.insertNode(v),m.setStartAfter(v),m.collapse(!0),c.removeAllRanges(),c.addRange(m),l()}if(d.key==="Backspace"&&g){let m=g.textContent||"";if(c.anchorOffset===0&&(m===""||m===`
3
+ `)){d.preventDefault();let N=n.createElement("p");N.appendChild(n.createElement("br")),g.parentNode?.replaceChild(N,g);let h=n.createRange();h.selectNodeContents(N),h.collapse(!0),c.removeAllRanges(),c.addRange(h),l()}}}e.addEventListener("keydown",L),e.addEventListener("paste",f),e.addEventListener("input",b);function p(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return t;t=t.parentNode}return null}function w(d){let c=n.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t)return!1;let g=p(t,d);if(!g)return!1;let m=p(t,"LI"),v=g.parentNode;if(!v)return!1;let C=[],N=null;for(let E of Array.from(g.childNodes)){if(E.nodeType!==1||E.tagName!=="LI")continue;let x=n.createElement("p");for(;E.firstChild;)x.appendChild(E.firstChild);x.firstChild||x.appendChild(n.createElement("br")),C.push(x),E===m&&(N=x)}for(let E of C)v.insertBefore(E,g);v.removeChild(g);let h=N??C[0];if(h){let E=n.createRange();E.selectNodeContents(h),E.collapse(!1),c.removeAllRanges(),c.addRange(E)}return l(),!0}function y(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return!0;t=t.parentNode}return!1}let z={exec(d,c){if(!B.has(d))throw new Error(`Unknown editor command: "${d}"`);switch(e.focus(),d){case"bold":n.execCommand("bold",!1);break;case"italic":n.execCommand("italic",!1);break;case"heading":{let t=c??"1";if(!["1","2","3"].includes(t))throw new Error(`Invalid heading level: "${t}". Use 1, 2, or 3`);let g=`H${t}`,m=n.getSelection()?.anchorNode;m&&z.queryState("heading")&&y(m,g)?n.execCommand("formatBlock",!1,"<p>"):n.execCommand("formatBlock",!1,`<h${t}>`);break}case"blockquote":z.queryState("blockquote")?n.execCommand("formatBlock",!1,"<p>"):n.execCommand("formatBlock",!1,"<blockquote>");break;case"unorderedList":w("UL")||n.execCommand("insertUnorderedList",!1);break;case"orderedList":w("OL")||n.execCommand("insertOrderedList",!1);break;case"link":{if(!c)throw new Error("Link command requires a URL value");let t=c.trim();if(!P(t,a.protocols)){u("error",new Error(`Protocol not allowed: ${t}`));return}n.execCommand("createLink",!1,t);break}case"unlink":n.execCommand("unlink",!1);break;case"codeBlock":{let t=n.getSelection();if(!t||t.rangeCount===0)break;let g=t.anchorNode,m=g?p(g,"PRE"):null;if(m){let v=n.createElement("p");v.textContent=m.textContent||"",m.parentNode?.replaceChild(v,m);let C=n.createRange();C.selectNodeContents(v),C.collapse(!1),t.removeAllRanges(),t.addRange(C)}else{let C=t.getRangeAt(0).startContainer;for(;C.parentNode&&C.parentNode!==e;)C=C.parentNode;let N=n.createElement("pre"),h=n.createElement("code"),E=C.textContent||"";h.textContent=E.endsWith(`
4
+ `)?E:E+`
5
+ `,N.appendChild(h),C.parentNode===e?e.replaceChild(N,C):e.appendChild(N);let x=n.createRange();x.selectNodeContents(h),x.collapse(!1),t.removeAllRanges(),t.addRange(x)}l();break}}},queryState(d){if(!B.has(d))throw new Error(`Unknown editor command: "${d}"`);let c=n.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t||!e.contains(t))return!1;switch(d){case"bold":return y(t,"STRONG")||y(t,"B");case"italic":return y(t,"EM")||y(t,"I");case"heading":return y(t,"H1")||y(t,"H2")||y(t,"H3");case"blockquote":return y(t,"BLOCKQUOTE");case"unorderedList":return y(t,"UL");case"orderedList":return y(t,"OL");case"link":return y(t,"A");case"unlink":return!1;case"codeBlock":return y(t,"PRE");default:return!1}},getHTML(){return e.innerHTML},getText(){return e.textContent??""},destroy(){e.removeEventListener("keydown",L),e.removeEventListener("paste",f),e.removeEventListener("input",b),s.destroy(),e.contentEditable="false"},on(d,c){i[d]||(i[d]=[]),i[d].push(c)}};return z}var j=require("react/jsx-runtime");function X(e){let{initialHTML:r,value:o,onChange:a,policy:i,className:n,editorRef:u}=e,l=(0,A.useRef)(null),s=(0,A.useRef)(null),f=(0,A.useRef)(a),b=(0,A.useRef)(i??T);return(0,A.useEffect)(()=>{f.current=a},[a]),(0,A.useEffect)(()=>{let L=l.current;if(!L)return;let p=i??T;b.current=p,L.replaceChildren(O(o??r??"",p));let w=q(L,{policy:p,onChange:y=>f.current?.(y)});return s.current=w,u?.(w),()=>{w.destroy(),s.current=null,u?.(null)}},[]),(0,A.useEffect)(()=>{let L=s.current,p=l.current;!L||!p||o===void 0||L.getHTML()!==o&&p.replaceChildren(O(o,b.current))},[o]),(0,j.jsx)("div",{ref:l,className:n})}
6
6
  //# sourceMappingURL=react.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/adapters/react.tsx", "../../src/defaults.ts", "../../src/shared.ts", "../../src/sanitize.ts", "../../src/policy.ts", "../../src/editor.ts"],
4
- "sourcesContent": ["import { useEffect, useRef } from 'react';\nimport { createEditor } from '../editor';\nimport { sanitizeToFragment } from '../sanitize';\nimport { DEFAULT_POLICY } from '../defaults';\nimport type { Editor, SanitizePolicy } from '../types';\n\nexport type { Editor, SanitizePolicy } from '../types';\n\nexport interface MinisiwygProps {\n initialHTML?: string;\n value?: string;\n onChange?: (html: string) => void;\n policy?: SanitizePolicy;\n className?: string;\n editorRef?: (editor: Editor | null) => void;\n}\n\nexport function Minisiwyg(props: MinisiwygProps) {\n const { initialHTML, value, onChange, policy, className, editorRef } = props;\n const hostRef = useRef<HTMLDivElement | null>(null);\n const editorInstance = useRef<Editor | null>(null);\n const onChangeRef = useRef<typeof onChange>(onChange);\n const policyRef = useRef<SanitizePolicy>(policy ?? DEFAULT_POLICY);\n\n useEffect(() => {\n onChangeRef.current = onChange;\n }, [onChange]);\n\n useEffect(() => {\n const el = hostRef.current;\n if (!el) return;\n const effectivePolicy = policy ?? DEFAULT_POLICY;\n policyRef.current = effectivePolicy;\n el.replaceChildren(sanitizeToFragment(value ?? initialHTML ?? '', effectivePolicy));\n const editor = createEditor(el, {\n policy: effectivePolicy,\n onChange: (html) => onChangeRef.current?.(html),\n });\n editorInstance.current = editor;\n editorRef?.(editor);\n return () => {\n editor.destroy();\n editorInstance.current = null;\n editorRef?.(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const editor = editorInstance.current;\n const el = hostRef.current;\n if (!editor || !el || value === undefined) return;\n if (editor.getHTML() !== value) {\n el.replaceChildren(sanitizeToFragment(value, policyRef.current));\n }\n }, [value]);\n\n return <div ref={hostRef} className={className} />;\n}\n", "import type { SanitizePolicy } from './types';\n\nconst policy: SanitizePolicy = {\n tags: {\n p: [],\n br: [],\n strong: [],\n em: [],\n a: ['href', 'title', 'target'],\n h1: [],\n h2: [],\n h3: [],\n ul: [],\n ol: [],\n li: [],\n blockquote: [],\n pre: [],\n code: [],\n },\n strip: true,\n maxDepth: 10,\n maxLength: 100_000,\n protocols: ['https', 'http', 'mailto'],\n};\n\n// Deep freeze to prevent mutation of security-critical defaults\nObject.freeze(policy);\nObject.freeze(policy.protocols);\nfor (const attrs of Object.values(policy.tags)) Object.freeze(attrs);\nObject.freeze(policy.tags);\n\nexport const DEFAULT_POLICY: Readonly<SanitizePolicy> = policy;\n", "/** Tag normalization map: browser-variant tags \u2192 semantic equivalents. */\nexport const TAG_NORMALIZE: Record<string, string> = {\n b: 'strong',\n i: 'em',\n};\n\n/** Attributes that contain URLs and need protocol validation. */\nexport const URL_ATTRS = new Set(['href', 'src', 'action', 'formaction']);\n\n/** Protocols that are always denied regardless of policy. */\nexport const DENIED_PROTOCOLS = new Set(['javascript', 'data']);\n\n/**\n * Parse a URL-like string and extract the protocol.\n * Returns the lowercase protocol name (without colon), or null if none found.\n */\nexport function extractProtocol(value: string): string | null {\n let decoded = value.trim();\n decoded = decoded.replace(/&#x([0-9a-f]+);?/gi, (_, hex) =>\n String.fromCharCode(parseInt(hex, 16)),\n );\n decoded = decoded.replace(/&#(\\d+);?/g, (_, dec) =>\n String.fromCharCode(parseInt(dec, 10)),\n );\n try {\n decoded = decodeURIComponent(decoded);\n } catch {\n // keep entity-decoded result\n }\n decoded = decoded.replace(/[\\s\\x00-\\x1f\\u00A0\\u1680\\u2000-\\u200B\\u2028\\u2029\\u202F\\u205F\\u3000\\uFEFF]+/g, '');\n const match = decoded.match(/^([a-z][a-z0-9+\\-.]*)\\s*:/i);\n return match ? match[1].toLowerCase() : null;\n}\n\n/**\n * Check if a URL value is allowed by the given protocol list.\n * javascript: and data: are always denied.\n */\nexport function isProtocolAllowed(value: string, allowedProtocols: string[]): boolean {\n const protocol = extractProtocol(value);\n if (protocol === null) return true;\n if (DENIED_PROTOCOLS.has(protocol)) return false;\n return allowedProtocols.includes(protocol);\n}\n", "import type { SanitizePolicy } from './types';\nimport { TAG_NORMALIZE, URL_ATTRS, isProtocolAllowed } from './shared';\nexport { DEFAULT_POLICY } from './defaults';\nexport type { SanitizePolicy } from './types';\n\n/**\n * Walk a DOM tree depth-first and sanitize according to policy.\n * Mutates the tree in place.\n */\nfunction walkAndSanitize(\n parent: Node,\n policy: SanitizePolicy,\n depth: number,\n): void {\n const children = Array.from(parent.childNodes);\n\n for (const node of children) {\n // Text nodes: always allowed (length enforcement happens after walk)\n if (node.nodeType === 3) continue;\n\n // Non-element, non-text nodes (comments, processing instructions): remove\n if (node.nodeType !== 1) {\n parent.removeChild(node);\n continue;\n }\n\n const el = node as Element;\n let tagName = el.tagName.toLowerCase();\n\n // Normalize tags (b\u2192strong, i\u2192em)\n const normalized = TAG_NORMALIZE[tagName];\n if (normalized) {\n tagName = normalized;\n }\n\n // Check depth limit\n if (depth >= policy.maxDepth) {\n parent.removeChild(el);\n continue;\n }\n\n // Check tag whitelist\n const allowedAttrs = policy.tags[tagName];\n if (allowedAttrs === undefined) {\n // Tag not allowed\n if (policy.strip) {\n // Remove the node and all its children\n parent.removeChild(el);\n } else {\n // Unwrap: sanitize children first, then move them up\n walkAndSanitize(el, policy, depth);\n while (el.firstChild) {\n parent.insertBefore(el.firstChild, el);\n }\n parent.removeChild(el);\n }\n continue;\n }\n\n // Tag is allowed. If it was normalized, replace with correct element.\n let current: Element = el;\n if (normalized && el.tagName.toLowerCase() !== normalized) {\n const doc = el.ownerDocument!;\n const replacement = doc.createElement(normalized);\n while (el.firstChild) {\n replacement.appendChild(el.firstChild);\n }\n parent.replaceChild(replacement, el);\n current = replacement;\n }\n\n // Strip disallowed attributes\n const attrs = Array.from(current.attributes);\n for (const attr of attrs) {\n const attrName = attr.name.toLowerCase();\n\n // Always strip event handlers (on*)\n if (attrName.startsWith('on')) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n // Check attribute whitelist\n if (!allowedAttrs.includes(attrName)) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n // Validate URL protocols on URL-bearing attributes\n if (URL_ATTRS.has(attrName)) {\n if (!isProtocolAllowed(attr.value, policy.protocols)) {\n current.removeAttribute(attr.name);\n }\n }\n }\n\n // Recurse into children\n walkAndSanitize(current, policy, depth + 1);\n }\n}\n\n/**\n * Sanitize an HTML string and return a DocumentFragment.\n * Avoids the serialize\u2192reparse round-trip that can cause mXSS.\n */\nexport function sanitizeToFragment(html: string, policy: SanitizePolicy): DocumentFragment {\n const template = document.createElement('template');\n if (!html) return template.content;\n\n template.innerHTML = html;\n const fragment = template.content;\n\n walkAndSanitize(fragment, policy, 0);\n\n if (policy.maxLength > 0 && (fragment.textContent?.length ?? 0) > policy.maxLength) {\n truncateToLength(fragment, policy.maxLength);\n }\n\n return fragment;\n}\n\n/**\n * Sanitize an HTML string according to the given policy.\n *\n * Uses a <template> element to parse HTML without executing scripts.\n * Walks the resulting DOM tree depth-first, removing disallowed elements\n * and attributes. Returns the sanitized HTML string.\n */\nexport function sanitize(html: string, policy: SanitizePolicy): string {\n if (!html) return '';\n\n const fragment = sanitizeToFragment(html, policy);\n const container = document.createElement('div');\n container.appendChild(fragment);\n return container.innerHTML;\n}\n\n/**\n * Truncate a DOM tree's text content to a maximum length.\n * Removes nodes beyond the limit while preserving structure.\n */\nfunction truncateToLength(node: Node, maxLength: number): number {\n let remaining = maxLength;\n\n const children = Array.from(node.childNodes);\n for (const child of children) {\n if (remaining <= 0) {\n node.removeChild(child);\n continue;\n }\n\n if (child.nodeType === 3) {\n // Text node\n const text = child.textContent ?? '';\n if (text.length > remaining) {\n child.textContent = text.slice(0, remaining);\n remaining = 0;\n } else {\n remaining -= text.length;\n }\n } else if (child.nodeType === 1) {\n remaining = truncateToLength(child, remaining);\n } else {\n node.removeChild(child);\n }\n }\n\n return remaining;\n}\n", "import type { SanitizePolicy } from './types';\nimport { TAG_NORMALIZE, URL_ATTRS, isProtocolAllowed } from './shared';\n\nexport { DEFAULT_POLICY } from './defaults';\nexport type { SanitizePolicy } from './types';\n\nexport interface PolicyEnforcer {\n destroy(): void;\n on(event: 'error', handler: (error: Error) => void): void;\n}\n\n/**\n * Get the nesting depth of a node within a root element.\n */\nfunction getDepth(node: Node, root: Node): number {\n let depth = 0;\n let current = node.parentNode;\n while (current && current !== root) {\n if (current.nodeType === 1) depth++;\n current = current.parentNode;\n }\n return depth;\n}\n\n/**\n * Check if an element is allowed by the policy and fix it if not.\n * Returns true if the node was removed/replaced.\n */\nfunction enforceElement(\n el: Element,\n policy: SanitizePolicy,\n root: HTMLElement,\n): boolean {\n let tagName = el.tagName.toLowerCase();\n const normalized = TAG_NORMALIZE[tagName];\n if (normalized) tagName = normalized;\n\n // Check depth\n const depth = getDepth(el, root);\n if (depth >= policy.maxDepth) {\n el.parentNode?.removeChild(el);\n return true;\n }\n\n // Check tag whitelist\n const allowedAttrs = policy.tags[tagName];\n if (allowedAttrs === undefined) {\n if (policy.strip) {\n el.parentNode?.removeChild(el);\n } else {\n // Unwrap: move children up, then remove the element\n const parent = el.parentNode;\n if (parent) {\n while (el.firstChild) {\n parent.insertBefore(el.firstChild, el);\n }\n parent.removeChild(el);\n }\n }\n return true;\n }\n\n // Normalize tag if needed (e.g. <b> \u2192 <strong>)\n let current: Element = el;\n if (normalized && el.tagName.toLowerCase() !== normalized) {\n const replacement = el.ownerDocument.createElement(normalized);\n while (el.firstChild) {\n replacement.appendChild(el.firstChild);\n }\n // Copy allowed attributes\n for (const attr of Array.from(el.attributes)) {\n replacement.setAttribute(attr.name, attr.value);\n }\n el.parentNode?.replaceChild(replacement, el);\n current = replacement;\n }\n\n // Strip disallowed attributes\n for (const attr of Array.from(current.attributes)) {\n const attrName = attr.name.toLowerCase();\n\n if (attrName.startsWith('on')) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n if (!allowedAttrs.includes(attrName)) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n if (URL_ATTRS.has(attrName)) {\n if (!isProtocolAllowed(attr.value, policy.protocols)) {\n current.removeAttribute(attr.name);\n }\n }\n }\n\n return false;\n}\n\n/**\n * Recursively enforce policy on all descendants of a node.\n */\nfunction enforceSubtree(node: Node, policy: SanitizePolicy, root: HTMLElement): void {\n const children = Array.from(node.childNodes);\n for (const child of children) {\n if (child.nodeType !== 1) {\n // Remove non-text, non-element nodes (comments, etc.)\n if (child.nodeType !== 3) {\n node.removeChild(child);\n }\n continue;\n }\n const removed = enforceElement(child as Element, policy, root);\n if (!removed) {\n enforceSubtree(child, policy, root);\n }\n }\n}\n\n/**\n * Create a policy enforcer that uses MutationObserver to enforce\n * the sanitization policy on a live DOM element.\n *\n * This is defense-in-depth \u2014 the paste handler is the primary security boundary.\n * The observer catches mutations from execCommand, programmatic DOM manipulation,\n * and other sources.\n */\nexport function createPolicyEnforcer(\n element: HTMLElement,\n policy: SanitizePolicy,\n): PolicyEnforcer {\n if (!policy || !policy.tags) {\n throw new TypeError('Policy must have a \"tags\" property');\n }\n\n let isApplyingFix = false;\n const errorHandlers: Array<(error: Error) => void> = [];\n\n function emitError(error: Error): void {\n for (const handler of errorHandlers) {\n handler(error);\n }\n }\n\n const observer = new MutationObserver((mutations) => {\n if (isApplyingFix) return;\n isApplyingFix = true;\n\n try {\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (const node of Array.from(mutation.addedNodes)) {\n // Skip text nodes\n if (node.nodeType === 3) continue;\n\n // Remove non-element nodes\n if (node.nodeType !== 1) {\n node.parentNode?.removeChild(node);\n continue;\n }\n\n const removed = enforceElement(node as Element, policy, element);\n if (!removed) {\n // Also enforce on all descendants of the added node\n enforceSubtree(node, policy, element);\n }\n }\n } else if (mutation.type === 'attributes') {\n const target = mutation.target as Element;\n if (target.nodeType !== 1) continue;\n\n const attrName = mutation.attributeName;\n if (!attrName) continue;\n\n const tagName = target.tagName.toLowerCase();\n const normalizedTag = TAG_NORMALIZE[tagName] || tagName;\n const allowedAttrs = policy.tags[normalizedTag];\n\n if (!allowedAttrs) continue;\n\n const lowerAttr = attrName.toLowerCase();\n\n if (lowerAttr.startsWith('on')) {\n target.removeAttribute(attrName);\n continue;\n }\n\n if (!allowedAttrs.includes(lowerAttr)) {\n target.removeAttribute(attrName);\n continue;\n }\n\n if (URL_ATTRS.has(lowerAttr)) {\n const value = target.getAttribute(attrName);\n if (value && !isProtocolAllowed(value, policy.protocols)) {\n target.removeAttribute(attrName);\n }\n }\n }\n }\n } catch (err) {\n emitError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n isApplyingFix = false;\n }\n });\n\n observer.observe(element, {\n childList: true,\n attributes: true,\n subtree: true,\n });\n\n return {\n destroy() {\n observer.disconnect();\n },\n on(event: 'error', handler: (error: Error) => void) {\n if (event === 'error') {\n errorHandlers.push(handler);\n }\n },\n };\n}\n", "import type { SanitizePolicy, EditorOptions, Editor } from './types';\nimport { DEFAULT_POLICY } from './defaults';\nimport { sanitizeToFragment } from './sanitize';\nimport { createPolicyEnforcer, type PolicyEnforcer } from './policy';\nimport { isProtocolAllowed } from './shared';\n\nexport type { Editor, EditorOptions } from './types';\nexport { DEFAULT_POLICY } from './defaults';\n\ntype EditorEvent = 'change' | 'paste' | 'overflow' | 'error';\ntype EventHandler = (...args: unknown[]) => void;\n\nconst SUPPORTED_COMMANDS = new Set([\n 'bold',\n 'italic',\n 'heading',\n 'blockquote',\n 'unorderedList',\n 'orderedList',\n 'link',\n 'unlink',\n 'codeBlock',\n]);\n\n/**\n * Create a contentEditable-based editor with built-in sanitization.\n *\n * The paste handler is the primary security boundary \u2014 it sanitizes HTML\n * before insertion via Selection/Range API. The MutationObserver-based\n * policy enforcer provides defense-in-depth.\n */\nexport function createEditor(\n element: HTMLElement,\n options?: EditorOptions,\n): Editor {\n if (!element) {\n throw new TypeError('createEditor requires an HTMLElement');\n }\n if (!element.ownerDocument || !element.parentNode) {\n throw new TypeError('createEditor requires an element attached to the DOM');\n }\n\n const src = options?.policy ?? DEFAULT_POLICY;\n const policy: SanitizePolicy = {\n tags: Object.fromEntries(\n Object.entries(src.tags).map(([k, v]) => [k, [...v]]),\n ),\n strip: src.strip,\n maxDepth: src.maxDepth,\n maxLength: src.maxLength,\n protocols: [...src.protocols],\n };\n\n const handlers: Record<string, EventHandler[]> = {};\n const doc = element.ownerDocument;\n\n function emit(event: EditorEvent, ...args: unknown[]): void {\n for (const handler of handlers[event] ?? []) {\n handler(...args);\n }\n }\n\n // Notify both subscription paths (editor.on('change') and options.onChange)\n // for programmatic edits that don't fire 'input' (codeBlock toggle, list unwrap).\n function emitChange(): void {\n const html = element.innerHTML;\n emit('change', html);\n options?.onChange?.(html);\n }\n\n // Set up contentEditable\n element.contentEditable = 'true';\n\n // Attach policy enforcer (MutationObserver defense-in-depth)\n const enforcer: PolicyEnforcer = createPolicyEnforcer(element, policy);\n enforcer.on('error', (err) => emit('error', err));\n\n // Paste handler \u2014 the primary security boundary\n function onPaste(e: ClipboardEvent): void {\n e.preventDefault();\n\n const clipboard = e.clipboardData;\n if (!clipboard) return;\n\n // Inside code block: paste as plain text only\n const sel = doc.getSelection();\n if (sel && sel.rangeCount > 0 && sel.anchorNode) {\n const pre = findAncestor(sel.anchorNode, 'PRE');\n if (pre) {\n const text = clipboard.getData('text/plain');\n if (!text) return;\n if (policy.maxLength > 0) {\n const currentLen = element.textContent?.length ?? 0;\n if (currentLen + text.length > policy.maxLength) {\n emit('overflow', policy.maxLength);\n }\n }\n const range = sel.getRangeAt(0);\n range.deleteContents();\n const textNode = doc.createTextNode(text);\n range.insertNode(textNode);\n range.setStartAfter(textNode);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n emit('paste', element.innerHTML);\n emitChange();\n return;\n }\n }\n\n // Prefer HTML, fall back to plain text\n let html = clipboard.getData('text/html');\n if (!html) {\n const text = clipboard.getData('text/plain');\n if (!text) return;\n // Escape plain text and convert newlines to <br>\n html = text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n .replace(/\\n/g, '<br>');\n }\n\n // Sanitize through policy \u2014 returns DocumentFragment directly\n // to avoid the serialize\u2192reparse mXSS vector\n const fragment = sanitizeToFragment(html, policy);\n\n // Insert via Selection/Range API (NOT execCommand('insertHTML'))\n const selection = doc.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n\n const range = selection.getRangeAt(0);\n range.deleteContents();\n\n // Check overflow using text content length\n if (policy.maxLength > 0) {\n const pasteTextLen = fragment.textContent?.length ?? 0;\n const currentLen = element.textContent?.length ?? 0;\n if (currentLen + pasteTextLen > policy.maxLength) {\n emit('overflow', policy.maxLength);\n }\n }\n\n // Remember last inserted node for cursor positioning\n let lastNode: Node | null = fragment.lastChild;\n range.insertNode(fragment);\n\n // Move cursor after inserted content\n if (lastNode) {\n const newRange = doc.createRange();\n newRange.setStartAfter(lastNode);\n newRange.collapse(true);\n selection.removeAllRanges();\n selection.addRange(newRange);\n }\n\n emit('paste', element.innerHTML);\n emitChange();\n }\n\n // Input handler for change events\n function onInput(): void {\n emitChange();\n }\n\n // Keydown handler for code block behavior\n function onKeydown(e: KeyboardEvent): void {\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const anchor = sel.anchorNode;\n if (!anchor) return;\n\n const pre = findAncestor(anchor, 'PRE');\n\n if (e.key === 'Enter' && pre) {\n // Insert newline instead of new paragraph\n e.preventDefault();\n const range = sel.getRangeAt(0);\n range.deleteContents();\n const textNode = doc.createTextNode('\\n');\n range.insertNode(textNode);\n range.setStartAfter(textNode);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n emitChange();\n }\n\n if (e.key === 'Backspace' && pre) {\n // At start of empty pre, convert to <p>\n const text = pre.textContent || '';\n const isAtStart = sel.anchorOffset === 0;\n const isEmpty = text === '' || text === '\\n';\n if (isAtStart && isEmpty) {\n e.preventDefault();\n const p = doc.createElement('p');\n p.appendChild(doc.createElement('br'));\n pre.parentNode?.replaceChild(p, pre);\n const range = doc.createRange();\n range.selectNodeContents(p);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n emitChange();\n }\n }\n }\n\n element.addEventListener('keydown', onKeydown);\n element.addEventListener('paste', onPaste);\n element.addEventListener('input', onInput);\n\n function findAncestor(node: Node, tagName: string): Element | null {\n let current: Node | null = node;\n while (current && current !== element) {\n if (current.nodeType === 1 && (current as Element).tagName === tagName) return current as Element;\n current = current.parentNode;\n }\n return null;\n }\n\n /**\n * Manually unwrap a list the selection is inside of, replacing each\n * <li> with a <p>. Returns true if an unwrap happened.\n *\n * We do this instead of relying on execCommand toggle-off because\n * browsers unwrap list items into <div> wrappers, which the policy\n * enforcer then strips (losing content).\n */\n function unwrapList(tag: 'UL' | 'OL'): boolean {\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) return false;\n const anchor = sel.anchorNode;\n if (!anchor) return false;\n const list = findAncestor(anchor, tag);\n if (!list) return false;\n\n const anchorLi = findAncestor(anchor, 'LI');\n const parent = list.parentNode;\n if (!parent) return false;\n\n const paragraphs: HTMLParagraphElement[] = [];\n let focusTarget: HTMLParagraphElement | null = null;\n\n for (const child of Array.from(list.childNodes)) {\n if (child.nodeType !== 1 || (child as Element).tagName !== 'LI') continue;\n const p = doc.createElement('p');\n while (child.firstChild) p.appendChild(child.firstChild);\n if (!p.firstChild) p.appendChild(doc.createElement('br'));\n paragraphs.push(p);\n if (child === anchorLi) focusTarget = p;\n }\n\n for (const p of paragraphs) parent.insertBefore(p, list);\n parent.removeChild(list);\n\n const target = focusTarget ?? paragraphs[0];\n if (target) {\n const r = doc.createRange();\n r.selectNodeContents(target);\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n }\n emitChange();\n return true;\n }\n\n function hasAncestor(node: Node, tagName: string): boolean {\n let current: Node | null = node;\n while (current && current !== element) {\n if (current.nodeType === 1 && (current as Element).tagName === tagName) return true;\n current = current.parentNode;\n }\n return false;\n }\n\n const editor: Editor = {\n exec(command: string, value?: string): void {\n if (!SUPPORTED_COMMANDS.has(command)) {\n throw new Error(`Unknown editor command: \"${command}\"`);\n }\n\n element.focus();\n\n switch (command) {\n case 'bold':\n doc.execCommand('bold', false);\n break;\n case 'italic':\n doc.execCommand('italic', false);\n break;\n case 'heading': {\n const level = value ?? '1';\n if (!['1', '2', '3'].includes(level)) {\n throw new Error(`Invalid heading level: \"${level}\". Use 1, 2, or 3`);\n }\n doc.execCommand('formatBlock', false, `<h${level}>`);\n break;\n }\n case 'blockquote':\n doc.execCommand('formatBlock', false, '<blockquote>');\n break;\n case 'unorderedList':\n if (!unwrapList('UL')) doc.execCommand('insertUnorderedList', false);\n break;\n case 'orderedList':\n if (!unwrapList('OL')) doc.execCommand('insertOrderedList', false);\n break;\n case 'link': {\n if (!value) {\n throw new Error('Link command requires a URL value');\n }\n const trimmed = value.trim();\n if (!isProtocolAllowed(trimmed, policy.protocols)) {\n emit('error', new Error(`Protocol not allowed: ${trimmed}`));\n return;\n }\n doc.execCommand('createLink', false, trimmed);\n break;\n }\n case 'unlink':\n doc.execCommand('unlink', false);\n break;\n case 'codeBlock': {\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) break;\n const anchor = sel.anchorNode;\n const pre = anchor ? findAncestor(anchor, 'PRE') : null;\n if (pre) {\n // Toggle off: unwrap <pre><code> to <p>\n const p = doc.createElement('p');\n p.textContent = pre.textContent || '';\n pre.parentNode?.replaceChild(p, pre);\n const r = doc.createRange();\n r.selectNodeContents(p);\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n } else {\n // Wrap current block in <pre><code>\n const range = sel.getRangeAt(0);\n let block = range.startContainer;\n while (block.parentNode && block.parentNode !== element) {\n block = block.parentNode;\n }\n const pre2 = doc.createElement('pre');\n const code = doc.createElement('code');\n const blockText = block.textContent || '';\n code.textContent = blockText.endsWith('\\n') ? blockText : blockText + '\\n';\n pre2.appendChild(code);\n if (block.parentNode === element) {\n element.replaceChild(pre2, block);\n } else {\n element.appendChild(pre2);\n }\n const r = doc.createRange();\n r.selectNodeContents(code);\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n }\n emitChange();\n break;\n }\n }\n },\n\n queryState(command: string): boolean {\n if (!SUPPORTED_COMMANDS.has(command)) {\n throw new Error(`Unknown editor command: \"${command}\"`);\n }\n\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) return false;\n\n const node = sel.anchorNode;\n if (!node || !element.contains(node)) return false;\n\n switch (command) {\n case 'bold':\n return hasAncestor(node, 'STRONG') || hasAncestor(node, 'B');\n case 'italic':\n return hasAncestor(node, 'EM') || hasAncestor(node, 'I');\n case 'heading':\n return hasAncestor(node, 'H1') || hasAncestor(node, 'H2') || hasAncestor(node, 'H3');\n case 'blockquote':\n return hasAncestor(node, 'BLOCKQUOTE');\n case 'unorderedList':\n return hasAncestor(node, 'UL');\n case 'orderedList':\n return hasAncestor(node, 'OL');\n case 'link':\n return hasAncestor(node, 'A');\n case 'unlink':\n return false;\n case 'codeBlock':\n return hasAncestor(node, 'PRE');\n default:\n return false;\n }\n },\n\n getHTML(): string {\n return element.innerHTML;\n },\n\n getText(): string {\n return element.textContent ?? '';\n },\n\n destroy(): void {\n element.removeEventListener('keydown', onKeydown);\n element.removeEventListener('paste', onPaste);\n element.removeEventListener('input', onInput);\n enforcer.destroy();\n element.contentEditable = 'false';\n },\n\n on(event: string, handler: EventHandler): void {\n if (!handlers[event]) handlers[event] = [];\n handlers[event].push(handler);\n },\n };\n\n return editor;\n}\n"],
5
- "mappings": "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",
6
- "names": ["react_exports", "__export", "Minisiwyg", "__toCommonJS", "import_react", "policy", "attrs", "DEFAULT_POLICY", "TAG_NORMALIZE", "URL_ATTRS", "DENIED_PROTOCOLS", "extractProtocol", "value", "decoded", "_", "hex", "dec", "match", "isProtocolAllowed", "allowedProtocols", "protocol", "walkAndSanitize", "parent", "policy", "depth", "children", "node", "el", "tagName", "normalized", "TAG_NORMALIZE", "allowedAttrs", "current", "replacement", "attrs", "attr", "attrName", "URL_ATTRS", "isProtocolAllowed", "sanitizeToFragment", "html", "template", "fragment", "truncateToLength", "truncateToLength", "node", "maxLength", "remaining", "children", "child", "text", "getDepth", "node", "root", "depth", "current", "enforceElement", "el", "policy", "tagName", "normalized", "TAG_NORMALIZE", "allowedAttrs", "parent", "replacement", "attr", "attrName", "URL_ATTRS", "isProtocolAllowed", "enforceSubtree", "children", "child", "createPolicyEnforcer", "element", "isApplyingFix", "errorHandlers", "emitError", "error", "handler", "observer", "mutations", "mutation", "target", "normalizedTag", "lowerAttr", "value", "err", "event", "SUPPORTED_COMMANDS", "createEditor", "element", "options", "src", "DEFAULT_POLICY", "policy", "k", "v", "handlers", "doc", "emit", "event", "args", "handler", "emitChange", "html", "enforcer", "createPolicyEnforcer", "err", "onPaste", "e", "clipboard", "sel", "findAncestor", "text", "range", "textNode", "fragment", "sanitizeToFragment", "selection", "pasteTextLen", "lastNode", "newRange", "onInput", "onKeydown", "anchor", "pre", "p", "node", "tagName", "current", "unwrapList", "tag", "list", "anchorLi", "parent", "paragraphs", "focusTarget", "child", "target", "r", "hasAncestor", "command", "value", "level", "trimmed", "isProtocolAllowed", "block", "pre2", "code", "blockText", "import_jsx_runtime", "Minisiwyg", "props", "initialHTML", "value", "onChange", "policy", "className", "editorRef", "hostRef", "editorInstance", "onChangeRef", "policyRef", "DEFAULT_POLICY", "el", "effectivePolicy", "sanitizeToFragment", "editor", "createEditor", "html"]
4
+ "sourcesContent": ["import { useEffect, useRef } from 'react';\nimport { createEditor } from '../editor';\nimport { sanitizeToFragment } from '../sanitize';\nimport { DEFAULT_POLICY } from '../defaults';\nimport type { Editor, SanitizePolicy } from '../types';\n\nexport type { Editor, SanitizePolicy } from '../types';\n\nexport interface MinisiwygProps {\n initialHTML?: string;\n value?: string;\n onChange?: (html: string) => void;\n policy?: SanitizePolicy;\n className?: string;\n editorRef?: (editor: Editor | null) => void;\n}\n\nexport function Minisiwyg(props: MinisiwygProps) {\n const { initialHTML, value, onChange, policy, className, editorRef } = props;\n const hostRef = useRef<HTMLDivElement | null>(null);\n const editorInstance = useRef<Editor | null>(null);\n const onChangeRef = useRef<typeof onChange>(onChange);\n const policyRef = useRef<SanitizePolicy>(policy ?? DEFAULT_POLICY);\n\n useEffect(() => {\n onChangeRef.current = onChange;\n }, [onChange]);\n\n useEffect(() => {\n const el = hostRef.current;\n if (!el) return;\n const effectivePolicy = policy ?? DEFAULT_POLICY;\n policyRef.current = effectivePolicy;\n el.replaceChildren(sanitizeToFragment(value ?? initialHTML ?? '', effectivePolicy));\n const editor = createEditor(el, {\n policy: effectivePolicy,\n onChange: (html) => onChangeRef.current?.(html),\n });\n editorInstance.current = editor;\n editorRef?.(editor);\n return () => {\n editor.destroy();\n editorInstance.current = null;\n editorRef?.(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const editor = editorInstance.current;\n const el = hostRef.current;\n if (!editor || !el || value === undefined) return;\n if (editor.getHTML() !== value) {\n el.replaceChildren(sanitizeToFragment(value, policyRef.current));\n }\n }, [value]);\n\n return <div ref={hostRef} className={className} />;\n}\n", "import type { SanitizePolicy } from './types';\n\nconst policy: SanitizePolicy = {\n tags: {\n p: [],\n br: [],\n strong: [],\n em: [],\n a: ['href', 'title', 'target'],\n h1: [],\n h2: [],\n h3: [],\n ul: [],\n ol: [],\n li: [],\n blockquote: [],\n pre: [],\n code: [],\n },\n strip: true,\n maxDepth: 10,\n maxLength: 100_000,\n protocols: ['https', 'http', 'mailto'],\n};\n\n// Deep freeze to prevent mutation of security-critical defaults\nObject.freeze(policy);\nObject.freeze(policy.protocols);\nfor (const attrs of Object.values(policy.tags)) Object.freeze(attrs);\nObject.freeze(policy.tags);\n\nexport const DEFAULT_POLICY: Readonly<SanitizePolicy> = policy;\n", "/** Tag normalization map: browser-variant tags \u2192 semantic equivalents. */\nexport const TAG_NORMALIZE: Record<string, string> = {\n b: 'strong',\n i: 'em',\n};\n\n/** Attributes that contain URLs and need protocol validation. */\nexport const URL_ATTRS = new Set(['href', 'src', 'action', 'formaction']);\n\n/** Protocols that are always denied regardless of policy. */\nexport const DENIED_PROTOCOLS = new Set(['javascript', 'data']);\n\n/**\n * Parse a URL-like string and extract the protocol.\n * Returns the lowercase protocol name (without colon), or null if none found.\n */\nexport function extractProtocol(value: string): string | null {\n let decoded = value.trim();\n decoded = decoded.replace(/&#x([0-9a-f]+);?/gi, (_, hex) =>\n String.fromCharCode(parseInt(hex, 16)),\n );\n decoded = decoded.replace(/&#(\\d+);?/g, (_, dec) =>\n String.fromCharCode(parseInt(dec, 10)),\n );\n try {\n decoded = decodeURIComponent(decoded);\n } catch {\n // keep entity-decoded result\n }\n decoded = decoded.replace(/[\\s\\x00-\\x1f\\u00A0\\u1680\\u2000-\\u200B\\u2028\\u2029\\u202F\\u205F\\u3000\\uFEFF]+/g, '');\n const match = decoded.match(/^([a-z][a-z0-9+\\-.]*)\\s*:/i);\n return match ? match[1].toLowerCase() : null;\n}\n\n/**\n * Check if a URL value is allowed by the given protocol list.\n * javascript: and data: are always denied.\n */\nexport function isProtocolAllowed(value: string, allowedProtocols: string[]): boolean {\n const protocol = extractProtocol(value);\n if (protocol === null) return true;\n if (DENIED_PROTOCOLS.has(protocol)) return false;\n return allowedProtocols.includes(protocol);\n}\n", "import type { SanitizePolicy } from './types';\nimport { TAG_NORMALIZE, URL_ATTRS, isProtocolAllowed } from './shared';\nexport { DEFAULT_POLICY } from './defaults';\nexport type { SanitizePolicy } from './types';\n\n/**\n * Walk a DOM tree depth-first and sanitize according to policy.\n * Mutates the tree in place.\n */\nfunction walkAndSanitize(\n parent: Node,\n policy: SanitizePolicy,\n depth: number,\n): void {\n const children = Array.from(parent.childNodes);\n\n for (const node of children) {\n // Text nodes: always allowed (length enforcement happens after walk)\n if (node.nodeType === 3) continue;\n\n // Non-element, non-text nodes (comments, processing instructions): remove\n if (node.nodeType !== 1) {\n parent.removeChild(node);\n continue;\n }\n\n const el = node as Element;\n let tagName = el.tagName.toLowerCase();\n\n // Normalize tags (b\u2192strong, i\u2192em)\n const normalized = TAG_NORMALIZE[tagName];\n if (normalized) {\n tagName = normalized;\n }\n\n // Check depth limit\n if (depth >= policy.maxDepth) {\n parent.removeChild(el);\n continue;\n }\n\n // Check tag whitelist\n const allowedAttrs = policy.tags[tagName];\n if (allowedAttrs === undefined) {\n // Tag not allowed\n if (policy.strip) {\n // Remove the node and all its children\n parent.removeChild(el);\n } else {\n // Unwrap: sanitize children first, then move them up\n walkAndSanitize(el, policy, depth);\n while (el.firstChild) {\n parent.insertBefore(el.firstChild, el);\n }\n parent.removeChild(el);\n }\n continue;\n }\n\n // Tag is allowed. If it was normalized, replace with correct element.\n let current: Element = el;\n if (normalized && el.tagName.toLowerCase() !== normalized) {\n const doc = el.ownerDocument!;\n const replacement = doc.createElement(normalized);\n while (el.firstChild) {\n replacement.appendChild(el.firstChild);\n }\n parent.replaceChild(replacement, el);\n current = replacement;\n }\n\n // Strip disallowed attributes\n const attrs = Array.from(current.attributes);\n for (const attr of attrs) {\n const attrName = attr.name.toLowerCase();\n\n // Always strip event handlers (on*)\n if (attrName.startsWith('on')) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n // Check attribute whitelist\n if (!allowedAttrs.includes(attrName)) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n // Validate URL protocols on URL-bearing attributes\n if (URL_ATTRS.has(attrName)) {\n if (!isProtocolAllowed(attr.value, policy.protocols)) {\n current.removeAttribute(attr.name);\n }\n }\n }\n\n // Recurse into children\n walkAndSanitize(current, policy, depth + 1);\n }\n}\n\n/**\n * Sanitize an HTML string and return a DocumentFragment.\n * Avoids the serialize\u2192reparse round-trip that can cause mXSS.\n */\nexport function sanitizeToFragment(html: string, policy: SanitizePolicy): DocumentFragment {\n const template = document.createElement('template');\n if (!html) return template.content;\n\n template.innerHTML = html;\n const fragment = template.content;\n\n walkAndSanitize(fragment, policy, 0);\n\n if (policy.maxLength > 0 && (fragment.textContent?.length ?? 0) > policy.maxLength) {\n truncateToLength(fragment, policy.maxLength);\n }\n\n return fragment;\n}\n\n/**\n * Sanitize an HTML string according to the given policy.\n *\n * Uses a <template> element to parse HTML without executing scripts.\n * Walks the resulting DOM tree depth-first, removing disallowed elements\n * and attributes. Returns the sanitized HTML string.\n */\nexport function sanitize(html: string, policy: SanitizePolicy): string {\n if (!html) return '';\n\n const fragment = sanitizeToFragment(html, policy);\n const container = document.createElement('div');\n container.appendChild(fragment);\n return container.innerHTML;\n}\n\n/**\n * Truncate a DOM tree's text content to a maximum length.\n * Removes nodes beyond the limit while preserving structure.\n */\nfunction truncateToLength(node: Node, maxLength: number): number {\n let remaining = maxLength;\n\n const children = Array.from(node.childNodes);\n for (const child of children) {\n if (remaining <= 0) {\n node.removeChild(child);\n continue;\n }\n\n if (child.nodeType === 3) {\n // Text node\n const text = child.textContent ?? '';\n if (text.length > remaining) {\n child.textContent = text.slice(0, remaining);\n remaining = 0;\n } else {\n remaining -= text.length;\n }\n } else if (child.nodeType === 1) {\n remaining = truncateToLength(child, remaining);\n } else {\n node.removeChild(child);\n }\n }\n\n return remaining;\n}\n", "import type { SanitizePolicy } from './types';\nimport { TAG_NORMALIZE, URL_ATTRS, isProtocolAllowed } from './shared';\n\nexport { DEFAULT_POLICY } from './defaults';\nexport type { SanitizePolicy } from './types';\n\nexport interface PolicyEnforcer {\n destroy(): void;\n on(event: 'error', handler: (error: Error) => void): void;\n}\n\n/**\n * Get the nesting depth of a node within a root element.\n */\nfunction getDepth(node: Node, root: Node): number {\n let depth = 0;\n let current = node.parentNode;\n while (current && current !== root) {\n if (current.nodeType === 1) depth++;\n current = current.parentNode;\n }\n return depth;\n}\n\n/**\n * Check if an element is allowed by the policy and fix it if not.\n * Returns true if the node was removed/replaced.\n */\nfunction enforceElement(\n el: Element,\n policy: SanitizePolicy,\n root: HTMLElement,\n): boolean {\n let tagName = el.tagName.toLowerCase();\n const normalized = TAG_NORMALIZE[tagName];\n if (normalized) tagName = normalized;\n\n // Check depth\n const depth = getDepth(el, root);\n if (depth >= policy.maxDepth) {\n el.parentNode?.removeChild(el);\n return true;\n }\n\n // Check tag whitelist\n const allowedAttrs = policy.tags[tagName];\n if (allowedAttrs === undefined) {\n if (policy.strip) {\n el.parentNode?.removeChild(el);\n } else {\n // Unwrap: move children up, then remove the element\n const parent = el.parentNode;\n if (parent) {\n while (el.firstChild) {\n parent.insertBefore(el.firstChild, el);\n }\n parent.removeChild(el);\n }\n }\n return true;\n }\n\n // Normalize tag if needed (e.g. <b> \u2192 <strong>)\n let current: Element = el;\n if (normalized && el.tagName.toLowerCase() !== normalized) {\n const replacement = el.ownerDocument.createElement(normalized);\n while (el.firstChild) {\n replacement.appendChild(el.firstChild);\n }\n // Copy allowed attributes\n for (const attr of Array.from(el.attributes)) {\n replacement.setAttribute(attr.name, attr.value);\n }\n el.parentNode?.replaceChild(replacement, el);\n current = replacement;\n }\n\n // Strip disallowed attributes\n for (const attr of Array.from(current.attributes)) {\n const attrName = attr.name.toLowerCase();\n\n if (attrName.startsWith('on')) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n if (!allowedAttrs.includes(attrName)) {\n current.removeAttribute(attr.name);\n continue;\n }\n\n if (URL_ATTRS.has(attrName)) {\n if (!isProtocolAllowed(attr.value, policy.protocols)) {\n current.removeAttribute(attr.name);\n }\n }\n }\n\n return false;\n}\n\n/**\n * Recursively enforce policy on all descendants of a node.\n */\nfunction enforceSubtree(node: Node, policy: SanitizePolicy, root: HTMLElement): void {\n const children = Array.from(node.childNodes);\n for (const child of children) {\n if (child.nodeType !== 1) {\n // Remove non-text, non-element nodes (comments, etc.)\n if (child.nodeType !== 3) {\n node.removeChild(child);\n }\n continue;\n }\n const removed = enforceElement(child as Element, policy, root);\n if (!removed) {\n enforceSubtree(child, policy, root);\n }\n }\n}\n\n/**\n * Create a policy enforcer that uses MutationObserver to enforce\n * the sanitization policy on a live DOM element.\n *\n * This is defense-in-depth \u2014 the paste handler is the primary security boundary.\n * The observer catches mutations from execCommand, programmatic DOM manipulation,\n * and other sources.\n */\nexport function createPolicyEnforcer(\n element: HTMLElement,\n policy: SanitizePolicy,\n): PolicyEnforcer {\n if (!policy || !policy.tags) {\n throw new TypeError('Policy must have a \"tags\" property');\n }\n\n let isApplyingFix = false;\n const errorHandlers: Array<(error: Error) => void> = [];\n\n function emitError(error: Error): void {\n for (const handler of errorHandlers) {\n handler(error);\n }\n }\n\n const observer = new MutationObserver((mutations) => {\n if (isApplyingFix) return;\n isApplyingFix = true;\n\n try {\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (const node of Array.from(mutation.addedNodes)) {\n // Skip text nodes\n if (node.nodeType === 3) continue;\n\n // Remove non-element nodes\n if (node.nodeType !== 1) {\n node.parentNode?.removeChild(node);\n continue;\n }\n\n const removed = enforceElement(node as Element, policy, element);\n if (!removed) {\n // Also enforce on all descendants of the added node\n enforceSubtree(node, policy, element);\n }\n }\n } else if (mutation.type === 'attributes') {\n const target = mutation.target as Element;\n if (target.nodeType !== 1) continue;\n\n const attrName = mutation.attributeName;\n if (!attrName) continue;\n\n const tagName = target.tagName.toLowerCase();\n const normalizedTag = TAG_NORMALIZE[tagName] || tagName;\n const allowedAttrs = policy.tags[normalizedTag];\n\n if (!allowedAttrs) continue;\n\n const lowerAttr = attrName.toLowerCase();\n\n if (lowerAttr.startsWith('on')) {\n target.removeAttribute(attrName);\n continue;\n }\n\n if (!allowedAttrs.includes(lowerAttr)) {\n target.removeAttribute(attrName);\n continue;\n }\n\n if (URL_ATTRS.has(lowerAttr)) {\n const value = target.getAttribute(attrName);\n if (value && !isProtocolAllowed(value, policy.protocols)) {\n target.removeAttribute(attrName);\n }\n }\n }\n }\n } catch (err) {\n emitError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n isApplyingFix = false;\n }\n });\n\n observer.observe(element, {\n childList: true,\n attributes: true,\n subtree: true,\n });\n\n return {\n destroy() {\n observer.disconnect();\n },\n on(event: 'error', handler: (error: Error) => void) {\n if (event === 'error') {\n errorHandlers.push(handler);\n }\n },\n };\n}\n", "import type { SanitizePolicy, EditorOptions, Editor } from './types';\nimport { DEFAULT_POLICY } from './defaults';\nimport { sanitizeToFragment } from './sanitize';\nimport { createPolicyEnforcer, type PolicyEnforcer } from './policy';\nimport { isProtocolAllowed } from './shared';\n\nexport type { Editor, EditorOptions } from './types';\nexport { DEFAULT_POLICY } from './defaults';\n\ntype EditorEvent = 'change' | 'paste' | 'overflow' | 'error';\ntype EventHandler = (...args: unknown[]) => void;\n\nconst SUPPORTED_COMMANDS = new Set([\n 'bold',\n 'italic',\n 'heading',\n 'blockquote',\n 'unorderedList',\n 'orderedList',\n 'link',\n 'unlink',\n 'codeBlock',\n]);\n\n/**\n * Create a contentEditable-based editor with built-in sanitization.\n *\n * The paste handler is the primary security boundary \u2014 it sanitizes HTML\n * before insertion via Selection/Range API. The MutationObserver-based\n * policy enforcer provides defense-in-depth.\n */\nexport function createEditor(\n element: HTMLElement,\n options?: EditorOptions,\n): Editor {\n if (!element) {\n throw new TypeError('createEditor requires an HTMLElement');\n }\n if (!element.ownerDocument || !element.parentNode) {\n throw new TypeError('createEditor requires an element attached to the DOM');\n }\n\n const src = options?.policy ?? DEFAULT_POLICY;\n const policy: SanitizePolicy = {\n tags: Object.fromEntries(\n Object.entries(src.tags).map(([k, v]) => [k, [...v]]),\n ),\n strip: src.strip,\n maxDepth: src.maxDepth,\n maxLength: src.maxLength,\n protocols: [...src.protocols],\n };\n\n const handlers: Record<string, EventHandler[]> = {};\n const doc = element.ownerDocument;\n\n function emit(event: EditorEvent, ...args: unknown[]): void {\n for (const handler of handlers[event] ?? []) {\n handler(...args);\n }\n }\n\n // Notify both subscription paths (editor.on('change') and options.onChange)\n // for programmatic edits that don't fire 'input' (codeBlock toggle, list unwrap).\n function emitChange(): void {\n const html = element.innerHTML;\n emit('change', html);\n options?.onChange?.(html);\n }\n\n // Set up contentEditable\n element.contentEditable = 'true';\n\n // Attach policy enforcer (MutationObserver defense-in-depth)\n const enforcer: PolicyEnforcer = createPolicyEnforcer(element, policy);\n enforcer.on('error', (err) => emit('error', err));\n\n // Paste handler \u2014 the primary security boundary\n function onPaste(e: ClipboardEvent): void {\n e.preventDefault();\n\n const clipboard = e.clipboardData;\n if (!clipboard) return;\n\n // Inside code block: paste as plain text only\n const sel = doc.getSelection();\n if (sel && sel.rangeCount > 0 && sel.anchorNode) {\n const pre = findAncestor(sel.anchorNode, 'PRE');\n if (pre) {\n const text = clipboard.getData('text/plain');\n if (!text) return;\n if (policy.maxLength > 0) {\n const currentLen = element.textContent?.length ?? 0;\n if (currentLen + text.length > policy.maxLength) {\n emit('overflow', policy.maxLength);\n }\n }\n const range = sel.getRangeAt(0);\n range.deleteContents();\n const textNode = doc.createTextNode(text);\n range.insertNode(textNode);\n range.setStartAfter(textNode);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n emit('paste', element.innerHTML);\n emitChange();\n return;\n }\n }\n\n // Prefer HTML, fall back to plain text\n let html = clipboard.getData('text/html');\n if (!html) {\n const text = clipboard.getData('text/plain');\n if (!text) return;\n // Escape plain text and convert newlines to <br>\n html = text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n .replace(/\\n/g, '<br>');\n }\n\n // Sanitize through policy \u2014 returns DocumentFragment directly\n // to avoid the serialize\u2192reparse mXSS vector\n const fragment = sanitizeToFragment(html, policy);\n\n // Insert via Selection/Range API (NOT execCommand('insertHTML'))\n const selection = doc.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n\n const range = selection.getRangeAt(0);\n range.deleteContents();\n\n // Check overflow using text content length\n if (policy.maxLength > 0) {\n const pasteTextLen = fragment.textContent?.length ?? 0;\n const currentLen = element.textContent?.length ?? 0;\n if (currentLen + pasteTextLen > policy.maxLength) {\n emit('overflow', policy.maxLength);\n }\n }\n\n // Remember last inserted node for cursor positioning\n let lastNode: Node | null = fragment.lastChild;\n range.insertNode(fragment);\n\n // Move cursor after inserted content\n if (lastNode) {\n const newRange = doc.createRange();\n newRange.setStartAfter(lastNode);\n newRange.collapse(true);\n selection.removeAllRanges();\n selection.addRange(newRange);\n }\n\n emit('paste', element.innerHTML);\n emitChange();\n }\n\n // Input handler for change events\n function onInput(): void {\n emitChange();\n }\n\n // Keydown handler for code block behavior\n function onKeydown(e: KeyboardEvent): void {\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const anchor = sel.anchorNode;\n if (!anchor) return;\n\n const pre = findAncestor(anchor, 'PRE');\n\n if (e.key === 'Enter' && pre) {\n // Insert newline instead of new paragraph\n e.preventDefault();\n const range = sel.getRangeAt(0);\n range.deleteContents();\n const textNode = doc.createTextNode('\\n');\n range.insertNode(textNode);\n range.setStartAfter(textNode);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n emitChange();\n }\n\n if (e.key === 'Backspace' && pre) {\n // At start of empty pre, convert to <p>\n const text = pre.textContent || '';\n const isAtStart = sel.anchorOffset === 0;\n const isEmpty = text === '' || text === '\\n';\n if (isAtStart && isEmpty) {\n e.preventDefault();\n const p = doc.createElement('p');\n p.appendChild(doc.createElement('br'));\n pre.parentNode?.replaceChild(p, pre);\n const range = doc.createRange();\n range.selectNodeContents(p);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n emitChange();\n }\n }\n }\n\n element.addEventListener('keydown', onKeydown);\n element.addEventListener('paste', onPaste);\n element.addEventListener('input', onInput);\n\n function findAncestor(node: Node, tagName: string): Element | null {\n let current: Node | null = node;\n while (current && current !== element) {\n if (current.nodeType === 1 && (current as Element).tagName === tagName) return current as Element;\n current = current.parentNode;\n }\n return null;\n }\n\n /**\n * Manually unwrap a list the selection is inside of, replacing each\n * <li> with a <p>. Returns true if an unwrap happened.\n *\n * We do this instead of relying on execCommand toggle-off because\n * browsers unwrap list items into <div> wrappers, which the policy\n * enforcer then strips (losing content).\n */\n function unwrapList(tag: 'UL' | 'OL'): boolean {\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) return false;\n const anchor = sel.anchorNode;\n if (!anchor) return false;\n const list = findAncestor(anchor, tag);\n if (!list) return false;\n\n const anchorLi = findAncestor(anchor, 'LI');\n const parent = list.parentNode;\n if (!parent) return false;\n\n const paragraphs: HTMLParagraphElement[] = [];\n let focusTarget: HTMLParagraphElement | null = null;\n\n for (const child of Array.from(list.childNodes)) {\n if (child.nodeType !== 1 || (child as Element).tagName !== 'LI') continue;\n const p = doc.createElement('p');\n while (child.firstChild) p.appendChild(child.firstChild);\n if (!p.firstChild) p.appendChild(doc.createElement('br'));\n paragraphs.push(p);\n if (child === anchorLi) focusTarget = p;\n }\n\n for (const p of paragraphs) parent.insertBefore(p, list);\n parent.removeChild(list);\n\n const target = focusTarget ?? paragraphs[0];\n if (target) {\n const r = doc.createRange();\n r.selectNodeContents(target);\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n }\n emitChange();\n return true;\n }\n\n function hasAncestor(node: Node, tagName: string): boolean {\n let current: Node | null = node;\n while (current && current !== element) {\n if (current.nodeType === 1 && (current as Element).tagName === tagName) return true;\n current = current.parentNode;\n }\n return false;\n }\n\n const editor: Editor = {\n exec(command: string, value?: string): void {\n if (!SUPPORTED_COMMANDS.has(command)) {\n throw new Error(`Unknown editor command: \"${command}\"`);\n }\n\n element.focus();\n\n switch (command) {\n case 'bold':\n doc.execCommand('bold', false);\n break;\n case 'italic':\n doc.execCommand('italic', false);\n break;\n case 'heading': {\n const level = value ?? '1';\n if (!['1', '2', '3'].includes(level)) {\n throw new Error(`Invalid heading level: \"${level}\". Use 1, 2, or 3`);\n }\n const tag = `H${level}`;\n const anchor = doc.getSelection()?.anchorNode;\n if (anchor && editor.queryState('heading') && hasAncestor(anchor, tag)) {\n doc.execCommand('formatBlock', false, '<p>');\n } else {\n doc.execCommand('formatBlock', false, `<h${level}>`);\n }\n break;\n }\n case 'blockquote':\n if (editor.queryState('blockquote')) {\n doc.execCommand('formatBlock', false, '<p>');\n } else {\n doc.execCommand('formatBlock', false, '<blockquote>');\n }\n break;\n case 'unorderedList':\n if (!unwrapList('UL')) doc.execCommand('insertUnorderedList', false);\n break;\n case 'orderedList':\n if (!unwrapList('OL')) doc.execCommand('insertOrderedList', false);\n break;\n case 'link': {\n if (!value) {\n throw new Error('Link command requires a URL value');\n }\n const trimmed = value.trim();\n if (!isProtocolAllowed(trimmed, policy.protocols)) {\n emit('error', new Error(`Protocol not allowed: ${trimmed}`));\n return;\n }\n doc.execCommand('createLink', false, trimmed);\n break;\n }\n case 'unlink':\n doc.execCommand('unlink', false);\n break;\n case 'codeBlock': {\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) break;\n const anchor = sel.anchorNode;\n const pre = anchor ? findAncestor(anchor, 'PRE') : null;\n if (pre) {\n // Toggle off: unwrap <pre><code> to <p>\n const p = doc.createElement('p');\n p.textContent = pre.textContent || '';\n pre.parentNode?.replaceChild(p, pre);\n const r = doc.createRange();\n r.selectNodeContents(p);\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n } else {\n // Wrap current block in <pre><code>\n const range = sel.getRangeAt(0);\n let block = range.startContainer;\n while (block.parentNode && block.parentNode !== element) {\n block = block.parentNode;\n }\n const pre2 = doc.createElement('pre');\n const code = doc.createElement('code');\n const blockText = block.textContent || '';\n code.textContent = blockText.endsWith('\\n') ? blockText : blockText + '\\n';\n pre2.appendChild(code);\n if (block.parentNode === element) {\n element.replaceChild(pre2, block);\n } else {\n element.appendChild(pre2);\n }\n const r = doc.createRange();\n r.selectNodeContents(code);\n r.collapse(false);\n sel.removeAllRanges();\n sel.addRange(r);\n }\n emitChange();\n break;\n }\n }\n },\n\n queryState(command: string): boolean {\n if (!SUPPORTED_COMMANDS.has(command)) {\n throw new Error(`Unknown editor command: \"${command}\"`);\n }\n\n const sel = doc.getSelection();\n if (!sel || sel.rangeCount === 0) return false;\n\n const node = sel.anchorNode;\n if (!node || !element.contains(node)) return false;\n\n switch (command) {\n case 'bold':\n return hasAncestor(node, 'STRONG') || hasAncestor(node, 'B');\n case 'italic':\n return hasAncestor(node, 'EM') || hasAncestor(node, 'I');\n case 'heading':\n return hasAncestor(node, 'H1') || hasAncestor(node, 'H2') || hasAncestor(node, 'H3');\n case 'blockquote':\n return hasAncestor(node, 'BLOCKQUOTE');\n case 'unorderedList':\n return hasAncestor(node, 'UL');\n case 'orderedList':\n return hasAncestor(node, 'OL');\n case 'link':\n return hasAncestor(node, 'A');\n case 'unlink':\n return false;\n case 'codeBlock':\n return hasAncestor(node, 'PRE');\n default:\n return false;\n }\n },\n\n getHTML(): string {\n return element.innerHTML;\n },\n\n getText(): string {\n return element.textContent ?? '';\n },\n\n destroy(): void {\n element.removeEventListener('keydown', onKeydown);\n element.removeEventListener('paste', onPaste);\n element.removeEventListener('input', onInput);\n enforcer.destroy();\n element.contentEditable = 'false';\n },\n\n on(event: string, handler: EventHandler): void {\n if (!handlers[event]) handlers[event] = [];\n handlers[event].push(handler);\n },\n };\n\n return editor;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["react_exports", "__export", "Minisiwyg", "__toCommonJS", "import_react", "policy", "attrs", "DEFAULT_POLICY", "TAG_NORMALIZE", "URL_ATTRS", "DENIED_PROTOCOLS", "extractProtocol", "value", "decoded", "_", "hex", "dec", "match", "isProtocolAllowed", "allowedProtocols", "protocol", "walkAndSanitize", "parent", "policy", "depth", "children", "node", "el", "tagName", "normalized", "TAG_NORMALIZE", "allowedAttrs", "current", "replacement", "attrs", "attr", "attrName", "URL_ATTRS", "isProtocolAllowed", "sanitizeToFragment", "html", "template", "fragment", "truncateToLength", "truncateToLength", "node", "maxLength", "remaining", "children", "child", "text", "getDepth", "node", "root", "depth", "current", "enforceElement", "el", "policy", "tagName", "normalized", "TAG_NORMALIZE", "allowedAttrs", "parent", "replacement", "attr", "attrName", "URL_ATTRS", "isProtocolAllowed", "enforceSubtree", "children", "child", "createPolicyEnforcer", "element", "isApplyingFix", "errorHandlers", "emitError", "error", "handler", "observer", "mutations", "mutation", "target", "normalizedTag", "lowerAttr", "value", "err", "event", "SUPPORTED_COMMANDS", "createEditor", "element", "options", "src", "DEFAULT_POLICY", "policy", "k", "v", "handlers", "doc", "emit", "event", "args", "handler", "emitChange", "html", "enforcer", "createPolicyEnforcer", "err", "onPaste", "e", "clipboard", "sel", "findAncestor", "text", "range", "textNode", "fragment", "sanitizeToFragment", "selection", "pasteTextLen", "lastNode", "newRange", "onInput", "onKeydown", "anchor", "pre", "p", "node", "tagName", "current", "unwrapList", "tag", "list", "anchorLi", "parent", "paragraphs", "focusTarget", "child", "target", "r", "hasAncestor", "editor", "command", "value", "level", "trimmed", "isProtocolAllowed", "block", "pre2", "code", "blockText", "import_jsx_runtime", "Minisiwyg", "props", "initialHTML", "value", "onChange", "policy", "className", "editorRef", "hostRef", "editorInstance", "onChangeRef", "policyRef", "DEFAULT_POLICY", "el", "effectivePolicy", "sanitizeToFragment", "editor", "createEditor", "html"]
7
7
  }
@@ -1,6 +1,6 @@
1
- import{useEffect as D,useRef as O}from"react";var P={tags:{p:[],br:[],strong:[],em:[],a:["href","title","target"],h1:[],h2:[],h3:[],ul:[],ol:[],li:[],blockquote:[],pre:[],code:[]},strip:!0,maxDepth:10,maxLength:1e5,protocols:["https","http","mailto"]};Object.freeze(P);Object.freeze(P.protocols);for(let e of Object.values(P.tags))Object.freeze(e);Object.freeze(P.tags);var A=P;var R={b:"strong",i:"em"},S=new Set(["href","src","action","formaction"]),q=new Set(["javascript","data"]);function j(e){let n=e.trim();n=n.replace(/&#x([0-9a-f]+);?/gi,(i,a)=>String.fromCharCode(parseInt(a,16))),n=n.replace(/&#(\d+);?/g,(i,a)=>String.fromCharCode(parseInt(a,10)));try{n=decodeURIComponent(n)}catch{}n=n.replace(/[\s\x00-\x1f\u00A0\u1680\u2000-\u200B\u2028\u2029\u202F\u205F\u3000\uFEFF]+/g,"");let o=n.match(/^([a-z][a-z0-9+\-.]*)\s*:/i);return o?o[1].toLowerCase():null}function T(e,n){let o=j(e);return o===null?!0:q.has(o)?!1:n.includes(o)}function z(e,n,o){let i=Array.from(e.childNodes);for(let a of i){if(a.nodeType===3)continue;if(a.nodeType!==1){e.removeChild(a);continue}let r=a,u=r.tagName.toLowerCase(),l=R[u];if(l&&(u=l),o>=n.maxDepth){e.removeChild(r);continue}let s=n.tags[u];if(s===void 0){if(n.strip)e.removeChild(r);else{for(z(r,n,o);r.firstChild;)e.insertBefore(r.firstChild,r);e.removeChild(r)}continue}let f=r;if(l&&r.tagName.toLowerCase()!==l){let p=r.ownerDocument.createElement(l);for(;r.firstChild;)p.appendChild(r.firstChild);e.replaceChild(p,r),f=p}let b=Array.from(f.attributes);for(let C of b){let p=C.name.toLowerCase();if(p.startsWith("on")){f.removeAttribute(C.name);continue}if(!s.includes(p)){f.removeAttribute(C.name);continue}S.has(p)&&(T(C.value,n.protocols)||f.removeAttribute(C.name))}z(f,n,o+1)}}function k(e,n){let o=document.createElement("template");if(!e)return o.content;o.innerHTML=e;let i=o.content;return z(i,n,0),n.maxLength>0&&(i.textContent?.length??0)>n.maxLength&&H(i,n.maxLength),i}function H(e,n){let o=n,i=Array.from(e.childNodes);for(let a of i){if(o<=0){e.removeChild(a);continue}if(a.nodeType===3){let r=a.textContent??"";r.length>o?(a.textContent=r.slice(0,o),o=0):o-=r.length}else a.nodeType===1?o=H(a,o):e.removeChild(a)}return o}function Y(e,n){let o=0,i=e.parentNode;for(;i&&i!==n;)i.nodeType===1&&o++,i=i.parentNode;return o}function U(e,n,o){let i=e.tagName.toLowerCase(),a=R[i];if(a&&(i=a),Y(e,o)>=n.maxDepth)return e.parentNode?.removeChild(e),!0;let u=n.tags[i];if(u===void 0){if(n.strip)e.parentNode?.removeChild(e);else{let s=e.parentNode;if(s){for(;e.firstChild;)s.insertBefore(e.firstChild,e);s.removeChild(e)}}return!0}let l=e;if(a&&e.tagName.toLowerCase()!==a){let s=e.ownerDocument.createElement(a);for(;e.firstChild;)s.appendChild(e.firstChild);for(let f of Array.from(e.attributes))s.setAttribute(f.name,f.value);e.parentNode?.replaceChild(s,e),l=s}for(let s of Array.from(l.attributes)){let f=s.name.toLowerCase();if(f.startsWith("on")){l.removeAttribute(s.name);continue}if(!u.includes(f)){l.removeAttribute(s.name);continue}S.has(f)&&(T(s.value,n.protocols)||l.removeAttribute(s.name))}return!1}function I(e,n,o){let i=Array.from(e.childNodes);for(let a of i){if(a.nodeType!==1){a.nodeType!==3&&e.removeChild(a);continue}U(a,n,o)||I(a,n,o)}}function _(e,n){if(!n||!n.tags)throw new TypeError('Policy must have a "tags" property');let o=!1,i=[];function a(u){for(let l of i)l(u)}let r=new MutationObserver(u=>{if(!o){o=!0;try{for(let l of u)if(l.type==="childList")for(let s of Array.from(l.addedNodes)){if(s.nodeType===3)continue;if(s.nodeType!==1){s.parentNode?.removeChild(s);continue}U(s,n,e)||I(s,n,e)}else if(l.type==="attributes"){let s=l.target;if(s.nodeType!==1)continue;let f=l.attributeName;if(!f)continue;let b=s.tagName.toLowerCase(),C=R[b]||b,p=n.tags[C];if(!p)continue;let w=f.toLowerCase();if(w.startsWith("on")){s.removeAttribute(f);continue}if(!p.includes(w)){s.removeAttribute(f);continue}if(S.has(w)){let y=s.getAttribute(f);y&&!T(y,n.protocols)&&s.removeAttribute(f)}}}catch(l){a(l instanceof Error?l:new Error(String(l)))}finally{o=!1}}});return r.observe(e,{childList:!0,attributes:!0,subtree:!0}),{destroy(){r.disconnect()},on(u,l){u==="error"&&i.push(l)}}}var F=new Set(["bold","italic","heading","blockquote","unorderedList","orderedList","link","unlink","codeBlock"]);function B(e,n){if(!e)throw new TypeError("createEditor requires an HTMLElement");if(!e.ownerDocument||!e.parentNode)throw new TypeError("createEditor requires an element attached to the DOM");let o=n?.policy??A,i={tags:Object.fromEntries(Object.entries(o.tags).map(([d,c])=>[d,[...c]])),strip:o.strip,maxDepth:o.maxDepth,maxLength:o.maxLength,protocols:[...o.protocols]},a={},r=e.ownerDocument;function u(d,...c){for(let t of a[d]??[])t(...c)}function l(){let d=e.innerHTML;u("change",d),n?.onChange?.(d)}e.contentEditable="true";let s=_(e,i);s.on("error",d=>u("error",d));function f(d){d.preventDefault();let c=d.clipboardData;if(!c)return;let t=r.getSelection();if(t&&t.rangeCount>0&&t.anchorNode&&p(t.anchorNode,"PRE")){let g=c.getData("text/plain");if(!g)return;i.maxLength>0&&(e.textContent?.length??0)+g.length>i.maxLength&&u("overflow",i.maxLength);let x=t.getRangeAt(0);x.deleteContents();let M=r.createTextNode(g);x.insertNode(M),x.setStartAfter(M),x.collapse(!0),t.removeAllRanges(),t.addRange(x),u("paste",e.innerHTML),l();return}let L=c.getData("text/html");if(!L){let m=c.getData("text/plain");if(!m)return;L=m.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/\n/g,"<br>")}let h=k(L,i),v=r.getSelection();if(!v||v.rangeCount===0)return;let E=v.getRangeAt(0);if(E.deleteContents(),i.maxLength>0){let m=h.textContent?.length??0;(e.textContent?.length??0)+m>i.maxLength&&u("overflow",i.maxLength)}let N=h.lastChild;if(E.insertNode(h),N){let m=r.createRange();m.setStartAfter(N),m.collapse(!0),v.removeAllRanges(),v.addRange(m)}u("paste",e.innerHTML),l()}function b(){l()}function C(d){let c=r.getSelection();if(!c||c.rangeCount===0)return;let t=c.anchorNode;if(!t)return;let L=p(t,"PRE");if(d.key==="Enter"&&L){d.preventDefault();let h=c.getRangeAt(0);h.deleteContents();let v=r.createTextNode(`
2
- `);h.insertNode(v),h.setStartAfter(v),h.collapse(!0),c.removeAllRanges(),c.addRange(h),l()}if(d.key==="Backspace"&&L){let h=L.textContent||"";if(c.anchorOffset===0&&(h===""||h===`
3
- `)){d.preventDefault();let N=r.createElement("p");N.appendChild(r.createElement("br")),L.parentNode?.replaceChild(N,L);let m=r.createRange();m.selectNodeContents(N),m.collapse(!0),c.removeAllRanges(),c.addRange(m),l()}}}e.addEventListener("keydown",C),e.addEventListener("paste",f),e.addEventListener("input",b);function p(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return t;t=t.parentNode}return null}function w(d){let c=r.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t)return!1;let L=p(t,d);if(!L)return!1;let h=p(t,"LI"),v=L.parentNode;if(!v)return!1;let E=[],N=null;for(let g of Array.from(L.childNodes)){if(g.nodeType!==1||g.tagName!=="LI")continue;let x=r.createElement("p");for(;g.firstChild;)x.appendChild(g.firstChild);x.firstChild||x.appendChild(r.createElement("br")),E.push(x),g===h&&(N=x)}for(let g of E)v.insertBefore(g,L);v.removeChild(L);let m=N??E[0];if(m){let g=r.createRange();g.selectNodeContents(m),g.collapse(!1),c.removeAllRanges(),c.addRange(g)}return l(),!0}function y(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return!0;t=t.parentNode}return!1}return{exec(d,c){if(!F.has(d))throw new Error(`Unknown editor command: "${d}"`);switch(e.focus(),d){case"bold":r.execCommand("bold",!1);break;case"italic":r.execCommand("italic",!1);break;case"heading":{let t=c??"1";if(!["1","2","3"].includes(t))throw new Error(`Invalid heading level: "${t}". Use 1, 2, or 3`);r.execCommand("formatBlock",!1,`<h${t}>`);break}case"blockquote":r.execCommand("formatBlock",!1,"<blockquote>");break;case"unorderedList":w("UL")||r.execCommand("insertUnorderedList",!1);break;case"orderedList":w("OL")||r.execCommand("insertOrderedList",!1);break;case"link":{if(!c)throw new Error("Link command requires a URL value");let t=c.trim();if(!T(t,i.protocols)){u("error",new Error(`Protocol not allowed: ${t}`));return}r.execCommand("createLink",!1,t);break}case"unlink":r.execCommand("unlink",!1);break;case"codeBlock":{let t=r.getSelection();if(!t||t.rangeCount===0)break;let L=t.anchorNode,h=L?p(L,"PRE"):null;if(h){let v=r.createElement("p");v.textContent=h.textContent||"",h.parentNode?.replaceChild(v,h);let E=r.createRange();E.selectNodeContents(v),E.collapse(!1),t.removeAllRanges(),t.addRange(E)}else{let E=t.getRangeAt(0).startContainer;for(;E.parentNode&&E.parentNode!==e;)E=E.parentNode;let N=r.createElement("pre"),m=r.createElement("code"),g=E.textContent||"";m.textContent=g.endsWith(`
4
- `)?g:g+`
5
- `,N.appendChild(m),E.parentNode===e?e.replaceChild(N,E):e.appendChild(N);let x=r.createRange();x.selectNodeContents(m),x.collapse(!1),t.removeAllRanges(),t.addRange(x)}l();break}}},queryState(d){if(!F.has(d))throw new Error(`Unknown editor command: "${d}"`);let c=r.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t||!e.contains(t))return!1;switch(d){case"bold":return y(t,"STRONG")||y(t,"B");case"italic":return y(t,"EM")||y(t,"I");case"heading":return y(t,"H1")||y(t,"H2")||y(t,"H3");case"blockquote":return y(t,"BLOCKQUOTE");case"unorderedList":return y(t,"UL");case"orderedList":return y(t,"OL");case"link":return y(t,"A");case"unlink":return!1;case"codeBlock":return y(t,"PRE");default:return!1}},getHTML(){return e.innerHTML},getText(){return e.textContent??""},destroy(){e.removeEventListener("keydown",C),e.removeEventListener("paste",f),e.removeEventListener("input",b),s.destroy(),e.contentEditable="false"},on(d,c){a[d]||(a[d]=[]),a[d].push(c)}}}import{jsx as $}from"react/jsx-runtime";function ue(e){let{initialHTML:n,value:o,onChange:i,policy:a,className:r,editorRef:u}=e,l=O(null),s=O(null),f=O(i),b=O(a??A);return D(()=>{f.current=i},[i]),D(()=>{let C=l.current;if(!C)return;let p=a??A;b.current=p,C.replaceChildren(k(o??n??"",p));let w=B(C,{policy:p,onChange:y=>f.current?.(y)});return s.current=w,u?.(w),()=>{w.destroy(),s.current=null,u?.(null)}},[]),D(()=>{let C=s.current,p=l.current;!C||!p||o===void 0||C.getHTML()!==o&&p.replaceChildren(k(o,b.current))},[o]),$("div",{ref:l,className:r})}export{ue as Minisiwyg};
1
+ import{useEffect as M,useRef as O}from"react";var P={tags:{p:[],br:[],strong:[],em:[],a:["href","title","target"],h1:[],h2:[],h3:[],ul:[],ol:[],li:[],blockquote:[],pre:[],code:[]},strip:!0,maxDepth:10,maxLength:1e5,protocols:["https","http","mailto"]};Object.freeze(P);Object.freeze(P.protocols);for(let e of Object.values(P.tags))Object.freeze(e);Object.freeze(P.tags);var A=P;var R={b:"strong",i:"em"},S=new Set(["href","src","action","formaction"]),j=new Set(["javascript","data"]);function Y(e){let n=e.trim();n=n.replace(/&#x([0-9a-f]+);?/gi,(i,a)=>String.fromCharCode(parseInt(a,16))),n=n.replace(/&#(\d+);?/g,(i,a)=>String.fromCharCode(parseInt(a,10)));try{n=decodeURIComponent(n)}catch{}n=n.replace(/[\s\x00-\x1f\u00A0\u1680\u2000-\u200B\u2028\u2029\u202F\u205F\u3000\uFEFF]+/g,"");let o=n.match(/^([a-z][a-z0-9+\-.]*)\s*:/i);return o?o[1].toLowerCase():null}function T(e,n){let o=Y(e);return o===null?!0:j.has(o)?!1:n.includes(o)}function D(e,n,o){let i=Array.from(e.childNodes);for(let a of i){if(a.nodeType===3)continue;if(a.nodeType!==1){e.removeChild(a);continue}let r=a,u=r.tagName.toLowerCase(),l=R[u];if(l&&(u=l),o>=n.maxDepth){e.removeChild(r);continue}let s=n.tags[u];if(s===void 0){if(n.strip)e.removeChild(r);else{for(D(r,n,o);r.firstChild;)e.insertBefore(r.firstChild,r);e.removeChild(r)}continue}let f=r;if(l&&r.tagName.toLowerCase()!==l){let p=r.ownerDocument.createElement(l);for(;r.firstChild;)p.appendChild(r.firstChild);e.replaceChild(p,r),f=p}let b=Array.from(f.attributes);for(let L of b){let p=L.name.toLowerCase();if(p.startsWith("on")){f.removeAttribute(L.name);continue}if(!s.includes(p)){f.removeAttribute(L.name);continue}S.has(p)&&(T(L.value,n.protocols)||f.removeAttribute(L.name))}D(f,n,o+1)}}function k(e,n){let o=document.createElement("template");if(!e)return o.content;o.innerHTML=e;let i=o.content;return D(i,n,0),n.maxLength>0&&(i.textContent?.length??0)>n.maxLength&&U(i,n.maxLength),i}function U(e,n){let o=n,i=Array.from(e.childNodes);for(let a of i){if(o<=0){e.removeChild(a);continue}if(a.nodeType===3){let r=a.textContent??"";r.length>o?(a.textContent=r.slice(0,o),o=0):o-=r.length}else a.nodeType===1?o=U(a,o):e.removeChild(a)}return o}function $(e,n){let o=0,i=e.parentNode;for(;i&&i!==n;)i.nodeType===1&&o++,i=i.parentNode;return o}function I(e,n,o){let i=e.tagName.toLowerCase(),a=R[i];if(a&&(i=a),$(e,o)>=n.maxDepth)return e.parentNode?.removeChild(e),!0;let u=n.tags[i];if(u===void 0){if(n.strip)e.parentNode?.removeChild(e);else{let s=e.parentNode;if(s){for(;e.firstChild;)s.insertBefore(e.firstChild,e);s.removeChild(e)}}return!0}let l=e;if(a&&e.tagName.toLowerCase()!==a){let s=e.ownerDocument.createElement(a);for(;e.firstChild;)s.appendChild(e.firstChild);for(let f of Array.from(e.attributes))s.setAttribute(f.name,f.value);e.parentNode?.replaceChild(s,e),l=s}for(let s of Array.from(l.attributes)){let f=s.name.toLowerCase();if(f.startsWith("on")){l.removeAttribute(s.name);continue}if(!u.includes(f)){l.removeAttribute(s.name);continue}S.has(f)&&(T(s.value,n.protocols)||l.removeAttribute(s.name))}return!1}function _(e,n,o){let i=Array.from(e.childNodes);for(let a of i){if(a.nodeType!==1){a.nodeType!==3&&e.removeChild(a);continue}I(a,n,o)||_(a,n,o)}}function F(e,n){if(!n||!n.tags)throw new TypeError('Policy must have a "tags" property');let o=!1,i=[];function a(u){for(let l of i)l(u)}let r=new MutationObserver(u=>{if(!o){o=!0;try{for(let l of u)if(l.type==="childList")for(let s of Array.from(l.addedNodes)){if(s.nodeType===3)continue;if(s.nodeType!==1){s.parentNode?.removeChild(s);continue}I(s,n,e)||_(s,n,e)}else if(l.type==="attributes"){let s=l.target;if(s.nodeType!==1)continue;let f=l.attributeName;if(!f)continue;let b=s.tagName.toLowerCase(),L=R[b]||b,p=n.tags[L];if(!p)continue;let w=f.toLowerCase();if(w.startsWith("on")){s.removeAttribute(f);continue}if(!p.includes(w)){s.removeAttribute(f);continue}if(S.has(w)){let y=s.getAttribute(f);y&&!T(y,n.protocols)&&s.removeAttribute(f)}}}catch(l){a(l instanceof Error?l:new Error(String(l)))}finally{o=!1}}});return r.observe(e,{childList:!0,attributes:!0,subtree:!0}),{destroy(){r.disconnect()},on(u,l){u==="error"&&i.push(l)}}}var B=new Set(["bold","italic","heading","blockquote","unorderedList","orderedList","link","unlink","codeBlock"]);function q(e,n){if(!e)throw new TypeError("createEditor requires an HTMLElement");if(!e.ownerDocument||!e.parentNode)throw new TypeError("createEditor requires an element attached to the DOM");let o=n?.policy??A,i={tags:Object.fromEntries(Object.entries(o.tags).map(([d,c])=>[d,[...c]])),strip:o.strip,maxDepth:o.maxDepth,maxLength:o.maxLength,protocols:[...o.protocols]},a={},r=e.ownerDocument;function u(d,...c){for(let t of a[d]??[])t(...c)}function l(){let d=e.innerHTML;u("change",d),n?.onChange?.(d)}e.contentEditable="true";let s=F(e,i);s.on("error",d=>u("error",d));function f(d){d.preventDefault();let c=d.clipboardData;if(!c)return;let t=r.getSelection();if(t&&t.rangeCount>0&&t.anchorNode&&p(t.anchorNode,"PRE")){let E=c.getData("text/plain");if(!E)return;i.maxLength>0&&(e.textContent?.length??0)+E.length>i.maxLength&&u("overflow",i.maxLength);let x=t.getRangeAt(0);x.deleteContents();let H=r.createTextNode(E);x.insertNode(H),x.setStartAfter(H),x.collapse(!0),t.removeAllRanges(),t.addRange(x),u("paste",e.innerHTML),l();return}let g=c.getData("text/html");if(!g){let h=c.getData("text/plain");if(!h)return;g=h.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/\n/g,"<br>")}let m=k(g,i),v=r.getSelection();if(!v||v.rangeCount===0)return;let C=v.getRangeAt(0);if(C.deleteContents(),i.maxLength>0){let h=m.textContent?.length??0;(e.textContent?.length??0)+h>i.maxLength&&u("overflow",i.maxLength)}let N=m.lastChild;if(C.insertNode(m),N){let h=r.createRange();h.setStartAfter(N),h.collapse(!0),v.removeAllRanges(),v.addRange(h)}u("paste",e.innerHTML),l()}function b(){l()}function L(d){let c=r.getSelection();if(!c||c.rangeCount===0)return;let t=c.anchorNode;if(!t)return;let g=p(t,"PRE");if(d.key==="Enter"&&g){d.preventDefault();let m=c.getRangeAt(0);m.deleteContents();let v=r.createTextNode(`
2
+ `);m.insertNode(v),m.setStartAfter(v),m.collapse(!0),c.removeAllRanges(),c.addRange(m),l()}if(d.key==="Backspace"&&g){let m=g.textContent||"";if(c.anchorOffset===0&&(m===""||m===`
3
+ `)){d.preventDefault();let N=r.createElement("p");N.appendChild(r.createElement("br")),g.parentNode?.replaceChild(N,g);let h=r.createRange();h.selectNodeContents(N),h.collapse(!0),c.removeAllRanges(),c.addRange(h),l()}}}e.addEventListener("keydown",L),e.addEventListener("paste",f),e.addEventListener("input",b);function p(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return t;t=t.parentNode}return null}function w(d){let c=r.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t)return!1;let g=p(t,d);if(!g)return!1;let m=p(t,"LI"),v=g.parentNode;if(!v)return!1;let C=[],N=null;for(let E of Array.from(g.childNodes)){if(E.nodeType!==1||E.tagName!=="LI")continue;let x=r.createElement("p");for(;E.firstChild;)x.appendChild(E.firstChild);x.firstChild||x.appendChild(r.createElement("br")),C.push(x),E===m&&(N=x)}for(let E of C)v.insertBefore(E,g);v.removeChild(g);let h=N??C[0];if(h){let E=r.createRange();E.selectNodeContents(h),E.collapse(!1),c.removeAllRanges(),c.addRange(E)}return l(),!0}function y(d,c){let t=d;for(;t&&t!==e;){if(t.nodeType===1&&t.tagName===c)return!0;t=t.parentNode}return!1}let z={exec(d,c){if(!B.has(d))throw new Error(`Unknown editor command: "${d}"`);switch(e.focus(),d){case"bold":r.execCommand("bold",!1);break;case"italic":r.execCommand("italic",!1);break;case"heading":{let t=c??"1";if(!["1","2","3"].includes(t))throw new Error(`Invalid heading level: "${t}". Use 1, 2, or 3`);let g=`H${t}`,m=r.getSelection()?.anchorNode;m&&z.queryState("heading")&&y(m,g)?r.execCommand("formatBlock",!1,"<p>"):r.execCommand("formatBlock",!1,`<h${t}>`);break}case"blockquote":z.queryState("blockquote")?r.execCommand("formatBlock",!1,"<p>"):r.execCommand("formatBlock",!1,"<blockquote>");break;case"unorderedList":w("UL")||r.execCommand("insertUnorderedList",!1);break;case"orderedList":w("OL")||r.execCommand("insertOrderedList",!1);break;case"link":{if(!c)throw new Error("Link command requires a URL value");let t=c.trim();if(!T(t,i.protocols)){u("error",new Error(`Protocol not allowed: ${t}`));return}r.execCommand("createLink",!1,t);break}case"unlink":r.execCommand("unlink",!1);break;case"codeBlock":{let t=r.getSelection();if(!t||t.rangeCount===0)break;let g=t.anchorNode,m=g?p(g,"PRE"):null;if(m){let v=r.createElement("p");v.textContent=m.textContent||"",m.parentNode?.replaceChild(v,m);let C=r.createRange();C.selectNodeContents(v),C.collapse(!1),t.removeAllRanges(),t.addRange(C)}else{let C=t.getRangeAt(0).startContainer;for(;C.parentNode&&C.parentNode!==e;)C=C.parentNode;let N=r.createElement("pre"),h=r.createElement("code"),E=C.textContent||"";h.textContent=E.endsWith(`
4
+ `)?E:E+`
5
+ `,N.appendChild(h),C.parentNode===e?e.replaceChild(N,C):e.appendChild(N);let x=r.createRange();x.selectNodeContents(h),x.collapse(!1),t.removeAllRanges(),t.addRange(x)}l();break}}},queryState(d){if(!B.has(d))throw new Error(`Unknown editor command: "${d}"`);let c=r.getSelection();if(!c||c.rangeCount===0)return!1;let t=c.anchorNode;if(!t||!e.contains(t))return!1;switch(d){case"bold":return y(t,"STRONG")||y(t,"B");case"italic":return y(t,"EM")||y(t,"I");case"heading":return y(t,"H1")||y(t,"H2")||y(t,"H3");case"blockquote":return y(t,"BLOCKQUOTE");case"unorderedList":return y(t,"UL");case"orderedList":return y(t,"OL");case"link":return y(t,"A");case"unlink":return!1;case"codeBlock":return y(t,"PRE");default:return!1}},getHTML(){return e.innerHTML},getText(){return e.textContent??""},destroy(){e.removeEventListener("keydown",L),e.removeEventListener("paste",f),e.removeEventListener("input",b),s.destroy(),e.contentEditable="false"},on(d,c){a[d]||(a[d]=[]),a[d].push(c)}};return z}import{jsx as G}from"react/jsx-runtime";function ue(e){let{initialHTML:n,value:o,onChange:i,policy:a,className:r,editorRef:u}=e,l=O(null),s=O(null),f=O(i),b=O(a??A);return M(()=>{f.current=i},[i]),M(()=>{let L=l.current;if(!L)return;let p=a??A;b.current=p,L.replaceChildren(k(o??n??"",p));let w=q(L,{policy:p,onChange:y=>f.current?.(y)});return s.current=w,u?.(w),()=>{w.destroy(),s.current=null,u?.(null)}},[]),M(()=>{let L=s.current,p=l.current;!L||!p||o===void 0||L.getHTML()!==o&&p.replaceChildren(k(o,b.current))},[o]),G("div",{ref:l,className:r})}export{ue as Minisiwyg};
6
6
  //# sourceMappingURL=react.js.map