@notectl/core 2.2.3 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/chunks/AlignmentPlugin-DeD4C4EP.mjs +2 -0
- package/dist/chunks/AlignmentPlugin-DeD4C4EP.mjs.map +1 -0
- package/dist/chunks/{BidiIsolationPlugin-Bm3h9J15.mjs → BidiIsolationPlugin-BddRbDCN.mjs} +2 -2
- package/dist/chunks/{BidiIsolationPlugin-Bm3h9J15.mjs.map → BidiIsolationPlugin-BddRbDCN.mjs.map} +1 -1
- package/dist/chunks/{BlockquotePlugin-CxrCA0vY.mjs → BlockquotePlugin-4V7KV_Sb.mjs} +2 -2
- package/dist/chunks/{BlockquotePlugin-CxrCA0vY.mjs.map → BlockquotePlugin-4V7KV_Sb.mjs.map} +1 -1
- package/dist/chunks/{CaretNavigationPlugin-Bb7W_FMg.mjs → CaretNavigationPlugin-C6XBwp0N.mjs} +2 -2
- package/dist/chunks/{CaretNavigationPlugin-Bb7W_FMg.mjs.map → CaretNavigationPlugin-C6XBwp0N.mjs.map} +1 -1
- package/dist/chunks/{CodeBlockPlugin-CcB0WaIz.mjs → CodeBlockPlugin-3V2K3RBe.mjs} +2 -2
- package/dist/chunks/{CodeBlockPlugin-CcB0WaIz.mjs.map → CodeBlockPlugin-3V2K3RBe.mjs.map} +1 -1
- package/dist/chunks/{ColorGrid-CXRdpP_n.mjs → ColorGrid-DPNiXxpD.mjs} +2 -2
- package/dist/chunks/{ColorGrid-CXRdpP_n.mjs.map → ColorGrid-DPNiXxpD.mjs.map} +1 -1
- package/dist/chunks/{ColorPickerPopup-C_74bqid.mjs → ColorPickerPopup-lcf3jS6G.mjs} +2 -2
- package/dist/chunks/{ColorPickerPopup-C_74bqid.mjs.map → ColorPickerPopup-lcf3jS6G.mjs.map} +1 -1
- package/dist/chunks/FontPlugin-DQMj65Bc.mjs +2 -0
- package/dist/chunks/{FontPlugin-1VYDwETg.mjs.map → FontPlugin-DQMj65Bc.mjs.map} +1 -1
- package/dist/chunks/{FontSizePlugin-L1uDVsKY.mjs → FontSizePlugin-9StCjEfN.mjs} +2 -2
- package/dist/chunks/{FontSizePlugin-L1uDVsKY.mjs.map → FontSizePlugin-9StCjEfN.mjs.map} +1 -1
- package/dist/chunks/{FormulaPlugin-66Cc9sH2.mjs → FormulaPlugin-CV5VUxkb.mjs} +2 -2
- package/dist/chunks/{FormulaPlugin-66Cc9sH2.mjs.map → FormulaPlugin-CV5VUxkb.mjs.map} +1 -1
- package/dist/chunks/GapCursorPlugin-Zvxjnr82.mjs +2 -0
- package/dist/chunks/{GapCursorPlugin-B29ieXel.mjs.map → GapCursorPlugin-Zvxjnr82.mjs.map} +1 -1
- package/dist/chunks/{HeadingPlugin-SICF6Tm0.mjs → HeadingPlugin-BuGoM9Rz.mjs} +2 -2
- package/dist/chunks/{HeadingPlugin-SICF6Tm0.mjs.map → HeadingPlugin-BuGoM9Rz.mjs.map} +1 -1
- package/dist/chunks/{HighlightPlugin-B3b_qe68.mjs → HighlightPlugin-C6pHifVz.mjs} +2 -2
- package/dist/chunks/{HighlightPlugin-B3b_qe68.mjs.map → HighlightPlugin-C6pHifVz.mjs.map} +1 -1
- package/dist/chunks/HorizontalRulePlugin-0FjHPZuA.mjs +2 -0
- package/dist/chunks/{HorizontalRulePlugin-BrFlrpmb.mjs.map → HorizontalRulePlugin-0FjHPZuA.mjs.map} +1 -1
- package/dist/chunks/{ImagePlugin-eRVtFFou.mjs → ImagePlugin-KpkuE2LH.mjs} +2 -2
- package/dist/chunks/{ImagePlugin-eRVtFFou.mjs.map → ImagePlugin-KpkuE2LH.mjs.map} +1 -1
- package/dist/chunks/{InlineCodePlugin-Cj1EZ3x2.mjs → InlineCodePlugin-DMr4O7c7.mjs} +2 -2
- package/dist/chunks/{InlineCodePlugin-Cj1EZ3x2.mjs.map → InlineCodePlugin-DMr4O7c7.mjs.map} +1 -1
- package/dist/chunks/{InlineStyleMarkSpec-4R6DC7ij.mjs → InlineStyleMarkSpec-C6LJT7kp.mjs} +2 -2
- package/dist/chunks/{InlineStyleMarkSpec-4R6DC7ij.mjs.map → InlineStyleMarkSpec-C6LJT7kp.mjs.map} +1 -1
- package/dist/chunks/LinkPlugin-DvZ3MPgL.mjs +2 -0
- package/dist/chunks/{LinkPlugin-B_ysjORS.mjs.map → LinkPlugin-DvZ3MPgL.mjs.map} +1 -1
- package/dist/chunks/{ListPlugin-C0BV8CnQ.mjs → ListPlugin-2yl6KiTK.mjs} +2 -2
- package/dist/chunks/{ListPlugin-C0BV8CnQ.mjs.map → ListPlugin-2yl6KiTK.mjs.map} +1 -1
- package/dist/chunks/PluginHelpers-KDuTlmXm.mjs +2 -0
- package/dist/chunks/{PluginHelpers-D4quRJ5a.mjs.map → PluginHelpers-KDuTlmXm.mjs.map} +1 -1
- package/dist/chunks/{PopupManager-CyOfZUab.mjs → PopupManager-DUtaK3WX.mjs} +2 -2
- package/dist/chunks/{PopupManager-CyOfZUab.mjs.map → PopupManager-DUtaK3WX.mjs.map} +1 -1
- package/dist/chunks/{PopupPositioning-DtUrY9bh.mjs → PopupPositioning-BB97-__N.mjs} +2 -2
- package/dist/chunks/{PopupPositioning-DtUrY9bh.mjs.map → PopupPositioning-BB97-__N.mjs.map} +1 -1
- package/dist/chunks/PrintPlugin-DyIZDY3Q.mjs +2 -0
- package/dist/chunks/PrintPlugin-DyIZDY3Q.mjs.map +1 -0
- package/dist/chunks/{PrintTypes-WdtgSj74.mjs → PrintTypes-CxU69qu3.mjs} +3 -2
- package/dist/chunks/PrintTypes-CxU69qu3.mjs.map +1 -0
- package/dist/chunks/{SimpleMark-Bdqjcz0G.mjs → SimpleMark-CaQtFAau.mjs} +2 -2
- package/dist/chunks/{SimpleMark-Bdqjcz0G.mjs.map → SimpleMark-CaQtFAau.mjs.map} +1 -1
- package/dist/chunks/{SmartPastePlugin-BhkoH03E.mjs → SmartPastePlugin-Bi_wpTCV.mjs} +2 -2
- package/dist/chunks/{SmartPastePlugin-BhkoH03E.mjs.map → SmartPastePlugin-Bi_wpTCV.mjs.map} +1 -1
- package/dist/chunks/StrikethroughPlugin-BLKDRWtf.mjs +2 -0
- package/dist/chunks/{StrikethroughPlugin-DRuwEZhu.mjs.map → StrikethroughPlugin-BLKDRWtf.mjs.map} +1 -1
- package/dist/chunks/StyleRuntime-BFKu52mS.mjs +2 -0
- package/dist/chunks/StyleRuntime-BFKu52mS.mjs.map +1 -0
- package/dist/chunks/{SuperSubPlugin-IZ_x8GS-.mjs → SuperSubPlugin-DVP7VnIz.mjs} +2 -2
- package/dist/chunks/{SuperSubPlugin-IZ_x8GS-.mjs.map → SuperSubPlugin-DVP7VnIz.mjs.map} +1 -1
- package/dist/chunks/{TablePlugin-CRp4qSZE.mjs → TablePlugin-BTH9VRZq.mjs} +2 -2
- package/dist/chunks/{TablePlugin-CRp4qSZE.mjs.map → TablePlugin-BTH9VRZq.mjs.map} +1 -1
- package/dist/chunks/TextColorPlugin-CEfZQ6Md.mjs +2 -0
- package/dist/chunks/{TextColorPlugin-CViEiYZk.mjs.map → TextColorPlugin-CEfZQ6Md.mjs.map} +1 -1
- package/dist/chunks/TextDirectionPlugin-DHB88HKf.mjs +2 -0
- package/dist/chunks/TextDirectionPlugin-DHB88HKf.mjs.map +1 -0
- package/dist/chunks/{TextFormattingPlugin-CpyZgtLi.mjs → TextFormattingPlugin-Cubp3e7f.mjs} +2 -2
- package/dist/chunks/{TextFormattingPlugin-CpyZgtLi.mjs.map → TextFormattingPlugin-Cubp3e7f.mjs.map} +1 -1
- package/dist/chunks/{ToolbarPlugin-B5UUc6dY.mjs → ToolbarPlugin-DVGrFf8v.mjs} +2 -2
- package/dist/chunks/{ToolbarPlugin-B5UUc6dY.mjs.map → ToolbarPlugin-DVGrFf8v.mjs.map} +1 -1
- package/dist/chunks/{VideoPlugin-mXIDODzC.mjs → VideoPlugin-B3uf1Dwp.mjs} +2 -2
- package/dist/chunks/{VideoPlugin-mXIDODzC.mjs.map → VideoPlugin-B3uf1Dwp.mjs.map} +1 -1
- package/dist/chunks/full-BBl7mvwH.mjs +2 -0
- package/dist/chunks/{full-CHkwduuY.mjs.map → full-BBl7mvwH.mjs.map} +1 -1
- package/dist/chunks/minimal-CtnR4dqp.mjs +2 -0
- package/dist/chunks/{minimal-D5v-raei.mjs.map → minimal-CtnR4dqp.mjs.map} +1 -1
- package/dist/chunks/src-C89Cw7ni.mjs +2 -0
- package/dist/chunks/{src-gVRDR_79.mjs.map → src-C89Cw7ni.mjs.map} +1 -1
- package/dist/editor/NotectlEditor.d.ts +22 -3
- package/dist/editor/NotectlEditor.d.ts.map +1 -1
- package/dist/full.mjs +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/alignment/AlignmentPlugin.d.ts +5 -0
- package/dist/plugins/alignment/AlignmentPlugin.d.ts.map +1 -1
- package/dist/plugins/alignment.mjs +1 -1
- package/dist/plugins/bidi-isolation.mjs +1 -1
- package/dist/plugins/blockquote.mjs +1 -1
- package/dist/plugins/caret-navigation.mjs +1 -1
- package/dist/plugins/code-block.mjs +1 -1
- package/dist/plugins/font-size.mjs +1 -1
- package/dist/plugins/font.mjs +1 -1
- package/dist/plugins/formula.mjs +1 -1
- package/dist/plugins/gap-cursor.mjs +1 -1
- package/dist/plugins/heading.mjs +1 -1
- package/dist/plugins/highlight.mjs +1 -1
- package/dist/plugins/horizontal-rule.mjs +1 -1
- package/dist/plugins/image.mjs +1 -1
- package/dist/plugins/inline-code.mjs +1 -1
- package/dist/plugins/link.mjs +1 -1
- package/dist/plugins/list.mjs +1 -1
- package/dist/plugins/print/PrintHostStyles.d.ts +50 -0
- package/dist/plugins/print/PrintHostStyles.d.ts.map +1 -0
- package/dist/plugins/print/PrintPlugin.d.ts.map +1 -1
- package/dist/plugins/print/PrintServiceImpl.d.ts +72 -3
- package/dist/plugins/print/PrintServiceImpl.d.ts.map +1 -1
- package/dist/plugins/print/PrintStyleCollector.d.ts +150 -21
- package/dist/plugins/print/PrintStyleCollector.d.ts.map +1 -1
- package/dist/plugins/print/StaticHostMarker.d.ts +18 -0
- package/dist/plugins/print/StaticHostMarker.d.ts.map +1 -0
- package/dist/plugins/print.mjs +1 -1
- package/dist/plugins/shared/PluginHelpers.d.ts +8 -0
- package/dist/plugins/shared/PluginHelpers.d.ts.map +1 -1
- package/dist/plugins/shared.mjs +1 -1
- package/dist/plugins/smart-paste.mjs +1 -1
- package/dist/plugins/strikethrough.mjs +1 -1
- package/dist/plugins/super-sub.mjs +1 -1
- package/dist/plugins/table/TableNodeViews.d.ts.map +1 -1
- package/dist/plugins/table.mjs +1 -1
- package/dist/plugins/text-color.mjs +1 -1
- package/dist/plugins/text-direction/TextDirectionPlugin.d.ts +0 -1
- package/dist/plugins/text-direction/TextDirectionPlugin.d.ts.map +1 -1
- package/dist/plugins/text-direction.mjs +1 -1
- package/dist/plugins/text-formatting.mjs +1 -1
- package/dist/plugins/toolbar.mjs +1 -1
- package/dist/plugins/video.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/style/StyleRuntime.d.ts +7 -0
- package/dist/style/StyleRuntime.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/chunks/AlignmentPlugin-h6sx-yIf.mjs +0 -2
- package/dist/chunks/AlignmentPlugin-h6sx-yIf.mjs.map +0 -1
- package/dist/chunks/FontPlugin-1VYDwETg.mjs +0 -2
- package/dist/chunks/GapCursorPlugin-B29ieXel.mjs +0 -2
- package/dist/chunks/HorizontalRulePlugin-BrFlrpmb.mjs +0 -2
- package/dist/chunks/LinkPlugin-B_ysjORS.mjs +0 -2
- package/dist/chunks/PluginHelpers-D4quRJ5a.mjs +0 -2
- package/dist/chunks/PrintPlugin-CXDFFzWB.mjs +0 -2
- package/dist/chunks/PrintPlugin-CXDFFzWB.mjs.map +0 -1
- package/dist/chunks/PrintTypes-WdtgSj74.mjs.map +0 -1
- package/dist/chunks/StrikethroughPlugin-DRuwEZhu.mjs +0 -2
- package/dist/chunks/StyleRuntime-BAutwDZT.mjs +0 -2
- package/dist/chunks/StyleRuntime-BAutwDZT.mjs.map +0 -1
- package/dist/chunks/TextColorPlugin-CViEiYZk.mjs +0 -2
- package/dist/chunks/TextDirectionPlugin-CQGkaS2s.mjs +0 -2
- package/dist/chunks/TextDirectionPlugin-CQGkaS2s.mjs.map +0 -1
- package/dist/chunks/full-CHkwduuY.mjs +0 -2
- package/dist/chunks/minimal-D5v-raei.mjs +0 -2
- package/dist/chunks/src-gVRDR_79.mjs +0 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{n as t,r as e}from"./HTMLUtils-C6B4UT00.mjs";import{i as n,l as o,r,s}from"./StyleRuntime-BFKu52mS.mjs";import{_ as i,c as a,d as c,h as l,i as u,n as p,r as m,s as h,t as d}from"./PrintTypes-CxU69qu3.mjs";import{c as f,o as g}from"./PluginHelpers-KDuTlmXm.mjs";import{t as y}from"./ShortcutFormatting-TwiufqeV.mjs";import $ from"dompurify";var v={label:"Print",tooltip:t=>`Print (${t})`,printingAnnouncement:"Printing"},b=/* #__PURE__ */Object.assign({"./locales/ar.ts":()=>import("./ar-Uzo1Azdw.mjs"),"./locales/de.ts":()=>import("./de-DcMhfRBu.mjs"),"./locales/es.ts":()=>import("./es-BJHF95J_.mjs"),"./locales/fr.ts":()=>import("./fr-KXmbys88.mjs"),"./locales/hi.ts":()=>import("./hi-l6DA-aOw.mjs"),"./locales/pt.ts":()=>import("./pt-C8sOyw0V.mjs"),"./locales/ru.ts":()=>import("./ru-B1m8J9dl.mjs"),"./locales/zh.ts":()=>import("./zh-dGSDBBzb.mjs")});async function S(t){return f(b,t,v)}var w=["h1","h2","h3","h4","h5","h6","p","div","span","br","hr","strong","b","em","i","u","s","sub","sup","small","ul","ol","li","table","thead","tbody","tr","th","td","img"],j=["style","class","src","alt","width","height","colspan","rowspan"];function k(t){const e="function"==typeof t?t():t;return $.sanitize(e,{ALLOWED_TAGS:[...w],ALLOWED_ATTR:[...j]})}var C="notectl-host";function T(t){const e=Array.from(t.styleSheets),n=t.adoptedStyleSheets;return n&&e.push(...n),e}function x(t,e){const n=t[e]??'"';let o=e+1;for(;o<t.length;){const e=t[o]??"";if("\\"!==e){if(e===n)return o+1;o++}else o+=2}return t.length}function L(t,e){if("url("!==t.slice(e,e+4).toLowerCase())return!1;const n=e>0?t[e-1]??"":"";return!/[\w-]/.test(n)}function A(t,e){let n=e+4;for(;n<t.length&&/\s/.test(t[n]??"");)n++;const o=t[n]??"";let r;if('"'===o||"'"===o){const e=x(t,n);r=t.slice(n+1,e-1),n=e}else{const e=t.indexOf(")",n);if(-1===e)return null;r=t.slice(n,e).trim(),n=e}for(;n<t.length&&/\s/.test(t[n]??"");)n++;return")"!==t[n]?null:{end:n+1,url:(s=r,s.replace(/\\([0-9a-f]{1,6}\s?|[\s\S])/gi,(t,e)=>{const n=e.trim();if(/^[0-9a-f]{1,6}$/i.test(n)){const t=Number.parseInt(n,16);return t>0&&t<=1114111?String.fromCodePoint(t):"�"}return e}))};var s}function R(t,e,n){if(!e||!function(t){return!t.startsWith("#")&&!t.startsWith("//")&&!/^[a-z][a-z0-9+.-]*:/i.test(t)}(e))return t;try{return`url("${new URL(e,n).href}")`}catch{return t}}function N(t,e){if(!t.toLowerCase().includes("url("))return t;let n="",o=0;for(;o<t.length;){const r=t[o]??"";if('"'===r||"'"===r){const e=x(t,o);n+=t.slice(o,e),o=e;continue}if(L(t,o)){const r=A(t,o);if(r){n+=R(t.slice(o,r.end),r.url,e),o=r.end;continue}}n+=r,o++}return n}function E(t,e,n,o,r){const s=n.trim(),i=s&&"all"!==s?` ${s}`:"";let a=C;""===o?a=`${C}.${function(t){return t.anonymousLayerCount+=1,`notectl-anonymous-${t.anonymousLayerCount}`}(t)}`:o&&(a=`${C}.${o}`);const c=r?.trim()?` supports(${r.trim()})`:"";var l;t.segments.push({kind:"import",statement:`@import url("${l=e,l.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}") layer(${a})${c}${i};`})}function z(t,e){const n=e.trim();return n&&"all"!==n?`@media ${n} {\n${t}\n}`:t}function B(t,e){if(e.seen.has(t))return;if(e.seen.add(t),t.disabled)return;let n;try{n=t.cssRules}catch{return void(t.href&&E(e,t.href,t.media.mediaText,null,null))}const o=t.href??e.baseURL,r=[];for(const i of n)3!==i.type?r.push(N(i.cssText,o)):H(i,e,r);const s=r.join("\n");s&&function(t,e){const n=t.segments[t.segments.length-1];"rules"!==n?.kind?t.segments.push({kind:"rules",css:e}):t.segments[t.segments.length-1]={kind:"rules",css:`${n.css}\n${e}`}}(e,z(s,t.media.mediaText))}function H(t,e,n){const o=t.layerName??null,r=t.supportsText??null,s=t.styleSheet;if(s&&!e.seen.has(s)){let i;try{i=s.cssRules}catch{i=null}if(i){e.seen.add(s);const a=s.href??e.baseURL,c=[];for(const t of i)3!==t.type?c.push(N(t.cssText,a)):H(t,e,c);let l=c.join("\n");return void(l&&(null!==o&&(l=""===o?`@layer {\n${l}\n}`:`@layer ${o} {\n${l}\n}`),r?.trim()&&(l=`@supports (${r.trim()}) {\n${l}\n}`),n.push(z(l,t.media.mediaText))))}}const i=s?.href??t.href;i&&E(e,i,t.media.mediaText,o,r)}var P="notectl-print",M="notectl-custom";function q(t){return t.replace(/;$/gm," !important;")}function D(t){return q(l(i).replace(":host {",`${t} {`))}function W(t,e){const n=getComputedStyle(t),o=[];for(let r=0;r<n.length;r++){const t=n[r]??"";if(t.startsWith("--notectl-")){const e=n.getPropertyValue(t).trim();e&&o.push(` ${t}: ${e};`)}}return 0===o.length?"":`${e} {\n${o.join("\n")}\n}`}var U=["","transparent","rgba(0, 0, 0, 0)"];function O(t){let e=t;for(;e;){const t=getComputedStyle(e).getPropertyValue("background-color").trim();if(!U.includes(t))return t;const n=e.getRootNode();e=e.parentElement??(n instanceof ShadowRoot?n.host:null)}return"#ffffff"}function V(t,e,n="#ffffff",o="body"){const r=getComputedStyle(t),s=["font-family","font-size","font-weight","line-height","letter-spacing","word-spacing","color"],i=[" margin: 0;",` background: ${n};`];for(const a of s){if("color"===a&&e){i.push(` color: ${e};`);continue}const t=r.getPropertyValue(a).trim();t&&i.push(` ${a}: ${t};`)}return`${o} {\n${i.join("\n")}\n}`}var I=["display: block","position: static","inset: auto","width: auto","height: auto","min-width: 0","min-height: 0","max-width: none","max-height: none","margin: 0","padding: 0","border: none","border-radius: 0","outline: none","box-shadow: none","background: transparent","overflow: visible","transform: none","filter: none","opacity: 1","contain: none","float: none","visibility: visible"],_=["display: block","visibility: visible","opacity: 1","position: static","height: auto","min-height: 0","max-height: none","overflow: visible","transform: none","filter: none","contain: none"];function F(t){return[`${t} {`,...I.map(t=>` ${t} !important;`),"}"].join("\n")}function G(t,e,n){const o=t.tagName.toLowerCase(),r=[],s=!1!==n.forceLightTheme,a=[F(o),["html, body {",..._.map(t=>` ${t} !important;`),"}"].join("\n")];if(s&&a.push(D(`:root, ${o}`)),!1!==n.printBackground&&a.push("* {\n -webkit-print-color-adjust: exact !important;\n print-color-adjust: exact !important;\n}"),r.push(`@layer ${P} {\n${a.join("\n\n")}\n}`),!s){const e=W(t,`:root, ${o}`);e&&r.push(e)}const l=s?i.primitives.foreground:void 0,u=s?"#ffffff":O(e);r.push(V(t,l,u));const p=function(t){const e=[];if(t.margin?e.push(` margin: ${t.margin};`):t.paperSize&&e.push(" margin: 0;"),t.paperSize||t.orientation){const n=[t.paperSize?c(t.paperSize):"A4"];t.orientation&&n.push(t.orientation),e.push(` size: ${n.join(" ")};`)}return 0===e.length?"":`@page {\n${e.join("\n")}\n}`}(n);return p&&r.push(p),n.customCSS&&(r.push(n.customCSS),r.push(`@layer ${M} {\n${n.customCSS}\n}`)),r.join("\n\n")}function K(t,e,n,o){const r=`${t.tagName.toLowerCase()}[${o}]`,s=!1!==n.forceLightTheme,a=[F(r)];s&&a.push(D(r)),!1!==n.printBackground&&a.push(`${r}, ${r} * {\n -webkit-print-color-adjust: exact !important;\n print-color-adjust: exact !important;\n}`);const c=s?i.primitives.foreground:void 0,l=s?"#ffffff":O(e);a.push(q(V(t,c,l,r)));const u=[`@layer ${P} {\n${a.join("\n\n")}\n}`];if(!s){const e=W(t,r);e&&u.push(e)}return u.join("\n\n")}var Y="data-notectl-print-fallback",J=["(function () {"," var head = document.head;"," if (head && head.querySelector('meta[name=\"notectl-print-export\"]')) {"," var bundles = document.querySelectorAll('template[data-notectl-print-styles]');"," for (var b = 0; b < bundles.length; b++) {"," if (bundles[b].content) head.appendChild(bundles[b].content);"," bundles[b].parentNode.removeChild(bundles[b]);"," }"," }"," var statics = document.querySelectorAll('[data-notectl-static]');"," for (var i = 0; i < statics.length; i++) {"," var host = statics[i];"," if (!host.shadowRoot && host.attachShadow) {"," for (var j = 0; j < host.children.length; j++) {"," var child = host.children[j];"," if (child.tagName !== 'TEMPLATE') continue;"," if (!child.getAttribute('shadowrootmode') || !child.content) continue;"," host.attachShadow({ mode: 'open' }).appendChild(child.content);"," host.removeChild(child);"," break;"," }"," }"," if (!host.shadowRoot) continue;"," var fallbacks = host.querySelectorAll('[data-notectl-print-fallback]');"," for (var k = 0; k < fallbacks.length; k++) {"," fallbacks[k].parentNode.removeChild(fallbacks[k]);"," }"," }","})();"].join("\n");function Q(t){return t.replace(/<\//g,"<\\/")}function X(e,n){const o=[];for(const r of Array.from(e.attributes))n(r.name)&&o.push(""===r.value?r.name:`${r.name}="${t(r.value)}"`);return o.length>0?` ${o.join(" ")}`:""}function Z(t){return t?X(t,t=>"class"===t||t.startsWith("data-")):""}function tt(e){const n=function(t){if(!(t instanceof HTMLElement))return"";const e=[];for(let n=0;n<t.style.length;n++){const o=t.style[n]??"";if(!o.startsWith("--notectl-"))continue;const r=t.style.getPropertyValue(o).trim();r&&e.push(`${o}: ${r}`)}return e.join("; ")}(e);return n?` style="${t(n)}"`:""}function et(n){const o=n.host.ownerDocument,r=n.host.tagName.toLowerCase(),s="export"===n.variant,i=s?["style","id",u]:["style",u],a=(c=n.host,l=i,X(c,t=>!l.includes(t)));var c,l;const p=n.carryThemeContext?tt(n.host):"",m=n.carryThemeContext?Z(o.documentElement)+tt(o.documentElement):"",h=n.carryThemeContext?Z(o.body)+tt(o.body):"",d=n.styleNonce?` nonce="${t(n.styleNonce)}"`:"",f=['<meta charset="utf-8">',`<title>${e(n.title)}</title>`];s&&f.push('<meta name="notectl-print-export">'),f.push(`<style${d}>@layer ${M}, ${P}, ${C};</style>`),f.push(`<style${d}>${Q(n.documentCSS)}</style>`);const g=n.hostSegments.map(t=>`<style${d}>${Q("import"===t.kind?t.statement:`@layer ${C} {\n${t.css}\n}`)}</style>`);s||f.push(...g);const y=s?`<template data-notectl-print-styles><style>${Q(n.standaloneCSS)}</style>${g.join("")}</template>`:"",$=s?`<div ${Y}><style>@scope ([${Y}]) {\n${Q(n.shadowCSS)}\n}</style>${n.contentHTML}</div>`:"",v=s?`<script>${J}<\/script>`:"",b=[`<${r}${a}${p} ${u}>`,'<template shadowrootmode="open">',`<style${d}>${Q(n.shadowCSS)}</style>`,n.contentHTML,"</template>",...$?[$]:[],`</${r}>`,...y?[y]:[],...v?[v]:[]];return["<!DOCTYPE html>",`<html lang="${t(n.lang)}"${m}>`,"<head>",...f,"</head>",`<body${h}>`,...b,"</body>","</html>"].join("\n")}var nt={print(){},toHTML:()=>"",dispose(){}},ot=class{id="print";name="Print";config;service=null;locale;constructor(t){this.config=t??{}}async init(t){this.locale=await g(t,this.config.locale,v,S);const e=t.getContainer(),i=e.getRootNode();if(i instanceof ShadowRoot){const a=i.host,c=t.getEventBus();this.service=function(t,e,i,a){const c=/* @__PURE__ */new Set;function l(t){return t??{}}function m(o){const r={options:o,cancelled:!1};if(a.emit(p,r),r.cancelled)return null;const c=r.options,l=function(t,e){const o=[],r=function(t){const e=[],o=[];for(const r of t.adoptedStyleSheets){const t=n(r)?o:e;for(const e of r.cssRules)t.push(e.cssText)}return{base:e.join("\n"),runtime:o.join("\n")}}(t);r.base&&o.push(`@layer notectl-base {\n${r.base}\n}`),r.runtime&&o.push(r.runtime);const s=function(t){const e=[];e.push(".notectl-content { height: auto !important; max-height: none !important; min-height: 0 !important; overflow: visible !important; }"),t.paperSize&&e.push(".notectl-content { padding: 48px 56px !important; }");const n=[];return n.push("[data-notectl-no-print] { display: none !important; }"),n.push(".notectl-code-block { white-space: pre-wrap !important; word-break: break-all !important; page-break-inside: avoid; }"),n.push("img { page-break-inside: avoid; }"),n.push(".notectl-table { break-inside: avoid; }"),!1!==t.printBackground&&n.push("* { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }"),e.push(`@media print {\n ${n.join("\n ")}\n}`),e.join("\n\n")}(e);return s&&o.push(s),e.customCSS&&o.push(e.customCSS),o.join("\n\n")}(t,c),m=function(t){const e={segments:[],seen:/* @__PURE__ */new Set,baseURL:t.ownerDocument.baseURI??"",anonymousLayerCount:0};for(const n of function(t){const e=[];let n=t.getRootNode();for(;n instanceof ShadowRoot;)e.push(n),n=n.host.getRootNode();return e.push(t.ownerDocument),e.reverse()}(t))for(const t of T(n))B(t,e);return e.segments}(e),h=function(t,e){const n=function(t){return(t.querySelector(".notectl-content")??t).cloneNode(!0)}(t);return function(t){t.removeAttribute("contenteditable");for(const e of["notectl-node-selected","notectl-content--empty"]){t.classList.remove(e);for(const n of t.querySelectorAll(`.${e}`))n.classList.remove(e)}t.removeAttribute("data-placeholder")}(n),function(t,e){if(e.excludeBlockTypes)for(const n of e.excludeBlockTypes){const e=t.querySelectorAll(`[data-block-type="${n}"]`);for(const t of e)t.remove()}if(e.pageBreakBefore)for(const n of e.pageBreakBefore){const e=t.querySelectorAll(`[data-block-type="${n}"]`);for(const t of e)s(t,{breakBefore:"page",pageBreakBefore:"always"})}}(n,e),function(t,e){if(e.header){const n=document.createElement("div");n.className="notectl-print-header",n.innerHTML=k(e.header),t.insertBefore(n,t.firstChild)}if(e.footer){const n=document.createElement("div");n.className="notectl-print-footer",n.innerHTML=k(e.footer),t.appendChild(n)}}(n,e),n}(i,c).outerHTML,f=c.title??"",g=e.ownerDocument,y=e.closest("[lang]")?.getAttribute("lang")??(g.documentElement.lang||"en"),$={documentCSS:K(e,i,c,u),standaloneCSS:G(e,i,c),hostSegments:m,shadowCSS:l,host:e,contentHTML:h,title:f,lang:y,carryThemeContext:!1===c.forceLightTheme,variant:"export"},v=et($),b={html:v};return a.emit(d,b),{html:v,input:$}}return{print(t){const e=m(l(t));if(!e)return;const n=et({...e.input,documentCSS:e.input.standaloneCSS,variant:"internal",styleNonce:r(i)}),s=document.createElement("iframe");o(s,"position:fixed;left:-9999px;width:0;height:0;border:none"),document.body.appendChild(s);const a=s.contentDocument,u=s.contentWindow;if(!a||!u)return void document.body.removeChild(s);a.open(),a.write(n),a.close();const p=()=>{s.remove()},h=()=>{u.addEventListener("afterprint",p,{once:!0}),u.print()};if("complete"===a.readyState)return void h();let d=!1;const f=()=>{d||(d=!0,c.delete(g),window.clearTimeout(y),h())},g=()=>{d=!0,window.clearTimeout(y),u.removeEventListener("load",f),s.remove()},y=window.setTimeout(f,4e3);c.add(g),u.addEventListener("load",f,{once:!0})},toHTML:t=>m(l(t))?.html??"",dispose(){for(const t of c)t();c.clear()}}}(i,a,e,c)}else this.service=nt;t.registerService(m,this.service);const a=this.config.defaults??{},c=this.service,l=this.locale;t.registerCommand("print",()=>(t.announce(l.printingAnnouncement),c.print(a),!0));const h=this.config.keyBinding??"Mod-P";if(t.registerKeymap({[h]:()=>(t.executeCommand("print"),!0)}),!1!==this.config.showToolbarItem){const e=y(h);t.registerToolbarItem({id:"print",group:"actions",label:this.locale.label,tooltip:this.locale.tooltip(e),icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/></svg>',command:"print"})}}destroy(){this.service?.dispose(),this.service=null}};export{v as n,S as r,ot as t};
|
|
2
|
+
//# sourceMappingURL=PrintPlugin-DyIZDY3Q.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PrintPlugin-DyIZDY3Q.mjs","names":[],"sources":["../../src/plugins/print/PrintLocale.ts","../../src/plugins/print/PrintContentPreparer.ts","../../src/plugins/print/PrintHostStyles.ts","../../src/plugins/print/PrintStyleCollector.ts","../../src/plugins/print/PrintServiceImpl.ts","../../src/plugins/print/PrintPlugin.ts"],"sourcesContent":["/** Locale interface and default English locale for the PrintPlugin. */\n\nimport { type LocaleModuleMap, loadLocaleModule } from '../shared/LocaleLoader.js';\n\n// --- Locale Interface ---\n\nexport interface PrintLocale {\n\treadonly label: string;\n\treadonly tooltip: (shortcut: string) => string;\n\treadonly printingAnnouncement: string;\n}\n\n// --- Default English Locale ---\n\nexport const PRINT_LOCALE_EN: PrintLocale = {\n\tlabel: 'Print',\n\ttooltip: (shortcut: string) => `Print (${shortcut})`,\n\tprintingAnnouncement: 'Printing',\n};\n\n// --- Lazy Locale Loader ---\n\nconst localeModules: LocaleModuleMap<PrintLocale> = import.meta.glob<{\n\tdefault: PrintLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadPrintLocale(lang: string): Promise<PrintLocale> {\n\treturn loadLocaleModule(localeModules, lang, PRINT_LOCALE_EN);\n}\n","/**\n * Prepares editor content for print output.\n * Clones the DOM, sanitizes interactive attributes, applies block filters,\n * and inserts header/footer elements.\n */\n\nimport DOMPurify from 'dompurify';\nimport { setStyleProperties } from '../../style/StyleRuntime.js';\nimport type { PrintOptions } from './PrintTypes.js';\n\n/** Deep-clones the .notectl-content element from the editor container. */\nexport function cloneContent(container: HTMLElement): HTMLElement {\n\tconst content: HTMLElement | null = container.querySelector('.notectl-content');\n\tconst source: HTMLElement = content ?? container;\n\treturn source.cloneNode(true) as HTMLElement;\n}\n\n/** Sanitizes the cloned content for print: removes interactive attributes and selection styling. */\nexport function sanitize(clone: HTMLElement): void {\n\t// Remove contenteditable from the root\n\tclone.removeAttribute('contenteditable');\n\n\t// Remove selection-related CSS classes\n\tconst selectionClasses: readonly string[] = ['notectl-node-selected', 'notectl-content--empty'];\n\tfor (const cls of selectionClasses) {\n\t\tclone.classList.remove(cls);\n\t\tfor (const el of clone.querySelectorAll(`.${cls}`)) {\n\t\t\tel.classList.remove(cls);\n\t\t}\n\t}\n\n\t// Remove data-placeholder to avoid showing placeholder in print\n\tclone.removeAttribute('data-placeholder');\n}\n\n/** Removes excluded block types and applies page-break-before to configured block types. */\nexport function applyBlockFilters(clone: HTMLElement, options: PrintOptions): void {\n\tif (options.excludeBlockTypes) {\n\t\tfor (const type of options.excludeBlockTypes) {\n\t\t\tconst elements: NodeListOf<Element> = clone.querySelectorAll(`[data-block-type=\"${type}\"]`);\n\t\t\tfor (const el of elements) {\n\t\t\t\tel.remove();\n\t\t\t}\n\t\t}\n\t}\n\n\tif (options.pageBreakBefore) {\n\t\tfor (const type of options.pageBreakBefore) {\n\t\t\tconst elements: NodeListOf<Element> = clone.querySelectorAll(`[data-block-type=\"${type}\"]`);\n\t\t\tfor (const el of elements) {\n\t\t\t\tsetStyleProperties(el as HTMLElement, {\n\t\t\t\t\tbreakBefore: 'page',\n\t\t\t\t\tpageBreakBefore: 'always',\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Tags allowed in print header/footer content. */\nconst HEADER_FOOTER_ALLOWED_TAGS: readonly string[] = [\n\t'h1',\n\t'h2',\n\t'h3',\n\t'h4',\n\t'h5',\n\t'h6',\n\t'p',\n\t'div',\n\t'span',\n\t'br',\n\t'hr',\n\t'strong',\n\t'b',\n\t'em',\n\t'i',\n\t'u',\n\t's',\n\t'sub',\n\t'sup',\n\t'small',\n\t'ul',\n\t'ol',\n\t'li',\n\t'table',\n\t'thead',\n\t'tbody',\n\t'tr',\n\t'th',\n\t'td',\n\t'img',\n];\n\n/** Attributes allowed in print header/footer content. */\nconst HEADER_FOOTER_ALLOWED_ATTRS: readonly string[] = [\n\t'style',\n\t'class',\n\t'src',\n\t'alt',\n\t'width',\n\t'height',\n\t'colspan',\n\t'rowspan',\n];\n\n/** Resolves a header/footer value to a sanitized HTML string. */\nfunction resolveContent(value: string | (() => string)): string {\n\tconst raw: string = typeof value === 'function' ? value() : value;\n\treturn DOMPurify.sanitize(raw, {\n\t\tALLOWED_TAGS: [...HEADER_FOOTER_ALLOWED_TAGS],\n\t\tALLOWED_ATTR: [...HEADER_FOOTER_ALLOWED_ATTRS],\n\t});\n}\n\n/** Inserts header and footer elements into the cloned content. */\nexport function insertHeaderFooter(clone: HTMLElement, options: PrintOptions): void {\n\tif (options.header) {\n\t\tconst headerEl: HTMLDivElement = document.createElement('div');\n\t\theaderEl.className = 'notectl-print-header';\n\t\theaderEl.innerHTML = resolveContent(options.header);\n\t\tclone.insertBefore(headerEl, clone.firstChild);\n\t}\n\n\tif (options.footer) {\n\t\tconst footerEl: HTMLDivElement = document.createElement('div');\n\t\tfooterEl.className = 'notectl-print-footer';\n\t\tfooterEl.innerHTML = resolveContent(options.footer);\n\t\tclone.appendChild(footerEl);\n\t}\n}\n\n/** Orchestrates all content preparation steps. Returns a ready-to-print DOM element. */\nexport function prepare(container: HTMLElement, options: PrintOptions): HTMLElement {\n\tconst clone: HTMLElement = cloneContent(container);\n\tsanitize(clone);\n\tapplyBlockFilters(clone, options);\n\tinsertHeaderFooter(clone, options);\n\treturn clone;\n}\n","/**\n * Copies the host page's stylesheets verbatim into the print document.\n *\n * The print document preserves the editor's shadow boundary (declarative\n * shadow DOM), so host-authored rules — `::part()`, element selectors, custom\n * properties, `@media`, `@layer`, `@supports`, CSS nesting — apply natively\n * and need no translation or rewriting.\n *\n * All copied CSS is wrapped in the `notectl-host` cascade layer. Layering does\n * not weaken it against the editor's shadow styles, because the cascade\n * compares tree context before layers (an outer normal declaration beats a\n * shadow-tree one regardless). It does keep the print document's own rules in\n * control: its unlayered rules (page setup, body typography, customCSS) win\n * over the copy for normal declarations regardless of specificity, and the\n * print guards (host-element reset, forced light theme) — `!important` inside\n * the earlier-declared `notectl-print` layer — win even against `!important`\n * host rules.\n *\n * Stylesheets whose rules are not readable (cross-origin) are re-referenced\n * via hoisted `@import ... layer(notectl-host)` statements so the print\n * document loads them itself. The copy is produced as an ordered segment list\n * (rule chunks and `@import` statements interleaved in source order) so\n * equal-specificity cascade ties resolve exactly as on the live page.\n * Disabled stylesheets are skipped and stylesheet-level media conditions are\n * preserved as `@media` wrappers.\n */\n\n/** Cascade layer that holds the copied host-page styles in print output. */\nexport const HOST_STYLE_LAYER = 'notectl-host';\n\nconst IMPORT_RULE_TYPE = 3;\n\n/**\n * One source-ordered piece of the host-page CSS copy. `rules` segments hold\n * verbatim rule text destined for the `notectl-host` layer; `import` segments\n * hold hoisted `@import` statements for unreadable (cross-origin) sheets.\n * `@import` must precede every other rule of its stylesheet, so each segment\n * is emitted as its own `<style>` element — keeping the live source order,\n * which decides equal-specificity ties within the layer.\n */\nexport type HostStyleSegment =\n\t| { readonly kind: 'rules'; readonly css: string }\n\t| { readonly kind: 'import'; readonly statement: string };\n\ninterface CopyState {\n\treadonly segments: HostStyleSegment[];\n\treadonly seen: Set<CSSStyleSheet>;\n\t/** Base URL for rules from sheets without an own URL (inline styles). */\n\treadonly baseURL: string;\n\t/** Counter for naming hoisted anonymous import layers uniquely. */\n\tanonymousLayerCount: number;\n}\n\n/**\n * Collects the style roots that can hold rules targeting this editor: the\n * document plus any shadow roots the editor is nested in (outermost first, so\n * later, closer scopes win source-order ties after flattening).\n */\nfunction styleRootsOf(host: HTMLElement): readonly (Document | ShadowRoot)[] {\n\tconst roots: (Document | ShadowRoot)[] = [];\n\tlet node: Node = host.getRootNode();\n\twhile (node instanceof ShadowRoot) {\n\t\troots.push(node);\n\t\tnode = node.host.getRootNode();\n\t}\n\troots.push(host.ownerDocument);\n\treturn roots.reverse();\n}\n\nfunction sheetsOfRoot(root: Document | ShadowRoot): readonly CSSStyleSheet[] {\n\tconst sheets: CSSStyleSheet[] = Array.from(root.styleSheets);\n\tconst adopted: readonly CSSStyleSheet[] | undefined = root.adoptedStyleSheets;\n\tif (adopted) sheets.push(...adopted);\n\treturn sheets;\n}\n\n/** Escapes a URL for embedding in `@import url(\"...\")`. */\nfunction escapeImportURL(url: string): string {\n\treturn url.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * URLs that must not be rebased: fragment-only references stay document-local\n * (SVG paint servers like `fill: url(#gradient)`), and URLs that already have\n * a scheme or are protocol-relative resolve identically everywhere.\n */\nfunction isRebasableURL(url: string): boolean {\n\tif (url.startsWith('#') || url.startsWith('//')) return false;\n\treturn !/^[a-z][a-z0-9+.-]*:/i.test(url);\n}\n\n/** Returns the index just past the closing quote of the string opening at `start`. */\nfunction skipString(cssText: string, start: number): number {\n\tconst quote: string = cssText[start] ?? '\"';\n\tlet i: number = start + 1;\n\twhile (i < cssText.length) {\n\t\tconst ch: string = cssText[i] ?? '';\n\t\tif (ch === '\\\\') {\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === quote) return i + 1;\n\t\ti++;\n\t}\n\treturn cssText.length;\n}\n\n/**\n * Resolves CSS escape sequences (`\\\"`, `\\2f `, …) in a `url()` argument so the\n * actual URL is rebased, not its escaped serialization.\n */\nfunction unescapeCSSValue(value: string): string {\n\treturn value.replace(\n\t\t/\\\\([0-9a-f]{1,6}\\s?|[\\s\\S])/gi,\n\t\t(_match: string, escaped: string): string => {\n\t\t\tconst hex: string = escaped.trim();\n\t\t\tif (/^[0-9a-f]{1,6}$/i.test(hex)) {\n\t\t\t\tconst code: number = Number.parseInt(hex, 16);\n\t\t\t\treturn code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : '�';\n\t\t\t}\n\t\t\treturn escaped;\n\t\t},\n\t);\n}\n\n/** True when a `url(` function token starts at `index` (not part of a longer identifier). */\nfunction isURLTokenStart(cssText: string, index: number): boolean {\n\tif (cssText.slice(index, index + 4).toLowerCase() !== 'url(') return false;\n\tconst before: string = index > 0 ? (cssText[index - 1] ?? '') : '';\n\treturn !/[\\w-]/.test(before);\n}\n\ninterface URLToken {\n\t/** Index just past the closing `)`. */\n\treadonly end: number;\n\t/** The unescaped URL argument. */\n\treadonly url: string;\n}\n\n/** Parses the `url(...)` token starting at `start`; null when malformed. */\nfunction parseURLToken(cssText: string, start: number): URLToken | null {\n\tlet i: number = start + 4;\n\twhile (i < cssText.length && /\\s/.test(cssText[i] ?? '')) i++;\n\tconst ch: string = cssText[i] ?? '';\n\tlet raw: string;\n\tif (ch === '\"' || ch === \"'\") {\n\t\tconst end: number = skipString(cssText, i);\n\t\traw = cssText.slice(i + 1, end - 1);\n\t\ti = end;\n\t} else {\n\t\tconst close: number = cssText.indexOf(')', i);\n\t\tif (close === -1) return null;\n\t\traw = cssText.slice(i, close).trim();\n\t\ti = close;\n\t}\n\twhile (i < cssText.length && /\\s/.test(cssText[i] ?? '')) i++;\n\tif (cssText[i] !== ')') return null;\n\treturn { end: i + 1, url: unescapeCSSValue(raw) };\n}\n\n/** Rewrites one parsed `url()` token, keeping it verbatim when not rebasable. */\nfunction rebaseURLToken(original: string, url: string, baseURL: string): string {\n\tif (!url || !isRebasableURL(url)) return original;\n\ttry {\n\t\treturn `url(\"${new URL(url, baseURL).href}\")`;\n\t} catch {\n\t\treturn original;\n\t}\n}\n\n/**\n * Rewrites relative `url()` references to absolute URLs against the source\n * stylesheet's URL. Copied `cssText` keeps URLs as authored; in the print\n * document they would re-resolve against the page URL instead of the\n * stylesheet's location, breaking fonts and images from stylesheets that live\n * in another directory. The scan is string-aware: a value that merely mentions\n * `url(...)` inside a CSS string (`content: \"url(info)\"`) is left untouched —\n * rewriting inside it would inject quotes and invalidate the declaration.\n */\nfunction rebaseCSSURLs(cssText: string, baseURL: string): string {\n\tif (!cssText.toLowerCase().includes('url(')) return cssText;\n\tlet result = '';\n\tlet i = 0;\n\twhile (i < cssText.length) {\n\t\tconst ch: string = cssText[i] ?? '';\n\t\tif (ch === '\"' || ch === \"'\") {\n\t\t\tconst end: number = skipString(cssText, i);\n\t\t\tresult += cssText.slice(i, end);\n\t\t\ti = end;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isURLTokenStart(cssText, i)) {\n\t\t\tconst token: URLToken | null = parseURLToken(cssText, i);\n\t\t\tif (token) {\n\t\t\t\tresult += rebaseURLToken(cssText.slice(i, token.end), token.url, baseURL);\n\t\t\t\ti = token.end;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tresult += ch;\n\t\ti++;\n\t}\n\treturn result;\n}\n\n/** Returns a unique sublayer name for a hoisted anonymous import layer. */\nfunction anonymousLayerName(state: CopyState): string {\n\tstate.anonymousLayerCount += 1;\n\treturn `notectl-anonymous-${state.anonymousLayerCount}`;\n}\n\n/** Appends rule text, coalescing with a preceding rules segment. */\nfunction pushRules(state: CopyState, css: string): void {\n\tconst last: HostStyleSegment | undefined = state.segments[state.segments.length - 1];\n\tif (last?.kind === 'rules') {\n\t\tstate.segments[state.segments.length - 1] = { kind: 'rules', css: `${last.css}\\n${css}` };\n\t\treturn;\n\t}\n\tstate.segments.push({ kind: 'rules', css });\n}\n\n/**\n * Re-references a stylesheet whose rules are not readable. The import's own\n * cascade-layer assignment survives as a sublayer of `notectl-host` (nested\n * layers order before the parent's direct rules, mirroring how layered import\n * content loses to unlayered page rules live), and its `supports()` condition\n * is carried verbatim. The statement is pushed as an ordered segment so the\n * re-referenced sheet keeps its live source-order position.\n */\nfunction hoistImport(\n\tstate: CopyState,\n\thref: string,\n\tmediaText: string,\n\tlayerName: string | null,\n\tsupportsText: string | null,\n): void {\n\tconst media: string = mediaText.trim();\n\tconst mediaCondition: string = media && media !== 'all' ? ` ${media}` : '';\n\tlet layer: string = HOST_STYLE_LAYER;\n\tif (layerName === '') {\n\t\tlayer = `${HOST_STYLE_LAYER}.${anonymousLayerName(state)}`;\n\t} else if (layerName) {\n\t\tlayer = `${HOST_STYLE_LAYER}.${layerName}`;\n\t}\n\tconst supports: string = supportsText?.trim() ? ` supports(${supportsText.trim()})` : '';\n\tstate.segments.push({\n\t\tkind: 'import',\n\t\tstatement: `@import url(\"${escapeImportURL(href)}\") layer(${layer})${supports}${mediaCondition};`,\n\t});\n}\n\n/** Wraps serialized sheet text in the sheet's own media condition, if any. */\nfunction wrapInSheetMedia(text: string, mediaText: string): string {\n\tconst media: string = mediaText.trim();\n\tif (!media || media === 'all') return text;\n\treturn `@media ${media} {\\n${text}\\n}`;\n}\n\nfunction serializeSheet(sheet: CSSStyleSheet, state: CopyState): void {\n\tif (state.seen.has(sheet)) return;\n\tstate.seen.add(sheet);\n\tif (sheet.disabled) return;\n\n\tlet cssRules: CSSRuleList;\n\ttry {\n\t\tcssRules = sheet.cssRules;\n\t} catch {\n\t\t// Cross-origin — rules are not readable. Re-reference so the print\n\t\t// document loads the stylesheet itself.\n\t\tif (sheet.href) hoistImport(state, sheet.href, sheet.media.mediaText, null, null);\n\t\treturn;\n\t}\n\n\tconst base: string = sheet.href ?? state.baseURL;\n\tconst body: string[] = [];\n\tfor (const rule of cssRules) {\n\t\tif (rule.type === IMPORT_RULE_TYPE) {\n\t\t\t// `@import` precedes every other rule of its sheet, so hoisted\n\t\t\t// statements emitted now stay ahead of this sheet's rule chunk.\n\t\t\tserializeImportRule(rule as CSSImportRule, state, body);\n\t\t\tcontinue;\n\t\t}\n\t\tbody.push(rebaseCSSURLs(rule.cssText, base));\n\t}\n\n\tconst text: string = body.join('\\n');\n\tif (text) pushRules(state, wrapInSheetMedia(text, sheet.media.mediaText));\n}\n\n/**\n * Inlines a readable `@import` target in place; unreadable targets are hoisted\n * as layered `@import` statements (`@import` is not valid inside a layer\n * block, so it cannot be copied verbatim). The import's own cascade-layer\n * assignment and `supports()` condition survive inlining as nested `@layer`\n * and `@supports` blocks — without them, layered import content would compete\n * on specificity against page rules it loses to live.\n */\nfunction serializeImportRule(rule: CSSImportRule, state: CopyState, body: string[]): void {\n\tconst layerName: string | null = rule.layerName ?? null;\n\tconst supportsText: string | null = rule.supportsText ?? null;\n\tconst imported: CSSStyleSheet | null = rule.styleSheet;\n\tif (imported && !state.seen.has(imported)) {\n\t\tlet importedRules: CSSRuleList | null;\n\t\ttry {\n\t\t\timportedRules = imported.cssRules;\n\t\t} catch {\n\t\t\timportedRules = null;\n\t\t}\n\t\tif (importedRules) {\n\t\t\tstate.seen.add(imported);\n\t\t\tconst base: string = imported.href ?? state.baseURL;\n\t\t\tconst inner: string[] = [];\n\t\t\tfor (const importedRule of importedRules) {\n\t\t\t\tif (importedRule.type === IMPORT_RULE_TYPE) {\n\t\t\t\t\tserializeImportRule(importedRule as CSSImportRule, state, inner);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tinner.push(rebaseCSSURLs(importedRule.cssText, base));\n\t\t\t}\n\t\t\tlet text: string = inner.join('\\n');\n\t\t\tif (text) {\n\t\t\t\tif (layerName !== null) {\n\t\t\t\t\t// Anonymous import layers stay anonymous via a nameless block.\n\t\t\t\t\ttext = layerName === '' ? `@layer {\\n${text}\\n}` : `@layer ${layerName} {\\n${text}\\n}`;\n\t\t\t\t}\n\t\t\t\tif (supportsText?.trim()) {\n\t\t\t\t\t// supports(display: grid) allows a bare declaration; the @supports\n\t\t\t\t\t// rule grammar does not, so the condition is parenthesized.\n\t\t\t\t\ttext = `@supports (${supportsText.trim()}) {\\n${text}\\n}`;\n\t\t\t\t}\n\t\t\t\tbody.push(wrapInSheetMedia(text, rule.media.mediaText));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\tconst href: string | null = imported?.href ?? rule.href;\n\tif (href) hoistImport(state, href, rule.media.mediaText, layerName, supportsText);\n}\n\n/**\n * Copies all stylesheets that can target this editor (document-level plus any\n * enclosing shadow roots, including adopted sheets) into an ordered list of\n * verbatim CSS segments for the print document.\n */\nexport function collectHostStyleSegments(host: HTMLElement): readonly HostStyleSegment[] {\n\tconst state: CopyState = {\n\t\tsegments: [],\n\t\tseen: new Set(),\n\t\tbaseURL: host.ownerDocument.baseURI ?? '',\n\t\tanonymousLayerCount: 0,\n\t};\n\tfor (const root of styleRootsOf(host)) {\n\t\tfor (const sheet of sheetsOfRoot(root)) {\n\t\t\tserializeSheet(sheet, state);\n\t\t}\n\t}\n\treturn state.segments;\n}\n","/**\n * Collects and aggregates CSS for print output.\n *\n * The print document preserves the editor's shadow boundary (declarative\n * shadow DOM), so styles split into two scopes:\n *\n * - Shadow scope (inside the template): the editor's own adopted styles\n * (base styles behind a cascade layer, runtime style-token rules\n * unlayered), print content rules, and `customCSS` as the final same-tree\n * override.\n * - Document scope: print guards (host-element reset, `html`/`body` page\n * reset, and forced light theme — `!important` inside the `notectl-print`\n * layer so they hold even against `!important` host rules such as\n * `@media print { body * { visibility: hidden } }`), the carried\n * theme-token snapshot (unlayered, so it beats the layered host copy), body\n * typography snapshot, `@page` setup, and two `customCSS` copies: unlayered\n * for normal-strength page-level rules (`@page`, `body`) and inside the\n * earliest `notectl-custom` layer so consumer `!important` rules outrank\n * the print guards.\n *\n * Document layer order is `notectl-custom, notectl-print, notectl-host`\n * (declared in {@link buildHTMLDocument}). For normal declarations unlayered\n * rules win over all layers; for `!important` declarations the layer order\n * reverses, so earlier layers win: customCSS > print guards > host copy.\n *\n * Two document-scope builders exist: {@link buildDocumentCSS} for the\n * internal print iframe (full page fidelity — `html`/`body` reset, body\n * typography, `@page`) and {@link buildExportDocumentCSS} for the export\n * variant, whose inline rules are all qualified with the replica marker\n * attribute so embedding the export into a live page (documented\n * innerHTML/setHTMLUnsafe flow) cannot restyle the consumer's document. The\n * export regains full page fidelity in standalone documents through the\n * script-hoisted standalone style bundle (see PrintServiceImpl).\n */\n\nimport { generateThemeCSS } from '../../editor/theme/ThemeEngine.js';\nimport { LIGHT_THEME } from '../../editor/theme/ThemeTokens.js';\nimport {\n\tPAPER_MARGIN_HORIZONTAL_PX,\n\tPAPER_MARGIN_TOP_PX,\n\tgetPaperCSSSize,\n} from '../../model/PaperSize.js';\nimport { isRuntimeStyleSheet } from '../../style/StyleRuntime.js';\nimport type { PrintOptions } from './PrintTypes.js';\n\n/** Cascade layer that holds the editor's own base styles in print output. */\nconst BASE_STYLE_LAYER = 'notectl-base';\n\n/**\n * Document-level cascade layer holding the print guards (host reset, forced\n * light theme). Declared before the host-copy layer, so its `!important`\n * declarations beat `!important` host rules.\n */\nexport const PRINT_STYLE_LAYER = 'notectl-print';\n\n/**\n * Earliest document-level cascade layer, holding a `customCSS` copy. Its\n * `!important` declarations are the strongest in the print document — the\n * consumer's escape hatch over the print guards and host rules.\n */\nexport const CUSTOM_STYLE_LAYER = 'notectl-custom';\n\n/**\n * Appends `!important` to every declaration of generated CSS. Only safe for\n * CSS this module generates itself (one declaration per line, no semicolons\n * inside values).\n */\nfunction markDeclarationsImportant(css: string): string {\n\treturn css.replace(/;$/gm, ' !important;');\n}\n\n/** Adopted shadow styles split by role for print emission. */\nexport interface AdoptedStylePartition {\n\t/** Editor base styles — emitted inside the print cascade layer. */\n\treadonly base: string;\n\t/**\n\t * Runtime style-token rules (strict-CSP mode) carrying user-applied content\n\t * formatting — emitted unlayered so they keep their cascade strength.\n\t */\n\treadonly runtime: string;\n}\n\n/**\n * Extracts CSS text from all adopted stylesheets on a ShadowRoot, separating\n * editor base styles from runtime style-token sheets. The distinction matters\n * for print: base styles go into a cascade layer so consumer overrides win\n * (the documented #202 semantic: `customCSS` overrides built-in element\n * styling regardless of selector specificity — the flip side, also\n * documented, is that broad customCSS resets such as `* { padding: 0 }`\n * override base styling too), but token rules represent user content\n * formatting (inline styles in non-CSP mode) and must not be demoted with\n * them.\n */\nexport function partitionAdoptedStyles(shadowRoot: ShadowRoot): AdoptedStylePartition {\n\tconst base: string[] = [];\n\tconst runtime: string[] = [];\n\tfor (const sheet of shadowRoot.adoptedStyleSheets) {\n\t\tconst target: string[] = isRuntimeStyleSheet(sheet) ? runtime : base;\n\t\tfor (const rule of sheet.cssRules) {\n\t\t\ttarget.push(rule.cssText);\n\t\t}\n\t}\n\treturn { base: base.join('\\n'), runtime: runtime.join('\\n') };\n}\n\n/**\n * Generates light theme custom properties targeting `selector` (a full\n * selector list). The internal print iframe passes `:root` plus the host\n * element, so page-level `customCSS` rules\n * (`body { background: var(--notectl-bg) }`) keep resolving while the host\n * element re-pins the tokens closer than the shadow theme sheet's `:host`\n * defaults; the export variant passes only the marker-qualified replica so\n * nothing can leak into an embedding page. Every declaration is `!important`;\n * emitted inside the `notectl-print` layer, it beats copied host-page token\n * overrides (even `!important` ones) and the shadow theme sheet (tree\n * context), guaranteeing readable print output regardless of the active\n * editor theme.\n */\nexport function generateLightThemeTokens(selector: string): string {\n\tconst css: string = generateThemeCSS(LIGHT_THEME).replace(':host {', `${selector} {`);\n\treturn markDeclarationsImportant(css);\n}\n\n/**\n * Serializes an element's inline `--notectl-*` declarations. Tokens set via\n * the `style` attribute or JS `setProperty` (runtime theme switchers) cannot\n * travel through the stylesheet copy; re-emitting them as inline style on the\n * print document's corresponding element preserves their live cascade\n * strength — inline declarations beat every copied stylesheet rule, exactly\n * as on the live page.\n */\nexport function collectInlineThemeTokens(el: Element | null): string {\n\tif (!(el instanceof HTMLElement)) return '';\n\tconst declarations: string[] = [];\n\tfor (let i = 0; i < el.style.length; i++) {\n\t\tconst prop: string = el.style[i] ?? '';\n\t\tif (!prop.startsWith('--notectl-')) continue;\n\t\tconst value: string = el.style.getPropertyValue(prop).trim();\n\t\tif (value) declarations.push(`${prop}: ${value}`);\n\t}\n\treturn declarations.join('; ');\n}\n\n/**\n * Snapshots all computed `--notectl-*` tokens from the host element when\n * carrying the live theme (`forceLightTheme: false`). The computed values are\n * the ground truth of what the user saw: tokens set via the host's inline\n * `style` attribute (stripped from the print replica), via JS `setProperty`,\n * or via rules scoped to wrapper elements that do not exist in the print\n * document cannot travel through the stylesheet copy. Targeted at `selector`\n * (a full selector list: the host element, plus `:root` in the internal\n * variant so page-level `customCSS` keeps resolving the tokens) and emitted\n * unlayered: unlayered normal declarations beat the layered host copy, so a\n * host-page rule that matches differently in the print document (its wrapper\n * ancestors are not replicated) cannot override the live values.\n * Print-specific token overrides therefore need `!important` in the host\n * page or `customCSS`.\n */\nexport function snapshotThemeTokens(host: HTMLElement, selector: string): string {\n\tconst computed: CSSStyleDeclaration = getComputedStyle(host);\n\tconst tokens: string[] = [];\n\n\tfor (let i = 0; i < computed.length; i++) {\n\t\tconst prop: string = computed[i] ?? '';\n\t\tif (prop.startsWith('--notectl-')) {\n\t\t\tconst value: string = computed.getPropertyValue(prop).trim();\n\t\t\tif (value) {\n\t\t\t\ttokens.push(` ${prop}: ${value};`);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (tokens.length === 0) return '';\n\treturn `${selector} {\\n${tokens.join('\\n')}\\n}`;\n}\n\n/** Computed background-color values that mean \"no own background\". */\nconst TRANSPARENT_BACKGROUNDS: readonly string[] = ['', 'transparent', 'rgba(0, 0, 0, 0)'];\n\n/**\n * Resolves the background the editor content visually sits on, for carrying\n * the live theme into print (`forceLightTheme: false`). Walks up from the\n * content container (crossing shadow boundaries) through the page body up to\n * the root element — dark themes commonly paint `html` and leave `body`\n * transparent — returning the first non-transparent computed\n * background-color. Falls back to white so print output never inherits an\n * undefined background.\n */\nexport function resolveCarriedBackground(container: HTMLElement): string {\n\tlet el: HTMLElement | null = container;\n\twhile (el) {\n\t\tconst background: string = getComputedStyle(el).getPropertyValue('background-color').trim();\n\t\tif (!TRANSPARENT_BACKGROUNDS.includes(background)) return background;\n\t\tconst root: Node = el.getRootNode();\n\t\tel = el.parentElement ?? (root instanceof ShadowRoot ? (root.host as HTMLElement) : null);\n\t}\n\treturn '#ffffff';\n}\n\n/**\n * Snapshots computed typography from the host element into a rule for\n * `selector` (the page body in the print iframe, the replica host in the\n * export variant). Copied host-page styles are layered and `body` rules from\n * the host page are meant for the host application, so print pins typography\n * explicitly to what the editor computed live. The background is pinned to\n * `background` — white when the light theme is forced, the carried theme\n * background otherwise — so dark-theme output never renders light text on\n * white paper.\n */\nexport function snapshotTypography(\n\thost: HTMLElement,\n\tcolorOverride?: string,\n\tbackground = '#ffffff',\n\tselector = 'body',\n): string {\n\tconst computed: CSSStyleDeclaration = getComputedStyle(host);\n\tconst props: string[] = [\n\t\t'font-family',\n\t\t'font-size',\n\t\t'font-weight',\n\t\t'line-height',\n\t\t'letter-spacing',\n\t\t'word-spacing',\n\t\t'color',\n\t];\n\tconst rules: string[] = [' margin: 0;', ` background: ${background};`];\n\tfor (const prop of props) {\n\t\tif (prop === 'color' && colorOverride) {\n\t\t\trules.push(` color: ${colorOverride};`);\n\t\t\tcontinue;\n\t\t}\n\t\tconst value: string = computed.getPropertyValue(prop).trim();\n\t\tif (value) {\n\t\t\trules.push(` ${prop}: ${value};`);\n\t\t}\n\t}\n\treturn `${selector} {\\n${rules.join('\\n')}\\n}`;\n}\n\n/** Declarations that neutralize screen-widget chrome on the print host. */\nconst HOST_RESET_DECLARATIONS: readonly string[] = [\n\t'display: block',\n\t'position: static',\n\t'inset: auto',\n\t'width: auto',\n\t'height: auto',\n\t'min-width: 0',\n\t'min-height: 0',\n\t'max-width: none',\n\t'max-height: none',\n\t'margin: 0',\n\t'padding: 0',\n\t'border: none',\n\t'border-radius: 0',\n\t'outline: none',\n\t'box-shadow: none',\n\t'background: transparent',\n\t'overflow: visible',\n\t'transform: none',\n\t'filter: none',\n\t'opacity: 1',\n\t'contain: none',\n\t'float: none',\n\t'visibility: visible',\n];\n\n/**\n * Declarations that keep the print page itself visible and unclipped. Host\n * pages commonly hide or clip at the page level — the classic\n * `@media print { body * { visibility: hidden } }` trick or app-shell\n * `html, body { height: 100%; overflow: hidden }` — and those rules are\n * copied verbatim into the host layer. Only hide/clip-capable properties are\n * guarded; box properties like `margin` stay overridable via plain customCSS.\n */\nconst PAGE_RESET_DECLARATIONS: readonly string[] = [\n\t'display: block',\n\t'visibility: visible',\n\t'opacity: 1',\n\t'position: static',\n\t'height: auto',\n\t'min-height: 0',\n\t'max-height: none',\n\t'overflow: visible',\n\t'transform: none',\n\t'filter: none',\n\t'contain: none',\n];\n\n/**\n * Neutralizes page-level hiding and clipping from copied host rules on\n * `html`/`body`. Every declaration is `!important`; emitted inside the\n * `notectl-print` layer it beats the host copy even against `!important`\n * host rules, while `customCSS` (earlier `notectl-custom` layer) still wins.\n */\nexport function generatePageResetCSS(): string {\n\treturn [\n\t\t'html, body {',\n\t\t...PAGE_RESET_DECLARATIONS.map((declaration: string): string => ` ${declaration} !important;`),\n\t\t'}',\n\t].join('\\n');\n}\n\n/**\n * Neutralizes screen-widget chrome on the print host element. Copied\n * host-page rules like `notectl-editor { height: 400px; border: … }` style\n * the live widget but must not constrain the paginated print flow. Every\n * declaration is `!important`; emitted inside the `notectl-print` layer, the\n * reset beats the host copy even against `!important` host rules. Inherited\n * properties (fonts, color) are deliberately not touched; `customCSS` can\n * reclaim any of these via `!important` (earlier `notectl-custom` layer).\n */\nexport function generateHostResetCSS(hostSelector: string): string {\n\treturn [\n\t\t`${hostSelector} {`,\n\t\t...HOST_RESET_DECLARATIONS.map((declaration: string): string => ` ${declaration} !important;`),\n\t\t'}',\n\t].join('\\n');\n}\n\n/** Generates the `@page` rule based on PrintOptions. */\nexport function generatePageCSS(options: PrintOptions): string {\n\tconst pageProps: string[] = [];\n\tif (options.margin) {\n\t\tpageProps.push(` margin: ${options.margin};`);\n\t} else if (options.paperSize) {\n\t\t// WYSIWYG: zero @page margin so body = full paper width;\n\t\t// document margins are applied as content padding below.\n\t\tpageProps.push(' margin: 0;');\n\t}\n\tif (options.paperSize || options.orientation) {\n\t\tconst sizeKeyword: string = options.paperSize ? getPaperCSSSize(options.paperSize) : 'A4';\n\t\tconst sizeParts: string[] = [sizeKeyword];\n\t\tif (options.orientation) {\n\t\t\tsizeParts.push(options.orientation);\n\t\t}\n\t\tpageProps.push(` size: ${sizeParts.join(' ')};`);\n\t}\n\tif (pageProps.length === 0) return '';\n\treturn `@page {\\n${pageProps.join('\\n')}\\n}`;\n}\n\n/** Generates print-specific content rules; emitted inside the shadow scope. */\nexport function generateContentPrintCSS(options: PrintOptions): string {\n\tconst parts: string[] = [];\n\n\t// Print flows the full document: screen scroll constraints on the content\n\t// area (the --notectl-content-max-height token or host ::part(content)\n\t// height rules) must not clip it to one scroll fold. Shadow-tree\n\t// `!important` beats document-tree `!important`, so this holds even\n\t// against host `::part()` rules; the shadow customCSS copy is emitted\n\t// later in source order and can still override.\n\tparts.push(\n\t\t'.notectl-content { height: auto !important; max-height: none !important; ' +\n\t\t\t'min-height: 0 !important; overflow: visible !important; }',\n\t);\n\n\t// WYSIWYG: apply paper-mode document margins to print content\n\tif (options.paperSize) {\n\t\tparts.push(\n\t\t\t`.notectl-content { padding: ${PAPER_MARGIN_TOP_PX}px ${PAPER_MARGIN_HORIZONTAL_PX}px !important; }`,\n\t\t);\n\t}\n\n\t// Print media rules\n\tconst printRules: string[] = [];\n\n\t// Hide non-printable elements\n\tprintRules.push('[data-notectl-no-print] { display: none !important; }');\n\n\t// Code blocks: preserve background, force wrapping\n\tprintRules.push(\n\t\t'.notectl-code-block { white-space: pre-wrap !important; ' +\n\t\t\t'word-break: break-all !important; page-break-inside: avoid; }',\n\t);\n\n\t// Images: avoid page breaks\n\tprintRules.push('img { page-break-inside: avoid; }');\n\n\t// Tables: avoid page breaks\n\tprintRules.push('.notectl-table { break-inside: avoid; }');\n\n\t// Preserve background colors and images in print output (WYSIWYG default).\n\t// Only omitted when the consumer explicitly opts out via printBackground: false.\n\tif (options.printBackground !== false) {\n\t\tprintRules.push(\n\t\t\t'* { -webkit-print-color-adjust: exact !important; ' +\n\t\t\t\t'print-color-adjust: exact !important; }',\n\t\t);\n\t}\n\n\tparts.push(`@media print {\\n ${printRules.join('\\n ')}\\n}`);\n\n\treturn parts.join('\\n\\n');\n}\n\n/**\n * Builds the CSS placed inside the print document's declarative shadow root:\n * editor base styles behind a cascade layer (so consumer overrides win),\n * runtime style-token rules unlayered, print content rules, and customCSS as\n * the final same-tree override.\n */\nexport function buildShadowCSS(shadowRoot: ShadowRoot, options: PrintOptions): string {\n\tconst parts: string[] = [];\n\n\tconst adopted: AdoptedStylePartition = partitionAdoptedStyles(shadowRoot);\n\tif (adopted.base) parts.push(`@layer ${BASE_STYLE_LAYER} {\\n${adopted.base}\\n}`);\n\tif (adopted.runtime) parts.push(adopted.runtime);\n\n\tconst contentCSS: string = generateContentPrintCSS(options);\n\tif (contentCSS) parts.push(contentCSS);\n\n\tif (options.customCSS) {\n\t\tparts.push(options.customCSS);\n\t}\n\n\treturn parts.join('\\n\\n');\n}\n\n/** The print-color-adjust guard; document-level, because the shadow-scope copy cannot reach document-tree elements. */\nconst COLOR_ADJUST_GUARD: string =\n\t'* {\\n -webkit-print-color-adjust: exact !important;\\n print-color-adjust: exact !important;\\n}';\n\n/**\n * Builds the document-level CSS of the internal print iframe: the print\n * guards (host reset, `html`/`body` page reset, and forced light theme —\n * `!important` inside the `notectl-print` layer), the unlayered theme-token\n * snapshot, body typography, `@page`, and two copies of customCSS. The\n * unlayered copy carries page-level custom rules (`@page`, `body` — they\n * cannot live inside the shadow scope) at normal strength; the\n * `notectl-custom` layer copy makes consumer `!important` rules outrank the\n * print guards and `!important` host rules.\n */\nexport function buildDocumentCSS(\n\thost: HTMLElement,\n\tcontainer: HTMLElement,\n\toptions: PrintOptions,\n): string {\n\tconst hostSelector: string = host.tagName.toLowerCase();\n\tconst parts: string[] = [];\n\n\tconst forceLightTheme: boolean = options.forceLightTheme !== false;\n\tconst guards: string[] = [generateHostResetCSS(hostSelector), generatePageResetCSS()];\n\tif (forceLightTheme) {\n\t\tguards.push(generateLightThemeTokens(`:root, ${hostSelector}`));\n\t}\n\t// Without the document-level copy of this guard, the pinned body background\n\t// (the carried theme background for forceLightTheme: false) is stripped by\n\t// the browser's default \"no background graphics\" print behavior.\n\tif (options.printBackground !== false) {\n\t\tguards.push(COLOR_ADJUST_GUARD);\n\t}\n\tparts.push(`@layer ${PRINT_STYLE_LAYER} {\\n${guards.join('\\n\\n')}\\n}`);\n\n\tif (!forceLightTheme) {\n\t\tconst themeTokens: string = snapshotThemeTokens(host, `:root, ${hostSelector}`);\n\t\tif (themeTokens) parts.push(themeTokens);\n\t}\n\n\tconst colorOverride: string | undefined = forceLightTheme\n\t\t? LIGHT_THEME.primitives.foreground\n\t\t: undefined;\n\tconst background: string = forceLightTheme ? '#ffffff' : resolveCarriedBackground(container);\n\tparts.push(snapshotTypography(host, colorOverride, background));\n\n\tconst pageCSS: string = generatePageCSS(options);\n\tif (pageCSS) parts.push(pageCSS);\n\n\tif (options.customCSS) {\n\t\tparts.push(options.customCSS);\n\t\tparts.push(`@layer ${CUSTOM_STYLE_LAYER} {\\n${options.customCSS}\\n}`);\n\t}\n\n\treturn parts.join('\\n\\n');\n}\n\n/**\n * Builds the inline document-level CSS of the export variant (`toHTML()`,\n * `AFTER_PRINT`). Every selector is qualified with `markerSelector` (the\n * static-replica marker attribute), so the rules can only ever match a\n * replica: embedding the export into a live page via the documented\n * innerHTML/setHTMLUnsafe flow cannot restyle the consumer's document, hide\n * its live editors, or force them light. (`@scope` cannot express this: its\n * implicit `:where(:scope)` descendant prefix excludes the scoping root\n * itself, and shadow content — every `::part()` subject — is outside the\n * scope.) Page-level rules (`html`/`body` reset, `body` typography, `@page`,\n * `:root` tokens, the customCSS document copies) are not emitted here; they\n * ship in the script-gated standalone style bundle and otherwise apply in the\n * `print()` iframe. Instead the typography and background snapshot is emitted\n * `!important` after the host reset in the same layer, so the replica paints\n * its own canvas even without scripts.\n */\nexport function buildExportDocumentCSS(\n\thost: HTMLElement,\n\tcontainer: HTMLElement,\n\toptions: PrintOptions,\n\tmarkerAttribute: string,\n): string {\n\tconst markedSelector: string = `${host.tagName.toLowerCase()}[${markerAttribute}]`;\n\tconst forceLightTheme: boolean = options.forceLightTheme !== false;\n\n\tconst guards: string[] = [generateHostResetCSS(markedSelector)];\n\tif (forceLightTheme) {\n\t\t// No `:root` copy here: `!important` tokens on the embedding page's root\n\t\t// would force every live editor light (they beat the shadow `:host`\n\t\t// theme sheet). The `:root` copy for page-level customCSS var() support\n\t\t// lives in the standalone bundle.\n\t\tguards.push(generateLightThemeTokens(markedSelector));\n\t}\n\tif (options.printBackground !== false) {\n\t\tguards.push(\n\t\t\t`${markedSelector}, ${markedSelector} * {\\n -webkit-print-color-adjust: exact !important;\\n print-color-adjust: exact !important;\\n}`,\n\t\t);\n\t}\n\tconst colorOverride: string | undefined = forceLightTheme\n\t\t? LIGHT_THEME.primitives.foreground\n\t\t: undefined;\n\tconst background: string = forceLightTheme ? '#ffffff' : resolveCarriedBackground(container);\n\t// Emitted after the host reset in the same layer: the source-order tie\n\t// lets the snapshot's background beat the reset's `background: transparent`.\n\tguards.push(\n\t\tmarkDeclarationsImportant(snapshotTypography(host, colorOverride, background, markedSelector)),\n\t);\n\n\tconst parts: string[] = [`@layer ${PRINT_STYLE_LAYER} {\\n${guards.join('\\n\\n')}\\n}`];\n\n\tif (!forceLightTheme) {\n\t\tconst themeTokens: string = snapshotThemeTokens(host, markedSelector);\n\t\tif (themeTokens) parts.push(themeTokens);\n\t}\n\n\treturn parts.join('\\n\\n');\n}\n","/**\n * PrintService implementation.\n *\n * Builds a print document that preserves the editor's shadow boundary via\n * declarative shadow DOM: the host page's stylesheets are copied verbatim\n * (layered), the editor's own styles live inside the shadow template, and the\n * cloned content keeps its `part` attributes. Host `::part()` rules,\n * specificity, custom properties, and conditional rules therefore behave\n * exactly as in the live editor — nothing is translated or re-emulated.\n *\n * Two document variants are built from the same inputs:\n *\n * - The export variant (returned by `toHTML()`, broadcast via `AFTER_PRINT`)\n * is embed-safe: its inline document-level CSS is qualified with the static\n * replica marker, while everything page-level (host-CSS copy, hoisted\n * `@import`s, `html`/`body`/`@page` rules) ships inside an inert\n * `<template>` bundle that the fallback script activates only when the\n * export is rendered as its own document — so injecting the output into a\n * live page (documented innerHTML/setHTMLUnsafe flow) cannot restyle or\n * break the consumer's document. It carries a static light-DOM fallback for\n * parsers without declarative shadow DOM, and never carries the page's CSP\n * nonce, which is a secret that must not be persisted into serialized HTML.\n * - The internal variant (written into the transient print iframe) inlines\n * the full page-fidelity CSS (`html`/`body` reset, body typography,\n * `@page`, unscoped host-CSS copy, hoisted imports) and carries the style\n * nonce so it renders under the inherited CSP. It omits fallback, bundle,\n * and script: the iframe is parsed by the same modern browser, where DSD is\n * guaranteed.\n */\n\nimport { escapeAttr, escapeHTML } from '../../model/HTMLUtils.js';\nimport { getStyleNonceForNode, setStyleText } from '../../style/StyleRuntime.js';\nimport type { PluginEventBus } from '../Plugin.js';\nimport { prepare } from './PrintContentPreparer.js';\nimport {\n\tHOST_STYLE_LAYER,\n\ttype HostStyleSegment,\n\tcollectHostStyleSegments,\n} from './PrintHostStyles.js';\nimport {\n\tCUSTOM_STYLE_LAYER,\n\tPRINT_STYLE_LAYER,\n\tbuildDocumentCSS,\n\tbuildExportDocumentCSS,\n\tbuildShadowCSS,\n\tcollectInlineThemeTokens,\n} from './PrintStyleCollector.js';\nimport {\n\tAFTER_PRINT,\n\ttype AfterPrintEvent,\n\tBEFORE_PRINT,\n\ttype BeforePrintEvent,\n\ttype PrintOptions,\n\ttype PrintService,\n} from './PrintTypes.js';\nimport { STATIC_HOST_ATTRIBUTE } from './StaticHostMarker.js';\n\n/**\n * Upper bound for waiting on the print iframe's load event. Referenced host\n * stylesheets (hoisted cross-origin `@import`) load asynchronously and are\n * worth a short wait so the first printed paint is styled, but a hanging\n * stylesheet must never block the print dialog indefinitely.\n */\nconst PRINT_LOAD_TIMEOUT_MS = 4000;\n\n/** Marker attribute on the static light-DOM fallback of the export variant. */\nexport const PRINT_FALLBACK_ATTRIBUTE = 'data-notectl-print-fallback';\n\n/**\n * Marker attribute on the inert `<template>` carrying the export variant's\n * standalone style bundle (page-level CSS and the verbatim host-CSS copy).\n */\nexport const PRINT_STYLE_BUNDLE_ATTRIBUTE = 'data-notectl-print-styles';\n\n/**\n * `<meta name>` marking the export document itself. The fallback script only\n * activates the standalone style bundle when this meta sits in\n * `document.head` — true when the export is the document being rendered\n * (file, iframe `srcdoc`, `document.write`, HTML-to-PDF engines), never when\n * it was inlined into a consumer page (fragment parsing keeps the meta inside\n * the injected subtree, outside `document.head`).\n */\nexport const PRINT_EXPORT_META_NAME = 'notectl-print-export';\n\n/**\n * Fallback for consumers that render the export variant with a parser that\n * does not attach declarative shadow roots but does execute scripts (older\n * WebViews, headless HTML-to-PDF engines). When the export is the document\n * itself (its marker meta sits in `document.head`), the inert standalone\n * style bundle (page-level CSS, verbatim host-CSS copy, hoisted imports) is\n * moved into the head, restoring full page fidelity; inlined into a consumer\n * page the meta never reaches the head, so the bundle stays inert and cannot\n * restyle the page. The shadow-attach step is strictly scoped to marked\n * replicas: only a `template[shadowrootmode]` that is a direct child of a\n * `[data-notectl-static]` host is attached — a document-wide sweep would\n * consume declarative templates belonging to the consumer's own components\n * when the export is inlined into a live page. Once a shadow root exists the\n * static light-DOM fallback is removed so extracted text is not doubled;\n * engines without Shadow DOM keep the rendered fallback. Native DSD parsing\n * leaves no template behind, making the attach branch a no-op. Kept ES5-safe\n * for legacy engines; must not contain a literal `</`. Deliberately emitted\n * without a nonce: the export variant never embeds the page's CSP secrets,\n * and if a consumer's CSP blocks the script the static fallback still\n * renders.\n */\nconst DSD_FALLBACK_SCRIPT: string = [\n\t'(function () {',\n\t' var head = document.head;',\n\t' if (head && head.querySelector(\\'meta[name=\"notectl-print-export\"]\\')) {',\n\t\" var bundles = document.querySelectorAll('template[data-notectl-print-styles]');\",\n\t' for (var b = 0; b < bundles.length; b++) {',\n\t' if (bundles[b].content) head.appendChild(bundles[b].content);',\n\t' bundles[b].parentNode.removeChild(bundles[b]);',\n\t' }',\n\t' }',\n\t\" var statics = document.querySelectorAll('[data-notectl-static]');\",\n\t' for (var i = 0; i < statics.length; i++) {',\n\t' var host = statics[i];',\n\t' if (!host.shadowRoot && host.attachShadow) {',\n\t' for (var j = 0; j < host.children.length; j++) {',\n\t' var child = host.children[j];',\n\t\" if (child.tagName !== 'TEMPLATE') continue;\",\n\t\" if (!child.getAttribute('shadowrootmode') || !child.content) continue;\",\n\t\" host.attachShadow({ mode: 'open' }).appendChild(child.content);\",\n\t' host.removeChild(child);',\n\t' break;',\n\t' }',\n\t' }',\n\t' if (!host.shadowRoot) continue;',\n\t\" var fallbacks = host.querySelectorAll('[data-notectl-print-fallback]');\",\n\t' for (var k = 0; k < fallbacks.length; k++) {',\n\t' fallbacks[k].parentNode.removeChild(fallbacks[k]);',\n\t' }',\n\t' }',\n\t'})();',\n].join('\\n');\n\n/**\n * Escapes `</` in CSS embedded into a `<style>` element. The HTML parser ends\n * the element at any literal `</style`, even inside a CSS string such as\n * `content: \"</style>\"`, which would break the print document. `\\/` is a\n * valid CSS escape for `/`, so the stylesheet semantics are unchanged.\n */\nfunction escapeStyleText(css: string): string {\n\treturn css.replace(/<\\//g, '<\\\\/');\n}\n\n/** Serializes an element's attributes matched by `include`, with a leading space. */\nfunction serializeFilteredAttributes(el: Element, include: (name: string) => boolean): string {\n\tconst parts: string[] = [];\n\tfor (const attribute of Array.from(el.attributes)) {\n\t\tif (!include(attribute.name)) continue;\n\t\tparts.push(\n\t\t\tattribute.value === ''\n\t\t\t\t? attribute.name\n\t\t\t\t: `${attribute.name}=\"${escapeAttr(attribute.value)}\"`,\n\t\t);\n\t}\n\treturn parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\n/**\n * Serializes an element's attributes for the print document. The `style`\n * attribute is stripped from the host element: element-attached declarations\n * would beat every stylesheet rule, defeating the print host reset.\n */\nfunction serializeAttributes(el: Element, skip: readonly string[]): string {\n\treturn serializeFilteredAttributes(el, (name: string): boolean => !skip.includes(name));\n}\n\n/**\n * Serializes theme-context attributes (`class` and `data-*`) so selectors\n * like `html.dark notectl-editor::part(x)` keep matching when the live theme\n * is carried into print (`forceLightTheme: false`).\n */\nfunction serializeThemeContextAttributes(el: Element | null): string {\n\tif (!el) return '';\n\treturn serializeFilteredAttributes(\n\t\tel,\n\t\t(name: string): boolean => name === 'class' || name.startsWith('data-'),\n\t);\n}\n\n/**\n * Serializes an element's inline `--notectl-*` tokens as a `style` attribute\n * (empty string when the element carries none). Used when the live theme is\n * carried: inline tokens beat every copied stylesheet rule, exactly as live.\n */\nfunction serializeInlineTokenStyle(el: Element | null): string {\n\tconst declarations: string = collectInlineThemeTokens(el);\n\treturn declarations ? ` style=\"${escapeAttr(declarations)}\"` : '';\n}\n\n/** Everything needed to assemble the print document. */\nexport interface PrintDocumentInput {\n\t/**\n\t * Document-level CSS matching the variant: full page fidelity for\n\t * `internal` ({@link buildDocumentCSS}), marker-qualified inline baseline\n\t * for `export` ({@link buildExportDocumentCSS}).\n\t */\n\treadonly documentCSS: string;\n\t/**\n\t * Full-fidelity page CSS ({@link buildDocumentCSS}) shipped by the export\n\t * variant inside the inert standalone style bundle; the fallback script\n\t * activates it only when the export is rendered as its own document.\n\t */\n\treadonly standaloneCSS: string;\n\t/** Source-ordered host-page CSS copy (rule chunks and hoisted `@import`s). */\n\treadonly hostSegments: readonly HostStyleSegment[];\n\t/** CSS placed inside the declarative shadow root. */\n\treadonly shadowCSS: string;\n\t/** The live editor host element; its tag and attributes are replicated. */\n\treadonly host: HTMLElement;\n\t/** Serialized cloned editor content. */\n\treadonly contentHTML: string;\n\treadonly title: string;\n\treadonly lang: string;\n\t/** Copy html/body theme-context attributes (only when the live theme is carried). */\n\treadonly carryThemeContext: boolean;\n\t/**\n\t * `export` (`toHTML()`, `AFTER_PRINT`) is embed-safe: its inline styles are\n\t * marker-qualified, and everything page-level — the host-CSS copy, hoisted\n\t * `@import`s, and `standaloneCSS` — ships inside an inert `<template>`\n\t * bundle that the replica-scoped fallback script activates only when the\n\t * export is rendered as its own document. A consumer page embedding the\n\t * output can neither load foreign stylesheets nor have its own elements\n\t * restyled. `internal` (the transient print iframe) inlines the unscoped\n\t * copy and the imports directly and omits fallback, bundle, and script: the\n\t * iframe is parsed by the same modern browser, where declarative shadow DOM\n\t * is guaranteed.\n\t */\n\treadonly variant: 'export' | 'internal';\n\t/**\n\t * CSP nonce applied to every embedded `<style>` element so the internal\n\t * print iframe renders under an inherited `style-src` nonce policy. Never\n\t * set on the export variant: serialized HTML must not persist the page's\n\t * live nonce.\n\t */\n\treadonly styleNonce?: string;\n}\n\n/** Builds the complete print document as an HTML string. */\nexport function buildHTMLDocument(input: PrintDocumentInput): string {\n\tconst doc: Document = input.host.ownerDocument;\n\tconst hostTag: string = input.host.tagName.toLowerCase();\n\tconst isExport: boolean = input.variant === 'export';\n\t// The static marker keeps the replica from booting a live editor when the\n\t// document is injected into a page where the component is registered. The\n\t// export variant also drops the id: embedded next to the live editor it\n\t// would be a duplicate, and getElementById would resolve to the replica.\n\t// The internal iframe is its own document, so it keeps the id and host\n\t// rules like `#editor::part(cell)` stay matching.\n\tconst skippedHostAttributes: readonly string[] = isExport\n\t\t? ['style', 'id', STATIC_HOST_ATTRIBUTE]\n\t\t: ['style', STATIC_HOST_ATTRIBUTE];\n\tconst hostAttributes: string = serializeAttributes(input.host, skippedHostAttributes);\n\tconst hostTokens: string = input.carryThemeContext ? serializeInlineTokenStyle(input.host) : '';\n\n\tconst htmlContext: string = input.carryThemeContext\n\t\t? serializeThemeContextAttributes(doc.documentElement) +\n\t\t\tserializeInlineTokenStyle(doc.documentElement)\n\t\t: '';\n\tconst bodyContext: string = input.carryThemeContext\n\t\t? serializeThemeContextAttributes(doc.body) + serializeInlineTokenStyle(doc.body)\n\t\t: '';\n\n\tconst nonceAttribute: string = input.styleNonce ? ` nonce=\"${escapeAttr(input.styleNonce)}\"` : '';\n\tconst head: string[] = ['<meta charset=\"utf-8\">', `<title>${escapeHTML(input.title)}</title>`];\n\tif (isExport) {\n\t\t// Standalone marker: only reachable inside document.head when this\n\t\t// export IS the rendered document (see PRINT_EXPORT_META_NAME).\n\t\thead.push(`<meta name=\"${PRINT_EXPORT_META_NAME}\">`);\n\t}\n\t// The layer-order statement must be the document's first style rule: layer\n\t// order is fixed by first appearance, and for `!important` declarations\n\t// earlier layers win — customCSS > print guards > host copy. It stays\n\t// inline even in the export variant; on an embedding page it merely pins\n\t// the relative order of these notectl-prefixed layers, which only ever\n\t// hold marker-qualified rules there.\n\thead.push(\n\t\t`<style${nonceAttribute}>@layer ${CUSTOM_STYLE_LAYER}, ${PRINT_STYLE_LAYER}, ${HOST_STYLE_LAYER};</style>`,\n\t);\n\thead.push(`<style${nonceAttribute}>${escapeStyleText(input.documentCSS)}</style>`);\n\t// One `<style>` per segment: `@import` must precede every other rule of its\n\t// stylesheet, so interleaving imports and rule chunks as separate elements\n\t// is the only way to keep the live source order — which decides\n\t// equal-specificity cascade ties inside the `notectl-host` layer. The\n\t// export variant ships all segments (and the page-level standaloneCSS)\n\t// inside the inert style bundle instead: copied host selectors and hoisted\n\t// imports must never act on a page that merely embeds the export.\n\tconst segmentStyles: string[] = input.hostSegments.map((segment: HostStyleSegment): string => {\n\t\tconst css: string =\n\t\t\tsegment.kind === 'import'\n\t\t\t\t? segment.statement\n\t\t\t\t: `@layer ${HOST_STYLE_LAYER} {\\n${segment.css}\\n}`;\n\t\treturn `<style${nonceAttribute}>${escapeStyleText(css)}</style>`;\n\t});\n\tif (!isExport) head.push(...segmentStyles);\n\n\tconst styleBundle: string = isExport\n\t\t? `<template ${PRINT_STYLE_BUNDLE_ATTRIBUTE}><style>${escapeStyleText(input.standaloneCSS)}</style>${segmentStyles.join('')}</template>`\n\t\t: '';\n\n\t// The static fallback renders only when no shadow root is attached (a\n\t// shadow host's unslotted light DOM is never rendered): consumers that\n\t// inject via innerHTML — no DSD parsing, no script execution — get\n\t// readable, statically styled output instead of a blank page. Its style\n\t// copy is wrapped in @scope: a <style> element applies document-wide even\n\t// while its subtree is not rendered, and without the scope the shadow CSS\n\t// (including customCSS body rules) would leak onto a consumer page that\n\t// embeds this document via setHTMLUnsafe. Engines without @scope drop the\n\t// block and render the fallback unstyled but readable.\n\tconst fallback: string = isExport\n\t\t? `<div ${PRINT_FALLBACK_ATTRIBUTE}><style>@scope ([${PRINT_FALLBACK_ATTRIBUTE}]) {\\n${escapeStyleText(input.shadowCSS)}\\n}</style>${input.contentHTML}</div>`\n\t\t: '';\n\tconst fallbackScript: string = isExport ? `<script>${DSD_FALLBACK_SCRIPT}</script>` : '';\n\n\tconst body: string[] = [\n\t\t`<${hostTag}${hostAttributes}${hostTokens} ${STATIC_HOST_ATTRIBUTE}>`,\n\t\t'<template shadowrootmode=\"open\">',\n\t\t`<style${nonceAttribute}>${escapeStyleText(input.shadowCSS)}</style>`,\n\t\tinput.contentHTML,\n\t\t'</template>',\n\t\t...(fallback ? [fallback] : []),\n\t\t`</${hostTag}>`,\n\t\t...(styleBundle ? [styleBundle] : []),\n\t\t...(fallbackScript ? [fallbackScript] : []),\n\t];\n\n\treturn [\n\t\t'<!DOCTYPE html>',\n\t\t`<html lang=\"${escapeAttr(input.lang)}\"${htmlContext}>`,\n\t\t'<head>',\n\t\t...head,\n\t\t'</head>',\n\t\t`<body${bodyContext}>`,\n\t\t...body,\n\t\t'</body>',\n\t\t'</html>',\n\t].join('\\n');\n}\n\n/** PrintService plus internal lifecycle hooks (not part of the public service API). */\nexport interface ManagedPrintService extends PrintService {\n\t/** Cancels pending print triggers and removes parked print iframes. */\n\tdispose(): void;\n}\n\n/** Creates and returns the PrintService implementation. */\nexport function createPrintService(\n\tshadowRoot: ShadowRoot,\n\thost: HTMLElement,\n\tcontainer: HTMLElement,\n\teventBus: PluginEventBus,\n): ManagedPrintService {\n\t/** Cancel callbacks for prints still waiting on their iframe's load event. */\n\tconst pendingPrints = new Set<() => void>();\n\n\tfunction mergeOptions(options?: PrintOptions): PrintOptions {\n\t\treturn options ?? {};\n\t}\n\n\t/** The export document plus the inputs to rebuild the internal variant. */\n\tinterface PrintDocumentBuild {\n\t\treadonly html: string;\n\t\treadonly input: PrintDocumentInput;\n\t}\n\n\tfunction executePrint(options: PrintOptions): PrintDocumentBuild | null {\n\t\t// 1. Emit BEFORE_PRINT — listeners can mutate options or cancel\n\t\tconst beforeEvent: BeforePrintEvent = {\n\t\t\toptions,\n\t\t\tcancelled: false,\n\t\t};\n\t\teventBus.emit(BEFORE_PRINT, beforeEvent);\n\n\t\tif (beforeEvent.cancelled) return null;\n\n\t\tconst finalOptions: PrintOptions = beforeEvent.options;\n\n\t\t// 2. Collect styles for both scopes\n\t\tconst shadowCSS: string = buildShadowCSS(shadowRoot, finalOptions);\n\t\tconst hostSegments: readonly HostStyleSegment[] = collectHostStyleSegments(host);\n\n\t\t// 3. Prepare content\n\t\tconst clone: HTMLElement = prepare(container, finalOptions);\n\t\tconst contentHTML: string = clone.outerHTML;\n\n\t\t// 4. Build the export document (no nonce — serialized output must not\n\t\t// persist the page's CSP secret; inline document CSS marker-qualified,\n\t\t// page-level CSS in the inert standalone bundle — embedding the output\n\t\t// must not restyle the consumer's page)\n\t\tconst title: string = finalOptions.title ?? '';\n\t\tconst ownerDocument: Document = host.ownerDocument;\n\t\tconst lang: string =\n\t\t\thost.closest('[lang]')?.getAttribute('lang') ?? (ownerDocument.documentElement.lang || 'en');\n\t\tconst input: PrintDocumentInput = {\n\t\t\tdocumentCSS: buildExportDocumentCSS(host, container, finalOptions, STATIC_HOST_ATTRIBUTE),\n\t\t\tstandaloneCSS: buildDocumentCSS(host, container, finalOptions),\n\t\t\thostSegments,\n\t\t\tshadowCSS,\n\t\t\thost,\n\t\t\tcontentHTML,\n\t\t\ttitle,\n\t\t\tlang,\n\t\t\tcarryThemeContext: finalOptions.forceLightTheme === false,\n\t\t\tvariant: 'export',\n\t\t};\n\t\tconst html: string = buildHTMLDocument(input);\n\n\t\t// 5. Emit AFTER_PRINT\n\t\tconst afterEvent: AfterPrintEvent = { html };\n\t\teventBus.emit(AFTER_PRINT, afterEvent);\n\n\t\treturn { html, input };\n\t}\n\n\treturn {\n\t\tprint(options?: PrintOptions): void {\n\t\t\tconst resolved: PrintOptions = mergeOptions(options);\n\t\t\tconst build: PrintDocumentBuild | null = executePrint(resolved);\n\t\t\tif (!build) return;\n\n\t\t\t// The iframe is parsed by the same browser, so the fallback is dead\n\t\t\t// weight there; the nonce keeps the styles alive under a CSP\n\t\t\t// inherited by the about:blank iframe. The internal variant inlines\n\t\t\t// the full-fidelity page CSS (html/body reset, body typography,\n\t\t\t// @page) that the embed-safe export ships only in its inert bundle.\n\t\t\tconst printHTML: string = buildHTMLDocument({\n\t\t\t\t...build.input,\n\t\t\t\tdocumentCSS: build.input.standaloneCSS,\n\t\t\t\tvariant: 'internal',\n\t\t\t\tstyleNonce: getStyleNonceForNode(container),\n\t\t\t});\n\n\t\t\t// Create hidden iframe\n\t\t\tconst iframe: HTMLIFrameElement = document.createElement('iframe');\n\t\t\tsetStyleText(iframe, 'position:fixed;left:-9999px;width:0;height:0;border:none');\n\t\t\tdocument.body.appendChild(iframe);\n\n\t\t\tconst iframeDoc: Document | null = iframe.contentDocument;\n\t\t\tconst iframeWindow: Window | null = iframe.contentWindow;\n\t\t\tif (!iframeDoc || !iframeWindow) {\n\t\t\t\tdocument.body.removeChild(iframe);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tiframeDoc.open();\n\t\t\tiframeDoc.write(printHTML);\n\t\t\tiframeDoc.close();\n\n\t\t\tconst cleanup = (): void => {\n\t\t\t\tiframe.remove();\n\t\t\t};\n\n\t\t\tconst triggerPrint = (): void => {\n\t\t\t\tiframeWindow.addEventListener('afterprint', cleanup, { once: true });\n\t\t\t\tiframeWindow.print();\n\t\t\t};\n\n\t\t\t// Referenced host stylesheets (cross-origin @import) load\n\t\t\t// asynchronously; wait for them so the first paint printed is styled.\n\t\t\t// A hanging stylesheet must not block the dialog forever, so the wait\n\t\t\t// is bounded — on timeout, printing proceeds with the styles loaded\n\t\t\t// so far. The pending trigger is cancellable: after the editor is\n\t\t\t// destroyed the dialog must not pop up over an unrelated view.\n\t\t\tif (iframeDoc.readyState === 'complete') {\n\t\t\t\ttriggerPrint();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet triggered = false;\n\t\t\tconst triggerOnce = (): void => {\n\t\t\t\tif (triggered) return;\n\t\t\t\ttriggered = true;\n\t\t\t\tpendingPrints.delete(cancelPending);\n\t\t\t\twindow.clearTimeout(timeoutId);\n\t\t\t\ttriggerPrint();\n\t\t\t};\n\t\t\tconst cancelPending = (): void => {\n\t\t\t\ttriggered = true;\n\t\t\t\twindow.clearTimeout(timeoutId);\n\t\t\t\tiframeWindow.removeEventListener('load', triggerOnce);\n\t\t\t\tiframe.remove();\n\t\t\t};\n\t\t\tconst timeoutId: number = window.setTimeout(triggerOnce, PRINT_LOAD_TIMEOUT_MS);\n\t\t\tpendingPrints.add(cancelPending);\n\t\t\tiframeWindow.addEventListener('load', triggerOnce, { once: true });\n\t\t},\n\n\t\ttoHTML(options?: PrintOptions): string {\n\t\t\tconst resolved: PrintOptions = mergeOptions(options);\n\t\t\treturn executePrint(resolved)?.html ?? '';\n\t\t},\n\n\t\tdispose(): void {\n\t\t\tfor (const cancel of pendingPrints) {\n\t\t\t\tcancel();\n\t\t\t}\n\t\t\tpendingPrints.clear();\n\t\t},\n\t};\n}\n","/**\n * PrintPlugin: provides print functionality for the notectl editor.\n * Registers a command, keymap, toolbar item, and exposes a PrintService.\n * Orchestrator only — delegates all logic to PrintServiceImpl, PrintStyleCollector,\n * and PrintContentPreparer.\n */\n\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { formatShortcut } from '../shared/ShortcutFormatting.js';\nimport { PRINT_LOCALE_EN, type PrintLocale, loadPrintLocale } from './PrintLocale.js';\nimport { type ManagedPrintService, createPrintService } from './PrintServiceImpl.js';\nimport type { PrintOptions, PrintPluginConfig, PrintService } from './PrintTypes.js';\nimport { PRINT_SERVICE_KEY } from './PrintTypes.js';\n\nconst PRINT_ICON: string =\n\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">' +\n\t'<path d=\"M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3' +\n\t' 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z\"/>' +\n\t'</svg>';\n\n/** No-op fallback service for environments without ShadowRoot. */\nconst NOOP_SERVICE: ManagedPrintService = {\n\tprint(): void {},\n\ttoHTML(): string {\n\t\treturn '';\n\t},\n\tdispose(): void {},\n};\n\nexport class PrintPlugin implements Plugin {\n\treadonly id = 'print';\n\treadonly name = 'Print';\n\n\tprivate readonly config: PrintPluginConfig;\n\tprivate service: ManagedPrintService | null = null;\n\tprivate locale!: PrintLocale;\n\n\tconstructor(config?: PrintPluginConfig) {\n\t\tthis.config = config ?? {};\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.locale = await resolveLocale(\n\t\t\tcontext,\n\t\t\tthis.config.locale,\n\t\t\tPRINT_LOCALE_EN,\n\t\t\tloadPrintLocale,\n\t\t);\n\t\tconst container: HTMLElement = context.getContainer();\n\t\tconst rootNode: Node = container.getRootNode();\n\n\t\tif (rootNode instanceof ShadowRoot) {\n\t\t\tconst host: HTMLElement = rootNode.host as HTMLElement;\n\t\t\tconst eventBus = context.getEventBus();\n\t\t\tthis.service = createPrintService(rootNode, host, container, eventBus);\n\t\t} else {\n\t\t\tthis.service = NOOP_SERVICE;\n\t\t}\n\n\t\tcontext.registerService(PRINT_SERVICE_KEY, this.service);\n\n\t\tconst defaults: PrintOptions = this.config.defaults ?? {};\n\t\tconst service: PrintService = this.service;\n\n\t\tconst printLocale = this.locale;\n\t\tcontext.registerCommand('print', () => {\n\t\t\tcontext.announce(printLocale.printingAnnouncement);\n\t\t\tservice.print(defaults);\n\t\t\treturn true;\n\t\t});\n\n\t\tconst keyBinding: string = this.config.keyBinding ?? 'Mod-P';\n\t\tcontext.registerKeymap({\n\t\t\t[keyBinding]: () => {\n\t\t\t\tcontext.executeCommand('print');\n\t\t\t\treturn true;\n\t\t\t},\n\t\t});\n\n\t\tif (this.config.showToolbarItem !== false) {\n\t\t\tconst shortcut: string = formatShortcut(keyBinding);\n\t\t\tcontext.registerToolbarItem({\n\t\t\t\tid: 'print',\n\t\t\t\tgroup: 'actions',\n\t\t\t\tlabel: this.locale.label,\n\t\t\t\ttooltip: this.locale.tooltip(shortcut),\n\t\t\t\ticon: PRINT_ICON,\n\t\t\t\tcommand: 'print',\n\t\t\t});\n\t\t}\n\t}\n\n\tdestroy(): void {\n\t\t// A print still waiting on its iframe's load event must not pop the\n\t\t// dialog after the editor is gone (e.g. after an SPA navigation).\n\t\tthis.service?.dispose();\n\t\tthis.service = null;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import{n as e,t as o}from"./TypedKeys-CA4bfw9O.mjs";import{i as t,n as r,r as n,t as
|
|
2
|
-
|
|
1
|
+
import{n as e,t as o}from"./TypedKeys-CA4bfw9O.mjs";import{i as t,n as r,r as n,t as a}from"./SyntaxTokenTypes-BUCOMtL8.mjs";var c={Light:"light",Dark:"dark",System:"system"},i={name:"light",primitives:{background:"#ffffff",foreground:"#1a1a1a",mutedForeground:"#666666",border:"#d0d0d0",borderFocus:"#4a90d9",primary:"#4a90d9",primaryForeground:"#1a5fa0",primaryMuted:"#d0e0f0",surfaceRaised:"#f8f8f8",surfaceOverlay:"#ffffff",hoverBackground:"#e8e8e8",activeBackground:"#d0e0f0",danger:"#dc2626",dangerMuted:"#fee2e2",success:"#1a8c1a",shadow:"rgba(0, 0, 0, 0.15)",focusRing:"rgba(74, 144, 217, 0.2)"},inlineCode:{background:"#edf0f5",foreground:"#334155"},codeBlock:{background:"#f8fafc",foreground:"#334155",headerBackground:"#f1f5f9",headerForeground:"#64748b",headerBorder:"#e2e8f0",syntax:{keyword:"#d73a49",string:"#032f62",comment:{color:"#6a737d",fontStyle:"italic"},number:"#005cc5",function:"#6f42c1",operator:"#d73a49",punctuation:"#24292e",boolean:"#005cc5",null:"#005cc5",property:"#005cc5",type:"#e36209",annotation:"#6f42c1",tag:"#22863a",attribute:"#6f42c1",constant:"#005cc5",regex:"#032f62"}},tooltip:{background:"#1a1a1a",foreground:"#ffffff"}},d={name:"dark",primitives:{background:"#1e1e2e",foreground:"#cdd6f4",mutedForeground:"#7f849c",border:"#45475a",borderFocus:"#89b4fa",primary:"#89b4fa",primaryForeground:"#89b4fa",primaryMuted:"rgba(137, 180, 250, 0.15)",surfaceRaised:"#313244",surfaceOverlay:"#313244",hoverBackground:"#45475a",activeBackground:"rgba(137, 180, 250, 0.15)",danger:"#f38ba8",dangerMuted:"rgba(243, 139, 168, 0.15)",success:"#a6e3a1",shadow:"rgba(0, 0, 0, 0.4)",focusRing:"rgba(137, 180, 250, 0.25)"},inlineCode:{background:"#313244",foreground:"#cdd6f4"},codeBlock:{background:"#313244",foreground:"#cdd6f4",headerBackground:"#45475a",headerForeground:"#bac2de",headerBorder:"#585b70",syntax:{keyword:"#cba6f7",string:"#a6e3a1",comment:{color:"#9399b2",fontStyle:"italic"},number:"#fab387",function:"#89b4fa",operator:"#89dceb",punctuation:"#bac2de",boolean:"#fab387",null:"#f38ba8",property:"#89b4fa",type:"#fab387",annotation:"#cba6f7",tag:"#a6e3a1",attribute:"#89b4fa",constant:"#f9e2af",regex:"#a6e3a1"}},tooltip:{background:"#585b70",foreground:"#cdd6f4"}};function l(e,o){return{name:o.name,primitives:{...e.primitives,...o.primitives},toolbar:o.toolbar?{...e.toolbar,...o.toolbar}:e.toolbar,inlineCode:o.inlineCode?{...e.inlineCode,...o.inlineCode}:e.inlineCode,codeBlock:o.codeBlock?{...e.codeBlock,...o.codeBlock,syntax:o.codeBlock?.syntax?e.codeBlock?.syntax?{...e.codeBlock.syntax,...o.codeBlock.syntax}:void 0:e.codeBlock?.syntax}:e.codeBlock,tooltip:o.tooltip?{...e.tooltip,...o.tooltip}:e.tooltip}}function s(e){if("object"==typeof e)return e;switch(e){case c.Light:return i;case c.Dark:return d;default:return i}}var f=[["primitives.background","--notectl-bg"],["primitives.foreground","--notectl-fg"],["primitives.mutedForeground","--notectl-fg-muted"],["primitives.border","--notectl-border"],["primitives.borderFocus","--notectl-border-focus"],["primitives.primary","--notectl-primary"],["primitives.primaryForeground","--notectl-primary-fg"],["primitives.primaryMuted","--notectl-primary-muted"],["primitives.surfaceRaised","--notectl-surface-raised"],["primitives.surfaceOverlay","--notectl-surface-overlay"],["primitives.hoverBackground","--notectl-hover-bg"],["primitives.activeBackground","--notectl-active-bg"],["primitives.danger","--notectl-danger"],["primitives.dangerMuted","--notectl-danger-muted"],["primitives.success","--notectl-success"],["primitives.shadow","--notectl-shadow"],["primitives.focusRing","--notectl-focus-ring"],["toolbar.background","--notectl-toolbar-bg"],["toolbar.borderColor","--notectl-toolbar-border"],["inlineCode.background","--notectl-code-bg"],["inlineCode.foreground","--notectl-code-color"],["codeBlock.background","--notectl-code-block-bg"],["codeBlock.foreground","--notectl-code-block-color"],["codeBlock.headerBackground","--notectl-code-block-header-bg"],["codeBlock.headerForeground","--notectl-code-block-header-color"],["codeBlock.headerBorder","--notectl-code-block-header-border"],["tooltip.background","--notectl-tooltip-bg"],["tooltip.foreground","--notectl-tooltip-fg"],...a.map(e=>[`codeBlock.syntax.${e}`,`--notectl-code-token-${e}`])],u=a.map(e=>[`--notectl-code-token-${e}`,"var(--notectl-code-block-color)"]),g=new Map([["--notectl-toolbar-bg","var(--notectl-surface-raised)"],["--notectl-toolbar-border","var(--notectl-border)"],["--notectl-code-bg","var(--notectl-surface-raised)"],["--notectl-code-color","var(--notectl-fg)"],["--notectl-code-block-bg","var(--notectl-surface-raised)"],["--notectl-code-block-color","var(--notectl-fg)"],["--notectl-code-block-header-bg","var(--notectl-surface-raised)"],["--notectl-code-block-header-color","var(--notectl-fg-muted)"],["--notectl-code-block-header-border","var(--notectl-border)"],["--notectl-tooltip-bg","var(--notectl-fg)"],["--notectl-tooltip-fg","var(--notectl-bg)"],...u]);function b(e,o){const t=o.split(".");let n=e;for(const r of t){if(null==n)return;if("object"!=typeof n)return;n=n[r]}return"string"==typeof n?n:"object"==typeof n&&null!==n&&"color"in n?r(n):void 0}function p(e){const o=[":host {"];for(const[t,r]of f){const n=b(e,t);if(void 0!==n)o.push(`\t${r}: ${n};`);else{const e=g.get(r);e&&o.push(`\t${r}: ${e};`)}}return e.codeBlock?.syntax&&function(e,o){for(const r of a){const a=e[r];if("object"!=typeof a)continue;const c=n(a);c&&o.push(`\t--notectl-code-token-${r}-font-style: ${c};`);const i=t(a);i&&o.push(`\t--notectl-code-token-${r}-font-weight: ${i};`)}}(e.codeBlock.syntax,o),o.push("}"),o.join("\n")}function m(e){const o=new CSSStyleSheet;return o.replaceSync(p(e)),o}var h=96/25.4,k={DINA4:"din-a4",DINA5:"din-a5",USLetter:"us-letter",USLegal:"us-legal"};function v(e){return Math.round(e*h)}var y=new Map([[k.DINA4,{widthMm:210,heightMm:297,widthPx:v(210),heightPx:v(297)}],[k.DINA5,{widthMm:148,heightMm:210,widthPx:v(148),heightPx:v(210)}],[k.USLetter,{widthMm:215.9,heightMm:279.4,widthPx:v(215.9),heightPx:v(279.4)}],[k.USLegal,{widthMm:215.9,heightMm:355.6,widthPx:v(215.9),heightPx:v(355.6)}]]),B=new Map([[k.DINA4,"A4"],[k.DINA5,"A5"],[k.USLetter,"letter"],[k.USLegal,"legal"]]);function w(e){const o=y.get(e);if(!o)throw new Error(`Unknown paper size: ${e}`);return o}var x=48,M=56,S=24;function $(e){return Object.values(k).includes(e)}function C(e){const o=B.get(e);if(!o)throw new Error(`Unknown paper size: ${e}`);return o}var F="data-notectl-static";function A(e){return e.hasAttribute(F)}function j(){/* @__PURE__ */
|
|
2
|
+
return new Error("Cannot initialize a static print replica (data-notectl-static). Remove the marker and its shadow content to reuse the element as an editor.")}var D=new e("print"),L=new o("print:before"),P=new o("print:after");export{i as _,j as a,s as b,x as c,C as d,w as f,d as g,p as h,F as i,S as l,m,L as n,A as o,$ as p,D as r,M as s,P as t,k as u,c as v,l as y};
|
|
3
|
+
//# sourceMappingURL=PrintTypes-CxU69qu3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PrintTypes-CxU69qu3.mjs","names":[],"sources":["../../src/editor/theme/ThemeTokens.ts","../../src/editor/theme/ThemeEngine.ts","../../src/model/PaperSize.ts","../../src/plugins/print/StaticHostMarker.ts","../../src/plugins/print/PrintTypes.ts"],"sourcesContent":["/**\n * Theme token types, built-in themes, and theme creation helpers.\n */\n\nimport type { SyntaxTokenType, TokenStyleValue } from './SyntaxTokenTypes.js';\nimport { resolveTokenColor } from './SyntaxTokenTypes.js';\n\n/** Primitive color palette a theme defines. */\nexport interface ThemePrimitives {\n\treadonly background: string;\n\treadonly foreground: string;\n\treadonly mutedForeground: string;\n\treadonly border: string;\n\treadonly borderFocus: string;\n\treadonly primary: string;\n\treadonly primaryForeground: string;\n\treadonly primaryMuted: string;\n\treadonly surfaceRaised: string;\n\treadonly surfaceOverlay: string;\n\treadonly hoverBackground: string;\n\treadonly activeBackground: string;\n\treadonly danger: string;\n\treadonly dangerMuted: string;\n\treadonly success: string;\n\treadonly shadow: string;\n\treadonly focusRing: string;\n}\n\n/** Component-level toolbar overrides. */\nexport interface ThemeToolbar {\n\treadonly background: string;\n\treadonly borderColor: string;\n}\n\n/** Syntax highlighting token styles — one entry per canonical token type. */\nexport type ThemeSyntax = { readonly [K in SyntaxTokenType]: TokenStyleValue };\n\n/** Component-level code block overrides. */\nexport interface ThemeCodeBlock {\n\treadonly background: string;\n\treadonly foreground: string;\n\treadonly headerBackground: string;\n\treadonly headerForeground: string;\n\treadonly headerBorder: string;\n\treadonly syntax?: ThemeSyntax;\n}\n\n/** Component-level inline code overrides. */\nexport interface ThemeInlineCode {\n\treadonly background: string;\n\treadonly foreground: string;\n}\n\n/** Component-level tooltip overrides. */\nexport interface ThemeTooltip {\n\treadonly background: string;\n\treadonly foreground: string;\n}\n\n/** Full theme definition. */\nexport interface Theme {\n\treadonly name: string;\n\treadonly primitives: ThemePrimitives;\n\treadonly toolbar?: Partial<ThemeToolbar>;\n\treadonly inlineCode?: Partial<ThemeInlineCode>;\n\treadonly codeBlock?: Partial<ThemeCodeBlock>;\n\treadonly tooltip?: Partial<ThemeTooltip>;\n}\n\n/** Partial custom theme — deeply partial for creating overrides. */\nexport interface PartialTheme {\n\treadonly name: string;\n\treadonly primitives?: Partial<ThemePrimitives>;\n\treadonly toolbar?: Partial<ThemeToolbar>;\n\treadonly inlineCode?: Partial<ThemeInlineCode>;\n\treadonly codeBlock?: Partial<Omit<ThemeCodeBlock, 'syntax'>> & {\n\t\treadonly syntax?: Partial<ThemeSyntax>;\n\t};\n\treadonly tooltip?: Partial<ThemeTooltip>;\n}\n\n/** Built-in theme presets. */\nexport const ThemePreset = {\n\tLight: 'light',\n\tDark: 'dark',\n\tSystem: 'system',\n} as const;\nexport type ThemePreset = (typeof ThemePreset)[keyof typeof ThemePreset];\n\n/** Built-in light theme. */\nexport const LIGHT_THEME: Theme = {\n\tname: 'light',\n\tprimitives: {\n\t\tbackground: '#ffffff',\n\t\tforeground: '#1a1a1a',\n\t\tmutedForeground: '#666666',\n\t\tborder: '#d0d0d0',\n\t\tborderFocus: '#4a90d9',\n\t\tprimary: '#4a90d9',\n\t\tprimaryForeground: '#1a5fa0',\n\t\tprimaryMuted: '#d0e0f0',\n\t\tsurfaceRaised: '#f8f8f8',\n\t\tsurfaceOverlay: '#ffffff',\n\t\thoverBackground: '#e8e8e8',\n\t\tactiveBackground: '#d0e0f0',\n\t\tdanger: '#dc2626',\n\t\tdangerMuted: '#fee2e2',\n\t\tsuccess: '#1a8c1a',\n\t\tshadow: 'rgba(0, 0, 0, 0.15)',\n\t\tfocusRing: 'rgba(74, 144, 217, 0.2)',\n\t},\n\tinlineCode: {\n\t\tbackground: '#edf0f5',\n\t\tforeground: '#334155',\n\t},\n\tcodeBlock: {\n\t\tbackground: '#f8fafc',\n\t\tforeground: '#334155',\n\t\theaderBackground: '#f1f5f9',\n\t\theaderForeground: '#64748b',\n\t\theaderBorder: '#e2e8f0',\n\t\tsyntax: {\n\t\t\tkeyword: '#d73a49',\n\t\t\tstring: '#032f62',\n\t\t\tcomment: { color: '#6a737d', fontStyle: 'italic' },\n\t\t\tnumber: '#005cc5',\n\t\t\tfunction: '#6f42c1',\n\t\t\toperator: '#d73a49',\n\t\t\tpunctuation: '#24292e',\n\t\t\tboolean: '#005cc5',\n\t\t\tnull: '#005cc5',\n\t\t\tproperty: '#005cc5',\n\t\t\ttype: '#e36209',\n\t\t\tannotation: '#6f42c1',\n\t\t\ttag: '#22863a',\n\t\t\tattribute: '#6f42c1',\n\t\t\tconstant: '#005cc5',\n\t\t\tregex: '#032f62',\n\t\t},\n\t},\n\ttooltip: {\n\t\tbackground: '#1a1a1a',\n\t\tforeground: '#ffffff',\n\t},\n};\n\n/** Built-in dark theme. */\nexport const DARK_THEME: Theme = {\n\tname: 'dark',\n\tprimitives: {\n\t\tbackground: '#1e1e2e',\n\t\tforeground: '#cdd6f4',\n\t\tmutedForeground: '#7f849c',\n\t\tborder: '#45475a',\n\t\tborderFocus: '#89b4fa',\n\t\tprimary: '#89b4fa',\n\t\tprimaryForeground: '#89b4fa',\n\t\tprimaryMuted: 'rgba(137, 180, 250, 0.15)',\n\t\tsurfaceRaised: '#313244',\n\t\tsurfaceOverlay: '#313244',\n\t\thoverBackground: '#45475a',\n\t\tactiveBackground: 'rgba(137, 180, 250, 0.15)',\n\t\tdanger: '#f38ba8',\n\t\tdangerMuted: 'rgba(243, 139, 168, 0.15)',\n\t\tsuccess: '#a6e3a1',\n\t\tshadow: 'rgba(0, 0, 0, 0.4)',\n\t\tfocusRing: 'rgba(137, 180, 250, 0.25)',\n\t},\n\tinlineCode: {\n\t\tbackground: '#313244',\n\t\tforeground: '#cdd6f4',\n\t},\n\tcodeBlock: {\n\t\tbackground: '#313244',\n\t\tforeground: '#cdd6f4',\n\t\theaderBackground: '#45475a',\n\t\theaderForeground: '#bac2de',\n\t\theaderBorder: '#585b70',\n\t\tsyntax: {\n\t\t\tkeyword: '#cba6f7',\n\t\t\tstring: '#a6e3a1',\n\t\t\tcomment: { color: '#9399b2', fontStyle: 'italic' },\n\t\t\tnumber: '#fab387',\n\t\t\tfunction: '#89b4fa',\n\t\t\toperator: '#89dceb',\n\t\t\tpunctuation: '#bac2de',\n\t\t\tboolean: '#fab387',\n\t\t\tnull: '#f38ba8',\n\t\t\tproperty: '#89b4fa',\n\t\t\ttype: '#fab387',\n\t\t\tannotation: '#cba6f7',\n\t\t\ttag: '#a6e3a1',\n\t\t\tattribute: '#89b4fa',\n\t\t\tconstant: '#f9e2af',\n\t\t\tregex: '#a6e3a1',\n\t\t},\n\t},\n\ttooltip: {\n\t\tbackground: '#585b70',\n\t\tforeground: '#cdd6f4',\n\t},\n};\n\n/** Creates a custom theme by extending a base theme with partial overrides. */\nexport function createTheme(base: Theme, overrides: PartialTheme): Theme {\n\tconst mergeSyntax = (): ThemeSyntax | undefined => {\n\t\tif (!overrides.codeBlock?.syntax) return base.codeBlock?.syntax;\n\t\tif (!base.codeBlock?.syntax) return undefined;\n\t\treturn { ...base.codeBlock.syntax, ...overrides.codeBlock.syntax } as ThemeSyntax;\n\t};\n\n\treturn {\n\t\tname: overrides.name,\n\t\tprimitives: { ...base.primitives, ...overrides.primitives },\n\t\ttoolbar: overrides.toolbar ? { ...base.toolbar, ...overrides.toolbar } : base.toolbar,\n\t\tinlineCode: overrides.inlineCode\n\t\t\t? { ...base.inlineCode, ...overrides.inlineCode }\n\t\t\t: base.inlineCode,\n\t\tcodeBlock: overrides.codeBlock\n\t\t\t? {\n\t\t\t\t\t...base.codeBlock,\n\t\t\t\t\t...overrides.codeBlock,\n\t\t\t\t\tsyntax: mergeSyntax(),\n\t\t\t\t}\n\t\t\t: base.codeBlock,\n\t\ttooltip: overrides.tooltip ? { ...base.tooltip, ...overrides.tooltip } : base.tooltip,\n\t};\n}\n\n// Re-export for convenience — avoids extra imports in consumer code.\nexport { resolveTokenColor };\n\n/** Resolves a ThemePreset string or Theme object to a full Theme. */\nexport function resolveTheme(theme: ThemePreset | Theme): Theme {\n\tif (typeof theme === 'object') return theme;\n\tswitch (theme) {\n\t\tcase ThemePreset.Light:\n\t\t\treturn LIGHT_THEME;\n\t\tcase ThemePreset.Dark:\n\t\t\treturn DARK_THEME;\n\t\tcase ThemePreset.System:\n\t\t\t// System is resolved externally via matchMedia; default to light.\n\t\t\treturn LIGHT_THEME;\n\t\tdefault:\n\t\t\treturn LIGHT_THEME;\n\t}\n}\n","/**\n * Theme engine — converts a Theme object into CSS custom properties.\n */\n\nimport {\n\tSYNTAX_TOKEN_TYPES,\n\tresolveTokenColor,\n\tresolveTokenFontStyle,\n\tresolveTokenFontWeight,\n} from './SyntaxTokenTypes.js';\nimport type { TokenStyleValue } from './SyntaxTokenTypes.js';\nimport type { Theme, ThemeSyntax } from './ThemeTokens.js';\n\n// --- Static entries (non-syntax) ---\n\nconst STATIC_ENTRIES: ReadonlyArray<readonly [string, string]> = [\n\t// Primitives\n\t['primitives.background', '--notectl-bg'],\n\t['primitives.foreground', '--notectl-fg'],\n\t['primitives.mutedForeground', '--notectl-fg-muted'],\n\t['primitives.border', '--notectl-border'],\n\t['primitives.borderFocus', '--notectl-border-focus'],\n\t['primitives.primary', '--notectl-primary'],\n\t['primitives.primaryForeground', '--notectl-primary-fg'],\n\t['primitives.primaryMuted', '--notectl-primary-muted'],\n\t['primitives.surfaceRaised', '--notectl-surface-raised'],\n\t['primitives.surfaceOverlay', '--notectl-surface-overlay'],\n\t['primitives.hoverBackground', '--notectl-hover-bg'],\n\t['primitives.activeBackground', '--notectl-active-bg'],\n\t['primitives.danger', '--notectl-danger'],\n\t['primitives.dangerMuted', '--notectl-danger-muted'],\n\t['primitives.success', '--notectl-success'],\n\t['primitives.shadow', '--notectl-shadow'],\n\t['primitives.focusRing', '--notectl-focus-ring'],\n\t// Component: toolbar\n\t['toolbar.background', '--notectl-toolbar-bg'],\n\t['toolbar.borderColor', '--notectl-toolbar-border'],\n\t// Component: inlineCode\n\t['inlineCode.background', '--notectl-code-bg'],\n\t['inlineCode.foreground', '--notectl-code-color'],\n\t// Component: codeBlock (non-syntax)\n\t['codeBlock.background', '--notectl-code-block-bg'],\n\t['codeBlock.foreground', '--notectl-code-block-color'],\n\t['codeBlock.headerBackground', '--notectl-code-block-header-bg'],\n\t['codeBlock.headerForeground', '--notectl-code-block-header-color'],\n\t['codeBlock.headerBorder', '--notectl-code-block-header-border'],\n\t// Component: tooltip\n\t['tooltip.background', '--notectl-tooltip-bg'],\n\t['tooltip.foreground', '--notectl-tooltip-fg'],\n];\n\n// --- Generated syntax variable map (derived from SYNTAX_TOKEN_TYPES) ---\n\nconst SYNTAX_VARIABLE_MAP: ReadonlyArray<readonly [string, string]> = SYNTAX_TOKEN_TYPES.map(\n\t(type) => [`codeBlock.syntax.${type}`, `--notectl-code-token-${type}`] as const,\n);\n\n/** Full variable map: static entries + syntax entries. */\nconst VARIABLE_MAP: ReadonlyArray<readonly [string, string]> = [\n\t...STATIC_ENTRIES,\n\t...SYNTAX_VARIABLE_MAP,\n];\n\n// --- Static fallbacks (non-syntax) ---\n\nconst STATIC_FALLBACKS: ReadonlyArray<readonly [string, string]> = [\n\t['--notectl-toolbar-bg', 'var(--notectl-surface-raised)'],\n\t['--notectl-toolbar-border', 'var(--notectl-border)'],\n\t['--notectl-code-bg', 'var(--notectl-surface-raised)'],\n\t['--notectl-code-color', 'var(--notectl-fg)'],\n\t['--notectl-code-block-bg', 'var(--notectl-surface-raised)'],\n\t['--notectl-code-block-color', 'var(--notectl-fg)'],\n\t['--notectl-code-block-header-bg', 'var(--notectl-surface-raised)'],\n\t['--notectl-code-block-header-color', 'var(--notectl-fg-muted)'],\n\t['--notectl-code-block-header-border', 'var(--notectl-border)'],\n\t['--notectl-tooltip-bg', 'var(--notectl-fg)'],\n\t['--notectl-tooltip-fg', 'var(--notectl-bg)'],\n];\n\n// --- Generated syntax fallbacks (derived from SYNTAX_TOKEN_TYPES) ---\n\nconst SYNTAX_FALLBACKS: ReadonlyArray<readonly [string, string]> = SYNTAX_TOKEN_TYPES.map(\n\t(type) => [`--notectl-code-token-${type}`, 'var(--notectl-code-block-color)'] as const,\n);\n\n/** Fallbacks for component tokens when not explicitly set. */\nconst COMPONENT_FALLBACKS: ReadonlyMap<string, string> = new Map([\n\t...STATIC_FALLBACKS,\n\t...SYNTAX_FALLBACKS,\n]);\n\n/** Resolves a dotted path like 'primitives.background' from a Theme. */\nfunction resolvePath(theme: Theme, path: string): string | undefined {\n\tconst parts: string[] = path.split('.');\n\tlet current: unknown = theme;\n\tfor (const part of parts) {\n\t\tif (current === undefined || current === null) return undefined;\n\t\tif (typeof current !== 'object') return undefined;\n\t\tcurrent = (current as Record<string, unknown>)[part];\n\t}\n\tif (typeof current === 'string') return current;\n\tif (typeof current === 'object' && current !== null && 'color' in current) {\n\t\treturn resolveTokenColor(current as TokenStyleValue);\n\t}\n\treturn undefined;\n}\n\n/** Emits font-style/font-weight CSS vars for syntax tokens that use TokenStyle objects. */\nfunction emitTokenStyleVars(syntax: ThemeSyntax, lines: string[]): void {\n\tfor (const type of SYNTAX_TOKEN_TYPES) {\n\t\tconst value: TokenStyleValue = syntax[type];\n\t\tif (typeof value !== 'object') continue;\n\n\t\tconst fontStyle: string | undefined = resolveTokenFontStyle(value);\n\t\tif (fontStyle) {\n\t\t\tlines.push(`\\t--notectl-code-token-${type}-font-style: ${fontStyle};`);\n\t\t}\n\t\tconst fontWeight: string | undefined = resolveTokenFontWeight(value);\n\t\tif (fontWeight) {\n\t\t\tlines.push(`\\t--notectl-code-token-${type}-font-weight: ${fontWeight};`);\n\t\t}\n\t}\n}\n\n/** Generates CSS text with all theme custom properties on :host. */\nexport function generateThemeCSS(theme: Theme): string {\n\tconst lines: string[] = [':host {'];\n\n\tfor (const [path, cssVar] of VARIABLE_MAP) {\n\t\tconst value: string | undefined = resolvePath(theme, path);\n\t\tif (value !== undefined) {\n\t\t\tlines.push(`\\t${cssVar}: ${value};`);\n\t\t} else {\n\t\t\tconst fallback: string | undefined = COMPONENT_FALLBACKS.get(cssVar);\n\t\t\tif (fallback) {\n\t\t\t\tlines.push(`\\t${cssVar}: ${fallback};`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Emit font-style / font-weight vars for TokenStyle entries\n\tif (theme.codeBlock?.syntax) {\n\t\temitTokenStyleVars(theme.codeBlock.syntax, lines);\n\t}\n\n\tlines.push('}');\n\treturn lines.join('\\n');\n}\n\n/** Creates a CSSStyleSheet populated with theme CSS variables. */\nexport function createThemeStyleSheet(theme: Theme): CSSStyleSheet {\n\tconst sheet: CSSStyleSheet = new CSSStyleSheet();\n\tsheet.replaceSync(generateThemeCSS(theme));\n\treturn sheet;\n}\n","/**\n * Paper size definitions and dimension helpers.\n * Used by PaperLayoutController for WYSIWYG page layout\n * and by PrintStyleCollector for @page CSS rules.\n */\n\n/** Conversion factor: 1mm = 96/25.4 px at CSS reference pixel (96 DPI). */\nconst MM_TO_PX: number = 96 / 25.4;\n\n/** Physical dimensions of a paper size. */\nexport interface PaperDimensions {\n\treadonly widthMm: number;\n\treadonly heightMm: number;\n\treadonly widthPx: number;\n\treadonly heightPx: number;\n}\n\n/** Available paper size presets. */\nexport const PaperSize = {\n\tDINA4: 'din-a4',\n\tDINA5: 'din-a5',\n\tUSLetter: 'us-letter',\n\tUSLegal: 'us-legal',\n} as const;\nexport type PaperSize = (typeof PaperSize)[keyof typeof PaperSize];\n\n/** Rounds to nearest integer for pixel dimensions. */\nfunction mmToPx(mm: number): number {\n\treturn Math.round(mm * MM_TO_PX);\n}\n\n/** Lookup table for paper dimensions. */\nconst PAPER_DIMENSIONS: ReadonlyMap<PaperSize, PaperDimensions> = new Map<\n\tPaperSize,\n\tPaperDimensions\n>([\n\t[PaperSize.DINA4, { widthMm: 210, heightMm: 297, widthPx: mmToPx(210), heightPx: mmToPx(297) }],\n\t[PaperSize.DINA5, { widthMm: 148, heightMm: 210, widthPx: mmToPx(148), heightPx: mmToPx(210) }],\n\t[\n\t\tPaperSize.USLetter,\n\t\t{ widthMm: 215.9, heightMm: 279.4, widthPx: mmToPx(215.9), heightPx: mmToPx(279.4) },\n\t],\n\t[\n\t\tPaperSize.USLegal,\n\t\t{ widthMm: 215.9, heightMm: 355.6, widthPx: mmToPx(215.9), heightPx: mmToPx(355.6) },\n\t],\n]);\n\n/** CSS @page size keywords for each paper size. */\nconst CSS_SIZE_KEYWORDS: ReadonlyMap<PaperSize, string> = new Map<PaperSize, string>([\n\t[PaperSize.DINA4, 'A4'],\n\t[PaperSize.DINA5, 'A5'],\n\t[PaperSize.USLetter, 'letter'],\n\t[PaperSize.USLegal, 'legal'],\n]);\n\n/** Returns the physical and pixel dimensions for a paper size. */\nexport function getPaperDimensions(size: PaperSize): PaperDimensions {\n\tconst dims: PaperDimensions | undefined = PAPER_DIMENSIONS.get(size);\n\tif (!dims) {\n\t\tthrow new Error(`Unknown paper size: ${size}`);\n\t}\n\treturn dims;\n}\n\n/**\n * Document margin constants for paper mode (in px).\n * These define the content padding inside the paper surface in the editor,\n * and become @page margins in print output to ensure WYSIWYG fidelity.\n */\nexport const PAPER_MARGIN_TOP_PX: number = 48;\nexport const PAPER_MARGIN_HORIZONTAL_PX: number = 56;\nexport const PAPER_VIEWPORT_PADDING_PX: number = 24;\n\n/** Type guard: returns true if the value is a valid PaperSize string. */\nexport function isValidPaperSize(value: string): value is PaperSize {\n\treturn (Object.values(PaperSize) as string[]).includes(value);\n}\n\n/** Returns the CSS @page size keyword (e.g. 'A4', 'letter') for a paper size. */\nexport function getPaperCSSSize(size: PaperSize): string {\n\tconst keyword: string | undefined = CSS_SIZE_KEYWORDS.get(size);\n\tif (!keyword) {\n\t\tthrow new Error(`Unknown paper size: ${size}`);\n\t}\n\treturn keyword;\n}\n","/**\n * Marker attribute for static host replicas.\n *\n * Print output replicates the editor host element (tag and attributes) so\n * host-page `::part()` rules keep matching in the print document. When that\n * markup is injected into a page where `<notectl-editor>` is registered, the\n * replica must stay static markup: upgrading it into a live editor would\n * replace the replicated shadow content with a fresh editor instance.\n *\n * The print plugin writes the marker; `NotectlEditor` honors it by refusing\n * to boot on marked hosts.\n */\nexport const STATIC_HOST_ATTRIBUTE = 'data-notectl-static';\n\n/** True when the element is a static replica that must not boot an editor. */\nexport function isStaticHostReplica(el: Element): boolean {\n\treturn el.hasAttribute(STATIC_HOST_ATTRIBUTE);\n}\n\n/** Error raised when editor lifecycle APIs are used on a static replica. */\nexport function createStaticReplicaError(): Error {\n\treturn new Error(\n\t\t'Cannot initialize a static print replica (data-notectl-static). ' +\n\t\t\t'Remove the marker and its shadow content to reuse the element as an editor.',\n\t);\n}\n","/**\n * Type definitions, service keys, and event keys for the PrintPlugin.\n */\n\nimport type { PaperSize } from '../../model/PaperSize.js';\nimport type { NodeTypeName } from '../../model/TypeBrands.js';\nimport { EventKey, ServiceKey } from '../Plugin.js';\n\n// --- Print Options ---\n\nexport interface PrintOptions {\n\t/** Page title shown in the browser print header. */\n\treadonly title?: string;\n\n\t/** Additional CSS appended to the print stylesheet. */\n\treadonly customCSS?: string;\n\n\t/**\n\t * Header HTML inserted once at the top of the print document.\n\t * For repeating per-page headers, use toHTML() with server-side PDF generation.\n\t */\n\treadonly header?: string | (() => string);\n\n\t/**\n\t * Footer HTML inserted once at the bottom of the print document.\n\t * For repeating per-page footers, use toHTML() with server-side PDF generation.\n\t */\n\treadonly footer?: string | (() => string);\n\n\t/** Page margins as CSS value (e.g. '2cm'). */\n\treadonly margin?: string;\n\n\t/** Force page break before these block types. */\n\treadonly pageBreakBefore?: readonly NodeTypeName[];\n\n\t/** Exclude these block types from print output. */\n\treadonly excludeBlockTypes?: readonly NodeTypeName[];\n\n\t/**\n\t * Whether to preserve background colors and images in print output.\n\t * Defaults to `true` (WYSIWYG). Set to `false` to let the browser strip\n\t * backgrounds (e.g. to save ink).\n\t */\n\treadonly printBackground?: boolean;\n\n\t/**\n\t * Force light theme tokens in print output for readability on paper.\n\t * Defaults to `true`. Set to `false` to print with the current editor theme.\n\t */\n\treadonly forceLightTheme?: boolean;\n\n\t/** Page orientation. */\n\treadonly orientation?: 'portrait' | 'landscape';\n\n\t/** Paper size for @page CSS rule. When set, overrides the default A4 page size. */\n\treadonly paperSize?: PaperSize;\n}\n\n// --- Plugin Configuration ---\n\nexport interface PrintPluginConfig {\n\t/** Default options applied to every print() call. */\n\treadonly defaults?: PrintOptions;\n\n\t/** Keyboard shortcut (default: 'Mod-P'). */\n\treadonly keyBinding?: string;\n\n\t/** Show toolbar button (default: true). */\n\treadonly showToolbarItem?: boolean;\n\n\t/** Locale override for user-facing strings. */\n\treadonly locale?: import('./PrintLocale.js').PrintLocale;\n}\n\n// --- Service Interface ---\n\nexport interface PrintService {\n\t/** Prints the editor content with optional configuration. */\n\tprint(options?: PrintOptions): void;\n\n\t/** Generates print-ready HTML as a complete document string. */\n\ttoHTML(options?: PrintOptions): string;\n}\n\n// --- Service & Event Keys ---\n\nexport const PRINT_SERVICE_KEY: ServiceKey<PrintService> = new ServiceKey<PrintService>('print');\n\nexport const BEFORE_PRINT: EventKey<BeforePrintEvent> = new EventKey<BeforePrintEvent>(\n\t'print:before',\n);\n\nexport const AFTER_PRINT: EventKey<AfterPrintEvent> = new EventKey<AfterPrintEvent>('print:after');\n\n// --- Event Payloads ---\n\nexport interface BeforePrintEvent {\n\t/** Options can be mutated — EventBus passes by reference. */\n\toptions: PrintOptions;\n\t/** Set to true to cancel the print. */\n\tcancelled: boolean;\n}\n\nexport interface AfterPrintEvent {\n\t/** The generated HTML (read-only). */\n\treadonly html: string;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{_ as o,m as r}from"./Commands-BIIpx_Zd.mjs";import{i as e}from"./TypeBrands-Ch9856Gb.mjs";import{n as m}from"./PluginHelpers-
|
|
2
|
-
//# sourceMappingURL=SimpleMark-
|
|
1
|
+
import{_ as o,m as r}from"./Commands-BIIpx_Zd.mjs";import{i as e}from"./TypeBrands-Ch9856Gb.mjs";import{n as m}from"./PluginHelpers-KDuTlmXm.mjs";function t(t,a){const i=e(a.markSpec.type);t.registerMarkSpec(a.markSpec),t.registerCommand(a.command,()=>m(t,o(t.getState(),i))),a.keyBinding&&t.registerKeymap({[a.keyBinding]:()=>t.executeCommand(a.command)}),a.toolbar&&t.registerToolbarItem({id:a.toolbar.id,group:a.toolbar.group,icon:a.toolbar.icon,label:a.toolbar.label,tooltip:a.toolbar.tooltip,command:a.command,isActive:o=>r(o,i)})}export{t};
|
|
2
|
+
//# sourceMappingURL=SimpleMark-CaQtFAau.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SimpleMark-
|
|
1
|
+
{"version":3,"file":"SimpleMark-CaQtFAau.mjs","names":[],"sources":["../../src/plugins/shared/SimpleMark.ts"],"sourcesContent":["/**\n * Registers the common core of a simple toggle mark: its MarkSpec, a toggle\n * command, an optional keyboard shortcut, and an optional toolbar button.\n *\n * Plugins that need more (input rules, exclusivity middleware, config-driven\n * variants) layer those on top after calling this — it deliberately models only\n * the shared core, not every mark plugin.\n */\n\nimport { isMarkActive, toggleMark } from '../../commands/Commands.js';\nimport type { MarkSpec } from '../../model/MarkSpec.js';\nimport { markType } from '../../model/TypeBrands.js';\nimport type { PluginContext } from '../Plugin.js';\nimport { dispatchIfPresent } from './PluginHelpers.js';\n\nexport interface SimpleMarkToolbar {\n\treadonly id: string;\n\treadonly group: string;\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly tooltip: string;\n}\n\nexport interface SimpleMarkDefinition {\n\t/** The mark spec to register (defines DOM/HTML rendering and parsing). */\n\treadonly markSpec: MarkSpec;\n\t/** Toggle command name, e.g. `'toggleStrikethrough'`. */\n\treadonly command: string;\n\t/** Keyboard shortcut bound to the command; omit or `null` to skip. */\n\treadonly keyBinding?: string | null;\n\t/** Toolbar button; omit to skip. */\n\treadonly toolbar?: SimpleMarkToolbar;\n}\n\n/** Registers a simple toggle mark's spec, command, keymap, and toolbar item. */\nexport function registerSimpleMark(context: PluginContext, def: SimpleMarkDefinition): void {\n\tconst type = markType(def.markSpec.type);\n\n\tcontext.registerMarkSpec(def.markSpec);\n\n\tcontext.registerCommand(def.command, () =>\n\t\tdispatchIfPresent(context, toggleMark(context.getState(), type)),\n\t);\n\n\tif (def.keyBinding) {\n\t\tcontext.registerKeymap({ [def.keyBinding]: () => context.executeCommand(def.command) });\n\t}\n\n\tif (def.toolbar) {\n\t\tcontext.registerToolbarItem({\n\t\t\tid: def.toolbar.id,\n\t\t\tgroup: def.toolbar.group,\n\t\t\ticon: def.toolbar.icon,\n\t\t\tlabel: def.toolbar.label,\n\t\t\ttooltip: def.toolbar.tooltip,\n\t\t\tcommand: def.command,\n\t\t\tisActive: (state) => isMarkActive(state, type),\n\t\t});\n\t}\n}\n"],"mappings":"kJAmCA,SAAgB,EAAmB,EAAwB,GAC1D,MAAM,EAAO,EAAS,EAAI,SAAS,MAEnC,EAAQ,iBAAiB,EAAI,UAE7B,EAAQ,gBAAgB,EAAI,QAAA,IAC3B,EAAkB,EAAS,EAAW,EAAQ,WAAY,KAGvD,EAAI,YACP,EAAQ,eAAe,CAAA,CAAG,EAAI,YAAA,IAAmB,EAAQ,eAAe,EAAI,WAGzE,EAAI,SACP,EAAQ,oBAAoB,CAC3B,GAAI,EAAI,QAAQ,GAChB,MAAO,EAAI,QAAQ,MACnB,KAAM,EAAI,QAAQ,KAClB,MAAO,EAAI,QAAQ,MACnB,QAAS,EAAI,QAAQ,QACrB,QAAS,EAAI,QACb,SAAW,GAAU,EAAa,EAAO,IAG5C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{c as t,i as e,u as n}from"./Document-r7vLOAGA.mjs";import{c as o,o as s,t as c,u as r}from"./Selection-HYoVFpAx.mjs";import{t as a}from"./Commands-BIIpx_Zd.mjs";import{a as i}from"./TypeBrands-Ch9856Gb.mjs";import{a as l,d,o as u,p as h,u as m}from"./BlockInsertion-6rR9tIGP.mjs";import{n as g}from"./TypedKeys-CA4bfw9O.mjs";import{
|
|
2
|
-
//# sourceMappingURL=SmartPastePlugin-
|
|
1
|
+
import{c as t,i as e,u as n}from"./Document-r7vLOAGA.mjs";import{c as o,o as s,t as c,u as r}from"./Selection-HYoVFpAx.mjs";import{t as a}from"./Commands-BIIpx_Zd.mjs";import{a as i}from"./TypeBrands-Ch9856Gb.mjs";import{a as l,d,o as u,p as h,u as m}from"./BlockInsertion-6rR9tIGP.mjs";import{n as g}from"./TypedKeys-CA4bfw9O.mjs";import{c as f,o as p}from"./PluginHelpers-KDuTlmXm.mjs";import{l as x}from"./XmlDetector-D7TQpw-e.mjs";var k=/\n(?:[ \t]*\n)+/;function C(t,e){return!t.detection&&!e.detection||!(!t.detection||!e.detection)&&t.detection.language===e.detection.language}function b(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 j={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 ")}.`}},B=/* #__PURE__ */Object.assign({"./locales/ar.ts":()=>import("./ar-BWOue6jG.mjs"),"./locales/de.ts":()=>import("./de-CTzAMkXC.mjs"),"./locales/es.ts":()=>import("./es-DDoqchUq.mjs"),"./locales/fr.ts":()=>import("./fr-eC0UdAPu.mjs"),"./locales/hi.ts":()=>import("./hi-DY55FuDK.mjs"),"./locales/pt.ts":()=>import("./pt-Bu7Mbz67.mjs"),"./locales/ru.ts":()=>import("./ru-DyCTlhjg.mjs"),"./locales/zh.ts":()=>import("./zh-D1HSmSOf.mjs")});async function y(t){return f(B,t,j)}var T=new g("smartPaste"),P=class{id="smart-paste";name="Smart Paste";dependencies=["code-block"];config;detectors=[];context=null;locale=j;constructor(t){this.config={...t}}async init(t){this.context=t,this.locale=await p(t,this.config.locale,j,y);const e=t.getService(x);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(T,{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(k).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||e.confidence===n.confidence&&e.language<n.language)&&(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];C(s,o)?e[e.length-1]=b(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,l){const d=o.selection,h=o.transaction("paste");let g;g=r(d)&&!s(d)?a(o,h)??d.anchor.blockId:m(d);const f=n(),p=e(i("code_block"),[t(l.formattedText)],f,{language:l.language,backgroundColor:""});return u(o,h,g,p,d,this.context?.getSchemaRegistry())?(h.setSelection(c(f,l.formattedText.length)),h.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,i=t.transaction("paste");let u;u=r(n)&&!s(n)?a(t,i)??n.anchor.blockId:m(n);const g=l(t,u),f=this.context?.getSchemaRegistry(),p=g?d(t,u,g,f):h(t,u,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);i.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))&&i.removeNode(p.parentPath,p.anchorIndex),k&&i.setSelection(c(k,b)),i.build()}createCodeBlockNode(o){return e(i("code_block"),[t(o.formattedText)],n(),{language:o.language,backgroundColor:""})}createParagraphNode(o){return e(i("paragraph"),[t(o)],n())}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{y as i,T as n,j as r,P as t};
|
|
2
|
+
//# sourceMappingURL=SmartPastePlugin-Bi_wpTCV.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartPastePlugin-BhkoH03E.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/**\n * Picks the highest-confidence detection result from all detectors.\n * Ties are broken deterministically by lexicographic language id so the\n * outcome does not depend on detector registration order.\n */\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) continue;\n\t\tif (\n\t\t\tbest === null ||\n\t\t\tresult.confidence > best.confidence ||\n\t\t\t(result.confidence === best.confidence && result.language < best.language)\n\t\t) {\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\nimport { type LocaleModuleMap, loadLocaleModule } from '../shared/LocaleLoader.js';\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: LocaleModuleMap<SmartPasteLocale> = import.meta.glob<{\n\tdefault: SmartPasteLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadSmartPasteLocale(lang: string): Promise<SmartPasteLocale> {\n\treturn loadLocaleModule(localeModules, lang, SMART_PASTE_LOCALE_EN);\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-Bi_wpTCV.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/**\n * Picks the highest-confidence detection result from all detectors.\n * Ties are broken deterministically by lexicographic language id so the\n * outcome does not depend on detector registration order.\n */\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) continue;\n\t\tif (\n\t\t\tbest === null ||\n\t\t\tresult.confidence > best.confidence ||\n\t\t\t(result.confidence === best.confidence && result.language < best.language)\n\t\t) {\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\nimport { type LocaleModuleMap, loadLocaleModule } from '../shared/LocaleLoader.js';\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: LocaleModuleMap<SmartPasteLocale> = import.meta.glob<{\n\tdefault: SmartPasteLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadSmartPasteLocale(lang: string): Promise<SmartPasteLocale> {\n\treturn loadLocaleModule(localeModules, lang, SMART_PASTE_LOCALE_EN);\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{c as t,o as s}from"./PluginHelpers-KDuTlmXm.mjs";import{t as o}from"./ShortcutFormatting-TwiufqeV.mjs";import{t as r}from"./SimpleMark-CaQtFAau.mjs";var i={label:"Strikethrough",tooltip:t=>`Strikethrough (${t})`},e=/* #__PURE__ */Object.assign({"./locales/ar.ts":()=>import("./ar-XmSFrtmd.mjs"),"./locales/de.ts":()=>import("./de-DViI26lG.mjs"),"./locales/es.ts":()=>import("./es-paXY7HFt.mjs"),"./locales/fr.ts":()=>import("./fr-ZsRdLuys.mjs"),"./locales/hi.ts":()=>import("./hi-CTWubxLY.mjs"),"./locales/pt.ts":()=>import("./pt-paXY7HFt.mjs"),"./locales/ru.ts":()=>import("./ru-Y11wuxOB.mjs"),"./locales/zh.ts":()=>import("./zh-C7iCgCRP.mjs")});async function a(s){return t(e,s,i)}var l={},h=class{id="strikethrough";name="Strikethrough";priority=22;config;locale;constructor(t){this.config={...l,...t}}async init(t){this.locale=await s(t,this.config.locale,i,a),r(t,{markSpec:{type:"strikethrough",rank:3,toDOM:()=>document.createElement("s"),toHTMLString:(t,s)=>`<s>${s}</s>`,parseHTML:[{tag:"s"},{tag:"strike"},{tag:"del"},{tag:"span",getAttrs:t=>!!t.style.textDecoration.includes("line-through")&&{}}],sanitize:{tags:["s"]}},command:"toggleStrikethrough",keyBinding:"Mod-Shift-X",toolbar:{id:"strikethrough",group:"format",icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z"/></svg>',label:this.locale.label,tooltip:this.locale.tooltip(o("Mod-Shift-X"))}})}};export{i as n,a as r,h as t};
|
|
2
|
+
//# sourceMappingURL=StrikethroughPlugin-BLKDRWtf.mjs.map
|
package/dist/chunks/{StrikethroughPlugin-DRuwEZhu.mjs.map → StrikethroughPlugin-BLKDRWtf.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StrikethroughPlugin-
|
|
1
|
+
{"version":3,"file":"StrikethroughPlugin-BLKDRWtf.mjs","names":[],"sources":["../../src/plugins/strikethrough/StrikethroughLocale.ts","../../src/plugins/strikethrough/StrikethroughPlugin.ts"],"sourcesContent":["/**\n * Locale interface and default English locale for the StrikethroughPlugin.\n */\n\nimport { type LocaleModuleMap, loadLocaleModule } from '../shared/LocaleLoader.js';\n\n// --- Locale Interface ---\n\nexport interface StrikethroughLocale {\n\treadonly label: string;\n\treadonly tooltip: (shortcut: string) => string;\n}\n\n// --- Default English Locale ---\n\nexport const STRIKETHROUGH_LOCALE_EN: StrikethroughLocale = {\n\tlabel: 'Strikethrough',\n\ttooltip: (shortcut: string) => `Strikethrough (${shortcut})`,\n};\n\n// --- Lazy Locale Loader ---\n\nconst localeModules: LocaleModuleMap<StrikethroughLocale> = import.meta.glob<{\n\tdefault: StrikethroughLocale;\n}>('./locales/*.ts', { eager: false });\n\nexport async function loadStrikethroughLocale(lang: string): Promise<StrikethroughLocale> {\n\treturn loadLocaleModule(localeModules, lang, STRIKETHROUGH_LOCALE_EN);\n}\n","/**\n * StrikethroughPlugin: registers a strikethrough inline mark with MarkSpec,\n * toggle command, keyboard shortcut (Mod-Shift-X), and a toolbar button.\n */\n\nimport type { Plugin, PluginContext } from '../Plugin.js';\nimport { resolveLocale } from '../shared/PluginHelpers.js';\nimport { formatShortcut } from '../shared/ShortcutFormatting.js';\nimport { registerSimpleMark } from '../shared/SimpleMark.js';\nimport {\n\tSTRIKETHROUGH_LOCALE_EN,\n\ttype StrikethroughLocale,\n\tloadStrikethroughLocale,\n} from './StrikethroughLocale.js';\n\n// --- Attribute Registry Augmentation ---\n\ndeclare module '../../model/AttrRegistry.js' {\n\tinterface MarkAttrRegistry {\n\t\tstrikethrough: Record<string, never>;\n\t}\n}\n\n// --- Configuration ---\n\nexport interface StrikethroughConfig {\n\treadonly locale?: StrikethroughLocale;\n}\n\nconst DEFAULT_CONFIG: StrikethroughConfig = {};\n\n// --- Plugin ---\n\nexport class StrikethroughPlugin implements Plugin {\n\treadonly id = 'strikethrough';\n\treadonly name = 'Strikethrough';\n\treadonly priority = 22;\n\n\tprivate readonly config: StrikethroughConfig;\n\tprivate locale!: StrikethroughLocale;\n\n\tconstructor(config?: Partial<StrikethroughConfig>) {\n\t\tthis.config = { ...DEFAULT_CONFIG, ...config };\n\t}\n\n\tasync init(context: PluginContext): Promise<void> {\n\t\tthis.locale = await resolveLocale(\n\t\t\tcontext,\n\t\t\tthis.config.locale,\n\t\t\tSTRIKETHROUGH_LOCALE_EN,\n\t\t\tloadStrikethroughLocale,\n\t\t);\n\t\tconst icon =\n\t\t\t'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M10 19h4v-3h-4v3zM5 4v3h5v3h4V7h5V4H5zM3 14h18v-2H3v2z\"/></svg>';\n\n\t\tregisterSimpleMark(context, {\n\t\t\tmarkSpec: {\n\t\t\t\ttype: 'strikethrough',\n\t\t\t\trank: 3,\n\t\t\t\ttoDOM() {\n\t\t\t\t\treturn document.createElement('s');\n\t\t\t\t},\n\t\t\t\ttoHTMLString: (_mark, content) => `<s>${content}</s>`,\n\t\t\t\tparseHTML: [\n\t\t\t\t\t{ tag: 's' },\n\t\t\t\t\t{ tag: 'strike' },\n\t\t\t\t\t{ tag: 'del' },\n\t\t\t\t\t{\n\t\t\t\t\t\ttag: 'span',\n\t\t\t\t\t\tgetAttrs: (el: HTMLElement) => {\n\t\t\t\t\t\t\treturn el.style.textDecoration.includes('line-through') ? {} : false;\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tsanitize: { tags: ['s'] },\n\t\t\t},\n\t\t\tcommand: 'toggleStrikethrough',\n\t\t\tkeyBinding: 'Mod-Shift-X',\n\t\t\ttoolbar: {\n\t\t\t\tid: 'strikethrough',\n\t\t\t\tgroup: 'format',\n\t\t\t\ticon,\n\t\t\t\tlabel: this.locale.label,\n\t\t\t\ttooltip: this.locale.tooltip(formatShortcut('Mod-Shift-X')),\n\t\t\t},\n\t\t});\n\t}\n}\n"],"mappings":"4JAeA,IAAa,EAA+C,CAC3D,MAAO,gBACP,QAAU,GAAqB,kBAAkB,MAK5C,iBAAsD,OAAA,OAAA,CAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,qBAAA,kBAAA,IAAA,OAAA,uBAI5D,eAAsB,EAAwB,GAC7C,OAAO,EAAiB,EAAe,EAAM,EAC9C,CCCA,IAAM,EAAsC,CAAC,EAIhC,EAAb,MACC,GAAc,gBACd,KAAgB,gBAChB,SAAoB,GAEpB,OACA,OAEA,WAAA,CAAY,GACX,KAAK,OAAS,IAAK,KAAmB,EACvC,CAEA,UAAM,CAAK,GACV,KAAK,aAAe,EACnB,EACA,KAAK,OAAO,OACZ,EACA,GAKD,EAAmB,EAAS,CAC3B,SAAU,CACT,KAAM,gBACN,KAAM,EACN,MAAA,IACQ,SAAS,cAAc,KAE/B,aAAA,CAAe,EAAO,IAAY,MAAM,QACxC,UAAW,CACV,CAAE,IAAK,KACP,CAAE,IAAK,UACP,CAAE,IAAK,OACP,CACC,IAAK,OACL,SAAW,KACH,EAAG,MAAM,eAAe,SAAS,iBAAkB,CAAC,IAI9D,SAAU,CAAE,KAAM,CAAC,OAEpB,QAAS,sBACT,WAAY,cACZ,QAAS,CACR,GAAI,gBACJ,MAAO,SACP,KAAA,uIACA,MAAO,KAAK,OAAO,MACnB,QAAS,KAAK,OAAO,QAAQ,EAAe,kBAG/C"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e="data-notectl-style-token",t=/* @__PURE__ */new WeakMap,n=/* @__PURE__ */new WeakMap,s=/* @__PURE__ */new WeakSet;function o(e){return s.has(e)}function i(){if("undefined"==typeof CSSStyleSheet)return null;try{const e=new CSSStyleSheet;return s.add(e),e}catch{return null}}function r(e,o){const i=n.get(e);i&&(i.destroy(),n.delete(e)),o.sheet&&s.add(o.sheet),t.set(e,{nonce:o.nonce,sheet:o.sheet??null})}function l(e){const s=n.get(e);s&&(s.destroy(),n.delete(e)),t.delete(e)}function c(e,t,n){const s=S(e);s?k(s.root,s.settings).setProperty(e,t,n):y(e,t,n)}function a(e,t){c(e,t,"")}function h(e,t){const n=S(e);if(n)k(n.root,n.settings).setProperties(e,t);else for(const[s,o]of Object.entries(t))y(e,s,o??"")}function u(e,t){const n=S(e);n?k(n.root,n.settings).setStyleText(e,t):e.style.cssText=t}function d(e,t){const n=S(e);if(!n){const n=e.style.cssText;return void(e.style.cssText=n?`${n}; ${t}`:t)}k(n.root,n.settings).appendStyleText(e,t)}function f(e){const n=g(e);if(n)return t.get(n)?.nonce}function y(e,t,n){t.startsWith("--")||t.includes("-")?n?e.style.setProperty(t,n):e.style.removeProperty(t):e.style[t]=n}function S(e){const n=function(e){return g(e)}(e);if(!n)return null;const s=t.get(n);return s?{root:n,settings:s}:null}function k(e,t){const s=n.get(e);if(s)return s;const o=new E(e,t);return n.set(e,o),o}function m(e){return"undefined"!=typeof ShadowRoot&&e instanceof ShadowRoot}function p(e){return"undefined"!=typeof Document&&e instanceof Document}function g(e){const t=m(e)||p(e)?e:e.getRootNode();return m(t)||p(t)?t:"undefined"!=typeof document?document:null}var E=class{declarationByElement=/* @__PURE__ */new WeakMap;tokenByDeclaration=/* @__PURE__ */new Map;declarationByToken=/* @__PURE__ */new Map;tokenRefCount=/* @__PURE__ */new Map;tokenByElement=/* @__PURE__ */new WeakMap;tokenCounter=0;root;nonce;sheet;fallbackStyleElement=null;ownsAdoptedSheet=!1;constructor(e,t){this.root=e,this.nonce=t.nonce,this.sheet=t.sheet??null}destroy(){this.clearRuntimeRules(),this.ownsAdoptedSheet&&this.sheet&&"adoptedStyleSheets"in this.root&&(this.root.adoptedStyleSheets=this.root.adoptedStyleSheets.filter(e=>e!==this.sheet)),this.fallbackStyleElement?.remove(),this.fallbackStyleElement=null,this.sheet=null,this.ownsAdoptedSheet=!1,this.tokenByDeclaration.clear(),this.declarationByToken.clear(),this.tokenRefCount.clear(),this.tokenCounter=0}setProperty(e,t,n){const s=this.getOrCreateDeclarations(e),o=T(t);n?s.set(o,n):s.delete(o),this.syncElementToken(e,s)}setProperties(e,t){const n=this.getOrCreateDeclarations(e);for(const[s,o]of Object.entries(t)){const e=T(s);o?n.set(e,o):n.delete(e)}this.syncElementToken(e,n)}setStyleText(e,t){const n=R(t);0!==n.size?(this.declarationByElement.set(e,n),this.syncElementToken(e,n)):this.clearElementStyles(e)}appendStyleText(e,t){const n=R(t);if(0===n.size)return;const s=this.getOrCreateDeclarations(e);for(const[o,i]of n)s.set(o,i);this.syncElementToken(e,s)}getStyleText(e){const t=this.declarationByElement.get(e);return t&&0!==t.size?C(t):""}getOrCreateDeclarations(e){const t=this.declarationByElement.get(e);if(t)return t;const n=/* @__PURE__ */new Map;return this.declarationByElement.set(e,n),n}syncElementToken(t,n){const s=this.tokenByElement.get(t);if(0===n.size)return void this.clearElementStyles(t);const o=C(n);let i=this.tokenByDeclaration.get(o);i||(i=`s${this.tokenCounter.toString(36)}`,this.tokenCounter+=1,this.tokenByDeclaration.set(o,i),this.declarationByToken.set(i,o),this.insertRule(i,o)),s!==i&&(this.retainToken(i),s&&this.releaseToken(s),this.tokenByElement.set(t,i)),t.setAttribute(e,i)}insertRule(e,t){const n=`${w(e)} { ${t}; }`,s=this.ensureSheet();if(s)try{s.insertRule(n,s.cssRules.length)}catch{}}ensureSheet(){if(this.sheet)return this.sheet;if("undefined"!=typeof CSSStyleSheet)try{const e=new CSSStyleSheet;if("adoptedStyleSheets"in this.root){const t=this.root.adoptedStyleSheets;return this.root.adoptedStyleSheets=[...t,e],s.add(e),this.sheet=e,this.ownsAdoptedSheet=!0,e}}catch{}const e=this.ensureFallbackStyleElement();return this.sheet=e.sheet??null,this.sheet&&s.add(this.sheet),this.sheet}clearElementStyles(t){this.declarationByElement.delete(t);const n=this.tokenByElement.get(t);n&&(this.tokenByElement.delete(t),this.releaseToken(n)),t.removeAttribute(e)}retainToken(e){const t=this.tokenRefCount.get(e)??0;this.tokenRefCount.set(e,t+1)}releaseToken(e){const t=this.tokenRefCount.get(e);if(!t)return;if(t>1)return void this.tokenRefCount.set(e,t-1);this.tokenRefCount.delete(e);const n=this.declarationByToken.get(e);n&&(this.declarationByToken.delete(e),this.tokenByDeclaration.delete(n)),this.deleteRule(e)}deleteRule(e){const t=this.sheet;if(!t)return;const n=w(e);for(let s=t.cssRules.length-1;s>=0;s-=1){const e=t.cssRules[s];if(e&&B(e,n)){try{t.deleteRule(s)}catch{}return}}}clearRuntimeRules(){const t=this.sheet;if(t)for(let n=t.cssRules.length-1;n>=0;n-=1){const s=t.cssRules[n];if(s&&s.cssText.includes(`[${e}="`))try{t.deleteRule(n)}catch{}}}ensureFallbackStyleElement(){if(this.fallbackStyleElement)return this.fallbackStyleElement;const e=document.createElement("style");return e.setAttribute("data-notectl-runtime-styles",""),this.nonce&&e.setAttribute("nonce",this.nonce),m(this.root)?this.root.appendChild(e):(this.root.head??this.root.documentElement).appendChild(e),this.fallbackStyleElement=e,e}};function R(e){const t=/* @__PURE__ */new Map;for(const n of e.split(";")){const e=n.trim();if(!e)continue;const s=e.indexOf(":");if(s<=0)continue;const o=T(e.slice(0,s)),i=e.slice(s+1).trim();i?t.set(o,i):t.delete(o)}return t}function C(e){return Array.from(e.entries()).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>`${e}: ${t}`).join("; ")}function T(e){const t=e.trim();return t?t.startsWith("--")?t:t.includes("-")?t.toLowerCase():t.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`).toLowerCase():""}function w(t){return`[${e}="${t}"]`}function B(e,t){return"undefined"!=typeof CSSStyleRule&&e instanceof CSSStyleRule?e.selectorText===t:e.cssText.includes(t)}export{r as a,c,o as i,u as l,i as n,a as o,f as r,h as s,d as t,l as u};
|
|
2
|
+
//# sourceMappingURL=StyleRuntime-BFKu52mS.mjs.map
|