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.
@@ -1,11 +1,11 @@
1
1
  /**
2
- * OverType v2.0.5
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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return e.replace(/[&<>"']/g,n=>t[n])}static preserveIndentation(e,t){let i=t.match(/^(\s*)/)[1].replace(/ /g,"&nbsp;");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(/^&gt; (.+)$/,(t,n)=>`<span class="blockquote"><span class="syntax-marker">&gt;</span> ${n}</span>`)}static parseBulletList(e){return e.replace(/^((?:&nbsp;)*)([-*])\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(/^((?:&nbsp;)*)-\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(/^((?:&nbsp;)*)(\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>&nbsp;</div>":`<div>${n}</div>`)}static parse(e,t=-1,n=!1,i,r=!1){this.resetLinkIndex();let s=e.split(`
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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return e.replace(/[&<>"']/g,n=>t[n])}static preserveIndentation(e,t){let i=t.match(/^(\s*)/)[1].replace(/ /g,"&nbsp;");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(/^&gt; (.+)$/,(t,n)=>`<span class="blockquote"><span class="syntax-marker">&gt;</span> ${n}</span>`)}static parseBulletList(e){return e.replace(/^((?:&nbsp;)*)([-*+])\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(/^((?:&nbsp;)*)-\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(/^((?:&nbsp;)*)(\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>&nbsp;</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)||"&nbsp;"}</div>`;if(/^```[^`]*$/.test(l))return a=!a,this.parseLine(l,r);if(a){let u=this.escapeHtml(l);return`<div>${this.preserveIndentation(u,l)||"&nbsp;"}</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>(?:&nbsp;)*<li class="bullet-list">.*?<\/li><\/div>\s*)+)/gs,r=>{let s=r.match(/<div>(?:&nbsp;)*<li class="bullet-list">.*?<\/li><\/div>/gs)||[];return s.length>0?"<ul>"+s.map(p=>{let d=p.match(/<div>((?:&nbsp;)*)<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>(?:&nbsp;)*<li class="ordered-list">.*?<\/li><\/div>\s*)+)/gs,r=>{let s=r.match(/<div>(?:&nbsp;)*<li class="ordered-list">.*?<\/li><\/div>/gs)||[];return s.length>0?"<ol>"+s.map(p=>{let d=p.match(/<div>((?:&nbsp;)*)<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(/&nbsp;/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"){e.preventDefault();let n=this.textarea.selectionStart,i=this.textarea.selectionEnd,r=this.textarea.value;if(n!==i&&e.shiftKey){let s=r.substring(0,n),a=r.substring(n,i),p=r.substring(i),l=a.split(`
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overtype",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay",
5
5
  "main": "dist/overtype.cjs",
6
6
  "module": "dist/overtype.esm.js",
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(/^((?:&nbsp;)*)([-*])\s(.+)$/, (match, indent, marker, content) => {
100
+ return html.replace(/^((?:&nbsp;)*)([-*+])\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
- html = html.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, '<em><span class="syntax-marker">*</span>$1<span class="syntax-marker">*</span></em>');
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
  */