@notectl/core 2.0.4 → 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/dist/chunks/{CaretNavigationPlugin-mQ1nxJ6m.mjs → CaretNavigationPlugin-C-6zNBbl.mjs} +2 -2
- package/dist/chunks/{CaretNavigationPlugin-mQ1nxJ6m.mjs.map → CaretNavigationPlugin-C-6zNBbl.mjs.map} +1 -1
- package/dist/chunks/{CodeBlockPlugin-oPGiMno0.mjs → CodeBlockPlugin-C8H0nfF6.mjs} +2 -2
- package/dist/chunks/{CodeBlockPlugin-oPGiMno0.mjs.map → CodeBlockPlugin-C8H0nfF6.mjs.map} +1 -1
- package/dist/chunks/FontPlugin-BDjM9v3y.mjs +2 -0
- package/dist/chunks/FontPlugin-BDjM9v3y.mjs.map +1 -0
- package/dist/chunks/{ListPlugin-CZgkinoS.mjs → ListPlugin-C_YjbVjT.mjs} +2 -2
- package/dist/chunks/ListPlugin-C_YjbVjT.mjs.map +1 -0
- package/dist/chunks/{SmartPastePlugin-BvYBbHSA.mjs → SmartPastePlugin-pejM1Zyk.mjs} +2 -2
- package/dist/chunks/{SmartPastePlugin-BvYBbHSA.mjs.map → SmartPastePlugin-pejM1Zyk.mjs.map} +1 -1
- package/dist/chunks/XmlDetector-B1y1NzDL.mjs +2 -0
- package/dist/chunks/XmlDetector-B1y1NzDL.mjs.map +1 -0
- package/dist/chunks/{XmlSupport-Czr8xQB1.mjs → XmlSupport-DRkIIe34.mjs} +2 -2
- package/dist/chunks/{XmlSupport-Czr8xQB1.mjs.map → XmlSupport-DRkIIe34.mjs.map} +1 -1
- package/dist/chunks/{full-B-bw4M7L.mjs → full-NtKyNKJ6.mjs} +2 -2
- package/dist/chunks/{full-B-bw4M7L.mjs.map → full-NtKyNKJ6.mjs.map} +1 -1
- package/dist/chunks/minimal-gSvMMQTU.mjs +2 -0
- package/dist/chunks/{minimal-CMt909kl.mjs.map → minimal-gSvMMQTU.mjs.map} +1 -1
- package/dist/chunks/src-CHVVMZB_.mjs +2 -0
- package/dist/chunks/src-CHVVMZB_.mjs.map +1 -0
- package/dist/editor/styles/list.d.ts +1 -1
- package/dist/editor/styles/list.d.ts.map +1 -1
- package/dist/full.mjs +1 -1
- package/dist/model/KeymapRegistry.d.ts.map +1 -1
- package/dist/notectl-core.mjs +1 -1
- package/dist/notectl-core.umd.js +1 -1
- package/dist/notectl-core.umd.js.map +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font/FontPlugin.d.ts.map +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/language.mjs +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/smart-paste/detectors/JavaDetector.d.ts.map +1 -1
- package/dist/plugins/smart-paste.mjs +1 -1
- package/dist/presets/full.mjs +1 -1
- package/dist/presets/minimal.mjs +1 -1
- package/dist/presets.mjs +1 -1
- package/dist/view/BlockWrapperManagement.d.ts +6 -4
- package/dist/view/BlockWrapperManagement.d.ts.map +1 -1
- package/dist/view/CaretNavigation.d.ts +5 -4
- package/dist/view/CaretNavigation.d.ts.map +1 -1
- package/dist/view/EditorView.d.ts +9 -1
- package/dist/view/EditorView.d.ts.map +1 -1
- package/dist/view/EditorViewEvents.d.ts +1 -1
- package/dist/view/EditorViewEvents.d.ts.map +1 -1
- package/dist/view/Reconciler.d.ts.map +1 -1
- package/package.json +8 -8
- package/dist/chunks/FontPlugin-DYntd3gN.mjs +0 -2
- package/dist/chunks/FontPlugin-DYntd3gN.mjs.map +0 -1
- package/dist/chunks/ListPlugin-CZgkinoS.mjs.map +0 -1
- package/dist/chunks/XmlDetector-BiWxa0RJ.mjs +0 -2
- package/dist/chunks/XmlDetector-BiWxa0RJ.mjs.map +0 -1
- package/dist/chunks/minimal-CMt909kl.mjs +0 -2
- package/dist/chunks/src-dxsvbiql.mjs +0 -2
- package/dist/chunks/src-dxsvbiql.mjs.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as t,m as e}from"./Document-DnPKbcPE.mjs";import{d as n,o as i,t as s,u as c}from"./Selection-B14e7gd0.mjs";import{a as o,t as r}from"./TypeBrands-BQR8dEYU.mjs";import{r as l}from"./AttrRegistry-DvqbnJ6T.mjs";import{n as a}from"./DomUtils-BkyCg-OO.mjs";import{t as d}from"./RangeIterator-DEuX1xKV.mjs";import{s as u}from"./StyleRuntime-Bwde5icB.mjs";import{i as p}from"./PluginHelpers-BZU5rE44.mjs";function m(t,e,n){return"checklist"===t?{listType:t,indent:e,checked:n??!1}:{listType:t,indent:e}}function h(t,e){const i=t.getState(),s=i.selection;return!!c(s)&&(s.anchor.blockId!==s.head.blockId?function(t,e,i){const s=e.selection;if(!c(s))return!1;const r=n(s,e.getBlockOrder()),a=g(e,r,i),u=e.transaction("command");return d(e,r,t=>{const n=e.getBlock(t);if(n)if(a)u.setBlockType(t,o("paragraph"));else{const e=l(n,"list_item")?n.attrs.indent:0,s=l(n,"list_item")&&"checklist"===n.attrs.listType&&n.attrs.checked;u.setBlockType(t,o("list_item"),m(i,e,s))}}),u.setSelection(s),t.dispatch(u.build()),!0}(t,i,e):function(t,e,n){const i=e.selection;if(!c(i))return!1;const s=e.getBlock(i.anchor.blockId);if(!s)return!1;if("list_item"===s.type&&s.attrs?.listType===n){const n=e.transaction("command").setBlockType(i.anchor.blockId,o("paragraph")).setSelection(i).build();return t.dispatch(n),!0}const r=m(n,l(s,"list_item")?s.attrs.indent:0),a=e.transaction("command").setBlockType(i.anchor.blockId,o("list_item"),r).setSelection(i).build();return t.dispatch(a),!0}(t,i,e))}function k(t,e,i){const s=t.getState();if(!c(s.selection))return!1;if(s.selection.anchor.blockId!==s.selection.head.blockId)return function(t,e,i,s){const r=e.selection;if(!c(r))return!1;const a=n(r,e.getBlockOrder()),u=e.transaction("command");let p=!1;return d(e,a,t=>{const n=e.getBlock(t);if(!n||!l(n,"list_item"))return;const c=n.attrs.indent+i;c<0||c>s||(u.setBlockType(t,o("list_item"),m(n.attrs.listType,c,n.attrs.checked)),p=!0)}),!!p&&(u.setSelection(r),t.dispatch(u.build()),!0)}(t,s,e,i);const r=s.selection,a=s.getBlock(r.anchor.blockId);if(!a||!l(a,"list_item"))return!1;const u=a.attrs.indent+e;if(u<0||u>i)return!1;const p=m(a.attrs.listType,u,a.attrs.checked),h=s.transaction("command").setBlockType(r.anchor.blockId,o("list_item"),p).setSelection(r).build();return t.dispatch(h),!0}function f(t,e,n){if(t.isReadOnly()&&!e)return!1;const i=t.getState(),s=n??(c(i.selection)?i.selection.anchor.blockId:null);if(!s)return!1;const r=i.getBlock(s);if(!r||!l(r,"list_item")||"checklist"!==r.attrs.listType)return!1;const a=m(r.attrs.listType,r.attrs.indent,!r.attrs.checked),d=i.transaction("command").setBlockType(s,o("list_item"),a).setSelection(i.selection).build();return t.dispatch(d),!0}function g(t,e,n){let i=!0;return d(t,e,e=>{const s=t.getBlock(e);s&&"list_item"===s.type&&s.attrs?.listType===n||(i=!1)}),i}var b=[{type:"bullet",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>',inputPattern:/^[-*] $/,inputPrefix:"- "},{type:"ordered",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></svg>',inputPattern:/^\d+\. $/,inputPrefix:"1. "},{type:"checklist",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 7h-9v2h9V7zm0 8h-9v2h9v-2zM5.54 11L2 7.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L5.54 11zm0 8L2 15.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L5.54 19z"/></svg>',inputPattern:/^\[[ x]] $/,inputPrefix:"[ ] "}];function y(t,e){const n=t.getState();if(!c(n.selection))return!1;const s=n.selection;if(!i(s))return!1;const o=n.getBlock(s.anchor.blockId);return!(!o||"list_item"!==o.type)&&e({state:n,block:o,blockId:s.anchor.blockId,offset:s.anchor.offset})}var v={bulletList:"Bullet List",numberedList:"Numbered List",checklist:"Checklist"},x=/* @__PURE__ */Object.assign({"./locales/ar.ts":()=>import("./ar-Cp9qv7la.mjs"),"./locales/de.ts":()=>import("./de-CF7zshOE.mjs"),"./locales/es.ts":()=>import("./es-5c_5O95N.mjs"),"./locales/fr.ts":()=>import("./fr-BushvDBo.mjs"),"./locales/hi.ts":()=>import("./hi-Btg6DNgW.mjs"),"./locales/pt.ts":()=>import("./pt-BM8sUzpI.mjs"),"./locales/ru.ts":()=>import("./ru-DkP8YLYT.mjs"),"./locales/zh.ts":()=>import("./zh-BBOheLpv.mjs")});async function I(t){if("en"===t)return v;const e=x[`./locales/${t}.ts`];if(!e)return v;try{return(await e()).default}catch{return v}}function T(t,e){return{bullet:e.bulletList,ordered:e.numberedList,checklist:e.checklist}[t]}function B(t,e){if(!c(t.selection))return!1;if(t.selection.anchor.blockId!==t.selection.head.blockId)return g(t,n(t.selection,t.getBlockOrder()),e);const i=t.getBlock(t.selection.anchor.blockId);return"list_item"===i?.type&&i.attrs?.listType===e}var L={types:["bullet","ordered","checklist"],maxIndent:4},S=class{id="list";name="List";priority=35;config;locale;context=null;checkboxClickHandler=null;constructor(t){this.config={...L,...t}}async init(n){this.context=n,this.locale=await p(n,this.config.locale,v,I);const r=this.getEnabledTypes();n.registerStyleSheet("\n/* List Wrappers — suppress native markers, we use ::before pseudo-elements */\n.notectl-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Ordered List — each <ol> wrapper resets the counter */\n.notectl-list--ordered {\n\tcounter-reset: notectl-ordered;\n}\n\n/* List Items */\n.notectl-list-item {\n\tmargin: 0;\n\tpadding: 2px 0;\n\tpadding-inline-start: 24px;\n\tmin-height: 1.6em;\n\tposition: relative;\n}\n\n.notectl-list-item::before {\n\tposition: absolute;\n\tinset-inline-start: 0;\n\tdisplay: inline-block;\n\twidth: 24px;\n\ttext-align: center;\n}\n\n/* Bullet List */\n.notectl-list-item--bullet::before {\n\tcontent: '\\2022';\n\tcolor: var(--notectl-fg);\n}\n\n/* Ordered List — counter increment per item */\n.notectl-list-item--ordered {\n\tcounter-increment: notectl-ordered;\n}\n\n.notectl-list-item--ordered::before {\n\tcontent: counter(notectl-ordered) '.';\n\tcolor: var(--notectl-fg);\n\tfont-size: 14px;\n}\n\n/* Checklist */\n.notectl-list-item--checklist::before {\n\tcontent: '\\2610';\n\tfont-size: 16px;\n\tcolor: var(--notectl-fg-muted);\n\tcursor: pointer;\n}\n\n.notectl-list-item--checklist[data-checked=\"true\"]::before {\n\tcontent: '\\2611';\n\tcolor: var(--notectl-success);\n}\n\n.notectl-list-item--checklist[data-checked=\"true\"] {\n\tcolor: var(--notectl-fg-muted);\n\ttext-decoration: line-through;\n}\n"),this.registerNodeSpec(n),function(t,e,n){for(const i of n)t.registerCommand(`toggleList:${i.type}`,()=>h(t,i.type));t.registerCommand("indentListItem",()=>k(t,1,e.maxIndent)),t.registerCommand("outdentListItem",()=>k(t,-1,e.maxIndent)),e.types.includes("checklist")&&t.registerCommand("toggleChecklistItem",()=>f(t,e.interactiveCheckboxes),{readonlyAllowed:!0}),t.registerKeymap({Tab:()=>k(t,1,e.maxIndent),"Shift-Tab":()=>k(t,-1,e.maxIndent)})}(n,this.config,r),function(n){n.registerKeymap({Enter:()=>function(n){return y(n,i=>{if(""===e(i.block)){const t=i.state.transaction("input").setBlockType(i.blockId,o("paragraph")).setSelection(i.state.selection).build();return n.dispatch(t),!0}const c=t(),r=m(l(i.block,"list_item")?i.block.attrs.listType:"bullet",l(i.block,"list_item")?i.block.attrs.indent:0),a=i.state.transaction("input").splitBlock(i.blockId,i.offset,c).setBlockType(c,o("list_item"),r).setSelection(s(c,0)).build();return n.dispatch(a),!0})}(n),Backspace:()=>function(t){return y(t,e=>{if(0!==e.offset)return!1;const n=e.state.transaction("input").setBlockType(e.blockId,o("paragraph")).setSelection(e.state.selection).build();return t.dispatch(n),!0})}(n)})}(n),function(t,e){for(const n of e)t.registerInputRule({pattern:n.inputPattern,handler:(t,e,r,l)=>{const a=t.selection;if(!c(a))return null;if(!i(a))return null;const d=t.getBlock(a.anchor.blockId);if(!d||"paragraph"!==d.type)return null;const u=e[0]??"",p=u.length,h=m(n.type,0,u.includes("[x]"));return t.transaction("input").deleteTextAt(a.anchor.blockId,r,r+p).setBlockType(a.anchor.blockId,o("list_item"),h).setSelection(s(a.anchor.blockId,0)).build()}})}(n,r),function(t,e,n){for(const i of e)t.registerToolbarItem({id:`list-${i.type}`,group:"block",icon:i.icon,label:T(i.type,n),command:`toggleList:${i.type}`,isActive:t=>B(t,i.type)})}(n,r,this.locale),this.registerCheckboxClickHandler(n)}destroy(){this.checkboxClickHandler&&this.context&&this.context.getContainer().removeEventListener("mousedown",this.checkboxClickHandler),this.checkboxClickHandler=null,this.context=null}registerNodeSpec(t){t.registerNodeSpec({type:"list_item",group:"block",content:{allow:["text"]},attrs:{listType:{default:"bullet"},indent:{default:0},checked:{default:!1}},toDOM(t){const e=t.attrs?.listType??"bullet",n=t.attrs?.indent??0,i=t.attrs?.checked??!1,s=a("li",t.id);return s.setAttribute("role","listitem"),s.setAttribute("data-list-type",e),s.setAttribute("data-indent",String(n)),s.className=`notectl-list-item notectl-list-item--${e}`,n>0&&u(s,"marginLeft",24*n+"px"),"checklist"===e&&(s.setAttribute("data-checked",String(i)),s.setAttribute("aria-checked",String(i))),s.setAttribute("aria-level",String(n+1)),s},wrapper(t){const e=t.attrs?.listType??"bullet";return{tag:"ordered"===e?"ol":"ul",key:`list-${e}`,className:`notectl-list notectl-list--${e}`,attrs:{role:"list"}}},toHTML(t,e){const n=t.attrs?.listType??"bullet",i=t.attrs?.checked??!1;if("checklist"===n){const t=i?" checked":"";return`<li role="checkbox" aria-checked="${String(i)}"><input type="checkbox" disabled${t}>${e||"<br>"}</li>`}return`<li>${e||"<br>"}</li>`},parseHTML:[{tag:"li"}],sanitize:{tags:["ul","ol","li","input"],attrs:["type","disabled","checked","role","aria-checked"]}})}registerCheckboxClickHandler(t){this.config.types.includes("checklist")&&(this.checkboxClickHandler=e=>{if(t.isReadOnly()&&!this.config.interactiveCheckboxes)return;const n=e.target;if(!(n instanceof HTMLElement))return;const i=n.closest(".notectl-list-item--checklist");if(!i)return;const s=i.getBoundingClientRect();if(("rtl"===getComputedStyle(i).direction?s.right-e.clientX:e.clientX-s.left)>=24)return;e.preventDefault();const c=i.getAttribute("data-block-id");c&&f(t,this.config.interactiveCheckboxes,r(c))},t.getContainer().addEventListener("mousedown",this.checkboxClickHandler))}getEnabledTypes(){return b.filter(t=>this.config.types.includes(t.type))}};export{v as n,I as r,S as t};
|
|
2
|
-
//# sourceMappingURL=ListPlugin-
|
|
1
|
+
import{c as t,m as e}from"./Document-DnPKbcPE.mjs";import{d as n,o as i,t as s,u as c}from"./Selection-B14e7gd0.mjs";import{a as o,t as r}from"./TypeBrands-BQR8dEYU.mjs";import{r as l}from"./AttrRegistry-DvqbnJ6T.mjs";import{n as a}from"./DomUtils-BkyCg-OO.mjs";import{t as d}from"./RangeIterator-DEuX1xKV.mjs";import{s as u}from"./StyleRuntime-Bwde5icB.mjs";import{i as p}from"./PluginHelpers-BZU5rE44.mjs";function m(t,e,n){return"checklist"===t?{listType:t,indent:e,checked:n??!1}:{listType:t,indent:e}}function h(t,e){const i=t.getState(),s=i.selection;return!!c(s)&&(s.anchor.blockId!==s.head.blockId?function(t,e,i){const s=e.selection;if(!c(s))return!1;const r=n(s,e.getBlockOrder()),a=g(e,r,i),u=e.transaction("command");return d(e,r,t=>{const n=e.getBlock(t);if(n)if(a)u.setBlockType(t,o("paragraph"));else{const e=l(n,"list_item")?n.attrs.indent:0,s=l(n,"list_item")&&"checklist"===n.attrs.listType&&n.attrs.checked;u.setBlockType(t,o("list_item"),m(i,e,s))}}),u.setSelection(s),t.dispatch(u.build()),!0}(t,i,e):function(t,e,n){const i=e.selection;if(!c(i))return!1;const s=e.getBlock(i.anchor.blockId);if(!s)return!1;if("list_item"===s.type&&s.attrs?.listType===n){const n=e.transaction("command").setBlockType(i.anchor.blockId,o("paragraph")).setSelection(i).build();return t.dispatch(n),!0}const r=m(n,l(s,"list_item")?s.attrs.indent:0),a=e.transaction("command").setBlockType(i.anchor.blockId,o("list_item"),r).setSelection(i).build();return t.dispatch(a),!0}(t,i,e))}function k(t,e,i){const s=t.getState();if(!c(s.selection))return!1;if(s.selection.anchor.blockId!==s.selection.head.blockId)return function(t,e,i,s){const r=e.selection;if(!c(r))return!1;const a=n(r,e.getBlockOrder()),u=e.transaction("command");let p=!1;return d(e,a,t=>{const n=e.getBlock(t);if(!n||!l(n,"list_item"))return;const c=n.attrs.indent+i;c<0||c>s||(u.setBlockType(t,o("list_item"),m(n.attrs.listType,c,n.attrs.checked)),p=!0)}),!!p&&(u.setSelection(r),t.dispatch(u.build()),!0)}(t,s,e,i);const r=s.selection,a=s.getBlock(r.anchor.blockId);if(!a||!l(a,"list_item"))return!1;const u=a.attrs.indent+e;if(u<0||u>i)return!1;const p=m(a.attrs.listType,u,a.attrs.checked),h=s.transaction("command").setBlockType(r.anchor.blockId,o("list_item"),p).setSelection(r).build();return t.dispatch(h),!0}function f(t,e,n){if(t.isReadOnly()&&!e)return!1;const i=t.getState(),s=n??(c(i.selection)?i.selection.anchor.blockId:null);if(!s)return!1;const r=i.getBlock(s);if(!r||!l(r,"list_item")||"checklist"!==r.attrs.listType)return!1;const a=m(r.attrs.listType,r.attrs.indent,!r.attrs.checked),d=i.transaction("command").setBlockType(s,o("list_item"),a).setSelection(i.selection).build();return t.dispatch(d),!0}function g(t,e,n){let i=!0;return d(t,e,e=>{const s=t.getBlock(e);s&&"list_item"===s.type&&s.attrs?.listType===n||(i=!1)}),i}var b=[{type:"bullet",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/></svg>',inputPattern:/^[-*] $/,inputPrefix:"- "},{type:"ordered",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/></svg>',inputPattern:/^\d+\. $/,inputPrefix:"1. "},{type:"checklist",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 7h-9v2h9V7zm0 8h-9v2h9v-2zM5.54 11L2 7.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L5.54 11zm0 8L2 15.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L5.54 19z"/></svg>',inputPattern:/^\[[ x]] $/,inputPrefix:"[ ] "}];function y(t,e){const n=t.getState();if(!c(n.selection))return!1;const s=n.selection;if(!i(s))return!1;const o=n.getBlock(s.anchor.blockId);return!(!o||"list_item"!==o.type)&&e({state:n,block:o,blockId:s.anchor.blockId,offset:s.anchor.offset})}var v={bulletList:"Bullet List",numberedList:"Numbered List",checklist:"Checklist"},x=/* @__PURE__ */Object.assign({"./locales/ar.ts":()=>import("./ar-Cp9qv7la.mjs"),"./locales/de.ts":()=>import("./de-CF7zshOE.mjs"),"./locales/es.ts":()=>import("./es-5c_5O95N.mjs"),"./locales/fr.ts":()=>import("./fr-BushvDBo.mjs"),"./locales/hi.ts":()=>import("./hi-Btg6DNgW.mjs"),"./locales/pt.ts":()=>import("./pt-BM8sUzpI.mjs"),"./locales/ru.ts":()=>import("./ru-DkP8YLYT.mjs"),"./locales/zh.ts":()=>import("./zh-BBOheLpv.mjs")});async function I(t){if("en"===t)return v;const e=x[`./locales/${t}.ts`];if(!e)return v;try{return(await e()).default}catch{return v}}function T(t,e){return{bullet:e.bulletList,ordered:e.numberedList,checklist:e.checklist}[t]}function B(t,e){if(!c(t.selection))return!1;if(t.selection.anchor.blockId!==t.selection.head.blockId)return g(t,n(t.selection,t.getBlockOrder()),e);const i=t.getBlock(t.selection.anchor.blockId);return"list_item"===i?.type&&i.attrs?.listType===e}var L={types:["bullet","ordered","checklist"],maxIndent:4},S=class{id="list";name="List";priority=35;config;locale;context=null;checkboxClickHandler=null;constructor(t){this.config={...L,...t}}async init(n){this.context=n,this.locale=await p(n,this.config.locale,v,I);const r=this.getEnabledTypes();n.registerStyleSheet("\n/* List Wrappers — suppress native markers, we use ::before pseudo-elements */\n.notectl-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Ordered List — each <ol> wrapper resets the counter */\n.notectl-list--ordered {\n\tcounter-reset: notectl-ordered;\n}\n\n/* List Items */\n.notectl-list-item {\n\tmargin: 0;\n\tpadding: 2px 0;\n\tpadding-inline-start: 24px;\n\tmin-height: 1.6em;\n\tposition: relative;\n}\n\n.notectl-list-item::before {\n\tposition: absolute;\n\tinset-inline-start: 0;\n\tdisplay: inline-block;\n\twidth: 24px;\n\ttext-align: center;\n\t-webkit-user-select: none;\n\tuser-select: none;\n\tpointer-events: none;\n}\n\n/* Bullet List */\n.notectl-list-item--bullet::before {\n\tcontent: '\\2022';\n\tcolor: var(--notectl-fg);\n}\n\n/* Ordered List — counter increment per item */\n.notectl-list-item--ordered {\n\tcounter-increment: notectl-ordered;\n}\n\n.notectl-list-item--ordered::before {\n\tcontent: counter(notectl-ordered) '.';\n\tcolor: var(--notectl-fg);\n\tfont-size: 14px;\n}\n\n/* Checklist */\n.notectl-list-item--checklist::before {\n\tcontent: '\\2610';\n\tfont-size: 16px;\n\tcolor: var(--notectl-fg-muted);\n\tcursor: pointer;\n\tpointer-events: auto;\n}\n\n.notectl-list-item--checklist[data-checked=\"true\"]::before {\n\tcontent: '\\2611';\n\tcolor: var(--notectl-success);\n}\n\n.notectl-list-item--checklist[data-checked=\"true\"] {\n\tcolor: var(--notectl-fg-muted);\n\ttext-decoration: line-through;\n}\n"),this.registerNodeSpec(n),function(t,e,n){for(const i of n)t.registerCommand(`toggleList:${i.type}`,()=>h(t,i.type));t.registerCommand("indentListItem",()=>k(t,1,e.maxIndent)),t.registerCommand("outdentListItem",()=>k(t,-1,e.maxIndent)),e.types.includes("checklist")&&t.registerCommand("toggleChecklistItem",()=>f(t,e.interactiveCheckboxes),{readonlyAllowed:!0}),t.registerKeymap({Tab:()=>k(t,1,e.maxIndent),"Shift-Tab":()=>k(t,-1,e.maxIndent)})}(n,this.config,r),function(n){n.registerKeymap({Enter:()=>function(n){return y(n,i=>{if(""===e(i.block)){const t=i.state.transaction("input").setBlockType(i.blockId,o("paragraph")).setSelection(i.state.selection).build();return n.dispatch(t),!0}const c=t(),r=m(l(i.block,"list_item")?i.block.attrs.listType:"bullet",l(i.block,"list_item")?i.block.attrs.indent:0),a=i.state.transaction("input").splitBlock(i.blockId,i.offset,c).setBlockType(c,o("list_item"),r).setSelection(s(c,0)).build();return n.dispatch(a),!0})}(n),Backspace:()=>function(t){return y(t,e=>{if(0!==e.offset)return!1;const n=e.state.transaction("input").setBlockType(e.blockId,o("paragraph")).setSelection(e.state.selection).build();return t.dispatch(n),!0})}(n)})}(n),function(t,e){for(const n of e)t.registerInputRule({pattern:n.inputPattern,handler:(t,e,r,l)=>{const a=t.selection;if(!c(a))return null;if(!i(a))return null;const d=t.getBlock(a.anchor.blockId);if(!d||"paragraph"!==d.type)return null;const u=e[0]??"",p=u.length,h=m(n.type,0,u.includes("[x]"));return t.transaction("input").deleteTextAt(a.anchor.blockId,r,r+p).setBlockType(a.anchor.blockId,o("list_item"),h).setSelection(s(a.anchor.blockId,0)).build()}})}(n,r),function(t,e,n){for(const i of e)t.registerToolbarItem({id:`list-${i.type}`,group:"block",icon:i.icon,label:T(i.type,n),command:`toggleList:${i.type}`,isActive:t=>B(t,i.type)})}(n,r,this.locale),this.registerCheckboxClickHandler(n)}destroy(){this.checkboxClickHandler&&this.context&&this.context.getContainer().removeEventListener("mousedown",this.checkboxClickHandler),this.checkboxClickHandler=null,this.context=null}registerNodeSpec(t){t.registerNodeSpec({type:"list_item",group:"block",content:{allow:["text"]},attrs:{listType:{default:"bullet"},indent:{default:0},checked:{default:!1}},toDOM(t){const e=t.attrs?.listType??"bullet",n=t.attrs?.indent??0,i=t.attrs?.checked??!1,s=a("li",t.id);return s.setAttribute("role","listitem"),s.setAttribute("data-list-type",e),s.setAttribute("data-indent",String(n)),s.className=`notectl-list-item notectl-list-item--${e}`,n>0&&u(s,"marginLeft",24*n+"px"),"checklist"===e&&(s.setAttribute("data-checked",String(i)),s.setAttribute("aria-checked",String(i))),s.setAttribute("aria-level",String(n+1)),s},wrapper(t){const e=t.attrs?.listType??"bullet";return{tag:"ordered"===e?"ol":"ul",key:`list-${e}`,className:`notectl-list notectl-list--${e}`,attrs:{role:"list"}}},toHTML(t,e){const n=t.attrs?.listType??"bullet",i=t.attrs?.checked??!1;return"checklist"===n?`<li role="checkbox" aria-checked="${String(i)}"><input type="checkbox" disabled${i?" checked":""}>${e||"<br>"}</li>`:`<li>${e||"<br>"}</li>`},parseHTML:[{tag:"li"}],sanitize:{tags:["ul","ol","li","input"],attrs:["type","disabled","checked","role","aria-checked"]}})}registerCheckboxClickHandler(t){this.config.types.includes("checklist")&&(this.checkboxClickHandler=e=>{if(t.isReadOnly()&&!this.config.interactiveCheckboxes)return;const n=e.target;if(!(n instanceof HTMLElement))return;const i=n.closest(".notectl-list-item--checklist");if(!i)return;const s=i.getBoundingClientRect();if(("rtl"===getComputedStyle(i).direction?s.right-e.clientX:e.clientX-s.left)>=24)return;e.preventDefault();const c=i.getAttribute("data-block-id");c&&f(t,this.config.interactiveCheckboxes,r(c))},t.getContainer().addEventListener("mousedown",this.checkboxClickHandler))}getEnabledTypes(){return b.filter(t=>this.config.types.includes(t.type))}};export{v as n,I as r,S as t};
|
|
2
|
+
//# sourceMappingURL=ListPlugin-C_YjbVjT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListPlugin-C_YjbVjT.mjs","names":[],"sources":["../../src/editor/styles/list.ts","../../src/plugins/list/ListAttrsFactory.ts","../../src/plugins/list/ListCommands.ts","../../src/plugins/list/ListDefinitions.ts","../../src/plugins/list/ListInputRules.ts","../../src/plugins/list/ListKeyboardHandlers.ts","../../src/plugins/list/ListLocale.ts","../../src/plugins/list/ListToolbarItems.ts","../../src/plugins/list/ListPlugin.ts"],"sourcesContent":["/** Width of the list marker / checkbox area in pixels. */\nexport const LIST_MARKER_WIDTH = 24;\n\n/** List styles — bullet, ordered, checklist items. */\nexport const LIST_CSS = `\n/* List Wrappers — suppress native markers, we use ::before pseudo-elements */\n.notectl-list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n/* Ordered List — each <ol> wrapper resets the counter */\n.notectl-list--ordered {\n\tcounter-reset: notectl-ordered;\n}\n\n/* List Items */\n.notectl-list-item {\n\tmargin: 0;\n\tpadding: 2px 0;\n\tpadding-inline-start: ${LIST_MARKER_WIDTH}px;\n\tmin-height: 1.6em;\n\tposition: relative;\n}\n\n.notectl-list-item::before {\n\tposition: absolute;\n\tinset-inline-start: 0;\n\tdisplay: inline-block;\n\twidth: ${LIST_MARKER_WIDTH}px;\n\ttext-align: center;\n\t-webkit-user-select: none;\n\tuser-select: none;\n\tpointer-events: none;\n}\n\n/* Bullet List */\n.notectl-list-item--bullet::before {\n\tcontent: '\\\\2022';\n\tcolor: var(--notectl-fg);\n}\n\n/* Ordered List — counter increment per item */\n.notectl-list-item--ordered {\n\tcounter-increment: notectl-ordered;\n}\n\n.notectl-list-item--ordered::before {\n\tcontent: counter(notectl-ordered) '.';\n\tcolor: var(--notectl-fg);\n\tfont-size: 14px;\n}\n\n/* Checklist */\n.notectl-list-item--checklist::before {\n\tcontent: '\\\\2610';\n\tfont-size: 16px;\n\tcolor: var(--notectl-fg-muted);\n\tcursor: pointer;\n\tpointer-events: auto;\n}\n\n.notectl-list-item--checklist[data-checked=\"true\"]::before {\n\tcontent: '\\\\2611';\n\tcolor: var(--notectl-success);\n}\n\n.notectl-list-item--checklist[data-checked=\"true\"] {\n\tcolor: var(--notectl-fg-muted);\n\ttext-decoration: line-through;\n}\n`;\n","/**\n * Factory for building list item block attributes.\n * Centralizes attribute construction to avoid repetitive inline object literals.\n */\n\nimport type { BlockAttrs } from '../../model/Document.js';\nimport type { ListType } from './ListPlugin.js';\n\n/** Builds a `BlockAttrs` object for a list_item block. */\nexport function buildListItemAttrs(\n\tlistType: ListType,\n\tindent: number,\n\tchecked?: boolean,\n): BlockAttrs {\n\tif (listType === 'checklist') {\n\t\treturn { listType, indent, checked: checked ?? false };\n\t}\n\treturn { listType, indent };\n}\n","/**\n * Command registration and implementations for list block types.\n * Handles toggle between list types and paragraph, indent/outdent,\n * and checklist toggle.\n */\n\nimport { forEachBlockIdInRange } from '../../commands/RangeIterator.js';\nimport { isNodeOfType } from '../../model/AttrRegistry.js';\nimport { isTextSelection, selectionRange } from '../../model/Selection.js';\nimport { type BlockId, nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { buildListItemAttrs } from './ListAttrsFactory.js';\nimport type { ListTypeDefinition } from './ListDefinitions.js';\nimport type { ListConfig, ListType } from './ListPlugin.js';\n\n/** Registers all list commands (toggleList:*, indent, outdent, toggleChecklistItem). */\nexport function registerListCommands(\n\tcontext: PluginContext,\n\tconfig: ListConfig,\n\tenabledTypes: readonly ListTypeDefinition[],\n): void {\n\tfor (const def of enabledTypes) {\n\t\tcontext.registerCommand(`toggleList:${def.type}`, () => {\n\t\t\treturn toggleList(context, def.type);\n\t\t});\n\t}\n\n\tcontext.registerCommand('indentListItem', () => changeIndent(context, 1, config.maxIndent));\n\n\tcontext.registerCommand('outdentListItem', () => changeIndent(context, -1, config.maxIndent));\n\n\tif (config.types.includes('checklist')) {\n\t\tcontext.registerCommand(\n\t\t\t'toggleChecklistItem',\n\t\t\t() => toggleChecked(context, config.interactiveCheckboxes),\n\t\t\t{ readonlyAllowed: true },\n\t\t);\n\t}\n\n\tcontext.registerKeymap({\n\t\tTab: () => changeIndent(context, 1, config.maxIndent),\n\t\t'Shift-Tab': () => changeIndent(context, -1, config.maxIndent),\n\t});\n}\n\n// --- Command Implementations ---\n\nfunction toggleList(context: PluginContext, listType: ListType): boolean {\n\tconst state: EditorState = context.getState();\n\tconst sel = state.selection;\n\tif (!isTextSelection(sel)) return false;\n\n\tif (sel.anchor.blockId !== sel.head.blockId) {\n\t\treturn toggleListRange(context, state, listType);\n\t}\n\treturn toggleListSingleBlock(context, state, listType);\n}\n\nfunction toggleListSingleBlock(\n\tcontext: PluginContext,\n\tstate: EditorState,\n\tlistType: ListType,\n): boolean {\n\tconst sel = state.selection;\n\tif (!isTextSelection(sel)) return false;\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block) return false;\n\n\tif (block.type === 'list_item' && block.attrs?.listType === listType) {\n\t\tconst tr = state\n\t\t\t.transaction('command')\n\t\t\t.setBlockType(sel.anchor.blockId, nodeType('paragraph'))\n\t\t\t.setSelection(sel)\n\t\t\t.build();\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t}\n\n\tconst existingIndent: number = isNodeOfType(block, 'list_item') ? block.attrs.indent : 0;\n\tconst attrs = buildListItemAttrs(listType, existingIndent);\n\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setBlockType(sel.anchor.blockId, nodeType('list_item'), attrs)\n\t\t.setSelection(sel)\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\nfunction toggleListRange(context: PluginContext, state: EditorState, listType: ListType): boolean {\n\tconst sel = state.selection;\n\tif (!isTextSelection(sel)) return false;\n\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst toggleOff: boolean = allBlocksMatchListType(state, range, listType);\n\tconst builder = state.transaction('command');\n\n\tforEachBlockIdInRange(state, range, (bid: BlockId) => {\n\t\tconst block = state.getBlock(bid);\n\t\tif (!block) return;\n\n\t\tif (toggleOff) {\n\t\t\tbuilder.setBlockType(bid, nodeType('paragraph'));\n\t\t} else {\n\t\t\tconst existingIndent: number = isNodeOfType(block, 'list_item') ? block.attrs.indent : 0;\n\t\t\tconst existingChecked: boolean =\n\t\t\t\tisNodeOfType(block, 'list_item') &&\n\t\t\t\tblock.attrs.listType === 'checklist' &&\n\t\t\t\tblock.attrs.checked;\n\t\t\tbuilder.setBlockType(\n\t\t\t\tbid,\n\t\t\t\tnodeType('list_item'),\n\t\t\t\tbuildListItemAttrs(listType, existingIndent, existingChecked),\n\t\t\t);\n\t\t}\n\t});\n\n\tbuilder.setSelection(sel);\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\n// --- Indent / Outdent ---\n\nfunction changeIndent(context: PluginContext, delta: 1 | -1, maxIndent: number): boolean {\n\tconst state: EditorState = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\n\tif (state.selection.anchor.blockId !== state.selection.head.blockId) {\n\t\treturn changeIndentRange(context, state, delta, maxIndent);\n\t}\n\n\tconst sel = state.selection;\n\tconst block = state.getBlock(sel.anchor.blockId);\n\tif (!block || !isNodeOfType(block, 'list_item')) return false;\n\n\tconst newIndent: number = block.attrs.indent + delta;\n\tif (newIndent < 0 || newIndent > maxIndent) return false;\n\n\tconst attrs = buildListItemAttrs(block.attrs.listType, newIndent, block.attrs.checked);\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setBlockType(sel.anchor.blockId, nodeType('list_item'), attrs)\n\t\t.setSelection(sel)\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\nfunction changeIndentRange(\n\tcontext: PluginContext,\n\tstate: EditorState,\n\tdelta: 1 | -1,\n\tmaxIndent: number,\n): boolean {\n\tconst sel = state.selection;\n\tif (!isTextSelection(sel)) return false;\n\n\tconst range = selectionRange(sel, state.getBlockOrder());\n\tconst builder = state.transaction('command');\n\tlet changed = false;\n\n\tforEachBlockIdInRange(state, range, (bid: BlockId) => {\n\t\tconst block = state.getBlock(bid);\n\t\tif (!block || !isNodeOfType(block, 'list_item')) return;\n\n\t\tconst newIndent: number = block.attrs.indent + delta;\n\t\tif (newIndent < 0 || newIndent > maxIndent) return;\n\n\t\tbuilder.setBlockType(\n\t\t\tbid,\n\t\t\tnodeType('list_item'),\n\t\t\tbuildListItemAttrs(block.attrs.listType, newIndent, block.attrs.checked),\n\t\t);\n\t\tchanged = true;\n\t});\n\n\tif (!changed) return false;\n\n\tbuilder.setSelection(sel);\n\tcontext.dispatch(builder.build());\n\treturn true;\n}\n\n// --- Checklist Toggle ---\n\n/** Toggles the checked state of a checklist item. Exported for use in checkbox click handler. */\nexport function toggleChecked(\n\tcontext: PluginContext,\n\tinteractiveCheckboxes?: boolean,\n\ttargetId?: BlockId,\n): boolean {\n\tif (context.isReadOnly() && !interactiveCheckboxes) return false;\n\n\tconst state: EditorState = context.getState();\n\tconst bid: BlockId | null =\n\t\ttargetId ?? (!isTextSelection(state.selection) ? null : state.selection.anchor.blockId);\n\tif (!bid) return false;\n\n\tconst block = state.getBlock(bid);\n\tif (!block || !isNodeOfType(block, 'list_item') || block.attrs.listType !== 'checklist') {\n\t\treturn false;\n\t}\n\n\tconst attrs = buildListItemAttrs(block.attrs.listType, block.attrs.indent, !block.attrs.checked);\n\n\tconst tr = state\n\t\t.transaction('command')\n\t\t.setBlockType(bid, nodeType('list_item'), attrs)\n\t\t.setSelection(state.selection)\n\t\t.build();\n\tcontext.dispatch(tr);\n\treturn true;\n}\n\n// --- Helpers ---\n\n/** Checks whether all blocks in a range are list items of the given type. Exported for toolbar. */\nexport function allBlocksMatchListType(\n\tstate: EditorState,\n\trange: ReturnType<typeof selectionRange>,\n\tlistType: ListType,\n): boolean {\n\tlet allMatch = true;\n\tforEachBlockIdInRange(state, range, (bid: BlockId) => {\n\t\tconst block = state.getBlock(bid);\n\t\tif (!block || block.type !== 'list_item' || block.attrs?.listType !== listType) {\n\t\t\tallMatch = false;\n\t\t}\n\t});\n\treturn allMatch;\n}\n","/**\n * List type metadata: icons, patterns, and type definitions.\n * Pure data constants shared across list plugin modules.\n */\n\nimport type { ListType } from './ListPlugin.js';\n\n// --- List Type Metadata ---\n\nexport interface ListTypeDefinition {\n\treadonly type: ListType;\n\treadonly icon: string;\n\treadonly inputPattern: RegExp;\n\treadonly inputPrefix: string;\n}\n\nconst BULLET_LIST_ICON =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z\"/></svg>';\nconst NUMBERED_LIST_ICON =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z\"/></svg>';\nconst CHECKLIST_ICON =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M22 7h-9v2h9V7zm0 8h-9v2h9v-2zM5.54 11L2 7.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L5.54 11zm0 8L2 15.46l1.41-1.41 2.12 2.12 4.24-4.24 1.41 1.41L5.54 19z\"/></svg>';\n\nexport const LIST_TYPE_DEFINITIONS: readonly ListTypeDefinition[] = [\n\t{\n\t\ttype: 'bullet',\n\t\ticon: BULLET_LIST_ICON,\n\t\tinputPattern: /^[-*] $/,\n\t\tinputPrefix: '- ',\n\t},\n\t{\n\t\ttype: 'ordered',\n\t\ticon: NUMBERED_LIST_ICON,\n\t\tinputPattern: /^\\d+\\. $/,\n\t\tinputPrefix: '1. ',\n\t},\n\t{\n\t\ttype: 'checklist',\n\t\ticon: CHECKLIST_ICON,\n\t\tinputPattern: /^\\[[ x]] $/,\n\t\tinputPrefix: '[ ] ',\n\t},\n];\n","/**\n * Input rule registration for list block types.\n * Converts `- `, `* `, `1. `, `[ ] `, `[x] ` at the start of a paragraph into list items.\n */\n\nimport { createCollapsedSelection, isCollapsed, isTextSelection } from '../../model/Selection.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { buildListItemAttrs } from './ListAttrsFactory.js';\nimport type { ListTypeDefinition } from './ListDefinitions.js';\n\n/** Registers markdown-style input rules for configured list types. */\nexport function registerListInputRules(\n\tcontext: PluginContext,\n\tenabledTypes: readonly ListTypeDefinition[],\n): void {\n\tfor (const def of enabledTypes) {\n\t\tcontext.registerInputRule({\n\t\t\tpattern: def.inputPattern,\n\t\t\thandler: (state, match, start, _end) => {\n\t\t\t\tconst sel = state.selection;\n\t\t\t\tif (!isTextSelection(sel)) return null;\n\t\t\t\tif (!isCollapsed(sel)) return null;\n\n\t\t\t\tconst block = state.getBlock(sel.anchor.blockId);\n\t\t\t\tif (!block || block.type !== 'paragraph') return null;\n\n\t\t\t\tconst matchStr = match[0] ?? '';\n\t\t\t\tconst matchLen = matchStr.length;\n\t\t\t\tconst attrs = buildListItemAttrs(def.type, 0, matchStr.includes('[x]'));\n\n\t\t\t\treturn state\n\t\t\t\t\t.transaction('input')\n\t\t\t\t\t.deleteTextAt(sel.anchor.blockId, start, start + matchLen)\n\t\t\t\t\t.setBlockType(sel.anchor.blockId, nodeType('list_item'), attrs)\n\t\t\t\t\t.setSelection(createCollapsedSelection(sel.anchor.blockId, 0))\n\t\t\t\t\t.build();\n\t\t\t},\n\t\t});\n\t}\n}\n","/**\n * Keyboard handlers for list block types.\n * Handles Enter (split/exit list) and Backspace (convert to paragraph).\n */\n\nimport { isNodeOfType } from '../../model/AttrRegistry.js';\nimport type { BlockNode } from '../../model/Document.js';\nimport { generateBlockId, getBlockText } from '../../model/Document.js';\nimport { createCollapsedSelection, isCollapsed, isTextSelection } from '../../model/Selection.js';\nimport type { BlockId } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { buildListItemAttrs } from './ListAttrsFactory.js';\n\n// --- Context Guard ---\n\ninterface ListContext {\n\treadonly state: EditorState;\n\treadonly block: BlockNode;\n\treadonly blockId: BlockId;\n\treadonly offset: number;\n}\n\n/**\n * Guards against NodeSelection, non-collapsed selections, and non-list blocks.\n * Returns false (not handled) if the cursor is not inside a list_item block.\n */\nfunction withListContext(context: PluginContext, handler: (ctx: ListContext) => boolean): boolean {\n\tconst state: EditorState = context.getState();\n\tif (!isTextSelection(state.selection)) return false;\n\n\tconst sel = state.selection;\n\tif (!isCollapsed(sel)) return false;\n\n\tconst block: BlockNode | undefined = state.getBlock(sel.anchor.blockId);\n\tif (!block || block.type !== 'list_item') return false;\n\n\treturn handler({\n\t\tstate,\n\t\tblock,\n\t\tblockId: sel.anchor.blockId,\n\t\toffset: sel.anchor.offset,\n\t});\n}\n\n/** Registers Enter and Backspace keymaps for list items. */\nexport function registerListKeymaps(context: PluginContext): void {\n\tcontext.registerKeymap({\n\t\tEnter: () => handleEnter(context),\n\t\tBackspace: () => handleBackspace(context),\n\t});\n}\n\n// --- Enter Handler ---\n\n/**\n * Handles Enter inside a list item.\n * Empty item → exit list (convert to paragraph).\n * Non-empty item → split and create a new list item with the same type.\n */\nfunction handleEnter(context: PluginContext): boolean {\n\treturn withListContext(context, (ctx) => {\n\t\tconst text: string = getBlockText(ctx.block);\n\n\t\tif (text === '') {\n\t\t\tconst tr = ctx.state\n\t\t\t\t.transaction('input')\n\t\t\t\t.setBlockType(ctx.blockId, nodeType('paragraph'))\n\t\t\t\t.setSelection(ctx.state.selection)\n\t\t\t\t.build();\n\t\t\tcontext.dispatch(tr);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst newBlockId = generateBlockId();\n\t\tconst listType = isNodeOfType(ctx.block, 'list_item') ? ctx.block.attrs.listType : 'bullet';\n\t\tconst indent: number = isNodeOfType(ctx.block, 'list_item') ? ctx.block.attrs.indent : 0;\n\t\tconst attrs = buildListItemAttrs(listType, indent);\n\n\t\tconst tr = ctx.state\n\t\t\t.transaction('input')\n\t\t\t.splitBlock(ctx.blockId, ctx.offset, newBlockId)\n\t\t\t.setBlockType(newBlockId, nodeType('list_item'), attrs)\n\t\t\t.setSelection(createCollapsedSelection(newBlockId, 0))\n\t\t\t.build();\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t});\n}\n\n// --- Backspace Handler ---\n\n/**\n * Handles Backspace at the start of a list item.\n * Converts the list item back to a paragraph, preserving text.\n */\nfunction handleBackspace(context: PluginContext): boolean {\n\treturn withListContext(context, (ctx) => {\n\t\tif (ctx.offset !== 0) return false;\n\n\t\tconst tr = ctx.state\n\t\t\t.transaction('input')\n\t\t\t.setBlockType(ctx.blockId, nodeType('paragraph'))\n\t\t\t.setSelection(ctx.state.selection)\n\t\t\t.build();\n\t\tcontext.dispatch(tr);\n\t\treturn true;\n\t});\n}\n","/**\n * Locale interface and default English locale for the ListPlugin.\n */\n\n// --- Locale Interface ---\n\nexport interface ListLocale {\n\treadonly bulletList: string;\n\treadonly numberedList: string;\n\treadonly checklist: string;\n}\n\n// --- Default English Locale ---\n\nexport const LIST_LOCALE_EN: ListLocale = {\n\tbulletList: 'Bullet List',\n\tnumberedList: 'Numbered List',\n\tchecklist: 'Checklist',\n};\n\n// --- Lazy Locale Loader ---\n\nconst localeModules: Record<string, () => Promise<{ default: ListLocale }>> = import.meta.glob<{\n\tdefault: ListLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadListLocale(lang: string): Promise<ListLocale> {\n\tif (lang === 'en') return LIST_LOCALE_EN;\n\tconst loader = localeModules[`./locales/${lang}.ts`];\n\tif (!loader) return LIST_LOCALE_EN;\n\ttry {\n\t\tconst mod = await loader();\n\t\treturn mod.default;\n\t} catch {\n\t\treturn LIST_LOCALE_EN;\n\t}\n}\n","/**\n * Toolbar item registration for list block types.\n * Registers bullet, ordered, and checklist toolbar buttons with active-state detection.\n */\n\nimport { isTextSelection, selectionRange } from '../../model/Selection.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { allBlocksMatchListType } from './ListCommands.js';\nimport type { ListTypeDefinition } from './ListDefinitions.js';\nimport type { ListLocale } from './ListLocale.js';\nimport type { ListType } from './ListPlugin.js';\n\n/** Registers toolbar items for each enabled list type. */\nexport function registerListToolbarItems(\n\tcontext: PluginContext,\n\tenabledTypes: readonly ListTypeDefinition[],\n\tlocale: ListLocale,\n): void {\n\tfor (const def of enabledTypes) {\n\t\tcontext.registerToolbarItem({\n\t\t\tid: `list-${def.type}`,\n\t\t\tgroup: 'block',\n\t\t\ticon: def.icon,\n\t\t\tlabel: getListLabel(def.type, locale),\n\t\t\tcommand: `toggleList:${def.type}`,\n\t\t\tisActive: (state) => isListActive(state, def.type),\n\t\t});\n\t}\n}\n\n// --- Helpers ---\n\nfunction getListLabel(type: ListType, locale: ListLocale): string {\n\tconst labels: Record<ListType, string> = {\n\t\tbullet: locale.bulletList,\n\t\tordered: locale.numberedList,\n\t\tchecklist: locale.checklist,\n\t};\n\treturn labels[type];\n}\n\nfunction isListActive(state: EditorState, listType: ListType): boolean {\n\tif (!isTextSelection(state.selection)) return false;\n\n\tif (state.selection.anchor.blockId !== state.selection.head.blockId) {\n\t\tconst range = selectionRange(state.selection, state.getBlockOrder());\n\t\treturn allBlocksMatchListType(state, range, listType);\n\t}\n\n\tconst block = state.getBlock(state.selection.anchor.blockId);\n\treturn block?.type === 'list_item' && block.attrs?.listType === listType;\n}\n","/**\n * ListPlugin: registers ordered, unordered, and checklist block types\n * with NodeSpecs, toggle commands, indent/outdent (Tab/Shift-Tab),\n * input rules, and toolbar buttons.\n *\n * This file is a thin orchestrator — command logic, input rules, keyboard\n * handlers, and toolbar rendering are delegated to dedicated modules.\n *\n * List items are modeled as flat blocks with a `listType` and `indent` attribute,\n * allowing simple nesting representation without deep tree structures.\n */\n\nimport { LIST_CSS, LIST_MARKER_WIDTH } from '../../editor/styles/list.js';\nimport { blockId } from '../../model/TypeBrands.js';\nimport { setStyleProperty } from '../../style/StyleRuntime.js';\nimport { createBlockElement } from '../../view/DomUtils.js';\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { registerListCommands, toggleChecked } from './ListCommands.js';\nimport { LIST_TYPE_DEFINITIONS, type ListTypeDefinition } from './ListDefinitions.js';\nimport { registerListInputRules } from './ListInputRules.js';\nimport { registerListKeymaps } from './ListKeyboardHandlers.js';\nimport { LIST_LOCALE_EN, type ListLocale, loadListLocale } from './ListLocale.js';\nimport { registerListToolbarItems } from './ListToolbarItems.js';\n\n// --- Attribute Registry Augmentation ---\n\nexport type ListType = 'bullet' | 'ordered' | 'checklist';\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface NodeAttrRegistry {\n\t\tlist_item: { listType: ListType; indent: number; checked: boolean };\n\t}\n}\n\n// --- Configuration ---\n\nexport interface ListConfig {\n\t/** Which list types to enable. Defaults to all. */\n\treadonly types: readonly ListType[];\n\t/** Maximum indent depth. Defaults to 4. */\n\treadonly maxIndent: number;\n\t/**\n\t * When true, checklist checkboxes remain interactive even in read-only mode.\n\t * Defaults to false (checkboxes are fully read-only).\n\t */\n\treadonly interactiveCheckboxes?: boolean;\n\treadonly locale?: ListLocale;\n}\n\nconst DEFAULT_CONFIG: ListConfig = {\n\ttypes: ['bullet', 'ordered', 'checklist'],\n\tmaxIndent: 4,\n};\n\n// --- Plugin ---\n\nexport class ListPlugin implements Plugin {\n\treadonly id = 'list';\n\treadonly name = 'List';\n\treadonly priority = 35;\n\n\tprivate readonly config: ListConfig;\n\tprivate locale!: ListLocale;\n\tprivate context: PluginContext | null = null;\n\tprivate checkboxClickHandler: ((e: MouseEvent) => void) | null = null;\n\n\tconstructor(config?: Partial<ListConfig>) {\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.context = context;\n\t\tthis.locale = await resolveLocale(context, this.config.locale, LIST_LOCALE_EN, loadListLocale);\n\t\tconst enabledTypes = this.getEnabledTypes();\n\n\t\tcontext.registerStyleSheet(LIST_CSS);\n\t\tthis.registerNodeSpec(context);\n\t\tregisterListCommands(context, this.config, enabledTypes);\n\t\tregisterListKeymaps(context);\n\t\tregisterListInputRules(context, enabledTypes);\n\t\tregisterListToolbarItems(context, enabledTypes, this.locale);\n\t\tthis.registerCheckboxClickHandler(context);\n\t}\n\n\tdestroy(): void {\n\t\tif (this.checkboxClickHandler && this.context) {\n\t\t\tconst container: HTMLElement = this.context.getContainer();\n\t\t\tcontainer.removeEventListener('mousedown', this.checkboxClickHandler);\n\t\t}\n\t\tthis.checkboxClickHandler = null;\n\t\tthis.context = null;\n\t}\n\n\tprivate registerNodeSpec(context: PluginContext): void {\n\t\tcontext.registerNodeSpec({\n\t\t\ttype: 'list_item',\n\t\t\tgroup: 'block',\n\t\t\tcontent: { allow: ['text'] },\n\t\t\tattrs: {\n\t\t\t\tlistType: { default: 'bullet' },\n\t\t\t\tindent: { default: 0 },\n\t\t\t\tchecked: { default: false },\n\t\t\t},\n\t\t\ttoDOM(node) {\n\t\t\t\tconst listType = node.attrs?.listType ?? 'bullet';\n\t\t\t\tconst indent = node.attrs?.indent ?? 0;\n\t\t\t\tconst checked = node.attrs?.checked ?? false;\n\n\t\t\t\tconst li = createBlockElement('li', node.id);\n\t\t\t\tli.setAttribute('role', 'listitem');\n\t\t\t\tli.setAttribute('data-list-type', listType);\n\t\t\t\tli.setAttribute('data-indent', String(indent));\n\t\t\t\tli.className = `notectl-list-item notectl-list-item--${listType}`;\n\n\t\t\t\tif (indent > 0) {\n\t\t\t\t\tsetStyleProperty(li, 'marginLeft', `${indent * LIST_MARKER_WIDTH}px`);\n\t\t\t\t}\n\n\t\t\t\tif (listType === 'checklist') {\n\t\t\t\t\tli.setAttribute('data-checked', String(checked));\n\t\t\t\t\tli.setAttribute('aria-checked', String(checked));\n\t\t\t\t}\n\t\t\t\tli.setAttribute('aria-level', String(indent + 1));\n\n\t\t\t\treturn li;\n\t\t\t},\n\t\t\twrapper(node) {\n\t\t\t\tconst listType = node.attrs?.listType ?? 'bullet';\n\t\t\t\tconst tag = listType === 'ordered' ? 'ol' : 'ul';\n\t\t\t\treturn {\n\t\t\t\t\ttag,\n\t\t\t\t\tkey: `list-${listType}`,\n\t\t\t\t\tclassName: `notectl-list notectl-list--${listType}`,\n\t\t\t\t\tattrs: { role: 'list' },\n\t\t\t\t};\n\t\t\t},\n\t\t\ttoHTML(node, content) {\n\t\t\t\tconst listType: string = (node.attrs?.listType as string) ?? 'bullet';\n\t\t\t\tconst checked: boolean = (node.attrs?.checked as boolean) ?? false;\n\n\t\t\t\tif (listType === 'checklist') {\n\t\t\t\t\tconst checkedAttr: string = checked ? ' checked' : '';\n\t\t\t\t\tconst ariaChecked: string = String(checked);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t`<li role=\"checkbox\" aria-checked=\"${ariaChecked}\">` +\n\t\t\t\t\t\t`<input type=\"checkbox\" disabled${checkedAttr}>` +\n\t\t\t\t\t\t`${content || '<br>'}</li>`\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn `<li>${content || '<br>'}</li>`;\n\t\t\t},\n\t\t\tparseHTML: [{ tag: 'li' }],\n\t\t\tsanitize: {\n\t\t\t\ttags: ['ul', 'ol', 'li', 'input'],\n\t\t\t\tattrs: ['type', 'disabled', 'checked', 'role', 'aria-checked'],\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate registerCheckboxClickHandler(context: PluginContext): void {\n\t\tif (!this.config.types.includes('checklist')) return;\n\n\t\tthis.checkboxClickHandler = (e: MouseEvent) => {\n\t\t\tif (context.isReadOnly() && !this.config.interactiveCheckboxes) return;\n\n\t\t\tconst target: EventTarget | null = e.target;\n\t\t\tif (!(target instanceof HTMLElement)) return;\n\n\t\t\tconst li: HTMLElement | null = target.closest('.notectl-list-item--checklist');\n\t\t\tif (!li) return;\n\n\t\t\tconst rect: DOMRect = li.getBoundingClientRect();\n\t\t\tconst isRtl: boolean = getComputedStyle(li).direction === 'rtl';\n\t\t\tconst markerDistance: number = isRtl ? rect.right - e.clientX : e.clientX - rect.left;\n\t\t\tif (markerDistance >= LIST_MARKER_WIDTH) return;\n\n\t\t\te.preventDefault();\n\n\t\t\tconst bid: string | null = li.getAttribute('data-block-id');\n\t\t\tif (!bid) return;\n\n\t\t\ttoggleChecked(context, this.config.interactiveCheckboxes, blockId(bid));\n\t\t};\n\n\t\tconst container: HTMLElement = context.getContainer();\n\t\tcontainer.addEventListener('mousedown', this.checkboxClickHandler);\n\t}\n\n\tprivate getEnabledTypes(): readonly ListTypeDefinition[] {\n\t\treturn LIST_TYPE_DEFINITIONS.filter((def) => this.config.types.includes(def.type));\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as t,r as e,s as n}from"./Document-DnPKbcPE.mjs";import{c as o,o as s,t as c,u as r}from"./Selection-B14e7gd0.mjs";import{t as i}from"./Commands-ByBjgD3-.mjs";import{a}from"./TypeBrands-BQR8dEYU.mjs";import{a as l,c as d,i as u,o as h,s as m}from"./BlockInsertion-Q8B21SaG.mjs";import{n as g}from"./Plugin-C4cBw3bs.mjs";import{i as f}from"./PluginHelpers-BZU5rE44.mjs";import{i as p}from"./XmlDetector-
|
|
2
|
-
//# sourceMappingURL=SmartPastePlugin-
|
|
1
|
+
import{c as t,r as e,s as n}from"./Document-DnPKbcPE.mjs";import{c as o,o as s,t as c,u as r}from"./Selection-B14e7gd0.mjs";import{t as i}from"./Commands-ByBjgD3-.mjs";import{a}from"./TypeBrands-BQR8dEYU.mjs";import{a as l,c as d,i as u,o as h,s as m}from"./BlockInsertion-Q8B21SaG.mjs";import{n as g}from"./Plugin-C4cBw3bs.mjs";import{i as f}from"./PluginHelpers-BZU5rE44.mjs";import{i as p}from"./XmlDetector-B1y1NzDL.mjs";var x=/\n(?:[ \t]*\n)+/;function k(t,e){return!t.detection&&!e.detection||!(!t.detection||!e.detection)&&t.detection.language===e.detection.language}function C(t,e){const n=`${t.text}\n\n${e.text}`;if(!t.detection&&!e.detection)return{text:n,detection:null};const o=t.detection,s=e.detection;return{text:n,detection:{language:o.language,formattedText:`${o.formattedText}\n\n${s.formattedText}`,confidence:Math.max(o.confidence,s.confidence)}}}var b={detectedAsCodeBlock:t=>`Pasted as ${t} code block with syntax highlighting.`,detectedMixedContent:(t,e,n)=>{const o=[];if(t>0&&o.push(`${t} paragraph${t>1?"s":""}`),e>0){const t=n.join(", ");o.push(`${e} code block${e>1?"s":""} (${t})`)}return`Pasted as ${o.join(" and ")}.`}},j=/* @__PURE__ */Object.assign({"./locales/ar.ts":()=>import("./ar-UKi-TYFz.mjs"),"./locales/de.ts":()=>import("./de-CwSQIBLx.mjs"),"./locales/es.ts":()=>import("./es-Di4t0cUf.mjs"),"./locales/fr.ts":()=>import("./fr-SxdLDSEQ.mjs"),"./locales/hi.ts":()=>import("./hi-B0EvMNl3.mjs"),"./locales/pt.ts":()=>import("./pt-DEs0I6eo.mjs"),"./locales/ru.ts":()=>import("./ru-B_8ujIHK.mjs"),"./locales/zh.ts":()=>import("./zh-DtzwqBHS.mjs")});async function B(t){if("en"===t)return b;const e=j[`./locales/${t}.ts`];if(!e)return b;try{return(await e()).default}catch{return b}}var P=new g("smartPaste"),$=class{id="smart-paste";name="Smart Paste";dependencies=["code-block"];config;detectors=[];context=null;locale=b;constructor(t){this.config={...t}}async init(t){this.context=t,this.locale=await f(t,this.config.locale,b,B);const e=t.getService(p);if(e&&e.onRegister(t=>{t.detection&&this.detectors.push(t.detection)}),this.config.detectors)for(const n of this.config.detectors)this.detectors.push(n);t.registerPasteInterceptor(this.handlePaste,{name:"smart-paste",priority:50}),t.registerService(P,{registerDetector:t=>{this.detectors.push(t)}})}destroy(){this.context=null,this.detectors.length=0}handlePaste=(t,e,n)=>{if(this.isCursorInCodeBlock(n))return null;const o=function(t,e){const n=t.split(x).map(t=>t.trim()).filter(t=>t.length>0);if(0===n.length)return null;let o=!1;const s=n.map(t=>{const n=function(t,e){let n=null;for(const o of e){const e=o.detect(t);e&&(null===n||e.confidence>n.confidence)&&(n=e)}return n}(t,e);return n&&(o=!0),{text:t,detection:n}});return o?function(t){if(t.length<=1)return t;const e=[t[0]];for(let n=1;n<t.length;n++){const o=t[n],s=e[e.length-1];k(s,o)?e[e.length-1]=C(s,o):e.push(o)}return e}(s):null}(t,this.detectors);return o?1===o.length&&o[0]?.detection?this.handleSingleCodeBlock(n,o[0].detection):this.handleMixedContent(n,o):null};isCursorInCodeBlock(t){const e=t.selection;return!!r(e)&&"code_block"===t.getBlock(e.anchor.blockId)?.type}handleSingleCodeBlock(t,e){const n=this.buildCodeBlockTransaction(t,e);return n&&this.context&&this.context.announce(this.locale.detectedAsCodeBlock(e.language)),n}buildCodeBlockTransaction(o,d){const u=o.selection,m=o.transaction("paste");let g;g=r(u)&&!s(u)?i(o,m)??u.anchor.blockId:h(u);const f=t(),p=e(a("code_block"),[n(d.formattedText)],f,{language:d.language,backgroundColor:""});return l(o,m,g,p,u,this.context?.getSchemaRegistry())?(m.setSelection(c(f,d.formattedText.length)),m.build()):null}handleMixedContent(t,e){const n=this.buildMultiBlockTransaction(t,e);return n&&this.context&&this.announceMixedContent(e),n}buildMultiBlockTransaction(t,e){const n=t.selection,a=t.transaction("paste");let l;l=r(n)&&!s(n)?i(t,a)??n.anchor.blockId:h(n);const g=u(t,l),f=this.context?.getSchemaRegistry(),p=g?m(t,l,g,f):d(t,l,f);if(!p)return null;const x=!g&&o(n)&&"before"===n.side?0:1;let k,C=p.anchorIndex+x,b=0;for(const o of e){const t=o.detection?this.createCodeBlockNode(o.detection):this.createParagraphNode(o.text);a.insertNode(p.parentPath,C,t),C++,k=t.id,b=o.detection?o.detection.formattedText.length:o.text.length}return(g?p.isAnchorEmpty&&p.anchorIndex>=0:p.isAnchorEmpty&&!o(n))&&a.removeNode(p.parentPath,p.anchorIndex),k&&a.setSelection(c(k,b)),a.build()}createCodeBlockNode(o){return e(a("code_block"),[n(o.formattedText)],t(),{language:o.language,backgroundColor:""})}createParagraphNode(o){return e(a("paragraph"),[n(o)],t())}announceMixedContent(t){let e=0,n=0;const o=[];for(const s of t)s.detection?(n++,o.includes(s.detection.language)||o.push(s.detection.language)):e++;this.context?.announce(this.locale.detectedMixedContent(e,n,o))}};export{B as i,P as n,b as r,$ as t};
|
|
2
|
+
//# sourceMappingURL=SmartPastePlugin-pejM1Zyk.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartPastePlugin-BvYBbHSA.mjs","names":[],"sources":["../../src/plugins/smart-paste/ContentSplitter.ts","../../src/plugins/smart-paste/SmartPasteLocale.ts","../../src/plugins/smart-paste/SmartPasteTypes.ts","../../src/plugins/smart-paste/SmartPastePlugin.ts"],"sourcesContent":["/**\n * Splits pasted text at blank-line boundaries and classifies each segment\n * using content detectors. Adjacent segments of the same type are merged.\n */\n\nimport type { ContentDetector, DetectionResult, PasteSegment } from './SmartPasteTypes.js';\n\n/** Pattern matching one or more consecutive blank lines (may contain whitespace). */\nconst BLANK_LINE_SPLIT: RegExp = /\\n(?:[ \\t]*\\n)+/;\n\n/**\n * Splits text at blank-line boundaries and classifies each segment\n * using the provided detectors. Returns null if no code is detected\n * in any segment (the paste should fall through to default handling).\n */\nexport function splitAndClassify(\n\ttext: string,\n\tdetectors: readonly ContentDetector[],\n): readonly PasteSegment[] | null {\n\tconst rawSegments: readonly string[] = text.split(BLANK_LINE_SPLIT);\n\tconst trimmed: readonly string[] = rawSegments\n\t\t.map((s: string) => s.trim())\n\t\t.filter((s: string) => s.length > 0);\n\n\tif (trimmed.length === 0) return null;\n\n\tlet hasCode = false;\n\tconst segments: PasteSegment[] = trimmed.map((segmentText: string): PasteSegment => {\n\t\tconst detection: DetectionResult | null = detectBest(segmentText, detectors);\n\t\tif (detection) hasCode = true;\n\t\treturn { text: segmentText, detection };\n\t});\n\n\tif (!hasCode) return null;\n\n\treturn mergeAdjacentSegments(segments);\n}\n\n/**\n * Merges adjacent segments that share the same classification:\n * consecutive text segments become one paragraph, consecutive code\n * segments of the same language become one code block.\n */\nexport function mergeAdjacentSegments(segments: readonly PasteSegment[]): readonly PasteSegment[] {\n\tif (segments.length <= 1) return segments;\n\n\tconst merged: PasteSegment[] = [segments[0] as PasteSegment];\n\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tconst current: PasteSegment = segments[i] as PasteSegment;\n\t\tconst previous: PasteSegment = merged[merged.length - 1] as PasteSegment;\n\n\t\tif (canMerge(previous, current)) {\n\t\t\tmerged[merged.length - 1] = merge(previous, current);\n\t\t} else {\n\t\t\tmerged.push(current);\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n/** Picks the highest-confidence detection result from all detectors. */\nfunction detectBest(text: string, detectors: readonly ContentDetector[]): DetectionResult | null {\n\tlet best: DetectionResult | null = null;\n\n\tfor (const detector of detectors) {\n\t\tconst result: DetectionResult | null = detector.detect(text);\n\t\tif (result && (best === null || result.confidence > best.confidence)) {\n\t\t\tbest = result;\n\t\t}\n\t}\n\n\treturn best;\n}\n\n/** Two segments can merge if both are text or both are code of the same language. */\nfunction canMerge(a: PasteSegment, b: PasteSegment): boolean {\n\tif (!a.detection && !b.detection) return true;\n\tif (a.detection && b.detection) return a.detection.language === b.detection.language;\n\treturn false;\n}\n\n/** Merges two compatible segments into one. */\nfunction merge(a: PasteSegment, b: PasteSegment): PasteSegment {\n\tconst joinedText: string = `${a.text}\\n\\n${b.text}`;\n\n\tif (!a.detection && !b.detection) {\n\t\treturn { text: joinedText, detection: null };\n\t}\n\n\tconst detA: DetectionResult = a.detection as DetectionResult;\n\tconst detB: DetectionResult = b.detection as DetectionResult;\n\n\treturn {\n\t\ttext: joinedText,\n\t\tdetection: {\n\t\t\tlanguage: detA.language,\n\t\t\tformattedText: `${detA.formattedText}\\n\\n${detB.formattedText}`,\n\t\t\tconfidence: Math.max(detA.confidence, detB.confidence),\n\t\t},\n\t};\n}\n","/** Locale interface and default English locale for the SmartPastePlugin. */\n\nexport interface SmartPasteLocale {\n\treadonly detectedAsCodeBlock: (language: string) => string;\n\treadonly detectedMixedContent: (\n\t\ttextCount: number,\n\t\tcodeCount: number,\n\t\tlanguages: readonly string[],\n\t) => string;\n}\n\nexport const SMART_PASTE_LOCALE_EN: SmartPasteLocale = {\n\tdetectedAsCodeBlock: (language: string) =>\n\t\t`Pasted as ${language} code block with syntax highlighting.`,\n\tdetectedMixedContent: (textCount: number, codeCount: number, languages: readonly string[]) => {\n\t\tconst parts: string[] = [];\n\t\tif (textCount > 0) {\n\t\t\tparts.push(`${textCount} paragraph${textCount > 1 ? 's' : ''}`);\n\t\t}\n\t\tif (codeCount > 0) {\n\t\t\tconst langList: string = languages.join(', ');\n\t\t\tparts.push(`${codeCount} code block${codeCount > 1 ? 's' : ''} (${langList})`);\n\t\t}\n\t\treturn `Pasted as ${parts.join(' and ')}.`;\n\t},\n};\n\nconst localeModules: Record<string, () => Promise<{ default: SmartPasteLocale }>> =\n\timport.meta.glob<{ default: SmartPasteLocale }>('./locales/*.ts', { eager: false });\n\nexport async function loadSmartPasteLocale(lang: string): Promise<SmartPasteLocale> {\n\tif (lang === 'en') return SMART_PASTE_LOCALE_EN;\n\tconst loader = localeModules[`./locales/${lang}.ts`];\n\tif (!loader) return SMART_PASTE_LOCALE_EN;\n\ttry {\n\t\tconst mod = await loader();\n\t\treturn mod.default;\n\t} catch {\n\t\treturn SMART_PASTE_LOCALE_EN;\n\t}\n}\n","/**\n * Types for the smart paste plugin — content detection and formatting.\n */\n\nimport { ServiceKey } from '../Plugin.js';\nimport type { SmartPasteLocale } from './SmartPasteLocale.js';\n\n/** Result of detecting structured content in pasted text. */\nexport interface DetectionResult {\n\treadonly language: string;\n\treadonly formattedText: string;\n\treadonly confidence: number;\n}\n\n/** A classified segment of pasted text — either plain text or detected code. */\nexport interface PasteSegment {\n\treadonly text: string;\n\treadonly detection: DetectionResult | null;\n}\n\n/** Interface for content detectors that identify and format pasted text. */\nexport interface ContentDetector {\n\treadonly id: string;\n\tdetect(text: string): DetectionResult | null;\n}\n\n/** Configuration for the SmartPastePlugin. */\nexport interface SmartPasteConfig {\n\treadonly detectors?: readonly ContentDetector[];\n\treadonly locale?: SmartPasteLocale;\n}\n\n/** Public service for registering additional content detectors at runtime. */\nexport interface SmartPasteService {\n\tregisterDetector(detector: ContentDetector): void;\n}\n\nexport const SMART_PASTE_SERVICE_KEY = new ServiceKey<SmartPasteService>('smartPaste');\n","/**\n * SmartPastePlugin: detects structured content in clipboard text\n * and inserts it as formatted code blocks with syntax highlighting.\n * Mixed content (text + code) is split into separate blocks.\n */\n\nimport {\n\ttype InsertionContext,\n\tfindTableCellAncestor,\n\tinsertBlockAfterAnchor,\n\tresolveAnchorBlockId,\n\tresolveCellInsertionContext,\n\tresolveRootInsertionContext,\n} from '../../commands/BlockInsertion.js';\nimport { addDeleteSelectionSteps } from '../../commands/Commands.js';\nimport {\n\ttype BlockNode,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n} from '../../model/Document.js';\nimport {\n\tcreateCollapsedSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisTextSelection,\n} from '../../model/Selection.js';\nimport type { BlockId, NodeTypeName } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport type { PasteInterceptor, Plugin, PluginContext } from '../Plugin.js';\nimport { LANGUAGE_REGISTRY_SERVICE_KEY } from '../language/LanguageTypes.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { splitAndClassify } from './ContentSplitter.js';\nimport {\n\tSMART_PASTE_LOCALE_EN,\n\ttype SmartPasteLocale,\n\tloadSmartPasteLocale,\n} from './SmartPasteLocale.js';\nimport type {\n\tContentDetector,\n\tDetectionResult,\n\tPasteSegment,\n\tSmartPasteConfig,\n} from './SmartPasteTypes.js';\nimport { SMART_PASTE_SERVICE_KEY } from './SmartPasteTypes.js';\n\nexport class SmartPastePlugin implements Plugin {\n\treadonly id = 'smart-paste';\n\treadonly name = 'Smart Paste';\n\treadonly dependencies = ['code-block'] as const;\n\n\tprivate readonly config: SmartPasteConfig;\n\tprivate readonly detectors: ContentDetector[] = [];\n\tprivate context: PluginContext | null = null;\n\tprivate locale: SmartPasteLocale = SMART_PASTE_LOCALE_EN;\n\n\tconstructor(config?: Partial<SmartPasteConfig>) {\n\t\tthis.config = { ...config };\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.context = context;\n\n\t\tthis.locale = await resolveLocale(\n\t\t\tcontext,\n\t\t\tthis.config.locale,\n\t\t\tSMART_PASTE_LOCALE_EN,\n\t\t\tloadSmartPasteLocale,\n\t\t);\n\n\t\t// Subscribe to language registry for detectors (replay provides built-ins)\n\t\tconst registry = context.getService(LANGUAGE_REGISTRY_SERVICE_KEY);\n\t\tif (registry) {\n\t\t\tregistry.onRegister((support) => {\n\t\t\t\tif (support.detection) {\n\t\t\t\t\tthis.detectors.push(support.detection);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Register config-provided detectors\n\t\tif (this.config.detectors) {\n\t\t\tfor (const detector of this.config.detectors) {\n\t\t\t\tthis.detectors.push(detector);\n\t\t\t}\n\t\t}\n\n\t\t// Register paste interceptor\n\t\tcontext.registerPasteInterceptor(this.handlePaste, { name: 'smart-paste', priority: 50 });\n\n\t\t// Register service for external detector registration\n\t\tcontext.registerService(SMART_PASTE_SERVICE_KEY, {\n\t\t\tregisterDetector: (detector: ContentDetector) => {\n\t\t\t\tthis.detectors.push(detector);\n\t\t\t},\n\t\t});\n\t}\n\n\tdestroy(): void {\n\t\tthis.context = null;\n\t\tthis.detectors.length = 0;\n\t}\n\n\t// --- Paste Interceptor ---\n\n\tprivate readonly handlePaste: PasteInterceptor = (\n\t\tplainText: string,\n\t\t_html: string,\n\t\tstate: EditorState,\n\t): Transaction | null => {\n\t\tif (this.isCursorInCodeBlock(state)) return null;\n\n\t\tconst segments: readonly PasteSegment[] | null = splitAndClassify(plainText, this.detectors);\n\t\tif (!segments) return null;\n\n\t\t// Single code-only segment: use existing fast path\n\t\tif (segments.length === 1 && segments[0]?.detection) {\n\t\t\treturn this.handleSingleCodeBlock(state, segments[0].detection);\n\t\t}\n\n\t\t// Mixed content: create multiple blocks\n\t\treturn this.handleMixedContent(state, segments);\n\t};\n\n\tprivate isCursorInCodeBlock(state: EditorState): boolean {\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel)) return false;\n\n\t\tconst block: BlockNode | undefined = state.getBlock(sel.anchor.blockId);\n\t\treturn block?.type === 'code_block';\n\t}\n\n\t// --- Single Code Block (existing behavior) ---\n\n\tprivate handleSingleCodeBlock(\n\t\tstate: EditorState,\n\t\tdetection: DetectionResult,\n\t): Transaction | null {\n\t\tconst tr: Transaction | null = this.buildCodeBlockTransaction(state, detection);\n\t\tif (tr && this.context) {\n\t\t\tthis.context.announce(this.locale.detectedAsCodeBlock(detection.language));\n\t\t}\n\t\treturn tr;\n\t}\n\n\tprivate buildCodeBlockTransaction(\n\t\tstate: EditorState,\n\t\tdetection: DetectionResult,\n\t): Transaction | null {\n\t\tconst sel = state.selection;\n\t\tconst builder = state.transaction('paste');\n\n\t\t// Delete selection if range-selected\n\t\tlet anchorBlockId: BlockId;\n\t\tif (isTextSelection(sel) && !isCollapsed(sel)) {\n\t\t\tconst landingId: BlockId | undefined = addDeleteSelectionSteps(state, builder);\n\t\t\tanchorBlockId = landingId ?? sel.anchor.blockId;\n\t\t} else {\n\t\t\tanchorBlockId = resolveAnchorBlockId(sel);\n\t\t}\n\n\t\t// Create the code block node\n\t\tconst newBlockId: BlockId = generateBlockId();\n\t\tconst codeBlock: BlockNode = createBlockNode(\n\t\t\tnodeType('code_block') as NodeTypeName,\n\t\t\t[createTextNode(detection.formattedText)],\n\t\t\tnewBlockId,\n\t\t\t{ language: detection.language, backgroundColor: '' },\n\t\t);\n\n\t\t// Insert at cursor (handles cell vs root, empty anchor removal)\n\t\tconst inserted: boolean = insertBlockAfterAnchor(\n\t\t\tstate,\n\t\t\tbuilder,\n\t\t\tanchorBlockId,\n\t\t\tcodeBlock,\n\t\t\tsel,\n\t\t\tthis.context?.getSchemaRegistry(),\n\t\t);\n\t\tif (!inserted) return null;\n\n\t\tbuilder.setSelection(createCollapsedSelection(newBlockId, detection.formattedText.length));\n\n\t\treturn builder.build();\n\t}\n\n\t// --- Mixed Content (multiple blocks) ---\n\n\tprivate handleMixedContent(\n\t\tstate: EditorState,\n\t\tsegments: readonly PasteSegment[],\n\t): Transaction | null {\n\t\tconst tr: Transaction | null = this.buildMultiBlockTransaction(state, segments);\n\t\tif (tr && this.context) {\n\t\t\tthis.announceMixedContent(segments);\n\t\t}\n\t\treturn tr;\n\t}\n\n\tprivate buildMultiBlockTransaction(\n\t\tstate: EditorState,\n\t\tsegments: readonly PasteSegment[],\n\t): Transaction | null {\n\t\tconst sel = state.selection;\n\t\tconst builder = state.transaction('paste');\n\n\t\tlet anchorBlockId: BlockId;\n\t\tif (isTextSelection(sel) && !isCollapsed(sel)) {\n\t\t\tconst landingId: BlockId | undefined = addDeleteSelectionSteps(state, builder);\n\t\t\tanchorBlockId = landingId ?? sel.anchor.blockId;\n\t\t} else {\n\t\t\tanchorBlockId = resolveAnchorBlockId(sel);\n\t\t}\n\n\t\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\t\tconst schemaRegistry = this.context?.getSchemaRegistry();\n\n\t\tconst ctx: InsertionContext | undefined = cellId\n\t\t\t? resolveCellInsertionContext(state, anchorBlockId, cellId, schemaRegistry)\n\t\t\t: resolveRootInsertionContext(state, anchorBlockId, schemaRegistry);\n\t\tif (!ctx) return null;\n\n\t\tconst insertOffset: number = !cellId && isGapCursor(sel) && sel.side === 'before' ? 0 : 1;\n\t\tlet insertIndex: number = ctx.anchorIndex + insertOffset;\n\t\tlet lastBlockId: BlockId | undefined;\n\t\tlet lastBlockTextLen = 0;\n\n\t\tfor (const segment of segments) {\n\t\t\tconst block: BlockNode = segment.detection\n\t\t\t\t? this.createCodeBlockNode(segment.detection)\n\t\t\t\t: this.createParagraphNode(segment.text);\n\n\t\t\tbuilder.insertNode(ctx.parentPath, insertIndex, block);\n\t\t\tinsertIndex++;\n\t\t\tlastBlockId = block.id;\n\t\t\tlastBlockTextLen = segment.detection\n\t\t\t\t? segment.detection.formattedText.length\n\t\t\t\t: segment.text.length;\n\t\t}\n\n\t\tconst shouldRemoveAnchor: boolean = cellId\n\t\t\t? ctx.isAnchorEmpty && ctx.anchorIndex >= 0\n\t\t\t: ctx.isAnchorEmpty && !isGapCursor(sel);\n\n\t\tif (shouldRemoveAnchor) {\n\t\t\tbuilder.removeNode(ctx.parentPath, ctx.anchorIndex);\n\t\t}\n\n\t\tif (lastBlockId) {\n\t\t\tbuilder.setSelection(createCollapsedSelection(lastBlockId, lastBlockTextLen));\n\t\t}\n\n\t\treturn builder.build();\n\t}\n\n\tprivate createCodeBlockNode(detection: DetectionResult): BlockNode {\n\t\treturn createBlockNode(\n\t\t\tnodeType('code_block') as NodeTypeName,\n\t\t\t[createTextNode(detection.formattedText)],\n\t\t\tgenerateBlockId(),\n\t\t\t{ language: detection.language, backgroundColor: '' },\n\t\t);\n\t}\n\n\tprivate createParagraphNode(text: string): BlockNode {\n\t\treturn createBlockNode(\n\t\t\tnodeType('paragraph') as NodeTypeName,\n\t\t\t[createTextNode(text)],\n\t\t\tgenerateBlockId(),\n\t\t);\n\t}\n\n\tprivate announceMixedContent(segments: readonly PasteSegment[]): void {\n\t\tlet textCount = 0;\n\t\tlet codeCount = 0;\n\t\tconst languages: string[] = [];\n\n\t\tfor (const segment of segments) {\n\t\t\tif (segment.detection) {\n\t\t\t\tcodeCount++;\n\t\t\t\tif (!languages.includes(segment.detection.language)) {\n\t\t\t\t\tlanguages.push(segment.detection.language);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttextCount++;\n\t\t\t}\n\t\t}\n\n\t\tthis.context?.announce(this.locale.detectedMixedContent(textCount, codeCount, languages));\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SmartPastePlugin-pejM1Zyk.mjs","names":[],"sources":["../../src/plugins/smart-paste/ContentSplitter.ts","../../src/plugins/smart-paste/SmartPasteLocale.ts","../../src/plugins/smart-paste/SmartPasteTypes.ts","../../src/plugins/smart-paste/SmartPastePlugin.ts"],"sourcesContent":["/**\n * Splits pasted text at blank-line boundaries and classifies each segment\n * using content detectors. Adjacent segments of the same type are merged.\n */\n\nimport type { ContentDetector, DetectionResult, PasteSegment } from './SmartPasteTypes.js';\n\n/** Pattern matching one or more consecutive blank lines (may contain whitespace). */\nconst BLANK_LINE_SPLIT: RegExp = /\\n(?:[ \\t]*\\n)+/;\n\n/**\n * Splits text at blank-line boundaries and classifies each segment\n * using the provided detectors. Returns null if no code is detected\n * in any segment (the paste should fall through to default handling).\n */\nexport function splitAndClassify(\n\ttext: string,\n\tdetectors: readonly ContentDetector[],\n): readonly PasteSegment[] | null {\n\tconst rawSegments: readonly string[] = text.split(BLANK_LINE_SPLIT);\n\tconst trimmed: readonly string[] = rawSegments\n\t\t.map((s: string) => s.trim())\n\t\t.filter((s: string) => s.length > 0);\n\n\tif (trimmed.length === 0) return null;\n\n\tlet hasCode = false;\n\tconst segments: PasteSegment[] = trimmed.map((segmentText: string): PasteSegment => {\n\t\tconst detection: DetectionResult | null = detectBest(segmentText, detectors);\n\t\tif (detection) hasCode = true;\n\t\treturn { text: segmentText, detection };\n\t});\n\n\tif (!hasCode) return null;\n\n\treturn mergeAdjacentSegments(segments);\n}\n\n/**\n * Merges adjacent segments that share the same classification:\n * consecutive text segments become one paragraph, consecutive code\n * segments of the same language become one code block.\n */\nexport function mergeAdjacentSegments(segments: readonly PasteSegment[]): readonly PasteSegment[] {\n\tif (segments.length <= 1) return segments;\n\n\tconst merged: PasteSegment[] = [segments[0] as PasteSegment];\n\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tconst current: PasteSegment = segments[i] as PasteSegment;\n\t\tconst previous: PasteSegment = merged[merged.length - 1] as PasteSegment;\n\n\t\tif (canMerge(previous, current)) {\n\t\t\tmerged[merged.length - 1] = merge(previous, current);\n\t\t} else {\n\t\t\tmerged.push(current);\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n/** Picks the highest-confidence detection result from all detectors. */\nfunction detectBest(text: string, detectors: readonly ContentDetector[]): DetectionResult | null {\n\tlet best: DetectionResult | null = null;\n\n\tfor (const detector of detectors) {\n\t\tconst result: DetectionResult | null = detector.detect(text);\n\t\tif (result && (best === null || result.confidence > best.confidence)) {\n\t\t\tbest = result;\n\t\t}\n\t}\n\n\treturn best;\n}\n\n/** Two segments can merge if both are text or both are code of the same language. */\nfunction canMerge(a: PasteSegment, b: PasteSegment): boolean {\n\tif (!a.detection && !b.detection) return true;\n\tif (a.detection && b.detection) return a.detection.language === b.detection.language;\n\treturn false;\n}\n\n/** Merges two compatible segments into one. */\nfunction merge(a: PasteSegment, b: PasteSegment): PasteSegment {\n\tconst joinedText: string = `${a.text}\\n\\n${b.text}`;\n\n\tif (!a.detection && !b.detection) {\n\t\treturn { text: joinedText, detection: null };\n\t}\n\n\tconst detA: DetectionResult = a.detection as DetectionResult;\n\tconst detB: DetectionResult = b.detection as DetectionResult;\n\n\treturn {\n\t\ttext: joinedText,\n\t\tdetection: {\n\t\t\tlanguage: detA.language,\n\t\t\tformattedText: `${detA.formattedText}\\n\\n${detB.formattedText}`,\n\t\t\tconfidence: Math.max(detA.confidence, detB.confidence),\n\t\t},\n\t};\n}\n","/** Locale interface and default English locale for the SmartPastePlugin. */\n\nexport interface SmartPasteLocale {\n\treadonly detectedAsCodeBlock: (language: string) => string;\n\treadonly detectedMixedContent: (\n\t\ttextCount: number,\n\t\tcodeCount: number,\n\t\tlanguages: readonly string[],\n\t) => string;\n}\n\nexport const SMART_PASTE_LOCALE_EN: SmartPasteLocale = {\n\tdetectedAsCodeBlock: (language: string) =>\n\t\t`Pasted as ${language} code block with syntax highlighting.`,\n\tdetectedMixedContent: (textCount: number, codeCount: number, languages: readonly string[]) => {\n\t\tconst parts: string[] = [];\n\t\tif (textCount > 0) {\n\t\t\tparts.push(`${textCount} paragraph${textCount > 1 ? 's' : ''}`);\n\t\t}\n\t\tif (codeCount > 0) {\n\t\t\tconst langList: string = languages.join(', ');\n\t\t\tparts.push(`${codeCount} code block${codeCount > 1 ? 's' : ''} (${langList})`);\n\t\t}\n\t\treturn `Pasted as ${parts.join(' and ')}.`;\n\t},\n};\n\nconst localeModules: Record<string, () => Promise<{ default: SmartPasteLocale }>> =\n\timport.meta.glob<{ default: SmartPasteLocale }>('./locales/*.ts', { eager: false });\n\nexport async function loadSmartPasteLocale(lang: string): Promise<SmartPasteLocale> {\n\tif (lang === 'en') return SMART_PASTE_LOCALE_EN;\n\tconst loader = localeModules[`./locales/${lang}.ts`];\n\tif (!loader) return SMART_PASTE_LOCALE_EN;\n\ttry {\n\t\tconst mod = await loader();\n\t\treturn mod.default;\n\t} catch {\n\t\treturn SMART_PASTE_LOCALE_EN;\n\t}\n}\n","/**\n * Types for the smart paste plugin — content detection and formatting.\n */\n\nimport { ServiceKey } from '../Plugin.js';\nimport type { SmartPasteLocale } from './SmartPasteLocale.js';\n\n/** Result of detecting structured content in pasted text. */\nexport interface DetectionResult {\n\treadonly language: string;\n\treadonly formattedText: string;\n\treadonly confidence: number;\n}\n\n/** A classified segment of pasted text — either plain text or detected code. */\nexport interface PasteSegment {\n\treadonly text: string;\n\treadonly detection: DetectionResult | null;\n}\n\n/** Interface for content detectors that identify and format pasted text. */\nexport interface ContentDetector {\n\treadonly id: string;\n\tdetect(text: string): DetectionResult | null;\n}\n\n/** Configuration for the SmartPastePlugin. */\nexport interface SmartPasteConfig {\n\treadonly detectors?: readonly ContentDetector[];\n\treadonly locale?: SmartPasteLocale;\n}\n\n/** Public service for registering additional content detectors at runtime. */\nexport interface SmartPasteService {\n\tregisterDetector(detector: ContentDetector): void;\n}\n\nexport const SMART_PASTE_SERVICE_KEY = new ServiceKey<SmartPasteService>('smartPaste');\n","/**\n * SmartPastePlugin: detects structured content in clipboard text\n * and inserts it as formatted code blocks with syntax highlighting.\n * Mixed content (text + code) is split into separate blocks.\n */\n\nimport {\n\ttype InsertionContext,\n\tfindTableCellAncestor,\n\tinsertBlockAfterAnchor,\n\tresolveAnchorBlockId,\n\tresolveCellInsertionContext,\n\tresolveRootInsertionContext,\n} from '../../commands/BlockInsertion.js';\nimport { addDeleteSelectionSteps } from '../../commands/Commands.js';\nimport {\n\ttype BlockNode,\n\tcreateBlockNode,\n\tcreateTextNode,\n\tgenerateBlockId,\n} from '../../model/Document.js';\nimport {\n\tcreateCollapsedSelection,\n\tisCollapsed,\n\tisGapCursor,\n\tisTextSelection,\n} from '../../model/Selection.js';\nimport type { BlockId, NodeTypeName } from '../../model/TypeBrands.js';\nimport { nodeType } from '../../model/TypeBrands.js';\nimport type { EditorState } from '../../state/EditorState.js';\nimport type { Transaction } from '../../state/Transaction.js';\nimport type { PasteInterceptor, Plugin, PluginContext } from '../Plugin.js';\nimport { LANGUAGE_REGISTRY_SERVICE_KEY } from '../language/LanguageTypes.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { splitAndClassify } from './ContentSplitter.js';\nimport {\n\tSMART_PASTE_LOCALE_EN,\n\ttype SmartPasteLocale,\n\tloadSmartPasteLocale,\n} from './SmartPasteLocale.js';\nimport type {\n\tContentDetector,\n\tDetectionResult,\n\tPasteSegment,\n\tSmartPasteConfig,\n} from './SmartPasteTypes.js';\nimport { SMART_PASTE_SERVICE_KEY } from './SmartPasteTypes.js';\n\nexport class SmartPastePlugin implements Plugin {\n\treadonly id = 'smart-paste';\n\treadonly name = 'Smart Paste';\n\treadonly dependencies = ['code-block'] as const;\n\n\tprivate readonly config: SmartPasteConfig;\n\tprivate readonly detectors: ContentDetector[] = [];\n\tprivate context: PluginContext | null = null;\n\tprivate locale: SmartPasteLocale = SMART_PASTE_LOCALE_EN;\n\n\tconstructor(config?: Partial<SmartPasteConfig>) {\n\t\tthis.config = { ...config };\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.context = context;\n\n\t\tthis.locale = await resolveLocale(\n\t\t\tcontext,\n\t\t\tthis.config.locale,\n\t\t\tSMART_PASTE_LOCALE_EN,\n\t\t\tloadSmartPasteLocale,\n\t\t);\n\n\t\t// Subscribe to language registry for detectors (replay provides built-ins)\n\t\tconst registry = context.getService(LANGUAGE_REGISTRY_SERVICE_KEY);\n\t\tif (registry) {\n\t\t\tregistry.onRegister((support) => {\n\t\t\t\tif (support.detection) {\n\t\t\t\t\tthis.detectors.push(support.detection);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t// Register config-provided detectors\n\t\tif (this.config.detectors) {\n\t\t\tfor (const detector of this.config.detectors) {\n\t\t\t\tthis.detectors.push(detector);\n\t\t\t}\n\t\t}\n\n\t\t// Register paste interceptor\n\t\tcontext.registerPasteInterceptor(this.handlePaste, { name: 'smart-paste', priority: 50 });\n\n\t\t// Register service for external detector registration\n\t\tcontext.registerService(SMART_PASTE_SERVICE_KEY, {\n\t\t\tregisterDetector: (detector: ContentDetector) => {\n\t\t\t\tthis.detectors.push(detector);\n\t\t\t},\n\t\t});\n\t}\n\n\tdestroy(): void {\n\t\tthis.context = null;\n\t\tthis.detectors.length = 0;\n\t}\n\n\t// --- Paste Interceptor ---\n\n\tprivate readonly handlePaste: PasteInterceptor = (\n\t\tplainText: string,\n\t\t_html: string,\n\t\tstate: EditorState,\n\t): Transaction | null => {\n\t\tif (this.isCursorInCodeBlock(state)) return null;\n\n\t\tconst segments: readonly PasteSegment[] | null = splitAndClassify(plainText, this.detectors);\n\t\tif (!segments) return null;\n\n\t\t// Single code-only segment: use existing fast path\n\t\tif (segments.length === 1 && segments[0]?.detection) {\n\t\t\treturn this.handleSingleCodeBlock(state, segments[0].detection);\n\t\t}\n\n\t\t// Mixed content: create multiple blocks\n\t\treturn this.handleMixedContent(state, segments);\n\t};\n\n\tprivate isCursorInCodeBlock(state: EditorState): boolean {\n\t\tconst sel = state.selection;\n\t\tif (!isTextSelection(sel)) return false;\n\n\t\tconst block: BlockNode | undefined = state.getBlock(sel.anchor.blockId);\n\t\treturn block?.type === 'code_block';\n\t}\n\n\t// --- Single Code Block (existing behavior) ---\n\n\tprivate handleSingleCodeBlock(\n\t\tstate: EditorState,\n\t\tdetection: DetectionResult,\n\t): Transaction | null {\n\t\tconst tr: Transaction | null = this.buildCodeBlockTransaction(state, detection);\n\t\tif (tr && this.context) {\n\t\t\tthis.context.announce(this.locale.detectedAsCodeBlock(detection.language));\n\t\t}\n\t\treturn tr;\n\t}\n\n\tprivate buildCodeBlockTransaction(\n\t\tstate: EditorState,\n\t\tdetection: DetectionResult,\n\t): Transaction | null {\n\t\tconst sel = state.selection;\n\t\tconst builder = state.transaction('paste');\n\n\t\t// Delete selection if range-selected\n\t\tlet anchorBlockId: BlockId;\n\t\tif (isTextSelection(sel) && !isCollapsed(sel)) {\n\t\t\tconst landingId: BlockId | undefined = addDeleteSelectionSteps(state, builder);\n\t\t\tanchorBlockId = landingId ?? sel.anchor.blockId;\n\t\t} else {\n\t\t\tanchorBlockId = resolveAnchorBlockId(sel);\n\t\t}\n\n\t\t// Create the code block node\n\t\tconst newBlockId: BlockId = generateBlockId();\n\t\tconst codeBlock: BlockNode = createBlockNode(\n\t\t\tnodeType('code_block') as NodeTypeName,\n\t\t\t[createTextNode(detection.formattedText)],\n\t\t\tnewBlockId,\n\t\t\t{ language: detection.language, backgroundColor: '' },\n\t\t);\n\n\t\t// Insert at cursor (handles cell vs root, empty anchor removal)\n\t\tconst inserted: boolean = insertBlockAfterAnchor(\n\t\t\tstate,\n\t\t\tbuilder,\n\t\t\tanchorBlockId,\n\t\t\tcodeBlock,\n\t\t\tsel,\n\t\t\tthis.context?.getSchemaRegistry(),\n\t\t);\n\t\tif (!inserted) return null;\n\n\t\tbuilder.setSelection(createCollapsedSelection(newBlockId, detection.formattedText.length));\n\n\t\treturn builder.build();\n\t}\n\n\t// --- Mixed Content (multiple blocks) ---\n\n\tprivate handleMixedContent(\n\t\tstate: EditorState,\n\t\tsegments: readonly PasteSegment[],\n\t): Transaction | null {\n\t\tconst tr: Transaction | null = this.buildMultiBlockTransaction(state, segments);\n\t\tif (tr && this.context) {\n\t\t\tthis.announceMixedContent(segments);\n\t\t}\n\t\treturn tr;\n\t}\n\n\tprivate buildMultiBlockTransaction(\n\t\tstate: EditorState,\n\t\tsegments: readonly PasteSegment[],\n\t): Transaction | null {\n\t\tconst sel = state.selection;\n\t\tconst builder = state.transaction('paste');\n\n\t\tlet anchorBlockId: BlockId;\n\t\tif (isTextSelection(sel) && !isCollapsed(sel)) {\n\t\t\tconst landingId: BlockId | undefined = addDeleteSelectionSteps(state, builder);\n\t\t\tanchorBlockId = landingId ?? sel.anchor.blockId;\n\t\t} else {\n\t\t\tanchorBlockId = resolveAnchorBlockId(sel);\n\t\t}\n\n\t\tconst cellId: BlockId | undefined = findTableCellAncestor(state, anchorBlockId);\n\t\tconst schemaRegistry = this.context?.getSchemaRegistry();\n\n\t\tconst ctx: InsertionContext | undefined = cellId\n\t\t\t? resolveCellInsertionContext(state, anchorBlockId, cellId, schemaRegistry)\n\t\t\t: resolveRootInsertionContext(state, anchorBlockId, schemaRegistry);\n\t\tif (!ctx) return null;\n\n\t\tconst insertOffset: number = !cellId && isGapCursor(sel) && sel.side === 'before' ? 0 : 1;\n\t\tlet insertIndex: number = ctx.anchorIndex + insertOffset;\n\t\tlet lastBlockId: BlockId | undefined;\n\t\tlet lastBlockTextLen = 0;\n\n\t\tfor (const segment of segments) {\n\t\t\tconst block: BlockNode = segment.detection\n\t\t\t\t? this.createCodeBlockNode(segment.detection)\n\t\t\t\t: this.createParagraphNode(segment.text);\n\n\t\t\tbuilder.insertNode(ctx.parentPath, insertIndex, block);\n\t\t\tinsertIndex++;\n\t\t\tlastBlockId = block.id;\n\t\t\tlastBlockTextLen = segment.detection\n\t\t\t\t? segment.detection.formattedText.length\n\t\t\t\t: segment.text.length;\n\t\t}\n\n\t\tconst shouldRemoveAnchor: boolean = cellId\n\t\t\t? ctx.isAnchorEmpty && ctx.anchorIndex >= 0\n\t\t\t: ctx.isAnchorEmpty && !isGapCursor(sel);\n\n\t\tif (shouldRemoveAnchor) {\n\t\t\tbuilder.removeNode(ctx.parentPath, ctx.anchorIndex);\n\t\t}\n\n\t\tif (lastBlockId) {\n\t\t\tbuilder.setSelection(createCollapsedSelection(lastBlockId, lastBlockTextLen));\n\t\t}\n\n\t\treturn builder.build();\n\t}\n\n\tprivate createCodeBlockNode(detection: DetectionResult): BlockNode {\n\t\treturn createBlockNode(\n\t\t\tnodeType('code_block') as NodeTypeName,\n\t\t\t[createTextNode(detection.formattedText)],\n\t\t\tgenerateBlockId(),\n\t\t\t{ language: detection.language, backgroundColor: '' },\n\t\t);\n\t}\n\n\tprivate createParagraphNode(text: string): BlockNode {\n\t\treturn createBlockNode(\n\t\t\tnodeType('paragraph') as NodeTypeName,\n\t\t\t[createTextNode(text)],\n\t\t\tgenerateBlockId(),\n\t\t);\n\t}\n\n\tprivate announceMixedContent(segments: readonly PasteSegment[]): void {\n\t\tlet textCount = 0;\n\t\tlet codeCount = 0;\n\t\tconst languages: string[] = [];\n\n\t\tfor (const segment of segments) {\n\t\t\tif (segment.detection) {\n\t\t\t\tcodeCount++;\n\t\t\t\tif (!languages.includes(segment.detection.language)) {\n\t\t\t\t\tlanguages.push(segment.detection.language);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttextCount++;\n\t\t\t}\n\t\t}\n\n\t\tthis.context?.announce(this.locale.detectedMixedContent(textCount, codeCount, languages));\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{n as t}from"./Plugin-C4cBw3bs.mjs";import{n as e,t as s}from"./XmlPatterns-B-V-mHF4.mjs";var n=new t("languageRegistry"),r=/(?:(?:public|private|protected|abstract|final|sealed|non-sealed)\s+)?(?:class|interface|enum|record)\s+\w+/,l=/^package\s+[\w.]+\s*;/m,i=/^import\s+(?:static\s+)?[\w.*]+\s*;/m,a=/(?:public|private|protected|static|final|abstract|synchronized|native)\s+[\w<>\[\]?,][\w<>\[\]?,\s]*\s+\w+\s*\(/,o=/^[ \t]*@\w+/m,c=/;\s*$/gm,u=/\s*[{}]\s*$/gm,m=/public\s+static\s+void\s+main\s*\(\s*String\s*\[\s*\]\s+\w+\s*\)/,g=/\b(?:String|Integer|Boolean|List|Map|Set|Optional|void|int|long|double|boolean|char|byte|float|short)\b/,p=/System\s*\.\s*(?:out|err)\s*\.\s*\w+\s*\(/,h=/\btry\s*\{[\s\S]*?\}\s*catch\s*\(/,f=/\bnew\s+[A-Z]\w*\s*[<(]/,d=class{id="java";detect(t){if(t.length>1e5)return null;const e=t.trim();return e.split("\n").filter(t=>t.trim().length>0).length<2||this.computeScore(e)<3?null:{language:"java",formattedText:e,confidence:.8}}computeScore(t){let e=0;return r.test(t)&&(e+=2),l.test(t)&&(e+=2),i.test(t)&&(e+=2),m.test(t)&&(e+=2),p.test(t)&&(e+=2),h.test(t)&&(e+=2),a.test(t)&&(e+=1),o.test(t)&&(e+=1),g.test(t)&&(e+=1),f.test(t)&&(e+=1),(t.match(c)??[]).length>=2&&(e+=1),(t.match(u)??[]).length>=2&&(e+=1),e}},x=class{id="json";detect(t){const e=t.trim();if(!e.startsWith("{")&&!e.startsWith("["))return null;try{const t=JSON.parse(e);if("object"!=typeof t||null===t)return null;const s=JSON.stringify(t,null,2);return s.length<=5?null:{language:"json",formattedText:s,confidence:.9}}catch{return null}}},w=/^(<\?[\s\S]*?\?>\s*)+/,b=new RegExp(`^<${e}${s}\\/?>`),S=new RegExp(["<\\?[^?]*\\?>","\x3c!--[\\s\\S]*?--\x3e","<!\\[CDATA\\[[\\s\\S]*?\\]\\]>",`<\\/(${e})>`,`<(${e})${s}\\/>`,`<(${e})${s}>`].join("|"),"g"),W=new RegExp(`<\\/${e}>\\s*$`),$=class{id="xml";detect(t){const e=t.trim();if(!e.startsWith("<"))return null;const s=e.replace(w,"");if(!b.test(s))return null;if(!this.isWellFormed(e))return null;const n=this.formatXml(e);return n.length<=10?null:{language:"xml",formattedText:n,confidence:.85}}isWellFormed(t){const e=[];let s=!1;S.lastIndex=0;let n=S.exec(t);for(;n;){const r=n[1],l=n[2],i=n[3];if(r){if(0===e.length||e[e.length-1]!==r)return!1;e.pop(),s=!0}else l?s=!0:i&&(e.push(i),s=!0);n=S.exec(t)}return s&&0===e.length}formatXml(t){const e=this.splitIntoTokens(t),s=[];let n=0;for(const r of e){const t=j[v(r)];"decrease"===t&&(n=Math.max(0,n-1)),s.push(" ".repeat(2*n)+r),"increase"===t&&n++}return s.join("\n")}splitIntoTokens(t){return t.replace(/>\s*</g,">\n<").split("\n").map(t=>t.trim()).filter(t=>t.length>0)}},j={pi:"none",comment:"none",cdata:"none",closing:"decrease",selfClosing:"none",inline:"none",opening:"increase",text:"none"};function v(t){return t.startsWith("<?")?"pi":t.startsWith("\x3c!--")?"comment":t.startsWith("<![CDATA[")?"cdata":t.startsWith("</")?"closing":t.endsWith("/>")?"selfClosing":t.startsWith("<")&&W.test(t)?"inline":t.startsWith("<")?"opening":"text"}export{n as i,x as n,d as r,$ as t};
|
|
2
|
+
//# sourceMappingURL=XmlDetector-B1y1NzDL.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XmlDetector-B1y1NzDL.mjs","names":[],"sources":["../../src/plugins/language/LanguageTypes.ts","../../src/plugins/smart-paste/detectors/JavaDetector.ts","../../src/plugins/smart-paste/detectors/JsonDetector.ts","../../src/plugins/smart-paste/detectors/XmlDetector.ts"],"sourcesContent":["/**\n * Unified language support types for programming language registration.\n *\n * Bundles syntax highlighting and paste detection into a single\n * registration unit so that all artifacts for one language live together.\n */\n\nimport { ServiceKey } from '../Plugin.js';\nimport type { LanguageDefinition } from '../code-block/highlighter/TokenizerTypes.js';\nimport type { ContentDetector } from '../smart-paste/SmartPasteTypes.js';\n\n/**\n * Unified language support bundle grouping all artifacts for one\n * programming language: syntax highlighting, paste detection, and\n * (optionally) code formatting.\n *\n * Pass to `LanguageRegistryService.register()` to make a language\n * available across all participating plugins in a single call.\n */\nexport interface LanguageSupport {\n\t/** Canonical lowercase identifier (e.g. `'java'`, `'python'`). */\n\treadonly id: string;\n\n\t/** Human-readable name shown in the language picker (e.g. `'Java'`). */\n\treadonly displayName: string;\n\n\t/** Alternative names accepted as aliases (e.g. `['py', 'py3']`). */\n\treadonly aliases?: readonly string[];\n\n\t/** Syntax highlighting token patterns for the RegexTokenizer. */\n\treadonly highlighting?: LanguageDefinition;\n\n\t/** Smart-paste detector for auto-detecting this language in pasted text. */\n\treadonly detection?: ContentDetector;\n\n\t/**\n\t * Code formatter for reformatting code block content on demand.\n\t * @experimental Not yet consumed by any built-in plugin.\n\t */\n\treadonly formatter?: CodeFormatter;\n}\n\n/**\n * Formatter that can reformat source code.\n * @experimental Reserved for future use.\n */\nexport interface CodeFormatter {\n\tformat(code: string): string | Promise<string>;\n}\n\n/** Service for registering and querying language support bundles. */\nexport interface LanguageRegistryService {\n\t/** Registers a language bundle. Re-registering the same `id` overwrites. */\n\tregister(support: LanguageSupport): void;\n\n\t/**\n\t * Subscribes to language registrations with replay semantics.\n\t * The listener is immediately called for all already-registered\n\t * bundles, then called for each future registration.\n\t */\n\tonRegister(listener: LanguageRegistryListener): void;\n\n\t/** Returns the bundle for the given language id, or `undefined`. */\n\tget(id: string): LanguageSupport | undefined;\n\n\t/** Returns all registered language support bundles. */\n\tgetAll(): readonly LanguageSupport[];\n}\n\n/** Internal listener type for registry change notifications. */\nexport type LanguageRegistryListener = (support: LanguageSupport) => void;\n\nexport const LANGUAGE_REGISTRY_SERVICE_KEY = new ServiceKey<LanguageRegistryService>(\n\t'languageRegistry',\n);\n","/**\n * Java content detector - identifies Java source code for code block insertion.\n *\n * Uses a scoring system based on structural patterns:\n * - Class/interface/enum/record declarations\n * - Package and import statements\n * - Method signatures with access modifiers\n * - Java-specific keywords and syntax patterns\n *\n * Requires multiple signals to avoid false positives on plain text.\n */\n\nimport type { ContentDetector, DetectionResult } from '../SmartPasteTypes.js';\n\n/** Maximum text length to analyze for Java detection. */\nconst MAX_DETECT_LENGTH = 100_000;\n\n/** Minimum number of non-empty lines to consider as code. */\nconst MIN_LINES = 2;\n\n/** Minimum score to consider the text as Java code. */\nconst MIN_SCORE = 3;\n\n/** Confidence score for detected Java code. */\nconst JAVA_CONFIDENCE = 0.8;\n\n/** Matches class, interface, enum, record declarations. */\nconst CLASS_DECLARATION =\n\t/(?:(?:public|private|protected|abstract|final|sealed|non-sealed)\\s+)?(?:class|interface|enum|record)\\s+\\w+/;\n\n/** Matches package statements. */\nconst PACKAGE_STATEMENT = /^package\\s+[\\w.]+\\s*;/m;\n\n/** Matches import statements. */\nconst IMPORT_STATEMENT = /^import\\s+(?:static\\s+)?[\\w.*]+\\s*;/m;\n\n/** Matches method signatures with return type. */\nconst METHOD_SIGNATURE =\n\t/(?:public|private|protected|static|final|abstract|synchronized|native)\\s+[\\w<>\\[\\]?,][\\w<>\\[\\]?,\\s]*\\s+\\w+\\s*\\(/;\n\n/** Matches annotation usage. */\nconst ANNOTATION = /^[ \\t]*@\\w+/m;\n\n/** Matches lines ending with semicolons (excluding CSS-like patterns). */\nconst SEMICOLON_LINES = /;\\s*$/gm;\n\n/** Matches Java-style braces on their own line or after code. */\nconst BRACE_PATTERN = /\\s*[{}]\\s*$/gm;\n\n/** Matches Java main method. */\nconst MAIN_METHOD = /public\\s+static\\s+void\\s+main\\s*\\(\\s*String\\s*\\[\\s*\\]\\s+\\w+\\s*\\)/;\n\n/** Matches common Java types. */\nconst JAVA_TYPES =\n\t/\\b(?:String|Integer|Boolean|List|Map|Set|Optional|void|int|long|double|boolean|char|byte|float|short)\\b/;\n\n/** Matches System.out or System.err calls. */\nconst SYSTEM_CALL = /System\\s*\\.\\s*(?:out|err)\\s*\\.\\s*\\w+\\s*\\(/;\n\n/** Matches try-catch blocks. */\nconst TRY_CATCH = /\\btry\\s*\\{[\\s\\S]*?\\}\\s*catch\\s*\\(/;\n\n/** Matches new keyword with type. */\nconst NEW_INSTANCE = /\\bnew\\s+[A-Z]\\w*\\s*[<(]/;\n\nexport class JavaDetector implements ContentDetector {\n\treadonly id = 'java';\n\n\tdetect(text: string): DetectionResult | null {\n\t\tif (text.length > MAX_DETECT_LENGTH) return null;\n\t\tconst trimmed: string = text.trim();\n\t\tconst lines: string[] = trimmed.split('\\n').filter((l: string) => l.trim().length > 0);\n\t\tif (lines.length < MIN_LINES) return null;\n\n\t\tconst score: number = this.computeScore(trimmed);\n\t\tif (score < MIN_SCORE) return null;\n\n\t\treturn {\n\t\t\tlanguage: 'java',\n\t\t\tformattedText: trimmed,\n\t\t\tconfidence: JAVA_CONFIDENCE,\n\t\t};\n\t}\n\n\tprivate computeScore(text: string): number {\n\t\tlet score = 0;\n\n\t\t// Strong signals (2 points each)\n\t\tif (CLASS_DECLARATION.test(text)) score += 2;\n\t\tif (PACKAGE_STATEMENT.test(text)) score += 2;\n\t\tif (IMPORT_STATEMENT.test(text)) score += 2;\n\t\tif (MAIN_METHOD.test(text)) score += 2;\n\t\tif (SYSTEM_CALL.test(text)) score += 2;\n\t\tif (TRY_CATCH.test(text)) score += 2;\n\n\t\t// Medium signals (1 point each)\n\t\tif (METHOD_SIGNATURE.test(text)) score += 1;\n\t\tif (ANNOTATION.test(text)) score += 1;\n\t\tif (JAVA_TYPES.test(text)) score += 1;\n\t\tif (NEW_INSTANCE.test(text)) score += 1;\n\n\t\t// Structural signals\n\t\tconst semicolonCount: number = (text.match(SEMICOLON_LINES) ?? []).length;\n\t\tif (semicolonCount >= 2) score += 1;\n\n\t\tconst braceCount: number = (text.match(BRACE_PATTERN) ?? []).length;\n\t\tif (braceCount >= 2) score += 1;\n\n\t\treturn score;\n\t}\n}\n","/**\n * JSON content detector — validates and formats JSON for code block insertion.\n */\n\nimport type { ContentDetector, DetectionResult } from '../SmartPasteTypes.js';\n\n/** Minimum length for formatted JSON to be considered meaningful. */\nconst MIN_FORMATTED_LENGTH = 5;\n\n/** Confidence score for valid JSON detected via JSON.parse. */\nconst JSON_CONFIDENCE = 0.9;\n\nexport class JsonDetector implements ContentDetector {\n\treadonly id = 'json';\n\n\tdetect(text: string): DetectionResult | null {\n\t\tconst trimmed: string = text.trim();\n\t\tif (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return null;\n\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(trimmed);\n\t\t\tif (typeof parsed !== 'object' || parsed === null) return null;\n\n\t\t\tconst formatted: string = JSON.stringify(parsed, null, 2);\n\t\t\tif (formatted.length <= MIN_FORMATTED_LENGTH) return null;\n\n\t\t\treturn { language: 'json', formattedText: formatted, confidence: JSON_CONFIDENCE };\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n","/**\n * XML content detector — validates and formats XML for code block insertion.\n */\n\nimport { XML_ATTRS, XML_TAG_NAME } from '../../shared/XmlPatterns.js';\nimport type { ContentDetector, DetectionResult } from '../SmartPasteTypes.js';\n\n/** Minimum length for formatted XML to be considered meaningful. */\nconst MIN_FORMATTED_LENGTH = 10;\n\n/** Confidence score for valid XML detected via structural analysis. */\nconst XML_CONFIDENCE = 0.85;\n\n/** Matches any leading processing instructions like <?xml ...?> or <?xml-stylesheet ...?> */\nconst LEADING_PIS_PATTERN = /^(<\\?[\\s\\S]*?\\?>\\s*)+/;\n\n/** Matches an opening tag like <root> or <ns:tag attr=\"val\"> */\nconst OPENING_TAG_PATTERN = new RegExp(`^<${XML_TAG_NAME}${XML_ATTRS}\\\\/?>`);\n\n// --- Well-formedness regex (decomposed for readability) ---\n\nconst PI_FRAGMENT = '<\\\\?[^?]*\\\\?>';\nconst COMMENT_FRAGMENT = '<!--[\\\\s\\\\S]*?-->';\nconst CDATA_FRAGMENT = '<!\\\\[CDATA\\\\[[\\\\s\\\\S]*?\\\\]\\\\]>';\nconst CLOSING_TAG_FRAGMENT = `<\\\\/(${XML_TAG_NAME})>`;\nconst SELF_CLOSING_TAG_FRAGMENT = `<(${XML_TAG_NAME})${XML_ATTRS}\\\\/>`;\nconst OPENING_TAG_FRAGMENT = `<(${XML_TAG_NAME})${XML_ATTRS}>`;\n\nconst WELL_FORMED_TAG_PATTERN = new RegExp(\n\t[\n\t\tPI_FRAGMENT,\n\t\tCOMMENT_FRAGMENT,\n\t\tCDATA_FRAGMENT,\n\t\tCLOSING_TAG_FRAGMENT,\n\t\tSELF_CLOSING_TAG_FRAGMENT,\n\t\tOPENING_TAG_FRAGMENT,\n\t].join('|'),\n\t'g',\n);\n\n/** Matches an inline element where open + close tag appear on the same token. */\nconst INLINE_ELEMENT_PATTERN = new RegExp(`<\\\\/${XML_TAG_NAME}>\\\\s*$`);\n\n/**\n * Detects well-formed XML content and formats it with proper indentation.\n *\n * Detection strategy:\n * 1. Text must start with `<` (after trimming)\n * 2. Must begin with an XML declaration, processing instruction, or opening tag\n * 3. All opened tags must be properly closed (basic well-formedness check)\n * 4. Must contain at least one tag pair or self-closing tag\n */\nexport class XmlDetector implements ContentDetector {\n\treadonly id = 'xml';\n\n\tdetect(text: string): DetectionResult | null {\n\t\tconst trimmed: string = text.trim();\n\t\tif (!trimmed.startsWith('<')) return null;\n\n\t\t// Quick structural check: strip leading PIs, then must start with an opening tag\n\t\tconst remainder: string = trimmed.replace(LEADING_PIS_PATTERN, '');\n\t\tif (!OPENING_TAG_PATTERN.test(remainder)) return null;\n\n\t\t// Validate well-formedness via tag balancing\n\t\tif (!this.isWellFormed(trimmed)) return null;\n\n\t\tconst formatted: string = this.formatXml(trimmed);\n\t\tif (formatted.length <= MIN_FORMATTED_LENGTH) return null;\n\n\t\treturn { language: 'xml', formattedText: formatted, confidence: XML_CONFIDENCE };\n\t}\n\n\t/**\n\t * Checks basic XML well-formedness by verifying that all opened tags\n\t * are properly closed in the correct nesting order.\n\t */\n\tprivate isWellFormed(xml: string): boolean {\n\t\tconst tagStack: string[] = [];\n\t\tlet foundTag = false;\n\n\t\t// Reset lastIndex — RegExp with 'g' flag is stateful\n\t\tWELL_FORMED_TAG_PATTERN.lastIndex = 0;\n\t\tlet match: RegExpExecArray | null = WELL_FORMED_TAG_PATTERN.exec(xml);\n\n\t\twhile (match) {\n\t\t\tconst closingTag: string | undefined = match[1];\n\t\t\tconst selfClosingTag: string | undefined = match[2];\n\t\t\tconst openingTag: string | undefined = match[3];\n\n\t\t\tif (closingTag) {\n\t\t\t\tif (tagStack.length === 0 || tagStack[tagStack.length - 1] !== closingTag) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\ttagStack.pop();\n\t\t\t\tfoundTag = true;\n\t\t\t} else if (selfClosingTag) {\n\t\t\t\tfoundTag = true;\n\t\t\t} else if (openingTag) {\n\t\t\t\ttagStack.push(openingTag);\n\t\t\t\tfoundTag = true;\n\t\t\t}\n\n\t\t\tmatch = WELL_FORMED_TAG_PATTERN.exec(xml);\n\t\t}\n\n\t\treturn foundTag && tagStack.length === 0;\n\t}\n\n\t/**\n\t * Formats XML with consistent 2-space indentation.\n\t */\n\tprivate formatXml(xml: string): string {\n\t\tconst tokens: string[] = this.splitIntoTokens(xml);\n\t\tconst parts: string[] = [];\n\t\tlet indent = 0;\n\t\tconst INDENT_STEP = 2;\n\n\t\tfor (const token of tokens) {\n\t\t\tconst kind: XmlTokenKind = classifyXmlToken(token);\n\t\t\tconst indentChange: IndentChange = INDENT_BEHAVIOR[kind];\n\n\t\t\tif (indentChange === 'decrease') {\n\t\t\t\tindent = Math.max(0, indent - 1);\n\t\t\t}\n\n\t\t\tparts.push(' '.repeat(indent * INDENT_STEP) + token);\n\n\t\t\tif (indentChange === 'increase') {\n\t\t\t\tindent++;\n\t\t\t}\n\t\t}\n\n\t\treturn parts.join('\\n');\n\t}\n\n\t/** Splits XML into line-level tokens by breaking between adjacent tags. */\n\tprivate splitIntoTokens(xml: string): string[] {\n\t\treturn xml\n\t\t\t.replace(/>\\s*</g, '>\\n<')\n\t\t\t.split('\\n')\n\t\t\t.map((line: string) => line.trim())\n\t\t\t.filter((line: string) => line.length > 0);\n\t}\n}\n\n// --- Token Classification ---\n\ntype XmlTokenKind =\n\t| 'pi'\n\t| 'comment'\n\t| 'cdata'\n\t| 'closing'\n\t| 'selfClosing'\n\t| 'inline'\n\t| 'opening'\n\t| 'text';\ntype IndentChange = 'increase' | 'decrease' | 'none';\n\nconst INDENT_BEHAVIOR: Readonly<Record<XmlTokenKind, IndentChange>> = {\n\tpi: 'none',\n\tcomment: 'none',\n\tcdata: 'none',\n\tclosing: 'decrease',\n\tselfClosing: 'none',\n\tinline: 'none',\n\topening: 'increase',\n\ttext: 'none',\n};\n\nfunction classifyXmlToken(token: string): XmlTokenKind {\n\tif (token.startsWith('<?')) return 'pi';\n\tif (token.startsWith('<!--')) return 'comment';\n\tif (token.startsWith('<![CDATA[')) return 'cdata';\n\tif (token.startsWith('</')) return 'closing';\n\tif (token.endsWith('/>')) return 'selfClosing';\n\tif (token.startsWith('<') && INLINE_ELEMENT_PATTERN.test(token)) return 'inline';\n\tif (token.startsWith('<')) return 'opening';\n\treturn 'text';\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{n as t,r as e,t as n}from"./XmlDetector-
|
|
2
|
-
//# sourceMappingURL=XmlSupport-
|
|
1
|
+
import{n as t,r as e,t as n}from"./XmlDetector-B1y1NzDL.mjs";import{n as a}from"./XmlPatterns-B-V-mHF4.mjs";var p={name:"java",aliases:[],patterns:[{type:"comment",pattern:/^\/\*[\s\S]*?\*\//},{type:"comment",pattern:/^\/\/[^\n]*/},{type:"string",pattern:/^"""[\s\S]*?"""/},{type:"string",pattern:/^"(?:[^"\\]|\\.)*"/},{type:"string",pattern:/^'(?:[^'\\]|\\.)*'/},{type:"annotation",pattern:/^@[a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*/},{type:"keyword",pattern:new RegExp(`^(?:${["abstract","assert","break","case","catch","class","const","continue","default","do","else","enum","exports","extends","final","finally","for","goto","if","implements","import","instanceof","interface","module","native","new","non-sealed","open","opens","package","permits","private","protected","provides","public","record","requires","return","sealed","static","strictfp","super","switch","synchronized","this","throw","throws","to","transient","transitive","try","uses","var","void","volatile","when","while","with","yield"].join("|")})\\b`)},{type:"boolean",pattern:/^(?:true|false)\b/},{type:"null",pattern:/^null\b/},{type:"number",pattern:/^0[xX][\da-fA-F](?:_*[\da-fA-F])*[lL]?/},{type:"number",pattern:/^0[bB][01](?:_*[01])*[lL]?/},{type:"number",pattern:/^\d(?:_*\d)*(?:\.(?:\d(?:_*\d)*)?)?(?:[eE][+-]?\d(?:_*\d)*)?[fFdDlL]?/},{type:"function",pattern:/^[a-zA-Z_]\w*(?=\s*\()/},{type:"operator",pattern:/^(?:>>>=?|>>=?|<<=?|->|::|\+\+|--|&&|\|\||[+\-*/%&|^~!=<>]=?|\?|:)/},{type:"punctuation",pattern:/^[{}[\]();.,]/}]},r={id:"java",displayName:"Java",highlighting:p,detection:new e},s={name:"json",aliases:["jsonc"],patterns:[{type:"property",pattern:/^"(?:[^"\\]|\\.)*"(?=\s*:)/},{type:"string",pattern:/^"(?:[^"\\]|\\.)*"/},{type:"number",pattern:/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/},{type:"boolean",pattern:/^(?:true|false)\b/},{type:"null",pattern:/^null\b/},{type:"punctuation",pattern:/^[{}[\]:,]/}]},i={id:"json",displayName:"JSON",aliases:["jsonc"],highlighting:s,detection:new t},o={name:"xml",aliases:["html","xhtml","svg","xsl"],patterns:[{type:"comment",pattern:/^<!--[\s\S]*?-->/},{type:"keyword",pattern:/^<!\[CDATA\[[\s\S]*?\]\]>/},{type:"keyword",pattern:/^<\?[\s\S]*?\?>/},{type:"tag",pattern:new RegExp(`^<\\/${a}`)},{type:"tag",pattern:new RegExp(`^<${a}`)},{type:"punctuation",pattern:/^\/>|^>|^=/},{type:"attribute",pattern:new RegExp(`^${a}(?=\\s*=)`)},{type:"string",pattern:/^"[^"]*"/},{type:"string",pattern:/^'[^']*'/}]},l={id:"xml",displayName:"XML",aliases:["html","xhtml","svg","xsl"],highlighting:o,detection:new n};export{r as a,s as i,o as n,p as o,i as r,l as t};
|
|
2
|
+
//# sourceMappingURL=XmlSupport-DRkIIe34.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XmlSupport-
|
|
1
|
+
{"version":3,"file":"XmlSupport-DRkIIe34.mjs","names":[],"sources":["../../src/plugins/code-block/highlighter/languages/java.ts","../../src/plugins/language/bundles/JavaSupport.ts","../../src/plugins/code-block/highlighter/languages/json.ts","../../src/plugins/language/bundles/JsonSupport.ts","../../src/plugins/code-block/highlighter/languages/xml.ts","../../src/plugins/language/bundles/XmlSupport.ts"],"sourcesContent":["/**\n * Java language definition for regex-based syntax highlighting.\n *\n * Covers Java 21 syntax including sealed classes, records, pattern matching,\n * modules, text blocks, and switch expressions.\n *\n * Token type mapping:\n * - `comment` - line `//`, block, and Javadoc comments\n * - `string` - double-quoted strings, text blocks, char literals\n * - `annotation` - annotations `@Name`, `@java.lang.Override`\n * - `keyword` - Java 21 keywords (including contextual: sealed, record, var, yield, when)\n * - `boolean` - `true`, `false`\n * - `null` - `null`\n * - `number` - decimal, hex, binary, float, scientific, with underscores\n * - `function` - method/function names (identifier followed by `(`)\n * - `operator` - all Java operators including `->`, `::`, `>>>=`\n * - `punctuation` - delimiters `{}[]();.,`\n */\n\nimport type { LanguageDefinition } from '../TokenizerTypes.js';\n\nconst JAVA_KEYWORDS: readonly string[] = [\n\t'abstract',\n\t'assert',\n\t'break',\n\t'case',\n\t'catch',\n\t'class',\n\t'const',\n\t'continue',\n\t'default',\n\t'do',\n\t'else',\n\t'enum',\n\t'exports',\n\t'extends',\n\t'final',\n\t'finally',\n\t'for',\n\t'goto',\n\t'if',\n\t'implements',\n\t'import',\n\t'instanceof',\n\t'interface',\n\t'module',\n\t'native',\n\t'new',\n\t'non-sealed',\n\t'open',\n\t'opens',\n\t'package',\n\t'permits',\n\t'private',\n\t'protected',\n\t'provides',\n\t'public',\n\t'record',\n\t'requires',\n\t'return',\n\t'sealed',\n\t'static',\n\t'strictfp',\n\t'super',\n\t'switch',\n\t'synchronized',\n\t'this',\n\t'throw',\n\t'throws',\n\t'to',\n\t'transient',\n\t'transitive',\n\t'try',\n\t'uses',\n\t'var',\n\t'void',\n\t'volatile',\n\t'when',\n\t'while',\n\t'with',\n\t'yield',\n];\n\nconst KEYWORD_PATTERN: RegExp = new RegExp(`^(?:${JAVA_KEYWORDS.join('|')})\\\\b`);\n\nexport const JAVA_LANGUAGE: LanguageDefinition = {\n\tname: 'java',\n\taliases: [],\n\tpatterns: [\n\t\t// 1. Block/Javadoc comments — before `/` operator\n\t\t{ type: 'comment', pattern: /^\\/\\*[\\s\\S]*?\\*\\// },\n\t\t// 2. Line comments — before `/` operator\n\t\t{ type: 'comment', pattern: /^\\/\\/[^\\n]*/ },\n\t\t// 3. Text blocks `\"\"\"...\"\"\"` — before empty string `\"\"`\n\t\t{ type: 'string', pattern: /^\"\"\"[\\s\\S]*?\"\"\"/ },\n\t\t// 4. Double-quoted strings — escape-aware\n\t\t{ type: 'string', pattern: /^\"(?:[^\"\\\\]|\\\\.)*\"/ },\n\t\t// 5. Char literals — escape-aware\n\t\t{ type: 'string', pattern: /^'(?:[^'\\\\]|\\\\.)*'/ },\n\t\t// 6. Annotations — before operators (captures `@`)\n\t\t{ type: 'annotation', pattern: /^@[a-zA-Z_]\\w*(?:\\.[a-zA-Z_]\\w*)*/ },\n\t\t// 7. Keywords — before functions (`if(` = keyword, not function)\n\t\t{ type: 'keyword', pattern: KEYWORD_PATTERN },\n\t\t// 8. Booleans\n\t\t{ type: 'boolean', pattern: /^(?:true|false)\\b/ },\n\t\t// 9. Null\n\t\t{ type: 'null', pattern: /^null\\b/ },\n\t\t// 10. Hex numbers — before decimal (both can start with `0`)\n\t\t{ type: 'number', pattern: /^0[xX][\\da-fA-F](?:_*[\\da-fA-F])*[lL]?/ },\n\t\t// 11. Binary numbers — before decimal\n\t\t{ type: 'number', pattern: /^0[bB][01](?:_*[01])*[lL]?/ },\n\t\t// 12. Decimal/float/scientific numbers — with underscore and suffix support\n\t\t{\n\t\t\ttype: 'number',\n\t\t\tpattern: /^\\d(?:_*\\d)*(?:\\.(?:\\d(?:_*\\d)*)?)?(?:[eE][+-]?\\d(?:_*\\d)*)?[fFdDlL]?/,\n\t\t},\n\t\t// 13. Function/method names — identifier followed by `(`\n\t\t{ type: 'function', pattern: /^[a-zA-Z_]\\w*(?=\\s*\\()/ },\n\t\t// 14. Operators\n\t\t{\n\t\t\ttype: 'operator',\n\t\t\tpattern: /^(?:>>>=?|>>=?|<<=?|->|::|\\+\\+|--|&&|\\|\\||[+\\-*/%&|^~!=<>]=?|\\?|:)/,\n\t\t},\n\t\t// 15. Punctuation — catch-all delimiters\n\t\t{ type: 'punctuation', pattern: /^[{}[\\]();.,]/ },\n\t],\n};\n","/**\n * Built-in Java language support bundle.\n * Includes syntax highlighting (Java 21) and smart-paste detection.\n */\n\nimport { JAVA_LANGUAGE } from '../../code-block/highlighter/languages/java.js';\nimport { JavaDetector } from '../../smart-paste/detectors/JavaDetector.js';\nimport type { LanguageSupport } from '../LanguageTypes.js';\n\nexport const JAVA_SUPPORT: LanguageSupport = {\n\tid: 'java',\n\tdisplayName: 'Java',\n\thighlighting: JAVA_LANGUAGE,\n\tdetection: new JavaDetector(),\n};\n","/**\n * JSON language definition for regex-based syntax highlighting.\n */\n\nimport type { LanguageDefinition } from '../TokenizerTypes.js';\n\nexport const JSON_LANGUAGE: LanguageDefinition = {\n\tname: 'json',\n\taliases: ['jsonc'],\n\tpatterns: [\n\t\t{ type: 'property', pattern: /^\"(?:[^\"\\\\]|\\\\.)*\"(?=\\s*:)/ },\n\t\t{ type: 'string', pattern: /^\"(?:[^\"\\\\]|\\\\.)*\"/ },\n\t\t{ type: 'number', pattern: /^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?/ },\n\t\t{ type: 'boolean', pattern: /^(?:true|false)\\b/ },\n\t\t{ type: 'null', pattern: /^null\\b/ },\n\t\t{ type: 'punctuation', pattern: /^[{}[\\]:,]/ },\n\t],\n};\n","/**\n * Built-in JSON language support bundle.\n * Includes syntax highlighting and smart-paste detection.\n */\n\nimport { JSON_LANGUAGE } from '../../code-block/highlighter/languages/json.js';\nimport { JsonDetector } from '../../smart-paste/detectors/JsonDetector.js';\nimport type { LanguageSupport } from '../LanguageTypes.js';\n\nexport const JSON_SUPPORT: LanguageSupport = {\n\tid: 'json',\n\tdisplayName: 'JSON',\n\taliases: ['jsonc'],\n\thighlighting: JSON_LANGUAGE,\n\tdetection: new JsonDetector(),\n};\n","/**\n * XML language definition for regex-based syntax highlighting.\n *\n * Token type mapping:\n * - `comment` — XML comments `<!-- ... -->`\n * - `keyword` — processing instructions, CDATA sections\n * - `tag` — tag names (consumed with `<`/`</` prefix)\n * - `attribute` — attribute names (identifier followed by `=`)\n * - `string` — attribute values (double or single quoted)\n * - `punctuation` — delimiters `/>`, `>`, `=`\n *\n * Tag names are combined with their `<` or `</` prefix into a single `tag` token\n * to prevent text content between tags from being incorrectly highlighted.\n */\n\nimport { XML_TAG_NAME } from '../../../shared/XmlPatterns.js';\nimport type { LanguageDefinition } from '../TokenizerTypes.js';\n\nexport const XML_LANGUAGE: LanguageDefinition = {\n\tname: 'xml',\n\taliases: ['html', 'xhtml', 'svg', 'xsl'],\n\tpatterns: [\n\t\t{ type: 'comment', pattern: /^<!--[\\s\\S]*?-->/ },\n\t\t{ type: 'keyword', pattern: /^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>/ },\n\t\t{ type: 'keyword', pattern: /^<\\?[\\s\\S]*?\\?>/ },\n\t\t{ type: 'tag', pattern: new RegExp(`^<\\\\/${XML_TAG_NAME}`) },\n\t\t{ type: 'tag', pattern: new RegExp(`^<${XML_TAG_NAME}`) },\n\t\t{ type: 'punctuation', pattern: /^\\/>|^>|^=/ },\n\t\t{ type: 'attribute', pattern: new RegExp(`^${XML_TAG_NAME}(?=\\\\s*=)`) },\n\t\t{ type: 'string', pattern: /^\"[^\"]*\"/ },\n\t\t{ type: 'string', pattern: /^'[^']*'/ },\n\t],\n};\n","/**\n * Built-in XML language support bundle.\n * Includes syntax highlighting and smart-paste detection.\n */\n\nimport { XML_LANGUAGE } from '../../code-block/highlighter/languages/xml.js';\nimport { XmlDetector } from '../../smart-paste/detectors/XmlDetector.js';\nimport type { LanguageSupport } from '../LanguageTypes.js';\n\nexport const XML_SUPPORT: LanguageSupport = {\n\tid: 'xml',\n\tdisplayName: 'XML',\n\taliases: ['html', 'xhtml', 'svg', 'xsl'],\n\thighlighting: XML_LANGUAGE,\n\tdetection: new XmlDetector(),\n};\n"],"mappings":"4GAmFA,IAEa,EAAoC,CAChD,KAAM,OACN,QAAS,GACT,SAAU,CAET,CAAE,KAAM,UAAW,QAAS,qBAE5B,CAAE,KAAM,UAAW,QAAS,eAE5B,CAAE,KAAM,SAAU,QAAS,mBAE3B,CAAE,KAAM,SAAU,QAAS,sBAE3B,CAAE,KAAM,SAAU,QAAS,sBAE3B,CAAE,KAAM,aAAc,QAAS,qCAE/B,CAAE,KAAM,UAAW,QAnBW,IAAI,OAAO,OA9DF,CACxC,WACA,SACA,QACA,OACA,QACA,QACA,QACA,WACA,UACA,KACA,OACA,OACA,UACA,UACA,QACA,UACA,MACA,OACA,KACA,aACA,SACA,aACA,YACA,SACA,SACA,MACA,aACA,OACA,QACA,UACA,UACA,UACA,YACA,WACA,SACA,SACA,WACA,SACA,SACA,SACA,WACA,QACA,SACA,eACA,OACA,QACA,SACA,KACA,YACA,aACA,MACA,OACA,MACA,OACA,WACA,OACA,QACA,OACA,SAG+D,KAAK,aAqBnE,CAAE,KAAM,UAAW,QAAS,qBAE5B,CAAE,KAAM,OAAQ,QAAS,WAEzB,CAAE,KAAM,SAAU,QAAS,0CAE3B,CAAE,KAAM,SAAU,QAAS,8BAE3B,CACC,KAAM,SACN,QAAS,yEAGV,CAAE,KAAM,WAAY,QAAS,0BAE7B,CACC,KAAM,WACN,QAAS,sEAGV,CAAE,KAAM,cAAe,QAAS,mBCnHrB,EAAgC,CAC5C,GAAI,OACJ,YAAa,OACb,aAAc,EACd,UAAW,IAAI,GCPH,EAAoC,CAChD,KAAM,OACN,QAAS,CAAC,SACV,SAAU,CACT,CAAE,KAAM,WAAY,QAAS,8BAC7B,CAAE,KAAM,SAAU,QAAS,sBAC3B,CAAE,KAAM,SAAU,QAAS,gDAC3B,CAAE,KAAM,UAAW,QAAS,qBAC5B,CAAE,KAAM,OAAQ,QAAS,WACzB,CAAE,KAAM,cAAe,QAAS,gBCNrB,EAAgC,CAC5C,GAAI,OACJ,YAAa,OACb,QAAS,CAAC,SACV,aAAc,EACd,UAAW,IAAI,GCIH,EAAmC,CAC/C,KAAM,MACN,QAAS,CAAC,OAAQ,QAAS,MAAO,OAClC,SAAU,CACT,CAAE,KAAM,UAAW,QAAS,oBAC5B,CAAE,KAAM,UAAW,QAAS,6BAC5B,CAAE,KAAM,UAAW,QAAS,mBAC5B,CAAE,KAAM,MAAO,QAAS,IAAI,OAAO,QAAQ,MAC3C,CAAE,KAAM,MAAO,QAAS,IAAI,OAAO,KAAK,MACxC,CAAE,KAAM,cAAe,QAAS,cAChC,CAAE,KAAM,YAAa,QAAS,IAAI,OAAO,IAAI,eAC7C,CAAE,KAAM,SAAU,QAAS,YAC3B,CAAE,KAAM,SAAU,QAAS,cCrBhB,EAA+B,CAC3C,GAAI,MACJ,YAAa,MACb,QAAS,CAAC,OAAQ,QAAS,MAAO,OAClC,aAAc,EACd,UAAW,IAAI"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t}from"./TextFormattingPlugin-CYVnhHH4.mjs";import{t as o}from"./FontPlugin-
|
|
2
|
-
//# sourceMappingURL=full-
|
|
1
|
+
import{t}from"./TextFormattingPlugin-CYVnhHH4.mjs";import{t as o}from"./FontPlugin-BDjM9v3y.mjs";import{n as i}from"./AlignmentPlugin-DGb1VtCU.mjs";import{t as m}from"./BlockquotePlugin-Dz8Pny1r.mjs";import{t as n}from"./CodeBlockPlugin-C8H0nfF6.mjs";import{t as r}from"./FontSizePlugin-CGlH-S6g.mjs";import{t as e}from"./HardBreakPlugin-CCrP_Kec.mjs";import{t as s}from"./HeadingPlugin-BiDNuBmw.mjs";import{t as l}from"./HighlightPlugin-CwrtRVfS.mjs";import{t as a}from"./HorizontalRulePlugin-CKIkbVwn.mjs";import{t as g}from"./ImagePlugin-BqRAKlG1.mjs";import{t as u}from"./LinkPlugin-Cb2cpFe8.mjs";import{t as p}from"./ListPlugin-C_YjbVjT.mjs";import{t as f}from"./PrintPlugin-80SINLGF.mjs";import{t as P}from"./SmartPastePlugin-pejM1Zyk.mjs";import{t as w}from"./StrikethroughPlugin-XBOGv5Oi.mjs";import{t as j}from"./SuperSubPlugin-CVjU4W9y.mjs";import{t as h}from"./TablePlugin-BHpp6h9H.mjs";import{t as k}from"./TextColorPlugin-CGbyMGTr.mjs";import{n as c}from"./TextDirectionCorePlugin-DPU7wy6g.mjs";function S(S){return{toolbar:[[new o({fonts:[],...S?.font}),new r(S?.fontSize)],[new t(S?.textFormatting),new w(S?.strikethrough),new j(S?.superSub)],[new k(S?.textColor),new l(S?.highlight)],[new s(S?.heading),new m(S?.blockquote),new n(S?.codeBlock)],[new i(S?.alignment),new c(S?.textDirection)],[new p(S?.list)],[new u(S?.link),new h(S?.table),new a(S?.horizontalRule),new g(S?.image)],[new f(S?.print)]],plugins:[new e,new P(S?.smartPaste)]}}export{S as t};
|
|
2
|
+
//# sourceMappingURL=full-NtKyNKJ6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"full-
|
|
1
|
+
{"version":3,"file":"full-NtKyNKJ6.mjs","names":[],"sources":["../../src/presets/FullPreset.ts"],"sourcesContent":["/**\n * Full preset: a fully-featured editor with all standard plugins.\n */\n\nimport { AlignmentPlugin } from '../plugins/alignment/AlignmentPlugin.js';\nimport { BlockquotePlugin } from '../plugins/blockquote/BlockquotePlugin.js';\nimport { CodeBlockPlugin } from '../plugins/code-block/CodeBlockPlugin.js';\nimport { FontSizePlugin } from '../plugins/font-size/FontSizePlugin.js';\nimport { FontPlugin } from '../plugins/font/FontPlugin.js';\nimport { HardBreakPlugin } from '../plugins/hard-break/HardBreakPlugin.js';\nimport { HeadingPlugin } from '../plugins/heading/HeadingPlugin.js';\nimport { HighlightPlugin } from '../plugins/highlight/HighlightPlugin.js';\nimport { HorizontalRulePlugin } from '../plugins/horizontal-rule/HorizontalRulePlugin.js';\nimport { ImagePlugin } from '../plugins/image/ImagePlugin.js';\nimport { LinkPlugin } from '../plugins/link/LinkPlugin.js';\nimport { ListPlugin } from '../plugins/list/ListPlugin.js';\nimport { PrintPlugin } from '../plugins/print/PrintPlugin.js';\nimport { SmartPastePlugin } from '../plugins/smart-paste/SmartPastePlugin.js';\nimport { StrikethroughPlugin } from '../plugins/strikethrough/StrikethroughPlugin.js';\nimport { SuperSubPlugin } from '../plugins/super-sub/SuperSubPlugin.js';\nimport { TablePlugin } from '../plugins/table/TablePlugin.js';\nimport { TextColorPlugin } from '../plugins/text-color/TextColorPlugin.js';\nimport { TextDirectionCorePlugin } from '../plugins/text-direction/TextDirectionCorePlugin.js';\nimport { TextFormattingPlugin } from '../plugins/text-formatting/TextFormattingPlugin.js';\nimport type { FullPresetOptions, PresetConfig } from './PresetTypes.js';\n\n/**\n * Creates a fully-featured editor preset with all standard plugins.\n *\n * Pass `font.fonts` to supply font definitions (e.g. `STARTER_FONTS`).\n * Without explicit fonts, the font picker toolbar renders empty.\n *\n * Toolbar groups:\n * 1. Typography: Font, FontSize\n * 2. Inline marks: TextFormatting, Strikethrough, SuperSub\n * 3. Colors: TextColor, Highlight\n * 4. Block types: Heading, Blockquote, CodeBlock\n * 5. Paragraph layout: Alignment, TextDirection\n * 6. Lists: List\n * 7. Insert objects: Link, Table, HorizontalRule, Image\n * 8. Utility: Print\n */\nexport function createFullPreset(options?: FullPresetOptions): PresetConfig {\n\treturn {\n\t\ttoolbar: [\n\t\t\t[new FontPlugin({ fonts: [], ...options?.font }), new FontSizePlugin(options?.fontSize)],\n\t\t\t[\n\t\t\t\tnew TextFormattingPlugin(options?.textFormatting),\n\t\t\t\tnew StrikethroughPlugin(options?.strikethrough),\n\t\t\t\tnew SuperSubPlugin(options?.superSub),\n\t\t\t],\n\t\t\t[new TextColorPlugin(options?.textColor), new HighlightPlugin(options?.highlight)],\n\t\t\t[\n\t\t\t\tnew HeadingPlugin(options?.heading),\n\t\t\t\tnew BlockquotePlugin(options?.blockquote),\n\t\t\t\tnew CodeBlockPlugin(options?.codeBlock),\n\t\t\t],\n\t\t\t[\n\t\t\t\tnew AlignmentPlugin(options?.alignment),\n\t\t\t\tnew TextDirectionCorePlugin(options?.textDirection),\n\t\t\t],\n\t\t\t[new ListPlugin(options?.list)],\n\t\t\t[\n\t\t\t\tnew LinkPlugin(options?.link),\n\t\t\t\tnew TablePlugin(options?.table),\n\t\t\t\tnew HorizontalRulePlugin(options?.horizontalRule),\n\t\t\t\tnew ImagePlugin(options?.image),\n\t\t\t],\n\t\t\t[new PrintPlugin(options?.print)],\n\t\t],\n\t\tplugins: [new HardBreakPlugin(), new SmartPastePlugin(options?.smartPaste)],\n\t};\n}\n"],"mappings":"g/BA0CA,SAAgB,EAAiB,GAChC,MAAO,CACN,QAAS,CACR,CAAC,IAAI,EAAW,CAAE,MAAO,MAAO,GAAS,OAAS,IAAI,EAAe,GAAS,WAC9E,CACC,IAAI,EAAqB,GAAS,gBAClC,IAAI,EAAoB,GAAS,eACjC,IAAI,EAAe,GAAS,WAE7B,CAAC,IAAI,EAAgB,GAAS,WAAY,IAAI,EAAgB,GAAS,YACvE,CACC,IAAI,EAAc,GAAS,SAC3B,IAAI,EAAiB,GAAS,YAC9B,IAAI,EAAgB,GAAS,YAE9B,CACC,IAAI,EAAgB,GAAS,WAC7B,IAAI,EAAwB,GAAS,gBAEtC,CAAC,IAAI,EAAW,GAAS,OACzB,CACC,IAAI,EAAW,GAAS,MACxB,IAAI,EAAY,GAAS,OACzB,IAAI,EAAqB,GAAS,gBAClC,IAAI,EAAY,GAAS,QAE1B,CAAC,IAAI,EAAY,GAAS,SAE3B,QAAS,CAAC,IAAI,EAAmB,IAAI,EAAiB,GAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"minimal-
|
|
1
|
+
{"version":3,"file":"minimal-gSvMMQTU.mjs","names":[],"sources":["../../src/presets/MinimalPreset.ts"],"sourcesContent":["/**\n * Minimal preset: a lightweight editor with only font selection.\n */\n\nimport { FontPlugin } from '../plugins/font/FontPlugin.js';\nimport type { MinimalPresetOptions, PresetConfig } from './PresetTypes.js';\n\n/**\n * Creates a minimal editor preset with font selection only.\n * CaretNavigation and GapCursor are auto-registered by the editor.\n *\n * Pass `font.fonts` to supply font definitions (e.g. `STARTER_FONTS`).\n * Without explicit fonts, the font picker toolbar renders empty.\n */\nexport function createMinimalPreset(options?: MinimalPresetOptions): PresetConfig {\n\tconst fontPlugin: FontPlugin = new FontPlugin({\n\t\tfonts: [],\n\t\t...options?.font,\n\t});\n\n\treturn {\n\t\ttoolbar: [[fontPlugin]],\n\t\tplugins: [],\n\t};\n}\n"],"mappings":"yCAcA,SAAgB,EAAoB,GAMnC,MAAO,CACN,QAAS,CAAC,CANoB,IAAI,EAAW,CAC7C,MAAO,MACJ,GAAS,SAKZ,QAAS"}
|