overtype 2.0.5 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/overtype-webcomponent.esm.js +29 -4
- package/dist/overtype-webcomponent.esm.js.map +2 -2
- package/dist/overtype-webcomponent.js +29 -4
- package/dist/overtype-webcomponent.js.map +2 -2
- package/dist/overtype-webcomponent.min.js +4 -4
- package/dist/overtype.cjs +29 -4
- package/dist/overtype.cjs.map +2 -2
- package/dist/overtype.esm.js +29 -4
- package/dist/overtype.esm.js.map +2 -2
- package/dist/overtype.js +31 -4
- package/dist/overtype.js.map +2 -2
- package/dist/overtype.min.js +6 -4
- package/package.json +1 -1
- package/src/overtype.js +8 -3
- package/src/parser.js +5 -4
- package/src/toolbar.js +28 -0
package/dist/overtype.min.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OverType v2.0.
|
|
2
|
+
* OverType v2.0.6
|
|
3
3
|
* A lightweight markdown editor library with perfect WYSIWYG alignment
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @author David Miranda
|
|
6
6
|
* https://github.com/panphora/overtype
|
|
7
7
|
*/
|
|
8
|
-
var OverType=(()=>{var j=Object.defineProperty;var Ne=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Oe=Object.prototype.hasOwnProperty;var je=(o,e,t)=>e in o?j(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var ze=(o,e)=>{for(var t in e)j(o,t,{get:e[t],enumerable:!0})},Fe=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of _e(e))!Oe.call(o,i)&&i!==t&&j(o,i,{get:()=>e[i],enumerable:!(n=Ne(e,i))||n.enumerable});return o};var Re=o=>Fe(j({},"__esModule",{value:!0}),o);var T=(o,e,t)=>(je(o,typeof e!="symbol"?e+"":e,t),t);var tt={};ze(tt,{OverType:()=>H,default:()=>et,defaultToolbarButtons:()=>G,toolbarButtons:()=>x});var S=class{static resetLinkIndex(){this.linkIndex=0}static setCodeHighlighter(e){this.codeHighlighter=e}static escapeHtml(e){let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,n=>t[n])}static preserveIndentation(e,t){let i=t.match(/^(\s*)/)[1].replace(/ /g," ");return e.replace(/^\s*/,i)}static parseHeader(e){return e.replace(/^(#{1,3})\s(.+)$/,(t,n,i)=>{let r=n.length;return`<h${r}><span class="syntax-marker">${n} </span>${i}</h${r}>`})}static parseHorizontalRule(e){return e.match(/^(-{3,}|\*{3,}|_{3,})$/)?`<div><span class="hr-marker">${e}</span></div>`:null}static parseBlockquote(e){return e.replace(/^> (.+)$/,(t,n)=>`<span class="blockquote"><span class="syntax-marker">></span> ${n}</span>`)}static parseBulletList(e){return e.replace(/^((?: )*)([
|
|
8
|
+
var OverType=(()=>{var j=Object.defineProperty;var Ne=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Oe=Object.prototype.hasOwnProperty;var je=(o,e,t)=>e in o?j(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var ze=(o,e)=>{for(var t in e)j(o,t,{get:e[t],enumerable:!0})},Fe=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of _e(e))!Oe.call(o,i)&&i!==t&&j(o,i,{get:()=>e[i],enumerable:!(n=Ne(e,i))||n.enumerable});return o};var Re=o=>Fe(j({},"__esModule",{value:!0}),o);var T=(o,e,t)=>(je(o,typeof e!="symbol"?e+"":e,t),t);var tt={};ze(tt,{OverType:()=>H,default:()=>et,defaultToolbarButtons:()=>G,toolbarButtons:()=>x});var S=class{static resetLinkIndex(){this.linkIndex=0}static setCodeHighlighter(e){this.codeHighlighter=e}static escapeHtml(e){let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,n=>t[n])}static preserveIndentation(e,t){let i=t.match(/^(\s*)/)[1].replace(/ /g," ");return e.replace(/^\s*/,i)}static parseHeader(e){return e.replace(/^(#{1,3})\s(.+)$/,(t,n,i)=>{let r=n.length;return`<h${r}><span class="syntax-marker">${n} </span>${i}</h${r}>`})}static parseHorizontalRule(e){return e.match(/^(-{3,}|\*{3,}|_{3,})$/)?`<div><span class="hr-marker">${e}</span></div>`:null}static parseBlockquote(e){return e.replace(/^> (.+)$/,(t,n)=>`<span class="blockquote"><span class="syntax-marker">></span> ${n}</span>`)}static parseBulletList(e){return e.replace(/^((?: )*)([-*+])\s(.+)$/,(t,n,i,r)=>`${n}<li class="bullet-list"><span class="syntax-marker">${i} </span>${r}</li>`)}static parseTaskList(e,t=!1){return e.replace(/^((?: )*)-\s+\[([ xX])\]\s+(.+)$/,(n,i,r,s)=>{if(t){let a=r.toLowerCase()==="x";return`${i}<li class="task-list"><input type="checkbox" ${a?"checked":""}> ${s}</li>`}else return`${i}<li class="task-list"><span class="syntax-marker">- [${r}] </span>${s}</li>`})}static parseNumberedList(e){return e.replace(/^((?: )*)(\d+\.)\s(.+)$/,(t,n,i,r)=>`${n}<li class="ordered-list"><span class="syntax-marker">${i} </span>${r}</li>`)}static parseCodeBlock(e){return/^`{3}[^`]*$/.test(e)?`<div><span class="code-fence">${e}</span></div>`:null}static parseBold(e){return e=e.replace(/\*\*(.+?)\*\*/g,'<strong><span class="syntax-marker">**</span>$1<span class="syntax-marker">**</span></strong>'),e=e.replace(/__(.+?)__/g,'<strong><span class="syntax-marker">__</span>$1<span class="syntax-marker">__</span></strong>'),e}static parseItalic(e){return e=e.replace(new RegExp("(?<![\\*>])\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)","g"),'<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>'),e=e.replace(new RegExp("(?<=^|\\s)_(?!_)(.+?)(?<!_)_(?!_)(?=\\s|$)","g"),'<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>'),e}static parseStrikethrough(e){return e=e.replace(new RegExp("(?<!~)~~(?!~)(.+?)(?<!~)~~(?!~)","g"),'<del><span class="syntax-marker">~~</span>$1<span class="syntax-marker">~~</span></del>'),e=e.replace(new RegExp("(?<!~)~(?!~)(.+?)(?<!~)~(?!~)","g"),'<del><span class="syntax-marker">~</span>$1<span class="syntax-marker">~</span></del>'),e}static parseInlineCode(e){return e.replace(new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)","g"),'<code><span class="syntax-marker">$1</span>$2<span class="syntax-marker">$3</span></code>')}static sanitizeUrl(e){let t=e.trim(),n=t.toLowerCase(),r=["http://","https://","mailto:","ftp://","ftps://"].some(a=>n.startsWith(a)),s=t.startsWith("/")||t.startsWith("#")||t.startsWith("?")||t.startsWith(".")||!t.includes(":")&&!t.includes("//");return r||s?e:"#"}static parseLinks(e){return e.replace(/\[(.+?)\]\((.+?)\)/g,(t,n,i)=>{let r=`--link-${this.linkIndex++}`;return`<a href="${this.sanitizeUrl(i)}" style="anchor-name: ${r}"><span class="syntax-marker">[</span>${n}<span class="syntax-marker url-part">](${i})</span></a>`})}static identifyAndProtectSanctuaries(e){let t=new Map,n=0,i=e,r=[],s=/\[([^\]]+)\]\(([^)]+)\)/g,a;for(;(a=s.exec(e))!==null;){let h=a.index+a[0].indexOf("](")+2,u=h+a[2].length;r.push({start:h,end:u})}let p=new RegExp("(?<!`)(`+)(?!`)((?:(?!\\1).)+?)(\\1)(?!`)","g"),d,l=[];for(;(d=p.exec(e))!==null;){let c=d.index,h=d.index+d[0].length;r.some(f=>c>=f.start&&h<=f.end)||l.push({match:d[0],index:d.index,openTicks:d[1],content:d[2],closeTicks:d[3]})}return l.sort((c,h)=>h.index-c.index),l.forEach(c=>{let h=`\uE000${n++}\uE001`;t.set(h,{type:"code",original:c.match,openTicks:c.openTicks,content:c.content,closeTicks:c.closeTicks}),i=i.substring(0,c.index)+h+i.substring(c.index+c.match.length)}),i=i.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(c,h,u)=>{let f=`\uE000${n++}\uE001`;return t.set(f,{type:"link",original:c,linkText:h,url:u}),f}),{protectedText:i,sanctuaries:t}}static restoreAndTransformSanctuaries(e,t){return Array.from(t.keys()).sort((i,r)=>{let s=e.indexOf(i),a=e.indexOf(r);return s-a}).forEach(i=>{let r=t.get(i),s;if(r.type==="code")s=`<code><span class="syntax-marker">${r.openTicks}</span>${r.content}<span class="syntax-marker">${r.closeTicks}</span></code>`;else if(r.type==="link"){let a=r.linkText;t.forEach((l,c)=>{if(a.includes(c)&&l.type==="code"){let h=`<code><span class="syntax-marker">${l.openTicks}</span>${l.content}<span class="syntax-marker">${l.closeTicks}</span></code>`;a=a.replace(c,h)}}),a=this.parseStrikethrough(a),a=this.parseBold(a),a=this.parseItalic(a);let p=`--link-${this.linkIndex++}`;s=`<a href="${this.sanitizeUrl(r.url)}" style="anchor-name: ${p}"><span class="syntax-marker">[</span>${a}<span class="syntax-marker url-part">](${r.url})</span></a>`}e=e.replace(i,s)}),e}static parseInlineElements(e){let{protectedText:t,sanctuaries:n}=this.identifyAndProtectSanctuaries(e),i=t;return i=this.parseStrikethrough(i),i=this.parseBold(i),i=this.parseItalic(i),i=this.restoreAndTransformSanctuaries(i,n),i}static parseLine(e,t=!1){let n=this.escapeHtml(e);n=this.preserveIndentation(n,e);let i=this.parseHorizontalRule(n);if(i)return i;let r=this.parseCodeBlock(n);return r||(n=this.parseHeader(n),n=this.parseBlockquote(n),n=this.parseTaskList(n,t),n=this.parseBulletList(n),n=this.parseNumberedList(n),n=this.parseInlineElements(n),n.trim()===""?"<div> </div>":`<div>${n}</div>`)}static parse(e,t=-1,n=!1,i,r=!1){this.resetLinkIndex();let s=e.split(`
|
|
9
9
|
`),a=!1,d=s.map((l,c)=>{if(n&&c===t)return`<div class="raw-line">${this.escapeHtml(l)||" "}</div>`;if(/^```[^`]*$/.test(l))return a=!a,this.parseLine(l,r);if(a){let u=this.escapeHtml(l);return`<div>${this.preserveIndentation(u,l)||" "}</div>`}return this.parseLine(l,r)}).join("");return this.postProcessHTML(d,i)}static postProcessHTML(e,t){if(typeof document>"u"||!document)return this.postProcessHTMLManual(e,t);let n=document.createElement("div");n.innerHTML=e;let i=null,r=null,s=null,a=!1,p=Array.from(n.children);for(let d=0;d<p.length;d++){let l=p[d];if(!l.parentNode)continue;let c=l.querySelector(".code-fence");if(c){let u=c.textContent;if(u.startsWith("```"))if(a){let f=t||this.codeHighlighter;if(s&&f&&s._codeContent)try{let m=f(s._codeContent,s._language||"");m&&typeof m.then=="function"?console.warn("Async highlighters are not supported in parse() because it returns an HTML string. The caller creates new DOM elements from that string, breaking references to the elements we would update. Use synchronous highlighters only."):m&&typeof m=="string"&&m.trim()&&(s._codeElement.innerHTML=m)}catch(m){console.warn("Code highlighting failed:",m)}a=!1,s=null;continue}else{a=!0,s=document.createElement("pre");let f=document.createElement("code");s.appendChild(f),s.className="code-block";let m=u.slice(3).trim();m&&(f.className=`language-${m}`),n.insertBefore(s,l.nextSibling),s._codeElement=f,s._language=m,s._codeContent="";continue}}if(a&&s&&l.tagName==="DIV"&&!l.querySelector(".code-fence")){let u=s._codeElement||s.querySelector("code");s._codeContent.length>0&&(s._codeContent+=`
|
|
10
10
|
`);let f=l.textContent.replace(/\u00A0/g," ");s._codeContent+=f,u.textContent.length>0&&(u.textContent+=`
|
|
11
11
|
`),u.textContent+=f,l.remove();continue}let h=null;if(l.tagName==="DIV"&&(h=l.querySelector("li")),h){let u=h.classList.contains("bullet-list"),f=h.classList.contains("ordered-list");if(!u&&!f){i=null,r=null;continue}let m=u?"ul":"ol";(!i||r!==m)&&(i=document.createElement(m),n.insertBefore(i,l),r=m);let g=[];for(let v of l.childNodes)if(v.nodeType===3&&v.textContent.match(/^\u00A0+$/))g.push(v.cloneNode(!0));else if(v===h)break;g.forEach(v=>{h.insertBefore(v,h.firstChild)}),i.appendChild(h),l.remove()}else i=null,r=null}return n.innerHTML}static postProcessHTMLManual(e,t){let n=e;n=n.replace(/((?:<div>(?: )*<li class="bullet-list">.*?<\/li><\/div>\s*)+)/gs,r=>{let s=r.match(/<div>(?: )*<li class="bullet-list">.*?<\/li><\/div>/gs)||[];return s.length>0?"<ul>"+s.map(p=>{let d=p.match(/<div>((?: )*)<li/),l=p.match(/<li class="bullet-list">.*?<\/li>/);if(d&&l){let c=d[1];return l[0].replace(/<li class="bullet-list">/,`<li class="bullet-list">${c}`)}return l?l[0]:""}).filter(Boolean).join("")+"</ul>":r}),n=n.replace(/((?:<div>(?: )*<li class="ordered-list">.*?<\/li><\/div>\s*)+)/gs,r=>{let s=r.match(/<div>(?: )*<li class="ordered-list">.*?<\/li><\/div>/gs)||[];return s.length>0?"<ol>"+s.map(p=>{let d=p.match(/<div>((?: )*)<li/),l=p.match(/<li class="ordered-list">.*?<\/li>/);if(d&&l){let c=d[1];return l[0].replace(/<li class="ordered-list">/,`<li class="ordered-list">${c}`)}return l?l[0]:""}).filter(Boolean).join("")+"</ol>":r});let i=/<div><span class="code-fence">(```[^<]*)<\/span><\/div>(.*?)<div><span class="code-fence">(```)<\/span><\/div>/gs;return n=n.replace(i,(r,s,a,p)=>{let l=(a.match(/<div>(.*?)<\/div>/gs)||[]).map(g=>g.replace(/<div>(.*?)<\/div>/s,"$1").replace(/ /g," ")).join(`
|
|
@@ -882,7 +882,7 @@ ${a}`:r;if(d){let L=o.value[o.selectionStart-1];o.selectionStart!==0&&L!=null&&!
|
|
|
882
882
|
}
|
|
883
883
|
|
|
884
884
|
${a}
|
|
885
|
-
`}var Z=class{constructor(e,t={}){this.editor=e,this.container=null,this.buttons={},this.toolbarButtons=t.toolbarButtons||[]}create(){this.container=document.createElement("div"),this.container.className="overtype-toolbar",this.container.setAttribute("role","toolbar"),this.container.setAttribute("aria-label","Formatting toolbar"),this.toolbarButtons.forEach(e=>{if(e.name==="separator"){let t=this.createSeparator();this.container.appendChild(t)}else{let t=this.createButton(e);this.buttons[e.name]=t,this.container.appendChild(t)}}),this.editor.container.insertBefore(this.container,this.editor.wrapper)}createSeparator(){let e=document.createElement("div");return e.className="overtype-toolbar-separator",e.setAttribute("role","separator"),e}createButton(e){let t=document.createElement("button");return t.className="overtype-toolbar-button",t.type="button",t.setAttribute("data-button",e.name),t.title=e.title||"",t.setAttribute("aria-label",e.title||e.name),t.innerHTML=this.sanitizeSVG(e.icon||""),e.name==="viewMode"?(t.classList.add("has-dropdown"),t.dataset.dropdown="true",t.addEventListener("click",n=>{n.preventDefault(),this.toggleViewModeDropdown(t)}),t):(t._clickHandler=async n=>{n.preventDefault(),this.editor.textarea.focus();try{e.action&&await e.action({editor:this.editor,getValue:()=>this.editor.getValue(),setValue:i=>this.editor.setValue(i),event:n})}catch(i){console.error(`Button "${e.name}" error:`,i),this.editor.wrapper.dispatchEvent(new CustomEvent("button-error",{detail:{buttonName:e.name,error:i}})),t.classList.add("button-error"),t.style.animation="buttonError 0.3s",setTimeout(()=>{t.classList.remove("button-error"),t.style.animation=""},300)}},t.addEventListener("click",t._clickHandler),t)}sanitizeSVG(e){return typeof e!="string"?"":e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"").replace(/\son\w+\s*=\s*["'][^"']*["']/gi,"").replace(/\son\w+\s*=\s*[^\s>]*/gi,"")}toggleViewModeDropdown(e){let t=document.querySelector(".overtype-dropdown-menu");if(t){t.remove(),e.classList.remove("dropdown-active");return}e.classList.add("dropdown-active");let n=this.createViewModeDropdown(e),i=e.getBoundingClientRect();n.style.position="absolute",n.style.top=`${i.bottom+5}px`,n.style.left=`${i.left}px`,document.body.appendChild(n),this.handleDocumentClick=r=>{!n.contains(r.target)&&!e.contains(r.target)&&(n.remove(),e.classList.remove("dropdown-active"),document.removeEventListener("click",this.handleDocumentClick))},setTimeout(()=>{document.addEventListener("click",this.handleDocumentClick)},0)}createViewModeDropdown(e){let t=document.createElement("div");t.className="overtype-dropdown-menu";let n=[{id:"normal",label:"Normal Edit",icon:"\u2713"},{id:"plain",label:"Plain Textarea",icon:"\u2713"},{id:"preview",label:"Preview Mode",icon:"\u2713"}],i=this.editor.container.dataset.mode||"normal";return n.forEach(r=>{let s=document.createElement("button");if(s.className="overtype-dropdown-item",s.type="button",s.textContent=r.label,r.id===i){s.classList.add("active"),s.setAttribute("aria-current","true");let a=document.createElement("span");a.className="overtype-dropdown-icon",a.textContent=r.icon,s.prepend(a)}s.addEventListener("click",a=>{switch(a.preventDefault(),r.id){case"plain":this.editor.showPlainTextarea();break;case"preview":this.editor.showPreviewMode();break;case"normal":default:this.editor.showNormalEditMode();break}t.remove(),e.classList.remove("dropdown-active"),document.removeEventListener("click",this.handleDocumentClick)}),t.appendChild(s)}),t}updateButtonStates(){var e;try{let t=((e=ye)==null?void 0:e(this.editor.textarea,this.editor.textarea.selectionStart))||[];Object.entries(this.buttons).forEach(([n,i])=>{if(n==="viewMode")return;let r=!1;switch(n){case"bold":r=t.includes("bold");break;case"italic":r=t.includes("italic");break;case"code":r=!1;break;case"bulletList":r=t.includes("bullet-list");break;case"orderedList":r=t.includes("numbered-list");break;case"taskList":r=t.includes("task-list");break;case"quote":r=t.includes("quote");break;case"h1":r=t.includes("header");break;case"h2":r=t.includes("header-2");break;case"h3":r=t.includes("header-3");break}i.classList.toggle("active",r),i.setAttribute("aria-pressed",r.toString())})}catch(t){}}destroy(){this.container&&(this.handleDocumentClick&&document.removeEventListener("click",this.handleDocumentClick),Object.values(this.buttons).forEach(e=>{e._clickHandler&&(e.removeEventListener("click",e._clickHandler),delete e._clickHandler)}),this.container.remove(),this.container=null,this.buttons={})}};var J=class{constructor(e){this.editor=e,this.tooltip=null,this.currentLink=null,this.hideTimeout=null,this.visibilityChangeHandler=null,this.useFloatingUI=!1,this.floatingUI=null,this.init()}async init(){if(!(CSS.supports("position-anchor: --x")&&CSS.supports("position-area: center")))try{let t=new Function("url","return import(url)"),{computePosition:n,offset:i,shift:r,flip:s}=await t("https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.4/+esm");this.floatingUI={computePosition:n,offset:i,shift:r,flip:s},this.useFloatingUI=!0}catch(t){console.warn("Failed to load Floating UI fallback:",t),this.floatingUI=null,this.useFloatingUI=!1}this.createTooltip(),this.editor.textarea.addEventListener("selectionchange",()=>this.checkCursorPosition()),this.editor.textarea.addEventListener("keyup",t=>{(t.key.includes("Arrow")||t.key==="Home"||t.key==="End")&&this.checkCursorPosition()}),this.editor.textarea.addEventListener("input",()=>this.hide()),this.editor.textarea.addEventListener("scroll",()=>{this.useFloatingUI&&this.currentLink?this.showWithFloatingUI(this.currentLink):this.hide()}),this.editor.textarea.addEventListener("blur",()=>this.hide()),this.visibilityChangeHandler=()=>{document.hidden&&this.hide()},document.addEventListener("visibilitychange",this.visibilityChangeHandler),this.tooltip.addEventListener("mouseenter",()=>this.cancelHide())}createTooltip(){this.tooltip=document.createElement("div"),this.tooltip.className="overtype-link-tooltip",this.tooltip.innerHTML=`
|
|
885
|
+
`}var Z=class{constructor(e,t={}){this.editor=e,this.container=null,this.buttons={},this.toolbarButtons=t.toolbarButtons||[]}create(){this.container=document.createElement("div"),this.container.className="overtype-toolbar",this.container.setAttribute("role","toolbar"),this.container.setAttribute("aria-label","Formatting toolbar"),this.toolbarButtons.forEach(e=>{if(e.name==="separator"){let t=this.createSeparator();this.container.appendChild(t)}else{let t=this.createButton(e);this.buttons[e.name]=t,this.container.appendChild(t)}}),this.editor.container.insertBefore(this.container,this.editor.wrapper)}createSeparator(){let e=document.createElement("div");return e.className="overtype-toolbar-separator",e.setAttribute("role","separator"),e}createButton(e){let t=document.createElement("button");return t.className="overtype-toolbar-button",t.type="button",t.setAttribute("data-button",e.name),t.title=e.title||"",t.setAttribute("aria-label",e.title||e.name),t.innerHTML=this.sanitizeSVG(e.icon||""),e.name==="viewMode"?(t.classList.add("has-dropdown"),t.dataset.dropdown="true",t.addEventListener("click",n=>{n.preventDefault(),this.toggleViewModeDropdown(t)}),t):(t._clickHandler=async n=>{n.preventDefault(),this.editor.textarea.focus();try{e.action&&await e.action({editor:this.editor,getValue:()=>this.editor.getValue(),setValue:i=>this.editor.setValue(i),event:n})}catch(i){console.error(`Button "${e.name}" error:`,i),this.editor.wrapper.dispatchEvent(new CustomEvent("button-error",{detail:{buttonName:e.name,error:i}})),t.classList.add("button-error"),t.style.animation="buttonError 0.3s",setTimeout(()=>{t.classList.remove("button-error"),t.style.animation=""},300)}},t.addEventListener("click",t._clickHandler),t)}async handleAction(e){this.editor.textarea.focus();try{e.action&&await e.action({editor:this.editor,getValue:()=>this.editor.getValue(),setValue:t=>this.editor.setValue(t),event:null})}catch(t){console.error(`Action "${e.name}" error:`,t),this.editor.wrapper.dispatchEvent(new CustomEvent("button-error",{detail:{buttonName:e.name,error:t}}))}}sanitizeSVG(e){return typeof e!="string"?"":e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"").replace(/\son\w+\s*=\s*["'][^"']*["']/gi,"").replace(/\son\w+\s*=\s*[^\s>]*/gi,"")}toggleViewModeDropdown(e){let t=document.querySelector(".overtype-dropdown-menu");if(t){t.remove(),e.classList.remove("dropdown-active");return}e.classList.add("dropdown-active");let n=this.createViewModeDropdown(e),i=e.getBoundingClientRect();n.style.position="absolute",n.style.top=`${i.bottom+5}px`,n.style.left=`${i.left}px`,document.body.appendChild(n),this.handleDocumentClick=r=>{!n.contains(r.target)&&!e.contains(r.target)&&(n.remove(),e.classList.remove("dropdown-active"),document.removeEventListener("click",this.handleDocumentClick))},setTimeout(()=>{document.addEventListener("click",this.handleDocumentClick)},0)}createViewModeDropdown(e){let t=document.createElement("div");t.className="overtype-dropdown-menu";let n=[{id:"normal",label:"Normal Edit",icon:"\u2713"},{id:"plain",label:"Plain Textarea",icon:"\u2713"},{id:"preview",label:"Preview Mode",icon:"\u2713"}],i=this.editor.container.dataset.mode||"normal";return n.forEach(r=>{let s=document.createElement("button");if(s.className="overtype-dropdown-item",s.type="button",s.textContent=r.label,r.id===i){s.classList.add("active"),s.setAttribute("aria-current","true");let a=document.createElement("span");a.className="overtype-dropdown-icon",a.textContent=r.icon,s.prepend(a)}s.addEventListener("click",a=>{switch(a.preventDefault(),r.id){case"plain":this.editor.showPlainTextarea();break;case"preview":this.editor.showPreviewMode();break;case"normal":default:this.editor.showNormalEditMode();break}t.remove(),e.classList.remove("dropdown-active"),document.removeEventListener("click",this.handleDocumentClick)}),t.appendChild(s)}),t}updateButtonStates(){var e;try{let t=((e=ye)==null?void 0:e(this.editor.textarea,this.editor.textarea.selectionStart))||[];Object.entries(this.buttons).forEach(([n,i])=>{if(n==="viewMode")return;let r=!1;switch(n){case"bold":r=t.includes("bold");break;case"italic":r=t.includes("italic");break;case"code":r=!1;break;case"bulletList":r=t.includes("bullet-list");break;case"orderedList":r=t.includes("numbered-list");break;case"taskList":r=t.includes("task-list");break;case"quote":r=t.includes("quote");break;case"h1":r=t.includes("header");break;case"h2":r=t.includes("header-2");break;case"h3":r=t.includes("header-3");break}i.classList.toggle("active",r),i.setAttribute("aria-pressed",r.toString())})}catch(t){}}destroy(){this.container&&(this.handleDocumentClick&&document.removeEventListener("click",this.handleDocumentClick),Object.values(this.buttons).forEach(e=>{e._clickHandler&&(e.removeEventListener("click",e._clickHandler),delete e._clickHandler)}),this.container.remove(),this.container=null,this.buttons={})}};var J=class{constructor(e){this.editor=e,this.tooltip=null,this.currentLink=null,this.hideTimeout=null,this.visibilityChangeHandler=null,this.useFloatingUI=!1,this.floatingUI=null,this.init()}async init(){if(!(CSS.supports("position-anchor: --x")&&CSS.supports("position-area: center")))try{let t=new Function("url","return import(url)"),{computePosition:n,offset:i,shift:r,flip:s}=await t("https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.4/+esm");this.floatingUI={computePosition:n,offset:i,shift:r,flip:s},this.useFloatingUI=!0}catch(t){console.warn("Failed to load Floating UI fallback:",t),this.floatingUI=null,this.useFloatingUI=!1}this.createTooltip(),this.editor.textarea.addEventListener("selectionchange",()=>this.checkCursorPosition()),this.editor.textarea.addEventListener("keyup",t=>{(t.key.includes("Arrow")||t.key==="Home"||t.key==="End")&&this.checkCursorPosition()}),this.editor.textarea.addEventListener("input",()=>this.hide()),this.editor.textarea.addEventListener("scroll",()=>{this.useFloatingUI&&this.currentLink?this.showWithFloatingUI(this.currentLink):this.hide()}),this.editor.textarea.addEventListener("blur",()=>this.hide()),this.visibilityChangeHandler=()=>{document.hidden&&this.hide()},document.addEventListener("visibilitychange",this.visibilityChangeHandler),this.tooltip.addEventListener("mouseenter",()=>this.cancelHide())}createTooltip(){this.tooltip=document.createElement("div"),this.tooltip.className="overtype-link-tooltip",this.tooltip.innerHTML=`
|
|
886
886
|
<span style="display: flex; align-items: center; gap: 6px;">
|
|
887
887
|
<svg width="12" height="12" viewBox="0 0 20 20" fill="currentColor" style="flex-shrink: 0;">
|
|
888
888
|
<path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"></path>
|
|
@@ -940,7 +940,7 @@ ${a}`:r;if(d){let L=o.value[o.selectionStart-1];o.selectionStart!==0&&L!=null&&!
|
|
|
940
940
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" fill="none"></path>
|
|
941
941
|
<circle cx="12" cy="12" r="3" fill="none"></circle>
|
|
942
942
|
</svg>`;var x={bold:{name:"bold",icon:Le,title:"Bold (Ctrl+B)",action:({editor:o,event:e})=>{U(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},italic:{name:"italic",icon:Se,title:"Italic (Ctrl+I)",action:({editor:o,event:e})=>{D(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},code:{name:"code",icon:$e,title:"Inline Code",action:({editor:o,event:e})=>{ue(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},separator:{name:"separator"},link:{name:"link",icon:Ae,title:"Insert Link",action:({editor:o,event:e})=>{q(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},h1:{name:"h1",icon:Ee,title:"Heading 1",action:({editor:o,event:e})=>{fe(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},h2:{name:"h2",icon:Ce,title:"Heading 2",action:({editor:o,event:e})=>{ge(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},h3:{name:"h3",icon:Te,title:"Heading 3",action:({editor:o,event:e})=>{ve(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},bulletList:{name:"bulletList",icon:He,title:"Bullet List",action:({editor:o,event:e})=>{W(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},orderedList:{name:"orderedList",icon:Me,title:"Numbered List",action:({editor:o,event:e})=>{K(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},taskList:{name:"taskList",icon:Pe,title:"Task List",action:({editor:o,event:e})=>{X&&(X(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0})))}},quote:{name:"quote",icon:Ie,title:"Quote",action:({editor:o,event:e})=>{me(o.textarea),o.textarea.dispatchEvent(new Event("input",{bubbles:!0}))}},viewMode:{name:"viewMode",icon:Be,title:"View mode"}},G=[x.bold,x.italic,x.code,x.separator,x.link,x.separator,x.h1,x.h2,x.h3,x.separator,x.bulletList,x.orderedList,x.taskList,x.separator,x.quote,x.separator,x.viewMode];var w=class w{constructor(e,t={}){let n;if(typeof e=="string"){if(n=document.querySelectorAll(e),n.length===0)throw new Error(`No elements found for selector: ${e}`);n=Array.from(n)}else if(e instanceof Element)n=[e];else if(e instanceof NodeList)n=Array.from(e);else if(Array.isArray(e))n=e;else throw new Error("Invalid target: must be selector string, Element, NodeList, or Array");return n.map(r=>{if(r.overTypeInstance)return r.overTypeInstance.reinit(t),r.overTypeInstance;let s=Object.create(w.prototype);return s._init(r,t),r.overTypeInstance=s,w.instances.set(r,s),s})}_init(e,t={}){this.element=e,this.instanceTheme=t.theme||null,this.options=this._mergeOptions(t),this.instanceId=++w.instanceCount,this.initialized=!1,w.injectStyles(),w.initGlobalListeners();let n=e.querySelector(".overtype-container"),i=e.querySelector(".overtype-wrapper");n||i?this._recoverFromDOM(n,i):this._buildFromScratch(),this.shortcuts=new O(this),this.linkTooltip=new J(this),requestAnimationFrame(()=>{requestAnimationFrame(()=>{this.textarea.scrollTop=this.preview.scrollTop,this.textarea.scrollLeft=this.preview.scrollLeft})}),this.initialized=!0,this.options.onChange&&this.options.onChange(this.getValue(),this)}_mergeOptions(e){let t={fontSize:"14px",lineHeight:1.6,fontFamily:'"SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Code", Consolas, "Roboto Mono", "Noto Sans Mono", "Droid Sans Mono", "Ubuntu Mono", "DejaVu Sans Mono", "Liberation Mono", "Courier New", Courier, monospace',padding:"16px",mobile:{fontSize:"16px",padding:"12px",lineHeight:1.5},textareaProps:{},autofocus:!1,autoResize:!1,minHeight:"100px",maxHeight:null,placeholder:"Start typing...",value:"",onChange:null,onKeydown:null,showActiveLineRaw:!1,showStats:!1,toolbar:!1,toolbarButtons:null,statsFormatter:null,smartLists:!0,codeHighlighter:null},{theme:n,colors:i,...r}=e;return{...t,...r}}_recoverFromDOM(e,t){if(e&&e.classList.contains("overtype-container"))this.container=e,this.wrapper=e.querySelector(".overtype-wrapper");else if(t){this.wrapper=t,this.container=document.createElement("div"),this.container.className="overtype-container";let n=this.instanceTheme||w.currentTheme||P,i=typeof n=="string"?n:n.name;if(i&&this.container.setAttribute("data-theme",i),this.instanceTheme){let r=typeof this.instanceTheme=="string"?B(this.instanceTheme):this.instanceTheme;if(r&&r.colors){let s=N(r.colors);this.container.style.cssText+=s}}t.parentNode.insertBefore(this.container,t),this.container.appendChild(t)}if(!this.wrapper){e&&e.remove(),t&&t.remove(),this._buildFromScratch();return}if(this.textarea=this.wrapper.querySelector(".overtype-input"),this.preview=this.wrapper.querySelector(".overtype-preview"),!this.textarea||!this.preview){this.container.remove(),this._buildFromScratch();return}this.wrapper._instance=this,this.options.fontSize&&this.wrapper.style.setProperty("--instance-font-size",this.options.fontSize),this.options.lineHeight&&this.wrapper.style.setProperty("--instance-line-height",String(this.options.lineHeight)),this.options.padding&&this.wrapper.style.setProperty("--instance-padding",this.options.padding),this._configureTextarea(),this._applyOptions()}_buildFromScratch(){let e=this._extractContent();this.element.innerHTML="",this._createDOM(),(e||this.options.value)&&this.setValue(e||this.options.value),this._applyOptions()}_extractContent(){let e=this.element.querySelector(".overtype-input");return e?e.value:this.element.textContent||""}_createDOM(){this.container=document.createElement("div"),this.container.className="overtype-container";let e=this.instanceTheme||w.currentTheme||P,t=typeof e=="string"?e:e.name;if(t&&this.container.setAttribute("data-theme",t),this.instanceTheme){let n=typeof this.instanceTheme=="string"?B(this.instanceTheme):this.instanceTheme;if(n&&n.colors){let i=N(n.colors);this.container.style.cssText+=i}}this.wrapper=document.createElement("div"),this.wrapper.className="overtype-wrapper",this.options.fontSize&&this.wrapper.style.setProperty("--instance-font-size",this.options.fontSize),this.options.lineHeight&&this.wrapper.style.setProperty("--instance-line-height",String(this.options.lineHeight)),this.options.padding&&this.wrapper.style.setProperty("--instance-padding",this.options.padding),this.wrapper._instance=this,this.textarea=document.createElement("textarea"),this.textarea.className="overtype-input",this.textarea.placeholder=this.options.placeholder,this._configureTextarea(),this.options.textareaProps&&Object.entries(this.options.textareaProps).forEach(([n,i])=>{n==="className"||n==="class"?this.textarea.className+=" "+i:n==="style"&&typeof i=="object"?Object.assign(this.textarea.style,i):this.textarea.setAttribute(n,i)}),this.preview=document.createElement("div"),this.preview.className="overtype-preview",this.preview.setAttribute("aria-hidden","true"),this.wrapper.appendChild(this.textarea),this.wrapper.appendChild(this.preview),this.container.appendChild(this.wrapper),this.options.showStats&&(this.statsBar=document.createElement("div"),this.statsBar.className="overtype-stats",this.container.appendChild(this.statsBar),this._updateStats()),this.element.appendChild(this.container),this.options.autoResize?this._setupAutoResize():this.container.classList.remove("overtype-auto-resize")}_configureTextarea(){this.textarea.setAttribute("autocomplete","off"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.setAttribute("data-gramm","false"),this.textarea.setAttribute("data-gramm_editor","false"),this.textarea.setAttribute("data-enable-grammarly","false")}_createToolbar(){let e=this.options.toolbarButtons||G;this.toolbar=new Z(this,{toolbarButtons:e}),this.toolbar.create(),this._toolbarSelectionListener=()=>{this.toolbar&&this.toolbar.updateButtonStates()},this._toolbarInputListener=()=>{this.toolbar&&this.toolbar.updateButtonStates()},this.textarea.addEventListener("selectionchange",this._toolbarSelectionListener),this.textarea.addEventListener("input",this._toolbarInputListener)}_cleanupToolbarListeners(){this._toolbarSelectionListener&&(this.textarea.removeEventListener("selectionchange",this._toolbarSelectionListener),this._toolbarSelectionListener=null),this._toolbarInputListener&&(this.textarea.removeEventListener("input",this._toolbarInputListener),this._toolbarInputListener=null)}_applyOptions(){this.options.autofocus&&this.textarea.focus(),this.options.autoResize?this.container.classList.contains("overtype-auto-resize")||this._setupAutoResize():this.container.classList.remove("overtype-auto-resize"),this.options.toolbar&&!this.toolbar?this._createToolbar():!this.options.toolbar&&this.toolbar&&(this._cleanupToolbarListeners(),this.toolbar.destroy(),this.toolbar=null),this.updatePreview()}updatePreview(){let e=this.textarea.value,t=this.textarea.selectionStart,n=this._getCurrentLine(e,t),i=this.container.dataset.mode==="preview",r=S.parse(e,n,this.options.showActiveLineRaw,this.options.codeHighlighter,i);this.preview.innerHTML=r||'<span style="color: #808080;">Start typing...</span>',this._applyCodeBlockBackgrounds(),this.options.showStats&&this.statsBar&&this._updateStats(),this.options.onChange&&this.initialized&&this.options.onChange(e,this)}_applyCodeBlockBackgrounds(){let e=this.preview.querySelectorAll(".code-fence");for(let t=0;t<e.length-1;t+=2){let n=e[t],i=e[t+1],r=n.parentElement,s=i.parentElement;!r||!s||(n.style.display="block",i.style.display="block",r.classList.add("code-block-line"),s.classList.add("code-block-line"))}}_getCurrentLine(e,t){return e.substring(0,t).split(`
|
|
943
|
-
`).length-1}handleInput(e){this.updatePreview()}handleKeydown(e){if(e.key==="Tab"){
|
|
943
|
+
`).length-1}handleInput(e){this.updatePreview()}handleKeydown(e){if(e.key==="Tab"){let n=this.textarea.selectionStart,i=this.textarea.selectionEnd,r=this.textarea.value;if(e.shiftKey&&n===i)return;if(e.preventDefault(),n!==i&&e.shiftKey){let s=r.substring(0,n),a=r.substring(n,i),p=r.substring(i),l=a.split(`
|
|
944
944
|
`).map(c=>c.replace(/^ /,"")).join(`
|
|
945
945
|
`);document.execCommand?(this.textarea.setSelectionRange(n,i),document.execCommand("insertText",!1,l)):(this.textarea.value=s+l+p,this.textarea.selectionStart=n,this.textarea.selectionEnd=n+l.length)}else if(n!==i){let s=r.substring(0,n),a=r.substring(n,i),p=r.substring(i),l=a.split(`
|
|
946
946
|
`).map(c=>" "+c).join(`
|
|
@@ -965,5 +965,7 @@ ${a}`:r;if(d){let L=o.value[o.selectionStart-1];o.selectionStart!==0&&L!=null&&!
|
|
|
965
965
|
|
|
966
966
|
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
967
967
|
window.OverType = OverType.default ? OverType.default : OverType;
|
|
968
|
+
window.toolbarButtons = OverType.toolbarButtons;
|
|
969
|
+
window.defaultToolbarButtons = OverType.defaultToolbarButtons;
|
|
968
970
|
}
|
|
969
971
|
|
package/package.json
CHANGED
package/src/overtype.js
CHANGED
|
@@ -561,12 +561,17 @@ class OverType {
|
|
|
561
561
|
handleKeydown(event) {
|
|
562
562
|
// Handle Tab key to prevent focus loss and insert spaces
|
|
563
563
|
if (event.key === 'Tab') {
|
|
564
|
-
event.preventDefault();
|
|
565
|
-
|
|
566
564
|
const start = this.textarea.selectionStart;
|
|
567
565
|
const end = this.textarea.selectionEnd;
|
|
568
566
|
const value = this.textarea.value;
|
|
569
|
-
|
|
567
|
+
|
|
568
|
+
// If Shift+Tab without a selection, allow default behavior (navigate to previous element)
|
|
569
|
+
if (event.shiftKey && start === end) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
event.preventDefault();
|
|
574
|
+
|
|
570
575
|
// If there's a selection, indent/outdent based on shift key
|
|
571
576
|
if (start !== end && event.shiftKey) {
|
|
572
577
|
// Outdent: remove 2 spaces from start of each selected line
|
package/src/parser.js
CHANGED
|
@@ -97,7 +97,7 @@ export class MarkdownParser {
|
|
|
97
97
|
* @returns {string} Parsed bullet list item
|
|
98
98
|
*/
|
|
99
99
|
static parseBulletList(html) {
|
|
100
|
-
return html.replace(/^((?: )*)([
|
|
100
|
+
return html.replace(/^((?: )*)([-*+])\s(.+)$/, (match, indent, marker, content) => {
|
|
101
101
|
return `${indent}<li class="bullet-list"><span class="syntax-marker">${marker} </span>${content}</li>`;
|
|
102
102
|
});
|
|
103
103
|
}
|
|
@@ -165,12 +165,13 @@ export class MarkdownParser {
|
|
|
165
165
|
*/
|
|
166
166
|
static parseItalic(html) {
|
|
167
167
|
// Single asterisk - must not be adjacent to other asterisks
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
// Also must not be inside a syntax-marker span (to avoid matching bullet list markers)
|
|
169
|
+
html = html.replace(/(?<![\*>])\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
|
|
170
|
+
|
|
170
171
|
// Single underscore - must be at word boundaries to avoid matching inside words
|
|
171
172
|
// This prevents matching underscores in the middle of words like "bold_with_underscore"
|
|
172
173
|
html = html.replace(/(?<=^|\s)_(?!_)(.+?)(?<!_)_(?!_)(?=\s|$)/g, '<em><span class="syntax-marker">_</span>$1<span class="syntax-marker">_</span></em>');
|
|
173
|
-
|
|
174
|
+
|
|
174
175
|
return html;
|
|
175
176
|
}
|
|
176
177
|
|
package/src/toolbar.js
CHANGED
|
@@ -112,6 +112,34 @@ export class Toolbar {
|
|
|
112
112
|
return button;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Handle button action programmatically (used by keyboard shortcuts)
|
|
117
|
+
* @param {Object} buttonConfig - Button configuration object with action function
|
|
118
|
+
*/
|
|
119
|
+
async handleAction(buttonConfig) {
|
|
120
|
+
// Focus textarea before action
|
|
121
|
+
this.editor.textarea.focus();
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
if (buttonConfig.action) {
|
|
125
|
+
// Call action with consistent context object
|
|
126
|
+
await buttonConfig.action({
|
|
127
|
+
editor: this.editor,
|
|
128
|
+
getValue: () => this.editor.getValue(),
|
|
129
|
+
setValue: (value) => this.editor.setValue(value),
|
|
130
|
+
event: null
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(`Action "${buttonConfig.name}" error:`, error);
|
|
135
|
+
|
|
136
|
+
// Dispatch error event
|
|
137
|
+
this.editor.wrapper.dispatchEvent(new CustomEvent('button-error', {
|
|
138
|
+
detail: { buttonName: buttonConfig.name, error }
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
/**
|
|
116
144
|
* Sanitize SVG to prevent XSS
|
|
117
145
|
*/
|