UNPKG

on-codemerge

Version:

A WYSIWYG editor for on-codemerge is a user-friendly interface that allows users to edit and view their code in real time, exactly as it will appear in the final product

3 lines (2 loc) 15.5 kB
/*! on-codemerge v1.3.1 @author Pavel Kuzmin @license MIT @homepage https://s00d.github.io/on-codemerge/ @repository git+https://github.com/s00d/on-codemerge.git Copyright (c) 2026 Pavel Kuzmin - Built on 2026-07-02T13:39:17.947Z */ "use strict";var p=Object.defineProperty;var y=(d,e,t)=>e in d?p(d,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):d[e]=t;var o=(d,e,t)=>y(d,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("./src/core/Plugin.cjs"),b=require("./src/core/services/HTMLFormatter.cjs"),C=require("./src/core/services/LocaleManager.cjs"),E=require("./src/core/services/TextFormatter/index.cjs"),S=require("./src/core/services/Selector.cjs"),D=require("./src/core/ui/NotificationManager.cjs"),u=require("./src/core/DOMContext.cjs"),l=class l{constructor(e,t={mode:"direct"}){o(this,"innerContainer");o(this,"container");o(this,"plugins");o(this,"eventHandlers");o(this,"formatter");o(this,"localeManager");o(this,"contentChangeCallbacks",[]);o(this,"mutationObserver");o(this,"textFormatter",null);o(this,"selector");o(this,"_disableObserver",!1);o(this,"notificationManager");o(this,"boundHandleSelectionChange");o(this,"boundClickToFocus");o(this,"boundDragStart");o(this,"boundDragEnd");o(this,"boundDragEnter");o(this,"boundDragOver");o(this,"boundDragLeave");o(this,"boundDrop");o(this,"boundPaste");o(this,"domContext");o(this,"options");this.options=t,this.domContext=new u.DOMContext(t.shadowRoot||void 0,t.iframe||void 0);let n=e;t.mode==="shadowRoot"?n=this.setupShadowRootMode(e):t.mode==="iframe"&&(n=this.setupIframeMode(e)),this.container=document.createElement("div"),this.container.className="html-editor",this.container.contentEditable="true",this.localeManager=new C.LocaleManager,this.notificationManager=D.NotificationManager.getInstance(),this.localeManager.initialize().catch(r=>{console.error("Failed to initialize LocaleManager:",r)}),n.appendChild(this.container),this.innerContainer=e,this.plugins=new v.DefaultPluginManager,this.eventHandlers=new Map,this.formatter=new b.HTMLFormatter,this.textFormatter=new E.TextFormatter(this.container,t.shadowRoot||void 0,t.formatDebug),this.selector=new S.Selector(this.container),this.boundClickToFocus=r=>{r.target.closest(".editor-block")||this.container.focus()},this.boundDragStart=r=>this.handleDragStart(r),this.boundDragEnd=r=>this.handleDragEnd(r),this.boundDragEnter=r=>this.handleDragEnter(r),this.boundDragOver=r=>this.handleDragOver(r),this.boundDragLeave=r=>this.handleDragLeave(r),this.boundDrop=r=>this.handleDrop(r),this.boundPaste=r=>this.handlePaste(r),this.container.addEventListener("click",this.boundClickToFocus),this.container.addEventListener("dragstart",this.boundDragStart),this.container.addEventListener("dragend",this.boundDragEnd),this.container.addEventListener("dragenter",this.boundDragEnter),this.container.addEventListener("dragover",this.boundDragOver),this.container.addEventListener("dragleave",this.boundDragLeave),this.container.addEventListener("drop",this.boundDrop),this.container.addEventListener("paste",this.boundPaste),this.boundHandleSelectionChange=r=>this.handleSelectionChange(r),this.domContext.addEventListener("selectionchange",this.boundHandleSelectionChange),this.mutationObserver=new MutationObserver(r=>this.handleMutations(r)),this.mutationObserver.observe(this.container,{childList:!0,subtree:!0,characterData:!0,attributes:!0})}syncDocumentStylesToShadowRoot(e){var t;e.querySelectorAll(`[${l.STYLE_SYNC_ATTR}]`).forEach(n=>n.remove()),(t=document.adoptedStyleSheets)!=null&&t.length&&(e.adoptedStyleSheets=Array.from(document.adoptedStyleSheets)),document.querySelectorAll("style").forEach(n=>{const r=document.createElement("style");r.setAttribute(l.STYLE_SYNC_ATTR,""),r.textContent=n.textContent||"",e.appendChild(r)}),document.querySelectorAll('link[rel="stylesheet"], link[rel="modulepreload"], link[rel="preload"], link[rel="preload stylesheet"]').forEach(n=>{const r=document.createElement("link");r.setAttribute(l.STYLE_SYNC_ATTR,""),r.rel="stylesheet",r.href=n.href,e.appendChild(r)})}applyStylesToShadowDOM(e){const t=()=>this.syncDocumentStylesToShadowRoot(e);document.readyState==="complete"?t():window.addEventListener("load",t,{once:!0})}applyStylesToIframe(e){const t=()=>{try{document.querySelectorAll("style").forEach(i=>{const a=e.createElement("style");a.textContent=i.textContent||"",e.head.appendChild(a)}),document.querySelectorAll('link[rel="modulepreload"], link[rel="preload"], link[rel="preload stylesheet"]').forEach(i=>{const a=e.createElement("link");a.rel="stylesheet",a.href=i.href,e.head.appendChild(a)}),document.adoptedStyleSheets&&e.adoptedStyleSheets&&(e.adoptedStyleSheets=Array.from(document.adoptedStyleSheets))}catch(n){console.warn("Failed to apply styles to iframe:",n)}};document.readyState==="complete"?t():window.addEventListener("load",t)}setupShadowRootMode(e){if(this.options.shadowRoot)return this.applyStylesToShadowDOM(this.options.shadowRoot),e;{const t=e.attachShadow({mode:"open"});this.options.shadowRoot=t,this.domContext=new u.DOMContext(t),this.applyStylesToShadowDOM(t);const n=document.createElement("div");return n.className="shadow-editor-container",t.appendChild(n),n}}setupIframeMode(e){var t;if(this.options.iframe)return(t=this.options.iframe.contentDocument)!=null&&t.body?this.options.iframe.contentDocument.body:e;{const n=document.createElement("iframe");n.style.border="none",n.style.width="100%",n.style.height="100%",n.style.minHeight="500px",n.style.display="block",n.style.overflow="hidden",e.appendChild(n);const r=new Promise(i=>{n.onload=()=>{n.contentDocument&&(n.contentDocument.body.style.margin="0",n.contentDocument.body.style.padding="0",n.contentDocument.body.style.minHeight="100vh",n.contentDocument.body.style.height="100%",n.contentDocument.body.style.width="100%",n.contentDocument.body.style.overflow="auto",this.applyStylesToIframe(n.contentDocument),this.container.parentElement&&n.contentDocument.body.appendChild(this.container),this.domContext=new u.DOMContext(void 0,n),i())}});return this.iframeReady=r,this.options.iframe=n,e}}async handleSelectionChange(e){var i;const t=(i=this.textFormatter)==null?void 0:i.getSelection();if(!t||t.rangeCount===0)return;const r=t.getRangeAt(0).commonAncestorContainer;this.isSelectionInsideEditor(r)&&this.triggerEvent("selectionchange",{event:e})}isSelectionInsideEditor(e){return e.nodeType===Node.TEXT_NODE?this.container.contains(e.parentElement):e.nodeType===Node.ELEMENT_NODE?this.container.contains(e):!1}handleMutations(e){e.some(n=>n.type==="childList"||n.type==="characterData"||n.type==="attributes")&&this.handleContentChange()}async handlePaste(e){e.preventDefault();const t=e.clipboardData;if(!t)return;const n=t.files,r=t.getData("text");n.length>0?this.handleFileDrop(n,e):r&&this.handleTextDrop(r)}handleDragStart(e){this.triggerEvent("drag-start",{e})}handleDragEnd(e){this.triggerEvent("drag-end",{e})}handleDragEnter(e){e.preventDefault(),this.container.classList.add("drag-overlay"),this.container.draggable=!0,this.triggerEvent("drag-enter",{e})}handleDragOver(e){e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy"),this.triggerEvent("drag-over",{e})}handleDragLeave(e){e.preventDefault(),this.container.classList.remove("drag-overlay"),this.container.draggable=!1,this.triggerEvent("drag-leave",{e})}handleDrop(e){if(e.preventDefault(),this.container.classList.remove("drag-overlay"),this.container.draggable=!1,e.dataTransfer){const t=e.dataTransfer.files,n=e.dataTransfer.getData("text");t.length>0?this.handleFileDrop(t,e):n&&this.handleTextDrop(n)}this.triggerEvent("drag-drop",{e})}async handleFileDrop(e,t){for(let n=0;n<e.length;n++){const r=e[n],i=r.type,a=r.name,s=await this.readFileContent(r);this.triggerEvent("file-drop",{type:i,name:a,content:s,e:t})}}handleTextDrop(e){this.insertTextAtCursor(e),this.triggerEvent("text-drop",e)}async readFileContent(e){return new Promise((t,n)=>{const r=new FileReader;r.onload=()=>{r.result?t(r.result):n(new Error("Failed to read file"))},r.onerror=()=>n(r.error),e.type.startsWith("text/")?r.readAsText(e):r.readAsDataURL(e)})}insertTextAtCursor(e){var n;const t=(n=this.textFormatter)==null?void 0:n.getSelection();if(t&&t.rangeCount>0){const r=t.getRangeAt(0);r.deleteContents(),r.insertNode(document.createTextNode(e)),r.collapse(!1)}}getContainer(){return this.container}getInnerContainer(){return this.innerContainer}getTextFormatter(){return this.textFormatter}getSelector(){return this.selector}getToolbar(){const e=this.plugins.getPlugin("toolbar");return e&&"getToolbar"in e?e.getToolbar():null}getDOMContext(){return this.domContext}getMode(){return this.options.mode}getShadowRoot(){return this.options.shadowRoot}getIframe(){return this.options.iframe}addStyle(e){var t;if(this.options.mode==="direct"){const n=document.createElement("style");n.textContent=e,document.head.appendChild(n)}else if(this.options.shadowRoot){const n=document.createElement("style");n.textContent=e,this.options.shadowRoot.appendChild(n)}else if((t=this.options.iframe)!=null&&t.contentDocument){const n=this.options.iframe.contentDocument.createElement("style");n.textContent=e,this.options.iframe.contentDocument.head.appendChild(n)}}async waitForIframeReady(){this.options.mode==="iframe"&&this.iframeReady&&await this.iframeReady}isSelectionInsideContainer(e){if(!e||e.rangeCount===0)return!1;const t=e.getRangeAt(0);return this.container.contains(t.startContainer)&&this.container.contains(t.endContainer)}getSelectionInsideContainer(){var t;const e=(t=this.textFormatter)==null?void 0:t.getSelection();return e&&this.isSelectionInsideContainer(e)?e:null}saveCursorPosition(){const e=this.getSelectionInsideContainer();if(!e||e.rangeCount===0)return null;const t=e.getRangeAt(0),n=t.cloneRange();return n.selectNodeContents(this.container),n.setEnd(t.startContainer,t.startOffset),{offset:n.toString().length}}restoreCursorPosition(e){var f;const t=(f=this.textFormatter)==null?void 0:f.getSelection();if(!t)return;const n=document.createRange();let r=e.offset;const i=document.createTreeWalker(this.container,NodeFilter.SHOW_TEXT);for(;i.nextNode();){const c=i.currentNode,h=c.length;if(r<h){n.setStart(c,r),n.collapse(!0),t.removeAllRanges(),t.addRange(n),this.container.focus();return}if(r===h){const m=this.findNextTextNode(c);if(m){n.setStart(m,0),n.collapse(!0),t.removeAllRanges(),t.addRange(n),this.container.focus();return}}r-=h}const a=document.createTreeWalker(this.container,NodeFilter.SHOW_TEXT);let s=null;for(;a.nextNode();)s=a.currentNode;s?(n.setStart(s,s.length),n.collapse(!0)):(n.selectNodeContents(this.container),n.collapse(!1)),t.removeAllRanges(),t.addRange(n),this.container.focus()}findNextTextNode(e){const t=document.createTreeWalker(this.container,NodeFilter.SHOW_TEXT);let n=!1;for(;t.nextNode();){if(n)return t.currentNode;t.currentNode===e&&(n=!0)}return null}use(e){try{return this.plugins.register(e),e.initialize(this),!0}catch(t){return console.warn(`Failed to remove plugin ${e.name}:`,t),!1}}remove(e){try{const t=typeof e=="string"?e:e.name;return this.plugins.unregister(t),!0}catch(t){const n=typeof e=="string"?e:e.name;return console.warn(`Failed to remove plugin ${n}:`,t),!1}}getPlugins(){return this.plugins.getPlugins()}getHotkeys(){return this.plugins.getHotkeys()}on(e,t){return this.eventHandlers.has(e)||this.eventHandlers.set(e,[]),this.eventHandlers.get(e).push(t),()=>{const n=this.eventHandlers.get(e);if(n){const r=n.indexOf(t);r!==-1&&n.splice(r,1),n.length===0&&this.eventHandlers.delete(e)}}}triggerEvent(e,...t){const n=this.eventHandlers.get(e);n&&n.forEach(r=>r(...t))}off(e,t){if(this.eventHandlers.has(e))if(t){const n=this.eventHandlers.get(e),r=n.indexOf(t);r!==-1&&n.splice(r,1),n.length===0&&this.eventHandlers.delete(e)}else this.eventHandlers.delete(e)}disableObserver(){this._disableObserver=!0}enableObserver(){this._disableObserver=!1}handleContentChange(){if(this._disableObserver)return;const e=this.getHtml();this.contentChangeCallbacks.forEach(t=>t(e))}subscribeToContentChange(e){return this.contentChangeCallbacks.push(e),()=>{const t=this.contentChangeCallbacks.indexOf(e);t!==-1&&this.contentChangeCallbacks.splice(t,1)}}ensureEditorFocus(){var n;const e=this.getContainer(),t=(n=this.textFormatter)==null?void 0:n.getSelection();if(t&&t.rangeCount>0){const r=t.getRangeAt(0);if(this.isSelectionInsideEditor(r.commonAncestorContainer))return}if(e.focus(),!t||!t.rangeCount){const r=document.createRange();r.selectNodeContents(e),r.collapse(!0),t==null||t.removeAllRanges(),t==null||t.addRange(r)}}getHtml(){const e=this.getContainer();if(!e)return"";const n=e.cloneNode(!0).innerHTML;return this.formatter.format(n)}waitForDOMStabilization(e,t=100){return new Promise(n=>{let r;const i=new MutationObserver(()=>{clearTimeout(r),r=setTimeout(()=>{i.disconnect(),n()},t)});i.observe(e,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),r=setTimeout(()=>{i.disconnect(),n()},t)})}async setHtml(e){await this.waitForDOMStabilization(this.container),this.container.innerHTML=this.formatter.format(e),await new Promise(t=>requestAnimationFrame(t))}insertContent(e){var n;this.ensureEditorFocus();const t=(n=this.textFormatter)==null?void 0:n.getSelection();if(t&&t.rangeCount>0){const r=t.getRangeAt(0);if(r.deleteContents(),typeof e=="string"){const i=document.createElement("div");for(i.innerHTML=e;i.firstChild;)r.insertNode(i.firstChild)}else r.insertNode(e);r.collapse(!1),t.removeAllRanges(),t.addRange(r)}else{const r=this.getContainer();typeof e=="string"?r.insertAdjacentHTML("beforeend",e):r.appendChild(e)}}t(e,t={}){return this.localeManager.isReady()?this.localeManager.translate(e,t):e}async setLocale(e){return await this.localeManager.setLocale(e)}getLocale(){return this.localeManager.getCurrentLocale()}showNotification(e,t="info",n=3e3){this.notificationManager.show({message:e,type:t,duration:n})}showSuccessNotification(e,t){this.notificationManager.success(e,{duration:t})}showErrorNotification(e,t){this.notificationManager.error(e,{duration:t})}showWarningNotification(e,t){this.notificationManager.warning(e,{duration:t})}showInfoNotification(e,t){this.notificationManager.info(e,{duration:t})}destroy(){this.mutationObserver.disconnect(),this.boundClickToFocus&&this.container.removeEventListener("click",this.boundClickToFocus),this.boundDragStart&&this.container.removeEventListener("dragstart",this.boundDragStart),this.boundDragEnd&&this.container.removeEventListener("dragend",this.boundDragEnd),this.boundDragEnter&&this.container.removeEventListener("dragenter",this.boundDragEnter),this.boundDragOver&&this.container.removeEventListener("dragover",this.boundDragOver),this.boundDragLeave&&this.container.removeEventListener("dragleave",this.boundDragLeave),this.boundDrop&&this.container.removeEventListener("drop",this.boundDrop),this.boundPaste&&this.container.removeEventListener("paste",this.boundPaste),this.boundHandleSelectionChange&&(this.domContext.removeEventListener("selectionchange",this.boundHandleSelectionChange),this.boundHandleSelectionChange=void 0),this.contentChangeCallbacks=[],this.plugins.destroy(),this.container.parentElement&&this.container.parentElement.removeChild(this.container),this.innerContainer=null,this.container=null,this.plugins=null,this.eventHandlers=null,this.formatter=null,this.localeManager=null,this.mutationObserver=null,this.textFormatter=null}};o(l,"STYLE_SYNC_ATTR","data-cm-style-sync");let g=l;exports.HTMLEditor=g;