decaframe 0.1.0 → 0.1.1

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.
Files changed (72) hide show
  1. package/dist/fit.js +3 -6
  2. package/dist/markdown.js +22 -22
  3. package/dist/mcp/server.js +1 -1
  4. package/dist/mcp/stdio.js +1 -1
  5. package/dist/theme/decaframe.json +1 -1
  6. package/dist/theme/emit.js +9 -9
  7. package/editor/assets/{App-BvOGZ4cc.js → App-DHq9LXNY.js} +1 -1
  8. package/editor/assets/{PresenterView-Barr5wsl.js → PresenterView-CGgNySm_.js} +1 -1
  9. package/editor/assets/{Shell-Dvc8Yvly.js → Shell-BQQAN1pA.js} +1 -1
  10. package/editor/assets/{abnfDiagram-N423BO3Z-DMNuYXhv.js → abnfDiagram-N423BO3Z-CChhA49z.js} +1 -1
  11. package/editor/assets/{arc-DOR9Dhm9.js → arc-DLkUP0af.js} +1 -1
  12. package/editor/assets/{architectureDiagram-T3A2C74G-CpcCHDGw.js → architectureDiagram-T3A2C74G-CJTnIEVC.js} +1 -1
  13. package/editor/assets/{blockDiagram-VBNYF7ZC-CbCqcHim.js → blockDiagram-VBNYF7ZC-DTpAV1dD.js} +1 -1
  14. package/editor/assets/{c4Diagram-5PPSVZJV-DouVYESX.js → c4Diagram-5PPSVZJV-B6fm_I1g.js} +1 -1
  15. package/editor/assets/channel-D14uu3K3.js +1 -0
  16. package/editor/assets/{chunk-2GRJ4B5K-DCJz-Q3N.js → chunk-2GRJ4B5K-CGzzubea.js} +1 -1
  17. package/editor/assets/{chunk-2Q5K7J3B-D5q9RvV7.js → chunk-2Q5K7J3B-BJZdFID0.js} +1 -1
  18. package/editor/assets/{chunk-5RXB4S5H-DnaXL-Vg.js → chunk-5RXB4S5H-BCp6V9qi.js} +1 -1
  19. package/editor/assets/{chunk-5VM5RSS4-Dsc890tp.js → chunk-5VM5RSS4-h31kvaQG.js} +1 -1
  20. package/editor/assets/{chunk-6Q2QTUOP-B5F620Er.js → chunk-6Q2QTUOP-BbKbbJ2P.js} +1 -1
  21. package/editor/assets/{chunk-GF5L2VYU-D6F2cocl.js → chunk-GF5L2VYU-DxHxK9FK.js} +1 -1
  22. package/editor/assets/{chunk-JWPE2WC7-bRknM2iE.js → chunk-JWPE2WC7-Dg3mcfES.js} +1 -1
  23. package/editor/assets/{chunk-KBJHAD2P-DOPClpqF.js → chunk-KBJHAD2P-FYlKxEex.js} +1 -1
  24. package/editor/assets/{chunk-RYQCIY6F-CvMFFqLU.js → chunk-RYQCIY6F-B3dIW2lQ.js} +1 -1
  25. package/editor/assets/{chunk-XXDRQBXY-1qxA3khf.js → chunk-XXDRQBXY-ByiXzl4z.js} +1 -1
  26. package/editor/assets/classDiagram-JCYQIIEL-BmVFSxMP.js +1 -0
  27. package/editor/assets/classDiagram-v2-OCEON4UE-BmVFSxMP.js +1 -0
  28. package/editor/assets/{cose-bilkent-JH36ORCC-ClwDXN93.js → cose-bilkent-JH36ORCC-DHH12UEC.js} +1 -1
  29. package/editor/assets/{cynefin-VYW2F7L2-D_3oIqit.js → cynefin-VYW2F7L2-CfTXmdLL.js} +1 -1
  30. package/editor/assets/{cynefinDiagram-MW4NZA55-5VKykgH3.js → cynefinDiagram-MW4NZA55-C8csZI9I.js} +1 -1
  31. package/editor/assets/{dagre-VZM6K2ZE-DcheWwdS.js → dagre-VZM6K2ZE-BHC_5PSB.js} +1 -1
  32. package/editor/assets/{diagram-7IWD3JNH-BKU8hufg.js → diagram-7IWD3JNH-CofsqT9y.js} +1 -1
  33. package/editor/assets/{diagram-B4RE2ZJO-C6NwNXMM.js → diagram-B4RE2ZJO-DqriiHKZ.js} +1 -1
  34. package/editor/assets/{diagram-LBJQPF4R-D5l8kaaB.js → diagram-LBJQPF4R-ahY4MQSH.js} +1 -1
  35. package/editor/assets/{diagram-Q27KOJAE-B839mp_E.js → diagram-Q27KOJAE-B1ArA4Ee.js} +1 -1
  36. package/editor/assets/{diagram-UB23O5K3-BEJ8nrHN.js → diagram-UB23O5K3-BPpNyFTU.js} +1 -1
  37. package/editor/assets/{ebnfDiagram-BXEA7PRR-Clexv7zP.js → ebnfDiagram-BXEA7PRR-Wxzqb4_v.js} +1 -1
  38. package/editor/assets/{erDiagram-JOGREHBK-CQiS9gcI.js → erDiagram-JOGREHBK-CPmLQJd5.js} +1 -1
  39. package/editor/assets/{flowDiagram-UKHOOZJN-CKnVd1JX.js → flowDiagram-UKHOOZJN-DT8vefN3.js} +1 -1
  40. package/editor/assets/{ganttDiagram-PKOTCBZU-B2_YsT7P.js → ganttDiagram-PKOTCBZU-BjMNqaRt.js} +1 -1
  41. package/editor/assets/{gitGraphDiagram-DS77QQ5N-woKMsCpe.js → gitGraphDiagram-DS77QQ5N-D9xT-pIF.js} +1 -1
  42. package/editor/assets/{index-CnMC8Z9d.js → index-DMhg0511.js} +1821 -1821
  43. package/editor/assets/{infoDiagram-6WML65LV-DQBHUzYs.js → infoDiagram-6WML65LV-CECaeFLo.js} +1 -1
  44. package/editor/assets/{ishikawaDiagram-WSZJBQD7-B0ECJjey.js → ishikawaDiagram-WSZJBQD7-Eu9Ino89.js} +1 -1
  45. package/editor/assets/{journeyDiagram-NVQOT4AX-C6IoE7ds.js → journeyDiagram-NVQOT4AX-aI4SblMV.js} +1 -1
  46. package/editor/assets/{kanban-definition-27J2QSJJ-BsRx6-QA.js → kanban-definition-27J2QSJJ-DeoS4Fd2.js} +1 -1
  47. package/editor/assets/{linear-CZdkbpsZ.js → linear-CPpfdWI4.js} +1 -1
  48. package/editor/assets/{mermaid.core-BD7SxJD7.js → mermaid.core-CEVxyIeU.js} +4 -4
  49. package/editor/assets/{mindmap-definition-FAOFIHXS-zQTmawZC.js → mindmap-definition-FAOFIHXS-Cejxbm9i.js} +1 -1
  50. package/editor/assets/{pegDiagram-VL7TDLO6-DwfoSDjb.js → pegDiagram-VL7TDLO6-Ap6CM0lB.js} +1 -1
  51. package/editor/assets/{pieDiagram-7S7Q4E2Y-CFju-NFP.js → pieDiagram-7S7Q4E2Y-Bsdr0XtC.js} +1 -1
  52. package/editor/assets/{quadrantDiagram-CIZ2JOQS-BD6kYPyE.js → quadrantDiagram-CIZ2JOQS-D-ZbloLC.js} +1 -1
  53. package/editor/assets/{railroadDiagram-AXF67PYL-RqQwm-lL.js → railroadDiagram-AXF67PYL-BjvO1EJZ.js} +1 -1
  54. package/editor/assets/{requirementDiagram-LRYGKXZP-piqHrBVs.js → requirementDiagram-LRYGKXZP-BvtN_tK8.js} +1 -1
  55. package/editor/assets/{sankeyDiagram-W5VNT64P-CiALLT6P.js → sankeyDiagram-W5VNT64P-Cvglcm0P.js} +1 -1
  56. package/editor/assets/{sequenceDiagram-SI44F4Z6-CjpaYPxa.js → sequenceDiagram-SI44F4Z6-B4zcPx76.js} +1 -1
  57. package/editor/assets/{sizeCapture-X5ZJPWSS-DG9i3uEZ.js → sizeCapture-X5ZJPWSS-PqSSJLnF.js} +1 -1
  58. package/editor/assets/{stateDiagram-OKZ733FA-BgfrUiLR.js → stateDiagram-OKZ733FA-DbC-6GRx.js} +1 -1
  59. package/editor/assets/stateDiagram-v2-UEYNNEHI-C6DCyWoe.js +1 -0
  60. package/editor/assets/{swimlanes-SLNWSIFB-edAhP15G.js → swimlanes-SLNWSIFB-BpgKQQAO.js} +2 -2
  61. package/editor/assets/swimlanesDiagram-ULZ7WXOC-Bj4I3_ZJ.js +8 -0
  62. package/editor/assets/{timeline-definition-Z64GVDOM-CLCag48V.js → timeline-definition-Z64GVDOM-BuRbvfsc.js} +1 -1
  63. package/editor/assets/{vennDiagram-T6HMQDX7-CNuGpJHG.js → vennDiagram-T6HMQDX7-M0HeKWul.js} +1 -1
  64. package/editor/assets/{wardleyDiagram-T6FBY63Y-D-NVpbtn.js → wardleyDiagram-T6FBY63Y-ByE7hlxp.js} +1 -1
  65. package/editor/assets/{xychartDiagram-ELKLHX3M-CmO8305Y.js → xychartDiagram-ELKLHX3M-CfENXbmg.js} +1 -1
  66. package/editor/index.html +1 -1
  67. package/package.json +1 -1
  68. package/editor/assets/channel-Dt7MgGxB.js +0 -1
  69. package/editor/assets/classDiagram-JCYQIIEL-DCQKoo1F.js +0 -1
  70. package/editor/assets/classDiagram-v2-OCEON4UE-DCQKoo1F.js +0 -1
  71. package/editor/assets/stateDiagram-v2-UEYNNEHI-OBUiw3fk.js +0 -1
  72. package/editor/assets/swimlanesDiagram-ULZ7WXOC-BvcKTcG0.js +0 -8
package/dist/fit.js CHANGED
@@ -1,6 +1,3 @@
1
- function w(u,e){const[n,i]=u.split("/").map(c=>parseFloat(c));return!n||!i||!Number.isFinite(n)||!Number.isFinite(i)?e.height:e.width*i/n}function F(u,e){const n=getComputedStyle(u),i=parseFloat(n.paddingBottom)||0,c=e.top+w(n.aspectRatio,e)-i;let r=-1/0;for(const l of u.children)l.classList.contains("page-accent")||(r=Math.max(r,l.getBoundingClientRect().bottom));return r===-1/0?0:Math.max(0,Math.round(r-c))}function L(u){const e=[];for(const n of u.querySelectorAll(".page")){if(!(n instanceof HTMLElement))continue;const i=n.getBoundingClientRect(),c=getComputedStyle(n),r=parseFloat(c.paddingBottom)||0,l=w(c.aspectRatio,i),[a,x]=c.aspectRatio.split("/").map(o=>parseFloat(o)),h=!!(a&&x&&Number.isFinite(a)&&Number.isFinite(x)),m=i.top+l-r,g=o=>o.closest("[data-slot]")===null,M=[];for(const o of n.querySelectorAll(".blk")){const d=o.getAttribute("data-id");if(!d||!g(o))continue;const f=o.getBoundingClientRect(),p=Math.round(f.bottom-m);p>0&&M.push({id:d,over:p,height:Math.round(f.height)})}let y=null;for(const o of n.children){if(!o.classList.contains("blk")&&o.getAttribute("data-node")!=="layout")continue;const d=o.getAttribute("data-id");d&&o.getBoundingClientRect().bottom<=m&&(y=d)}let s=null;for(const o of n.querySelectorAll("[data-slot]")){const d=o.getAttribute("data-slot");if(!d||!o.textContent?.trim())continue;const f=Math.round(o.getBoundingClientRect().height);if(s&&f<=s.height)continue;const p=o.closest(".blk")?.getAttribute("data-id");p&&(s={blockId:p,node:d,child:o.closest("[data-node]")?.getAttribute("data-node")??"",height:f})}if(!s)for(const o of n.querySelectorAll(".blk")){const d=o.getAttribute("data-id");if(!d||!g(o)||!o.textContent?.trim())continue;const f=Math.round(o.getBoundingClientRect().height);(!s||f>s.height)&&(s={blockId:d,node:"",child:"",height:f})}e.push({id:n.getAttribute("data-id")??"",bounded:h,over:F(n,i),frame:Math.round(l),past:M,lastFitting:y,tallest:s})}return e}function T(u,e){const n=[];for(const i of u.querySelectorAll(".page")){if(!(i instanceof HTMLElement))continue;const c=i.getAttribute("data-id")??"",r=getComputedStyle(i),l=i.getBoundingClientRect(),a={left:l.left+parseFloat(r.paddingLeft),top:l.top+parseFloat(r.paddingTop),right:l.right-parseFloat(r.paddingRight),bottom:l.bottom-parseFloat(r.paddingBottom)},x=i.querySelector(':scope > [data-node="accent"], :scope > .page-accent'),h=i.getAttribute("data-accent");if(x&&h&&h!=="full"){const t=x.getBoundingClientRect();h==="lead"?a.left=Math.max(a.left,t.right):h==="trail"?a.right=Math.min(a.right,t.left):h==="top"&&(a.top=Math.max(a.top,t.bottom))}const m=a.right-a.left,g=a.bottom-a.top;if(m<=0||g<=0){n.push(null);continue}const M=[];for(const t of i.children)if(!t.matches('[data-node="accent"], .page-accent')){if(t.matches('[data-node="layout"], .blk-layout')){for(const b of t.querySelectorAll('[data-node="region"], .layout-region'))for(const S of b.children)M.push(S.getBoundingClientRect());continue}M.push(t.getBoundingClientRect())}const y=!!h||i.hasAttribute("data-surface"),s=M.filter(t=>t.width>1&&t.height>1);if(!s.length){n.push({id:c,ink:0,vSpan:0,wide:0,gap:100,parts:0,painted:y});continue}const o=new Uint8Array(e*e);for(const t of s){const b=Math.max(0,Math.floor((t.left-a.left)/m*e)),S=Math.min(e,Math.ceil((t.right-a.left)/m*e)),v=Math.max(0,Math.floor((t.top-a.top)/g*e)),H=Math.min(e,Math.ceil((t.bottom-a.top)/g*e));for(let C=v;C<H;C++)for(let R=b;R<S;R++)o[C*e+R]=1}let d=0;for(let t=0;t<o.length;t++)d+=o[t];const f=Math.min(...s.map(t=>t.top)),p=Math.max(...s.map(t=>t.bottom)),q=Math.max(...s.map(t=>t.width)),k=s.map(t=>[t.top,t.bottom]).sort((t,b)=>t[0]-b[0]);let A=0,B=k[0][1];for(const[t,b]of k.slice(1))t>B&&(A=Math.max(A,t-B)),B=Math.max(B,b);A=Math.max(A,f-a.top,a.bottom-p),n.push({id:c,ink:Math.round(d/(e*e)*100),vSpan:Math.round((p-f)/g*100),wide:Math.round(q/m*100),gap:Math.round(A/g*100),parts:s.length,painted:y})}return n}const O=.8,$=.02;function E(u,e,n,i){return!(u>0)||!(e>0)||!(n>0)?1:Math.max(i,Math.min(1,u*n/e))}function I(u,e,n){const i=[];for(const c of u.querySelectorAll(".page")){if(!(c instanceof HTMLElement))continue;const r=c.getAttribute("style");let l=1,a=F(c,c.getBoundingClientRect());for(;a>0&&l>e;)l=Math.max(e,Math.round((l-n)*1e3)/1e3),c.style.setProperty("--fit",String(l)),a=F(c,c.getBoundingClientRect());r===null?c.removeAttribute("style"):c.setAttribute("style",r),i.push({id:c.getAttribute("data-id")??"",scale:l,over:a})}return i}const N=()=>`const frameHeight = ${w};
2
- const overflowOf = ${F};
3
- const readFit = ${L};
4
- const readFill = ${T};
5
- const readScale = ${I};
6
- `;export{O as FIT_FLOOR,$ as FIT_STEP,E as fitFactorOf,N as fitSource,w as frameHeight,F as overflowOf,T as readFill,L as readFit,I as readScale};
1
+ function k(l,e){const[n,i]=l.split("/").map(c=>parseFloat(c));return!n||!i||!Number.isFinite(n)||!Number.isFinite(i)?e.height:e.width*i/n}function S(l,e){const n=getComputedStyle(l),i=parseFloat(n.paddingBottom)||0,c=e.top+k(n.aspectRatio,e)-i;let d=-1/0;for(const s of l.children)s.classList.contains("page-accent")||(d=Math.max(d,s.getBoundingClientRect().bottom));return d===-1/0?0:Math.max(0,Math.round(d-c))}function T(l){const e=[];for(const n of l.querySelectorAll(".page")){if(!(n instanceof HTMLElement))continue;const i=n.getBoundingClientRect(),c=getComputedStyle(n),d=parseFloat(c.paddingBottom)||0,s=k(c.aspectRatio,i),[a,x]=c.aspectRatio.split("/").map(o=>parseFloat(o)),h=!!(a&&x&&Number.isFinite(a)&&Number.isFinite(x)),m=i.top+s-d,g=o=>o.closest("[data-slot]")===null,M=[];for(const o of n.querySelectorAll(".blk")){const u=o.getAttribute("data-id");if(!u||!g(o))continue;const f=o.getBoundingClientRect(),p=Math.round(f.bottom-m);p>0&&M.push({id:u,over:p,height:Math.round(f.height)})}let y=null;for(const o of n.children){if(!o.classList.contains("blk")&&o.getAttribute("data-node")!=="layout")continue;const u=o.getAttribute("data-id");u&&o.getBoundingClientRect().bottom<=m&&(y=u)}let r=null;for(const o of n.querySelectorAll("[data-slot]")){const u=o.getAttribute("data-slot");if(!u||!o.textContent?.trim())continue;const f=Math.round(o.getBoundingClientRect().height);if(r&&f<=r.height)continue;const p=o.closest(".blk")?.getAttribute("data-id");p&&(r={blockId:p,node:u,child:o.closest("[data-node]")?.getAttribute("data-node")??"",height:f})}if(!r)for(const o of n.querySelectorAll(".blk")){const u=o.getAttribute("data-id");if(!u||!g(o)||!o.textContent?.trim())continue;const f=Math.round(o.getBoundingClientRect().height);(!r||f>r.height)&&(r={blockId:u,node:"",child:"",height:f})}e.push({id:n.getAttribute("data-id")??"",bounded:h,over:S(n,i),frame:Math.round(s),past:M,lastFitting:y,tallest:r})}return e}function I(l,e){const n=[];for(const i of l.querySelectorAll(".page")){if(!(i instanceof HTMLElement))continue;const c=i.getAttribute("data-id")??"",d=getComputedStyle(i),s=i.getBoundingClientRect(),a={left:s.left+parseFloat(d.paddingLeft),top:s.top+parseFloat(d.paddingTop),right:s.right-parseFloat(d.paddingRight),bottom:s.bottom-parseFloat(d.paddingBottom)},x=i.querySelector(':scope > [data-node="accent"], :scope > .page-accent'),h=i.getAttribute("data-accent");if(x&&h&&h!=="full"){const t=x.getBoundingClientRect();h==="lead"?a.left=Math.max(a.left,t.right):h==="trail"?a.right=Math.min(a.right,t.left):h==="top"&&(a.top=Math.max(a.top,t.bottom))}const m=a.right-a.left,g=a.bottom-a.top;if(m<=0||g<=0){n.push(null);continue}const M=[];for(const t of i.children)if(!t.matches('[data-node="accent"], .page-accent')){if(t.matches('[data-node="layout"], .blk-layout')){for(const b of t.querySelectorAll('[data-node="region"], .layout-region'))for(const C of b.children)M.push(C.getBoundingClientRect());continue}M.push(t.getBoundingClientRect())}const y=!!h||i.hasAttribute("data-surface"),r=M.filter(t=>t.width>1&&t.height>1);if(!r.length){n.push({id:c,ink:0,vSpan:0,wide:0,gap:100,parts:0,painted:y});continue}const o=new Uint8Array(e*e);for(const t of r){const b=Math.max(0,Math.floor((t.left-a.left)/m*e)),C=Math.min(e,Math.ceil((t.right-a.left)/m*e)),H=Math.max(0,Math.floor((t.top-a.top)/g*e)),L=Math.min(e,Math.ceil((t.bottom-a.top)/g*e));for(let R=H;R<L;R++)for(let w=b;w<C;w++)o[R*e+w]=1}let u=0;for(let t=0;t<o.length;t++)u+=o[t];const f=Math.min(...r.map(t=>t.top)),p=Math.max(...r.map(t=>t.bottom)),v=Math.max(...r.map(t=>t.width)),q=r.map(t=>[t.top,t.bottom]).sort((t,b)=>t[0]-b[0]);let A=0,F=q[0][1];for(const[t,b]of q.slice(1))t>F&&(A=Math.max(A,t-F)),F=Math.max(F,b);A=Math.max(A,f-a.top,a.bottom-p),n.push({id:c,ink:Math.round(u/(e*e)*100),vSpan:Math.round((p-f)/g*100),wide:Math.round(v/m*100),gap:Math.round(A/g*100),parts:r.length,painted:y})}return n}const E=.8,N=.02;function $(l,e,n,i){return!(l>0)||!(e>0)||!(n>0)?1:Math.max(i,Math.min(1,l*n/e))}function O(l,e,n){const i=[];for(const c of l.querySelectorAll(".page")){if(!(c instanceof HTMLElement))continue;const d=c.getAttribute("style");let s=1,a=S(c,c.getBoundingClientRect());for(;a>0&&s>e;)s=Math.max(e,Math.round((s-n)*1e3)/1e3),c.style.setProperty("--fit",String(s)),a=S(c,c.getBoundingClientRect());d===null?c.removeAttribute("style"):c.setAttribute("style",d),i.push({id:c.getAttribute("data-id")??"",scale:s,over:a})}return i}const P=()=>B("frameHeight",k)+B("overflowOf",S)+B("readFit",T)+B("readFill",I)+B("readScale",O),B=(l,e)=>`const ${l} = ${e};
2
+ `+(e.name&&e.name!==l?`const ${e.name} = ${l};
3
+ `:"");export{E as FIT_FLOOR,N as FIT_STEP,$ as fitFactorOf,P as fitSource,k as frameHeight,S as overflowOf,I as readFill,T as readFit,O as readScale};
package/dist/markdown.js CHANGED
@@ -1,32 +1,32 @@
1
- import{Markdown as xt,MarkdownManager as St}from"@tiptap/markdown";import{CATALOGUE as At,byNode as z,byType as Tt}from"./blocks.js";import{LAYOUT_NAMES as F,layoutNamed as W}from"./layout.js";import{ID_TYPES as It,mintId as w}from"./id.js";import{ARRANGEMENT_ATTR as A,BLOCK_PLACES as Z,FIGURE_ATTR as P,ICON_POSITIONS as J,ICON_POSITION_ATTR as E,MARK_ATTR as N,MIN_WIDTH as Q,PLACE_ATTR as M,VARIANT_ATTR as S,WIDTH_ATTR as O}from"./design.js";import{nearestIcon as vt,resolveIcon as Et}from"./icons.js";import{mediaSrcProblem as tt,youtubeId as et}from"./media.js";import{CHART_STACKS as nt,seriesProblem as Nt}from"./chart.js";import{ACCENT_POSITIONS as rt,PAGE_SURFACES as st}from"./page.js";import{CORE_VARIANTS as Mt,DEFAULT_VARIANT as Ot,variantsFor as Bt}from"./theme/emit.js";import{decaframeExtensions as Ct}from"./extensions.js";const _t="renderChildren",Rt=e=>{const t=String(e);return/^[\w./:#%?&=+~@-]+$/.test(t)&&t!==""?t:t.includes('"')?`'${t.replace(/'/g,"")}'`:`"${t}"`},B=e=>Object.entries(e).filter(([,t])=>t!=null&&t!==!1&&t!=="").map(([t,n])=>n===!0?t:`${t}=${Rt(n)}`).join(" "),I=(e,t={})=>{const n=B(t);return n?`::${e}: ${n}::`:`::${e}::`},C=e=>{const t=B(e);return t?` {${t}}`:""};function v(e){const t=z(e.type??""),n=e.attrs??{},r=n[P]??n[A];return{[S]:n[S]&&n[S]!==Ot?n[S]:null,[A]:r??null,[E]:n[E],[N]:n[N],[O]:n[O],[M]:n[M]}}const x=(e,t,n)=>{const r=(e.content??[]).findIndex(o=>o.type===t);return r<0?"":n.renderChild(e.content[r],r).trim()},D="::end::",_="about:blank",ot=e=>({renderMarkdown:(t,n)=>(t.content??[]).map((r,o)=>n.renderChild(r,o)).filter(r=>r.trim()).join(e)}),k=()=>({renderMarkdown:(e,t)=>e.content?.length?t[_t](e,""):""}),jt=()=>({renderMarkdown:(e,t)=>{const n=z(e.type??"");if(!n||!e.content?.length)return"";const r=e.content.map((o,a)=>t.renderChild(o,a).trim()).filter(Boolean);return[I(n.type,v(e)),...r,D].join(`
1
+ import{Markdown as At,MarkdownManager as Tt}from"@tiptap/markdown";import{CATALOGUE as It,byNode as W,byType as vt}from"./blocks.js";import{LAYOUT_NAMES as Z,layoutNamed as J}from"./layout.js";import{ID_TYPES as Et,mintId as w}from"./id.js";import{ARRANGEMENT_ATTR as T,BLOCK_PLACES as Q,FIGURE_ATTR as P,ICON_POSITIONS as tt,ICON_POSITION_ATTR as E,MARK_ATTR as N,MIN_WIDTH as et,PLACE_ATTR as M,VARIANT_ATTR as S,WIDTH_ATTR as O}from"./design.js";import{nearestIcon as Nt,resolveIcon as Mt}from"./icons.js";import{mediaSrcProblem as nt,youtubeId as rt}from"./media.js";import{CHART_STACKS as st,seriesProblem as Ot}from"./chart.js";import{ACCENT_POSITIONS as ot,PAGE_SURFACES as at}from"./page.js";import{CORE_VARIANTS as Bt,DEFAULT_VARIANT as Ct,variantsFor as _t}from"./theme/emit.js";import{decaframeExtensions as Rt}from"./extensions.js";const jt="renderChildren",Lt=t=>{const e=String(t);return/^[\w./:#%?&=+~@-]+$/.test(e)&&e!==""?e:e.includes('"')?`'${e.replace(/'/g,"")}'`:`"${e}"`},B=t=>Object.entries(t).filter(([,e])=>e!=null&&e!==!1&&e!=="").map(([e,n])=>n===!0?e:`${e}=${Lt(n)}`).join(" "),I=(t,e={})=>{const n=B(e);return n?`::${t}: ${n}::`:`::${t}::`},C=t=>{const e=B(t);return e?` {${e}}`:""};function v(t){const e=W(t.type??""),n=t.attrs??{},r=n[P]??n[T];return{[S]:n[S]&&n[S]!==Ct?n[S]:null,[T]:r??null,[E]:n[E],[N]:n[N],[O]:n[O],[M]:n[M]}}const x=(t,e,n)=>{const r=(t.content??[]).findIndex(o=>o.type===e);return r<0?"":n.renderChild(t.content[r],r).trim()},D="::end::",_="about:blank",it=t=>({renderMarkdown:(e,n)=>(e.content??[]).map((r,o)=>n.renderChild(r,o)).filter(r=>r.trim()).join(t)}),k=()=>({renderMarkdown:(t,e)=>t.content?.length?e[jt](t,""):""}),Yt=()=>({renderMarkdown:(t,e)=>{const n=W(t.type??"");if(!n||!t.content?.length)return"";const r=t.content.map((o,a)=>e.renderChild(o,a).trim()).filter(Boolean);return[I(n.type,v(t)),...r,D].join(`
2
2
 
3
- `)}}),Lt=()=>({renderMarkdown:(e,t)=>{const n=e.attrs??{},r=x(e,"itemTitle",t),o=x(e,"itemBody",t),a=`### ${r}${C({icon:n.icon,meta:x(e,"itemMeta",t)||null,emphasis:n.emphasis===!0})}`;return o?`${a}
4
- ${o}`:a}}),Yt=()=>({renderMarkdown:(e,t)=>{const n=x(e,"statValue",t),r=x(e,"statNote",t),o=`### ${n}${C({label:x(e,"statLabel",t)||null})}`;return r?`${o}
5
- ${r}`:o}}),at={doc:ot(`
3
+ `)}}),Pt=()=>({renderMarkdown:(t,e)=>{const n=t.attrs??{},r=x(t,"itemTitle",e),o=x(t,"itemBody",e),a=`### ${r}${C({icon:n.icon,meta:x(t,"itemMeta",e)||null,emphasis:n.emphasis===!0})}`;return o?`${a}
4
+ ${o}`:a}}),Dt=()=>({renderMarkdown:(t,e)=>{const n=x(t,"statValue",e),r=x(t,"statNote",e),o=`### ${n}${C({label:x(t,"statLabel",e)||null})}`;return r?`${o}
5
+ ${r}`:o}}),ct={doc:it(`
6
6
 
7
- `),page:{renderMarkdown:(e,t)=>{const n=e.attrs??{},r=u=>{const p=(e.content??[]).find(m=>m.type===u);return p?T(p):""},o=(e.content??[]).find(u=>u.type==="pageAccent"),a=I("page",{eyebrow:r("pageEyebrow")||null,subtitle:r("pageSubtitle")||null,accent:n.accent,src:o?.attrs?.src??null,alt:o?.attrs?.alt??null,surface:n.surface,mode:n.mode}),s=r("pageTitle"),c=(e.content??[]).map((u,p)=>t.renderChild(u,p)).filter(u=>u.trim()).join(`
7
+ `),page:{renderMarkdown:(t,e)=>{const n=t.attrs??{},r=u=>{const p=(t.content??[]).find(m=>m.type===u);return p?A(p):""},o=(t.content??[]).find(u=>u.type==="pageAccent"),a=I("page",{eyebrow:r("pageEyebrow")||null,subtitle:r("pageSubtitle")||null,accent:n.accent,src:o?.attrs?.src??null,alt:o?.attrs?.alt??null,surface:n.surface,mode:n.mode}),s=r("pageTitle"),c=(t.content??[]).map((u,p)=>e.renderChild(u,p)).filter(u=>u.trim()).join(`
8
8
 
9
- `),l=e.attrs?.notes,i=l?`<!-- notes
9
+ `),l=t.attrs?.notes,i=l?`<!-- notes
10
10
  ${l}
11
11
  -->`:"";return[a,s?`# ${s}`:"",c,i].filter(Boolean).join(`
12
12
 
13
- `)}},layout:{renderMarkdown:(e,t)=>{const n=(e.content??[]).map((r,o)=>t.renderChild(r,o)).filter(r=>r.trim());return[I("layout",{layout:e.attrs?.layout}),...n,D].join(`
13
+ `)}},layout:{renderMarkdown:(t,e)=>{const n=(t.content??[]).map((r,o)=>e.renderChild(r,o)).filter(r=>r.trim());return[I("layout",{layout:t.attrs?.layout}),...n,D].join(`
14
14
 
15
- `)}},layoutRegion:{renderMarkdown:(e,t)=>{const n=(e.content??[]).map((r,o)=>t.renderChild(r,o)).filter(r=>r.trim());return[`::${e.attrs?.region}::`,...n].join(`
15
+ `)}},layoutRegion:{renderMarkdown:(t,e)=>{const n=(t.content??[]).map((r,o)=>e.renderChild(r,o)).filter(r=>r.trim());return[`::${t.attrs?.region}::`,...n].join(`
16
16
 
17
- `)}},pageAccent:{renderMarkdown:()=>""},pageEyebrow:{renderMarkdown:()=>""},pageTitle:{renderMarkdown:()=>""},pageSubtitle:{renderMarkdown:()=>""},quote:{markdownTokenName:"blockquote",parseMarkdown:(e,t)=>{const n=t.parseChildren(e.tokens??[]),r=n[n.length-1],o=r?.type==="paragraph"?T(r):"",a=n.length>1&&/^— /.test(o)?o.slice(2).trim():null,s=a?n.slice(0,-1):n,c=[];for(const l of s)c.length&&c.push({type:"hardBreak"}),c.push(...l.content??[]);return t.createNode("quote",null,[t.createNode("quoteText",null,c),...a?[t.createNode("attribution",null,[{type:"text",text:a}])]:[]])},renderMarkdown:(e,t)=>{const r=(e.content??[]).map((c,l)=>t.renderChild(c,l).trim()).filter(Boolean);if(!r.length)return"";const[o,a]=r;return[o,...a?["",`\u2014 ${a}`]:[]].map(c=>c?`> ${c}`:">").join(`
18
- `)}},quoteText:k(),attribution:k(),table:{renderMarkdown:(e,t)=>{const n=(e.content??[]).map((u,p)=>(u.content??[]).map((m,d)=>t.renderChild(m,d).trim().replace(/\|/g,"\\|")));if(!n.length)return"";const r=Math.max(...n.map(u=>u.length)),o=u=>[...u,...Array(r-u.length).fill("")],a=u=>`| ${o(u).join(" | ")} |`,[s,...c]=n,l=[a(s),`|${" --- |".repeat(r)}`,...c.map(a)].join(`
19
- `);return B(v(e))?`${I("table",v(e))}
20
- ${l}`:l}},tableRow:{renderMarkdown:()=>""},paragraph:{renderMarkdown:(e,t)=>{const n=e.content??[];if(!n.length)return"";const r=t.renderChildren(n,"");return B(v(e))?`${I("text",v(e))}
21
- ${r}`:r}},tableCell:k(),tableHeader:k(),chart:{renderMarkdown:(e,t)=>{const n=g=>e.content?.[g]?t.renderChild(e.content[g],g).trim():"",[r,o,a]=[n(0),n(1),n(2)],s=e.attrs??{},c=s.categories??[],l=s.series??[],i=g=>g.replace(/\|/g,"\\|"),u=g=>`| ${g.map(i).join(" | ")} |`,p=["",...l.map(g=>`${g.name??""}${C({as:g.as??null})}`)],m=c.map((g,f)=>u([g,...l.map(y=>{const $=y.values?.[f];return $==null?"":String($)})])),d=[u(p),`|${" --- |".repeat(p.length)}`,...m].join(`
22
- `);return[I("chart",{...v(e),stack:s.stack&&s.stack!=="none"?s.stack:null,title:r||null,axisX:a||null,axisY:o||null}),d,D].join(`
17
+ `)}},pageAccent:{renderMarkdown:()=>""},pageEyebrow:{renderMarkdown:()=>""},pageTitle:{renderMarkdown:()=>""},pageSubtitle:{renderMarkdown:()=>""},quote:{markdownTokenName:"blockquote",parseMarkdown:(t,e)=>{const n=e.parseChildren(t.tokens??[]),r=n[n.length-1],o=r?.type==="paragraph"?A(r):"",a=n.length>1&&/^— /.test(o)?o.slice(2).trim():null,s=a?n.slice(0,-1):n,c=[];for(const l of s)c.length&&c.push({type:"hardBreak"}),c.push(...l.content??[]);return e.createNode("quote",null,[e.createNode("quoteText",null,c),...a?[e.createNode("attribution",null,[{type:"text",text:a}])]:[]])},renderMarkdown:(t,e)=>{const r=(t.content??[]).map((c,l)=>e.renderChild(c,l).trim()).filter(Boolean);if(!r.length)return"";const[o,a]=r;return[o,...a?["",`\u2014 ${a}`]:[]].map(c=>c?`> ${c}`:">").join(`
18
+ `)}},quoteText:k(),attribution:k(),table:{renderMarkdown:(t,e)=>{const n=(t.content??[]).map((u,p)=>(u.content??[]).map((m,d)=>e.renderChild(m,d).trim().replace(/\|/g,"\\|")));if(!n.length)return"";const r=Math.max(...n.map(u=>u.length)),o=u=>[...u,...Array(r-u.length).fill("")],a=u=>`| ${o(u).join(" | ")} |`,[s,...c]=n,l=[a(s),`|${" --- |".repeat(r)}`,...c.map(a)].join(`
19
+ `);return B(v(t))?`${I("table",v(t))}
20
+ ${l}`:l}},tableRow:{renderMarkdown:()=>""},paragraph:{renderMarkdown:(t,e)=>{const n=t.content??[];if(!n.length)return"";const r=e.renderChildren(n,"");return B(v(t))?`${I("text",v(t))}
21
+ ${r}`:r}},tableCell:k(),tableHeader:k(),chart:{renderMarkdown:(t,e)=>{const n=g=>t.content?.[g]?e.renderChild(t.content[g],g).trim():"",[r,o,a]=[n(0),n(1),n(2)],s=t.attrs??{},c=s.categories??[],l=s.series??[],i=g=>g.replace(/\|/g,"\\|"),u=g=>`| ${g.map(i).join(" | ")} |`,p=["",...l.map(g=>`${g.name??""}${C({as:g.as??null})}`)],m=c.map((g,f)=>u([g,...l.map(y=>{const $=y.values?.[f];return $==null?"":String($)})])),d=[u(p),`|${" --- |".repeat(p.length)}`,...m].join(`
22
+ `);return[I("chart",{...v(t),stack:s.stack&&s.stack!=="none"?s.stack:null,title:r||null,axisX:a||null,axisY:o||null}),d,D].join(`
23
23
 
24
- `)}},diagram:{renderMarkdown:e=>{const t=String(e.attrs?.source??"").trim();return t?["```mermaid",t,"```"].join(`
25
- `):""}},chartTitle:k(),chartAxisX:k(),chartAxisY:k(),mediaItem:{renderMarkdown:(e,t,n)=>{const r=e.attrs??{},o=r.src??"",a=(r.alt??"").replace(/[[\]]/g,""),s=n?.parentType==="videoBlock"||!!o&&!!et(o),c=(s?o||_:`![${a}](${o||_})`)+C({alt:s&&a?a:null,caption:x(e,"mediaCaption",t)||null,focusX:r.focusX,focusY:r.focusY,zoom:r.zoom}),l=x(e,"mediaTitle",t),i=x(e,"mediaBody",t);return[c,l?`### ${l}`:"",i].filter(Boolean).join(`
26
- `)}},mediaTitle:k(),mediaBody:k(),mediaCaption:k(),itemMeta:k(),itemTitle:k(),itemBody:ot(`
24
+ `)}},diagram:{renderMarkdown:t=>{const e=String(t.attrs?.source??"").trim();return e?["```mermaid",e,"```"].join(`
25
+ `):""}},chartTitle:k(),chartAxisX:k(),chartAxisY:k(),mediaItem:{renderMarkdown:(t,e,n)=>{const r=t.attrs??{},o=r.src??"",a=(r.alt??"").replace(/[[\]]/g,""),s=n?.parentType==="videoBlock"||!!o&&!!rt(o),c=(s?o||_:`![${a}](${o||_})`)+C({alt:s&&a?a:null,caption:x(t,"mediaCaption",e)||null,focusX:r.focusX,focusY:r.focusY,zoom:r.zoom}),l=x(t,"mediaTitle",e),i=x(t,"mediaBody",e);return[c,l?`### ${l}`:"",i].filter(Boolean).join(`
26
+ `)}},mediaTitle:k(),mediaBody:k(),mediaCaption:k(),itemMeta:k(),itemTitle:k(),itemBody:it(`
27
27
 
28
- `),statLabel:k(),statValue:k(),statNote:k(),item:Lt(),stat:Yt()};for(const e of At)e.archetype==="prose"||e.archetype==="table"||(at[e.node]??=jt());function Pt(){return[...Ct().map(t=>{const n=at[t.name];return n?t.extend(n):t}),xt]}const it=()=>new St({extensions:Pt()}),Dt=new Set(It);function R(e){const t=e.type??"";if(t==="tableCell"||t==="tableHeader"){const o=(e.content??[]).flatMap(a=>a.content??[]);return{...e,attrs:{colspan:1,rowspan:1},content:o.length?o:void 0}}if(t==="listItem"||t==="taskItem"){const o=(e.content??[]).map(R).filter(Boolean),a=o.filter(c=>c.type==="paragraph"),s=o.filter(c=>c.type!=="paragraph");if(a.length>1){const c=[];for(const l of a)c.length&&c.push({type:"hardBreak"}),c.push(...l.content??[]);a.splice(1,a.length-1),a[0]={...a[0],content:c}}return{...e,attrs:{...e.attrs},content:[...a,...s]}}const n=(e.content??[]).map(R).filter(Boolean),r=Dt.has(t)?{...e,attrs:{...e.attrs,id:w(t)}}:e;return n.length?{...r,content:n}:{...r,content:void 0}}const ct=/^::\s*([a-zA-Z0-9-]+)\s*(?::\s*(.*?))?\s*::$/,K=/\s*\{([^{}]*)\}\s*$/,Kt=/([A-Za-z][\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+))|([A-Za-z][\w-]*)/g;function j(e,t={}){const n={},r=e.trim();if(t.positional!==!1&&r&&!r.includes("=")&&!/\s/.test(r))return{positional:r,attrs:n};for(const o of r.matchAll(Kt))o[5]?n[o[5]]=!0:n[o[1]]=o[2]??o[3]??o[4]??"";return{positional:null,attrs:n}}const Xt=(e,t)=>{const{positional:n,attrs:r}=j(t),o=Tt(e),a=e==="layout"||F.includes(e),s=a?"layout":e==="page"?"page":e==="end"?"end":o?"block":"region";return{kind:s,name:e,attrs:r,positional:a&&e!=="layout"?e:n,meta:s==="block"?o:void 0}},lt=["eyebrow","subtitle","accent","src","alt","surface","mode"],L=[S,A,E,N,O,M],ut=["stack","title","axisX","axisY"],X=["icon","meta","emphasis"],pt=["label","note","value"],ft=["caption","alt","focusX","focusY","zoom"],Ht=/^https?:\/\/\S+$/,Gt=new Set(["paragraph","bulletList","orderedList"]);function dt(e){const t=[[]];for(const n of e.content??[]){if(n.type==="hardBreak"){t.push([]);continue}if(n.type!=="text"||n.text===void 0){t[t.length-1].push(n);continue}n.text.split(`
29
- `).forEach((o,a)=>{a>0&&t.push([]),o&&t[t.length-1].push({...n,text:o})})}return t}function Y(e,t){const n=[];t.forEach((o,a)=>{if(a>0){const s=n[n.length-1],c=o[0];if(s?.type==="text"&&c?.type==="text"&&JSON.stringify(s.marks??[])===JSON.stringify(c.marks??[])){n[n.length-1]={...s,text:`${s.text}
30
- ${c.text}`},n.push(...o.slice(1));return}n.push({type:"hardBreak"})}n.push(...o)});const r=n.filter(o=>o.type!=="text"||o.text);return r.length?{...e,content:r}:null}function Ut(e){if(e.type!=="paragraph")return null;const t=dt(e),n=a=>a.length&&a.every(s=>s.type==="text")?a.map(s=>s.text??"").join("").trim():null,r=t.findIndex(a=>{const s=n(a);return s!==null&&ct.test(s)});if(r<0)return null;const o=ct.exec(n(t[r]));return{before:Y(e,t.slice(0,r)),marker:Xt(o[1],o[2]??""),rest:Y(e,t.slice(r+1))}}function H(e){const t=[];let n=e;for(;n;){const r=Ut(n);if(!r){t.push({block:n});break}r.before&&t.push({block:r.before}),t.push({marker:r.marker}),n=r.rest}return t}const b=e=>e.join(", ");function G(e){const t=[...e.content??[]],n=t[t.length-1];if(n?.type==="text"&&n.text){const r=K.exec(n.text);if(r){const o=n.text.slice(0,r.index).replace(/\s+$/,"");return o?t[t.length-1]={...n,text:o}:t.pop(),{title:t,attrs:j(r[1],{positional:!1}).attrs}}}return{title:t,attrs:{}}}function mt(e,t){const n=[];let r=0;for(;r<e.length;){const o=e[r];if(o.marker?.kind!=="block"){n.push(o),r+=1;continue}const{marker:a}=o,s=a.meta,c=`"::${a.name}::"`,l={id:w(s.node),v:s.version};for(const[p,m]of Object.entries(a.attrs)){if(s.archetype==="chart"&&ut.includes(p))continue;if(!L.includes(p)){const h=s.archetype==="chart"?[...L,...ut]:L,g=X.includes(p)?` "${p}" belongs to an item, in the braces after its heading: \`### Title {${p}=\u2026}\`.`:"";return{pieces:n,problem:`${c} has no attribute "${p}". A block marker takes: ${b(h)}.${g}`}}const d=String(m);if(p===S){const h=s.variantSlots.length?Bt(s.variantSlots,t,{core:!0}):[Mt[0]];if(!h.includes(d))return{pieces:n,problem:`${c} has no style "${d}". Its styles: ${b(h)}.`};l[S]=d}else if(p===A){const h=s.arrangements.map(f=>f.name),g=(s.figures??[]).map(f=>f.name);if(h.includes(d))l[A]=d;else if(g.includes(d))l[P]=d;else return{pieces:n,problem:`${c} has no shape "${d}". Its shapes: ${b(h)||"none"}${g.length?`; its figures: ${b(g)}`:""}.`}}else if(p===E){if(!s.slots.includes("icon"))return{pieces:n,problem:`${c} draws no icon, so "${p}" means nothing on it.`};if(!J.includes(d))return{pieces:n,problem:`${c} has no icon position "${d}". Positions: ${b(J)}.`};l[E]=d}else if(p===N){if(!s.marks.length)return{pieces:n,problem:`${c} marks nothing, so "${p}" means nothing on it.`};if(!s.marks.includes(d))return{pieces:n,problem:`${c} has no mark "${d}". Its marks: ${b(s.marks)}.`};l[N]=d}else if(p===O){const h=Number(d);if(Number.isNaN(h)||h<Q||h>100)return{pieces:n,problem:`${c} has width "${d}"; a width is a share of the frame, ${Q} to 100.`};l[O]=h}else if(p===M){if(!Z.includes(d))return{pieces:n,problem:`${c} has no place "${d}". Places: ${b(Z)}.`};l[M]=d}}const i=[];for(r+=1;r<e.length;){const p=e[r];if(p.marker){p.marker.kind==="end"&&(r+=1);break}i.push(p.block),r+=1}const u=s.archetype==="items"?Vt(s,c,l,i):s.archetype==="stats"?qt(s,c,l,i):s.archetype==="media"?zt(s,c,l,i):s.archetype==="chart"?Ft(s,c,a.attrs,l,i):Wt(s,c,l,i);if(typeof u=="string")return{pieces:n,problem:u};n.push(...u.map(p=>({block:p})))}return{pieces:n,problem:null}}const ht=e=>e.flatMap((t,n)=>[...n?[{type:"hardBreak"}]:[],...t.content??[]]);function U(e){const t=[...e],n=t[t.length-1];if(n?.type==="text"&&n.text){const r=K.exec(n.text);if(r){const o=n.text.slice(0,r.index).replace(/\s+$/,"");return o?t[t.length-1]={...n,text:o}:t.pop(),{rest:t,attrs:j(r[1],{positional:!1}).attrs}}}return{rest:t,attrs:{}}}const V=(e,t,n,r)=>`${t} under ${e} has no attribute "${n}". It takes: ${b(r)}.`;function Vt(e,t,n,r){const o=[];let a=null;const s=()=>{if(!a)return null;const i={id:w("item")};let u=null;for(const[m,d]of Object.entries(a.attrs)){if(!X.includes(m))return V(t,"an item",m,X);if(m==="icon"){const h=Et(String(d));if(!h){const g=vt(String(d));return`an item under ${t} asks for icon "${String(d)}", which does not exist${g?` \u2014 did you mean "${g}"?`:"."}`}i.icon=h}else if(m==="emphasis"){if(d!==!0&&d!=="true")return`an item under ${t} gives emphasis "${String(d)}"; it is a flag \u2014 write {emphasis} or leave it out.`;i.emphasis=!0}else u=String(d)}const p=[];return u&&p.push({type:"itemMeta",content:[{type:"text",text:u}]}),a.title.length?(p.push({type:"itemTitle",content:a.title}),a.body.length&&p.push({type:"itemBody",content:a.body}),o.push({type:"item",attrs:i,content:p}),a=null,null):`an item under ${t} has no title \u2014 a heading that is only its braces.`};for(const i of r)if(i.type==="heading"){const u=s();if(u)return u;a={...G(i),body:[]}}else if(a)if(Gt.has(i.type??""))a.body.push(i);else return`an item under ${t} holds a ${i.type} block, which an item's body cannot: paragraphs and lists only.`;else return`${t} opens with something that is not an item: an item is a HEADING, and the lines under it are its body.`;const c=s();if(c)return c;if(!o.length)return`${t} holds no item. An item is a heading under the marker, with its text beneath it.`;const l=n[P];if(l){const[i,u]=(e.figures??[]).find(p=>p.name===l).countRange;if(o.length<i||o.length>u)return`${t} wears the figure "${l}", which holds ${i===u?i:`${i}\u2013${u}`} items and was given ${o.length}. A figure's range is a limit.`}return[{type:e.node,attrs:n,content:o}]}function qt(e,t,n,r){const o=[];let a=null;const s=()=>{if(!a)return null;const l=[];let i=ht(a.note),u=a.value,p=null;for(const[m,d]of Object.entries(a.attrs)){if(!pt.includes(m))return V(t,"a figure",m,pt);m==="label"?p=String(d):m==="note"?i=[{type:"text",text:String(d)}]:(p===null&&a.value.length&&(p=a.value.map(h=>h.text??"").join("")),u=[{type:"text",text:String(d)}])}return p&&l.push({type:"statLabel",content:[{type:"text",text:p}]}),u.length?(l.push({type:"statValue",content:u}),i.length&&l.push({type:"statNote",content:i}),o.push({type:"stat",attrs:{id:w("stat")},content:l}),a=null,null):`a figure under ${t} has no value \u2014 a heading that is only its braces.`};for(const l of r)if(l.type==="heading"){const i=s();if(i)return i;const{title:u,attrs:p}=G(l);a={value:u,attrs:p,note:[]}}else if(a){if(l.type!=="paragraph")return`a figure under ${t} holds a ${l.type} block; a note is a line of text.`;a.note.push(l)}else return`${t} opens with something that is not a figure: a figure is a HEADING holding its value, with its note beneath.`;const c=s();return c||(o.length?[{type:e.node,attrs:n,content:o}]:`${t} holds no figure. A figure is a heading under the marker whose text is the value.`)}function zt(e,t,n,r){const o=e.type==="youtube",a=[];let s=null;const c=()=>{if(!s)return null;if(s.src===_&&(s.src=""),s.src){const h=tt(s.src,o?"video":"image");if(h)return`under ${t}: ${h}`;if(o&&!et(s.src))return`${t} takes a YouTube address line \u2014 'https://www.youtube.com/watch?v=\u2026' or 'https://youtu.be/\u2026' \u2014 and "${s.src}" is not one.`}const m={id:w("mediaItem"),src:s.src||null,alt:s.alt},d=[];for(const[h,g]of Object.entries(s.attrs)){if(!ft.includes(h))return V(t,"a picture",h,ft);if(h==="alt")m.alt=String(g);else if(h==="caption")d.push({type:"mediaCaption",content:[{type:"text",text:String(g)}]});else{const f=Number(g);if(Number.isNaN(f))return`a picture under ${t} gives ${h} "${String(g)}", which is not a number.`;m[h]=f}}return s.title?.length&&d.push({type:"mediaTitle",content:s.title}),s.body.length&&d.push({type:"mediaBody",content:ht(s.body)}),a.push({type:"mediaItem",attrs:m,content:d}),s=null,null},l=m=>Ht.test(m)||m===_,i=m=>m[0]?.type==="inlineImage"||m.length>0&&m.every(d=>d.type==="text")&&l(U(m).rest.map(d=>d.text??"").join("").trim()),u=r.flatMap(m=>{if(m.type!=="paragraph")return[m];const d=dt(m),h=[];let g=[];for(const y of d){if(i(y)&&g.length){const $=Y(m,g);$&&h.push($),g=[]}g.push(y)}const f=Y(m,g);return f&&h.push(f),h});for(const m of u){const d=m.type==="paragraph"?m.content?.[0]:m.type==="inlineImage"?m:void 0,h=d?.type==="inlineImage"?d:null,g=d?.type==="text"&&m.content?.every(f=>f.type==="text")?U(m.content??[]).rest.map(f=>f.text??"").join("").trim():"";if(h||l(g)){const f=c();if(f)return f;if(h&&o)return`${t} takes a video ADDRESS on a line of its own, not a picture.`;if(!h&&!o)return`${t} takes a picture line \u2014 ![alt](https://\u2026) \u2014 not a bare address; a bare address is ::youtube::'s.`;const{attrs:y}=U(m.type==="inlineImage"?[]:(m.content??[]).slice(h?1:0));s={src:h?String(h.attrs?.src??""):g,alt:h?h.attrs?.alt??null:null,attrs:y,title:null,body:[]}}else if(s)if(m.type==="heading"){if(s.title)return`a picture under ${t} has two headings; one is its title, the paragraphs under it are its words.`;s.title=G(m).title}else{if(m.type!=="paragraph")return`a picture under ${t} holds a ${m.type} block; its words are paragraphs.`;s.body.push(m)}else return`${t} opens with something that is not a ${o?"video address":"picture"}: ${o?"a bare YouTube address":"![alt](https://\u2026)"} on a line of its own starts each entry.`}const p=c();return p||(a.length?[{type:e.node,attrs:n,content:a}]:`${t} holds no ${o?"video":"picture"}.`)}function Ft(e,t,n,r,o){const a=o[0];if(!a||a.type!=="table"||o.length!==1)return`${t} must be followed by exactly ONE markdown table: the header row names the series, each row after it is a category and its values.`;const s=(a.content??[]).map(f=>(f.content??[]).map(y=>T(y).trim()));if(s.length<2)return`${t}'s table needs a header row and at least one row of values.`;const[c,...l]=s,i=l.map(f=>f[0]??""),u=c.slice(1).map((f,y)=>{const $=K.exec(f),q=$?j($[1],{positional:!1}).attrs.as:void 0;return{name:$?f.slice(0,$.index).trim():f,...q?{as:String(q)}:{},values:l.map(wt=>(wt[y+1]??"").trim())}});for(const f of u)for(const[y,$]of f.values.entries())if($!==""&&Number.isNaN(Number($)))return`${t}: the cell "${$}" (row "${i[y]}", column "${f.name}") is not a number. A gap is an empty cell.`;const p={categories:i,series:u.map(f=>({name:f.name,..."as"in f?{as:f.as}:{},values:f.values.map(y=>y===""?null:Number(y))}))},m=r[A],d=Nt(p,m);if(d)return`${t}: ${d}`;const h={};for(const[f,y]of Object.entries(n))if(!L.includes(f))if(f==="stack"){if(!nt.includes(String(y)))return`${t} has no stack "${String(y)}". Stacks: ${b(nt)}.`;r.stack=String(y)}else h[f]=String(y);const g=(f,y)=>y?{type:f,content:[{type:"text",text:y}]}:{type:f};return[{type:e.node,attrs:{...r,categories:p.categories,series:p.series},content:[g("chartTitle",h.title),g("chartAxisY",h.axisY),g("chartAxisX",h.axisX)]}]}function Wt(e,t,n,r){const{id:o,v:a,...s}=n;if(!e.arrangements.length&&!e.variantSlots.length&&e.type!=="diagram")return`${t} sets nothing: a ${e.type} has no style and no shape to name. Write it as ordinary markdown.`;if(!Object.keys(s).length)return`${t} names no attribute. It takes: ${b([...e.variantSlots.length?[S]:[],...e.arrangements.length?[A]:[]])}.`;const[c,...l]=r,i=e.type==="diagram"&&c?.type==="codeBlock"&&String(c.attrs?.language??"").toLowerCase()==="mermaid";return!c||c.type!==e.node&&!i?`${t} must sit directly over a ${e.type} block${c?`, and what follows it is a ${c.type}`:""}.`:[{...c,attrs:{...c.attrs??{},...s}},...l]}function Zt(e){for(const[t,n]of Object.entries(e.attrs)){if(!lt.includes(t))return`"::page::" has no attribute "${t}". A page line takes: ${b(lt)}.`;const r=String(n);if(t==="accent"&&!rt.includes(r))return`"::page::" has no accent "${r}". Accents: ${b(rt)}.`;if(t==="surface"&&!st.includes(r))return`"::page::" has no surface "${r}". Surfaces: ${b(st)}.${["dark","light"].includes(r)?` A ${r} page is mode=${r}.`:""}`;if(t==="mode"&&!["light","dark"].includes(r))return`"::page::" has no mode "${r}". Modes: light, dark.`;if(t==="src"){const o=tt(r,"image");if(o)return`"::page::" src: ${o}`;if(!e.attrs.accent)return'"::page::" names a src but no accent to put it in. Add accent=lead, trail, top or full.'}}return e.positional?`"::page::" takes key=value pairs, not "${e.positional}".`:null}function de(e,t={}){const n=mt(e.flatMap(H),t.theme);if(n.problem)return n.problem;let r=null,o=new Set;for(const a of n.pieces){const s=a.marker;if(!s)continue;if(s.kind==="page"){if(t.pageless)return'"::page::" describes a page of its own, and this import lands on a page that exists: use set_page for its look.';const l=Zt(s);if(l)return l;continue}if(s.kind==="layout"){const l=s.positional??String(s.attrs.layout??""),i=W(l);if(!i)return`unknown layout "${l}". Layouts: ${F.join(", ")}`;r=i,o=new Set;continue}if(s.kind==="end"){if(!r)return'"::end::" closes nothing: no block or layout marker is open above it.';r=null;continue}if(!r)return`"::${s.name}::" has no "::layout: \u2026::" above it, so there is no region by that name. A region marker only means something inside a layout.`;const c=r.regions.map(l=>l.name);if(!c.includes(s.name))return`"${r.name}" has no region "${s.name}". Its regions: ${c.join(", ")}`;if(o.has(s.name))return`"::${s.name}::" appears twice under one "${r.name}". Each region is written once, and everything until the next marker belongs to it.`;o.add(s.name)}return null}function gt(e,t){if(!e.some(i=>i.marker))return{blocks:e.map(i=>i.block),page:null};const n=[];let r=null,o=null,a=null,s=null;const c=()=>{!o||!a||(n.push({type:"layout",attrs:{id:w("layout"),layout:o.name},content:o.regions.map(i=>({type:"layoutRegion",attrs:{id:w("layoutRegion"),region:i.name},content:a.get(i.name)?.length?a.get(i.name):[{type:"paragraph"}]}))}),o=null,a=null,s=null)},l=i=>{o&&a&&s?a.set(s,[...a.get(s)??[],i]):(o&&a&&c(),n.push(i))};for(const i of mt(e,t).pieces){if(i.block){l(i.block);continue}const u=i.marker;if(u.kind==="page"){r??=u.attrs;continue}if(u.kind==="end"){c();continue}u.kind==="layout"?(c(),o=W(u.positional??String(u.attrs.layout??"")),a=new Map):s=u.name}return c(),{blocks:n,page:r}}const yt=`<!-- notes
31
- `,$t=`
32
- -->`;function Jt(e){let t;const n=e.filter(r=>{if(r.type!=="paragraph")return!0;const o=r.content;if(o?.length!==1||o[0].type!=="text")return!0;const a=o[0].text??"";return!a.startsWith(yt)||!a.endsWith($t)?!0:(t===void 0&&(t=a.slice(yt.length,-$t.length)),!1)});return t===void 0?{blocks:e}:{blocks:n,notes:t}}function me(e,t=1,n){const o=(it().parse(e).content??[]).map(R).filter(Boolean),a=[];let s=null;const c=(i,u)=>{s={page:{blocks:[]},pieces:[],opened:i,attrs:u},a.push(s)},l=()=>s&&!s.pieces.length&&!s.attrs;for(const i of o.flatMap(H)){if(i.marker?.kind==="page"){if(l()){s.attrs=i.marker.attrs,s.opened==="start"&&(s.opened="marker");continue}c("marker",i.marker.attrs);continue}const u=i.block,p=u?.type==="heading"&&u.attrs?.level===t;if(p&&s&&!s.page.title&&!s.pieces.length&&s.opened==="marker"){s.page.title=T(u);continue}if((p||!s)&&(c(p?"heading":"start",null),p)){s.page.title=T(u);continue}s.pieces.push(i)}for(const i of a){const u=i.pieces.filter(y=>y.block).map(y=>y.block),{blocks:p,notes:m}=Jt(u),d=new Set(p),h=i.pieces.filter(y=>y.marker||d.has(y.block)),g=gt(h,n);i.page.blocks=bt(g.blocks),m!==void 0&&(i.page.notes=m);const f=i.attrs??g.page;f&&(f.eyebrow&&(i.page.eyebrow=String(f.eyebrow)),f.subtitle&&(i.page.subtitle=String(f.subtitle)),f.accent&&(i.page.accent=String(f.accent)),f.src&&(i.page.accentSrc=String(f.src)),f.alt&&(i.page.accentAlt=String(f.alt)),f.surface&&(i.page.surface=String(f.surface)),f.mode&&(i.page.mode=String(f.mode)))}return a.map(i=>i.page)}const he=(e,t)=>bt(gt(Qt(e).flatMap(H),t).blocks),Qt=e=>(it().parse(e).content??[]).map(R).filter(Boolean),kt=e=>te(ee(e)),bt=e=>e.map(t=>t.type!=="layout"?kt(t):{...t,content:(t.content??[]).map(n=>({...n,content:(n.content??[]).map(kt)}))});function te(e){if(e.type!=="codeBlock"||String(e.attrs?.language??"").trim().toLowerCase()!=="mermaid")return e;const t=T(e).trim();if(!t)return e;const{width:n,place:r}=e.attrs??{};return{type:"diagram",attrs:{id:w("diagram"),source:t,...n?{width:n}:{},...r?{place:r}:{}}}}function ee(e){const t=e.type==="inlineImage"?e:(e.content??[]).length===1?e.content[0]:null;return e.type!=="paragraph"&&e.type!=="inlineImage"?e:t?.type!=="inlineImage"||!t.attrs?.src?e.type==="inlineImage"?{type:"paragraph",attrs:{id:w("paragraph")},content:[e]}:e:{type:"imageBlock",attrs:{id:w("imageBlock")},content:[{type:"mediaItem",attrs:{id:w("mediaItem"),src:t.attrs.src,alt:t.attrs.alt??null}}]}}function T(e){return e.type==="text"?e.text??"":(e.content??[]).map(T).join("")}export{at as MARKDOWN_SPECS,he as blocksFromMarkdown,Qt as blocksOnly,de as directiveProblem,Pt as markdownExtensions,it as markdownManager,me as pagesFromMarkdown};
28
+ `),statLabel:k(),statValue:k(),statNote:k(),item:Pt(),stat:Dt()};for(const t of It)t.archetype==="prose"||t.archetype==="table"||(ct[t.node]??=Yt());function Kt(){return[...Rt().map(e=>{const n=ct[e.name];return n?e.extend(n):e}),At]}const lt=()=>new Tt({extensions:Kt()}),Xt=new Set(Et);function R(t){const e=t.type??"";if(e==="tableCell"||e==="tableHeader"){const o=(t.content??[]).flatMap(a=>a.content??[]);return{...t,attrs:{colspan:1,rowspan:1},content:o.length?o:void 0}}if(e==="listItem"||e==="taskItem"){const o=(t.content??[]).map(R).filter(Boolean),a=o.filter(c=>c.type==="paragraph"),s=o.filter(c=>c.type!=="paragraph");if(a.length>1){const c=[];for(const l of a)c.length&&c.push({type:"hardBreak"}),c.push(...l.content??[]);a.splice(1,a.length-1),a[0]={...a[0],content:c}}return{...t,attrs:{...t.attrs},content:[...a,...s]}}const n=(t.content??[]).map(R).filter(Boolean),r=Xt.has(e)?{...t,attrs:{...t.attrs,id:w(e)}}:t;return n.length?{...r,content:n}:{...r,content:void 0}}const K=/^::\s*([a-zA-Z0-9-]+)\s*(?::\s*(.*?))?\s*::$/,X=/\s*\{([^{}]*)\}\s*$/,Ht=/([A-Za-z][\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+))|([A-Za-z][\w-]*)/g;function j(t,e={}){const n={},r=t.trim();if(e.positional!==!1&&r&&!r.includes("=")&&!/\s/.test(r))return{positional:r,attrs:n};for(const o of r.matchAll(Ht))o[5]?n[o[5]]=!0:n[o[1]]=o[2]??o[3]??o[4]??"";return{positional:null,attrs:n}}const ut=(t,e)=>{const{positional:n,attrs:r}=j(e),o=vt(t),a=t==="layout"||Z.includes(t),s=a?"layout":t==="page"?"page":t==="end"?"end":o?"block":"region";return{kind:s,name:t,attrs:r,positional:a&&t!=="layout"?t:n,meta:s==="block"?o:void 0}},pt=["eyebrow","subtitle","accent","src","alt","surface","mode"],L=[S,T,E,N,O,M],ft=["stack","title","axisX","axisY"],H=["icon","meta","emphasis"],dt=["label","note","value"],mt=["caption","alt","focusX","focusY","zoom"],Ut=/^https?:\/\/\S+$/,Gt=new Set(["paragraph","bulletList","orderedList"]);function ht(t){const e=[[]];for(const n of t.content??[]){if(n.type==="hardBreak"){e.push([]);continue}if(n.type!=="text"||n.text===void 0){e[e.length-1].push(n);continue}n.text.split(`
29
+ `).forEach((o,a)=>{a>0&&e.push([]),o&&e[e.length-1].push({...n,text:o})})}return e}function Y(t,e){const n=[];e.forEach((o,a)=>{if(a>0){const s=n[n.length-1],c=o[0];if(s?.type==="text"&&c?.type==="text"&&JSON.stringify(s.marks??[])===JSON.stringify(c.marks??[])){n[n.length-1]={...s,text:`${s.text}
30
+ ${c.text}`},n.push(...o.slice(1));return}n.push({type:"hardBreak"})}n.push(...o)});const r=n.filter(o=>o.type!=="text"||o.text);return r.length?{...t,content:r}:null}function Vt(t){if(t.type!=="paragraph")return null;const e=ht(t),n=a=>a.length&&a.every(s=>s.type==="text")?a.map(s=>s.text??"").join("").trim():null,r=e.findIndex(a=>{const s=n(a);return s!==null&&K.test(s)});if(r<0)return null;const o=K.exec(n(e[r]));return{before:Y(t,e.slice(0,r)),marker:ut(o[1],o[2]??""),rest:Y(t,e.slice(r+1))}}const U=t=>t.type==="text"?t.text??"":(t.content??[]).map(U).join("");function qt(t){const e=[...t.content??[]],n=[];for(;e.length>1;){const r=e[e.length-1].content??[],o=U(r[0]??{}).trim(),a=K.exec(o);if(!a||r.slice(1).some(s=>U(s).trim()))break;n.unshift(ut(a[1],a[2]??"")),e.pop()}return{table:n.length?{...t,content:e}:t,markers:n}}function G(t){const e=[];if(t.type==="table"){const r=qt(t);return[{block:r.table},...r.markers.map(o=>({marker:o}))]}let n=t;for(;n;){const r=Vt(n);if(!r){e.push({block:n});break}r.before&&e.push({block:r.before}),e.push({marker:r.marker}),n=r.rest}return e}const b=t=>t.join(", ");function V(t){const e=[...t.content??[]],n=e[e.length-1];if(n?.type==="text"&&n.text){const r=X.exec(n.text);if(r){const o=n.text.slice(0,r.index).replace(/\s+$/,"");return o?e[e.length-1]={...n,text:o}:e.pop(),{title:e,attrs:j(r[1],{positional:!1}).attrs}}}return{title:e,attrs:{}}}function gt(t,e){const n=[];let r=0;for(;r<t.length;){const o=t[r];if(o.marker?.kind!=="block"){n.push(o),r+=1;continue}const{marker:a}=o,s=a.meta,c=`"::${a.name}::"`,l={id:w(s.node),v:s.version};for(const[p,m]of Object.entries(a.attrs)){if(s.archetype==="chart"&&ft.includes(p))continue;if(!L.includes(p)){const h=s.archetype==="chart"?[...L,...ft]:L,g=H.includes(p)?` "${p}" belongs to an item, in the braces after its heading: \`### Title {${p}=\u2026}\`.`:"";return{pieces:n,problem:`${c} has no attribute "${p}". A block marker takes: ${b(h)}.${g}`}}const d=String(m);if(p===S){const h=s.variantSlots.length?_t(s.variantSlots,e,{core:!0}):[Bt[0]];if(!h.includes(d))return{pieces:n,problem:`${c} has no style "${d}". Its styles: ${b(h)}.`};l[S]=d}else if(p===T){const h=s.arrangements.map(f=>f.name),g=(s.figures??[]).map(f=>f.name);if(h.includes(d))l[T]=d;else if(g.includes(d))l[P]=d;else return{pieces:n,problem:`${c} has no shape "${d}". Its shapes: ${b(h)||"none"}${g.length?`; its figures: ${b(g)}`:""}.`}}else if(p===E){if(!s.slots.includes("icon"))return{pieces:n,problem:`${c} draws no icon, so "${p}" means nothing on it.`};if(!tt.includes(d))return{pieces:n,problem:`${c} has no icon position "${d}". Positions: ${b(tt)}.`};l[E]=d}else if(p===N){if(!s.marks.length)return{pieces:n,problem:`${c} marks nothing, so "${p}" means nothing on it.`};if(!s.marks.includes(d))return{pieces:n,problem:`${c} has no mark "${d}". Its marks: ${b(s.marks)}.`};l[N]=d}else if(p===O){const h=Number(d);if(Number.isNaN(h)||h<et||h>100)return{pieces:n,problem:`${c} has width "${d}"; a width is a share of the frame, ${et} to 100.`};l[O]=h}else if(p===M){if(!Q.includes(d))return{pieces:n,problem:`${c} has no place "${d}". Places: ${b(Q)}.`};l[M]=d}}const i=[];for(r+=1;r<t.length;){const p=t[r];if(p.marker){p.marker.kind==="end"&&(r+=1);break}i.push(p.block),r+=1}const u=s.archetype==="items"?zt(s,c,l,i):s.archetype==="stats"?Ft(s,c,l,i):s.archetype==="media"?Wt(s,c,l,i):s.archetype==="chart"?Zt(s,c,a.attrs,l,i):Jt(s,c,l,i);if(typeof u=="string")return{pieces:n,problem:u};n.push(...u.map(p=>({block:p})))}return{pieces:n,problem:null}}const yt=t=>t.flatMap((e,n)=>[...n?[{type:"hardBreak"}]:[],...e.content??[]]);function q(t){const e=[...t],n=e[e.length-1];if(n?.type==="text"&&n.text){const r=X.exec(n.text);if(r){const o=n.text.slice(0,r.index).replace(/\s+$/,"");return o?e[e.length-1]={...n,text:o}:e.pop(),{rest:e,attrs:j(r[1],{positional:!1}).attrs}}}return{rest:e,attrs:{}}}const z=(t,e,n,r)=>`${e} under ${t} has no attribute "${n}". It takes: ${b(r)}.`;function zt(t,e,n,r){const o=[];let a=null;const s=()=>{if(!a)return null;const i={id:w("item")};let u=null;for(const[m,d]of Object.entries(a.attrs)){if(!H.includes(m))return z(e,"an item",m,H);if(m==="icon"){const h=Mt(String(d));if(!h){const g=Nt(String(d));return`an item under ${e} asks for icon "${String(d)}", which does not exist${g?` \u2014 did you mean "${g}"?`:"."}`}i.icon=h}else if(m==="emphasis"){if(d!==!0&&d!=="true")return`an item under ${e} gives emphasis "${String(d)}"; it is a flag \u2014 write {emphasis} or leave it out.`;i.emphasis=!0}else u=String(d)}const p=[];return u&&p.push({type:"itemMeta",content:[{type:"text",text:u}]}),a.title.length?(p.push({type:"itemTitle",content:a.title}),a.body.length&&p.push({type:"itemBody",content:a.body}),o.push({type:"item",attrs:i,content:p}),a=null,null):`an item under ${e} has no title \u2014 a heading that is only its braces.`};for(const i of r)if(i.type==="heading"){const u=s();if(u)return u;a={...V(i),body:[]}}else if(a)if(Gt.has(i.type??""))a.body.push(i);else return`an item under ${e} holds a ${i.type} block, which an item's body cannot: paragraphs and lists only.`;else return`${e} opens with something that is not an item: an item is a HEADING, and the lines under it are its body.`;const c=s();if(c)return c;if(!o.length)return`${e} holds no item. An item is a heading under the marker, with its text beneath it.`;const l=n[P];if(l){const[i,u]=(t.figures??[]).find(p=>p.name===l).countRange;if(o.length<i||o.length>u)return`${e} wears the figure "${l}", which holds ${i===u?i:`${i}\u2013${u}`} items and was given ${o.length}. A figure's range is a limit.`}return[{type:t.node,attrs:n,content:o}]}function Ft(t,e,n,r){const o=[];let a=null;const s=()=>{if(!a)return null;const l=[];let i=yt(a.note),u=a.value,p=null;for(const[m,d]of Object.entries(a.attrs)){if(!dt.includes(m))return z(e,"a figure",m,dt);m==="label"?p=String(d):m==="note"?i=[{type:"text",text:String(d)}]:(p===null&&a.value.length&&(p=a.value.map(h=>h.text??"").join("")),u=[{type:"text",text:String(d)}])}return p&&l.push({type:"statLabel",content:[{type:"text",text:p}]}),u.length?(l.push({type:"statValue",content:u}),i.length&&l.push({type:"statNote",content:i}),o.push({type:"stat",attrs:{id:w("stat")},content:l}),a=null,null):`a figure under ${e} has no value \u2014 a heading that is only its braces.`};for(const l of r)if(l.type==="heading"){const i=s();if(i)return i;const{title:u,attrs:p}=V(l);a={value:u,attrs:p,note:[]}}else if(a){if(l.type!=="paragraph")return`a figure under ${e} holds a ${l.type} block; a note is a line of text.`;a.note.push(l)}else return`${e} opens with something that is not a figure: a figure is a HEADING holding its value, with its note beneath.`;const c=s();return c||(o.length?[{type:t.node,attrs:n,content:o}]:`${e} holds no figure. A figure is a heading under the marker whose text is the value.`)}function Wt(t,e,n,r){const o=t.type==="youtube",a=[];let s=null;const c=()=>{if(!s)return null;if(s.src===_&&(s.src=""),s.src){const h=nt(s.src,o?"video":"image");if(h)return`under ${e}: ${h}`;if(o&&!rt(s.src))return`${e} takes a YouTube address line \u2014 'https://www.youtube.com/watch?v=\u2026' or 'https://youtu.be/\u2026' \u2014 and "${s.src}" is not one.`}const m={id:w("mediaItem"),src:s.src||null,alt:s.alt},d=[];for(const[h,g]of Object.entries(s.attrs)){if(!mt.includes(h))return z(e,"a picture",h,mt);if(h==="alt")m.alt=String(g);else if(h==="caption")d.push({type:"mediaCaption",content:[{type:"text",text:String(g)}]});else{const f=Number(g);if(Number.isNaN(f))return`a picture under ${e} gives ${h} "${String(g)}", which is not a number.`;m[h]=f}}return s.title?.length&&d.push({type:"mediaTitle",content:s.title}),s.body.length&&d.push({type:"mediaBody",content:yt(s.body)}),a.push({type:"mediaItem",attrs:m,content:d}),s=null,null},l=m=>Ut.test(m)||m===_,i=m=>m[0]?.type==="inlineImage"||m.length>0&&m.every(d=>d.type==="text")&&l(q(m).rest.map(d=>d.text??"").join("").trim()),u=r.flatMap(m=>{if(m.type!=="paragraph")return[m];const d=ht(m),h=[];let g=[];for(const y of d){if(i(y)&&g.length){const $=Y(m,g);$&&h.push($),g=[]}g.push(y)}const f=Y(m,g);return f&&h.push(f),h});for(const m of u){const d=m.type==="paragraph"?m.content?.[0]:m.type==="inlineImage"?m:void 0,h=d?.type==="inlineImage"?d:null,g=d?.type==="text"&&m.content?.every(f=>f.type==="text")?q(m.content??[]).rest.map(f=>f.text??"").join("").trim():"";if(h||l(g)){const f=c();if(f)return f;if(h&&o)return`${e} takes a video ADDRESS on a line of its own, not a picture.`;if(!h&&!o)return`${e} takes a picture line \u2014 ![alt](https://\u2026) \u2014 not a bare address; a bare address is ::youtube::'s.`;const{attrs:y}=q(m.type==="inlineImage"?[]:(m.content??[]).slice(h?1:0));s={src:h?String(h.attrs?.src??""):g,alt:h?h.attrs?.alt??null:null,attrs:y,title:null,body:[]}}else if(s)if(m.type==="heading"){if(s.title)return`a picture under ${e} has two headings; one is its title, the paragraphs under it are its words.`;s.title=V(m).title}else{if(m.type!=="paragraph")return`a picture under ${e} holds a ${m.type} block; its words are paragraphs.`;s.body.push(m)}else return`${e} opens with something that is not a ${o?"video address":"picture"}: ${o?"a bare YouTube address":"![alt](https://\u2026)"} on a line of its own starts each entry.`}const p=c();return p||(a.length?[{type:t.node,attrs:n,content:a}]:`${e} holds no ${o?"video":"picture"}.`)}function Zt(t,e,n,r,o){const a=o[0];if(!a||a.type!=="table"||o.length!==1)return`${e} must be followed by exactly ONE markdown table: the header row names the series, each row after it is a category and its values.`;const s=(a.content??[]).map(f=>(f.content??[]).map(y=>A(y).trim()));if(s.length<2)return`${e}'s table needs a header row and at least one row of values.`;const[c,...l]=s,i=l.map(f=>f[0]??""),u=c.slice(1).map((f,y)=>{const $=X.exec(f),F=$?j($[1],{positional:!1}).attrs.as:void 0;return{name:$?f.slice(0,$.index).trim():f,...F?{as:String(F)}:{},values:l.map(St=>(St[y+1]??"").trim())}});for(const f of u)for(const[y,$]of f.values.entries())if($!==""&&Number.isNaN(Number($)))return`${e}: the cell "${$}" (row "${i[y]}", column "${f.name}") is not a number. A gap is an empty cell.`;const p={categories:i,series:u.map(f=>({name:f.name,..."as"in f?{as:f.as}:{},values:f.values.map(y=>y===""?null:Number(y))}))},m=r[T],d=Ot(p,m);if(d)return`${e}: ${d}`;const h={};for(const[f,y]of Object.entries(n))if(!L.includes(f))if(f==="stack"){if(!st.includes(String(y)))return`${e} has no stack "${String(y)}". Stacks: ${b(st)}.`;r.stack=String(y)}else h[f]=String(y);const g=(f,y)=>y?{type:f,content:[{type:"text",text:y}]}:{type:f};return[{type:t.node,attrs:{...r,categories:p.categories,series:p.series},content:[g("chartTitle",h.title),g("chartAxisY",h.axisY),g("chartAxisX",h.axisX)]}]}function Jt(t,e,n,r){const{id:o,v:a,...s}=n;if(!t.arrangements.length&&!t.variantSlots.length&&t.type!=="diagram")return`${e} sets nothing: a ${t.type} has no style and no shape to name. Write it as ordinary markdown.`;const[c,...l]=r,i=t.type==="diagram"&&c?.type==="codeBlock"&&String(c.attrs?.language??"").toLowerCase()==="mermaid";return!c||c.type!==t.node&&!i?`${e} must sit directly over a ${t.type} block${c?`, and what follows it is a ${c.type}`:""}.`:[{...c,attrs:{...c.attrs??{},...s}},...l]}function Qt(t){for(const[e,n]of Object.entries(t.attrs)){if(!pt.includes(e))return`"::page::" has no attribute "${e}". A page line takes: ${b(pt)}.`;const r=String(n);if(e==="accent"&&!ot.includes(r))return`"::page::" has no accent "${r}". Accents: ${b(ot)}.`;if(e==="surface"&&!at.includes(r))return`"::page::" has no surface "${r}". Surfaces: ${b(at)}.${["dark","light"].includes(r)?` A ${r} page is mode=${r}.`:""}`;if(e==="mode"&&!["light","dark"].includes(r))return`"::page::" has no mode "${r}". Modes: light, dark.`;if(e==="src"){const o=nt(r,"image");if(o)return`"::page::" src: ${o}`;if(!t.attrs.accent)return'"::page::" names a src but no accent to put it in. Add accent=lead, trail, top or full.'}}return t.positional?`"::page::" takes key=value pairs, not "${t.positional}".`:null}function he(t,e={}){const n=gt(t.flatMap(G),e.theme);if(n.problem)return n.problem;let r=null,o=new Set;for(const a of n.pieces){const s=a.marker;if(!s)continue;if(s.kind==="page"){if(e.pageless)return'"::page::" describes a page of its own, and this import lands on a page that exists: use set_page for its look.';const l=Qt(s);if(l)return l;continue}if(s.kind==="layout"){const l=s.positional??String(s.attrs.layout??""),i=J(l);if(!i)return`unknown layout "${l}". Layouts: ${Z.join(", ")}`;r=i,o=new Set;continue}if(s.kind==="end"){if(!r)return'"::end::" closes nothing: no block or layout marker is open above it.';r=null;continue}if(!r)return`"::${s.name}::" has no "::layout: \u2026::" above it, so there is no region by that name. A region marker only means something inside a layout.`;const c=r.regions.map(l=>l.name);if(!c.includes(s.name))return`"${r.name}" has no region "${s.name}". Its regions: ${c.join(", ")}`;if(o.has(s.name))return`"::${s.name}::" appears twice under one "${r.name}". Each region is written once, and everything until the next marker belongs to it.`;o.add(s.name)}return null}function $t(t,e){if(!t.some(i=>i.marker))return{blocks:t.map(i=>i.block),page:null};const n=[];let r=null,o=null,a=null,s=null;const c=()=>{!o||!a||(n.push({type:"layout",attrs:{id:w("layout"),layout:o.name},content:o.regions.map(i=>({type:"layoutRegion",attrs:{id:w("layoutRegion"),region:i.name},content:a.get(i.name)?.length?a.get(i.name):[{type:"paragraph"}]}))}),o=null,a=null,s=null)},l=i=>{o&&a&&s?a.set(s,[...a.get(s)??[],i]):(o&&a&&c(),n.push(i))};for(const i of gt(t,e).pieces){if(i.block){l(i.block);continue}const u=i.marker;if(u.kind==="page"){r??=u.attrs;continue}if(u.kind==="end"){c();continue}u.kind==="layout"?(c(),o=J(u.positional??String(u.attrs.layout??"")),a=new Map):s=u.name}return c(),{blocks:n,page:r}}const kt=`<!-- notes
31
+ `,bt=`
32
+ -->`;function te(t){let e;const n=t.filter(r=>{if(r.type!=="paragraph")return!0;const o=r.content;if(o?.length!==1||o[0].type!=="text")return!0;const a=o[0].text??"";return!a.startsWith(kt)||!a.endsWith(bt)?!0:(e===void 0&&(e=a.slice(kt.length,-bt.length)),!1)});return e===void 0?{blocks:t}:{blocks:n,notes:e}}function ge(t,e=1,n){const o=(lt().parse(t).content??[]).map(R).filter(Boolean),a=[];let s=null;const c=(i,u)=>{s={page:{blocks:[]},pieces:[],opened:i,attrs:u},a.push(s)},l=()=>s&&!s.pieces.length&&!s.attrs;for(const i of o.flatMap(G)){if(i.marker?.kind==="page"){if(l()){s.attrs=i.marker.attrs,s.opened==="start"&&(s.opened="marker");continue}c("marker",i.marker.attrs);continue}const u=i.block,p=u?.type==="heading"&&u.attrs?.level===e;if(p&&s&&!s.page.title&&!s.pieces.length&&s.opened==="marker"){s.page.title=A(u);continue}if((p||!s)&&(c(p?"heading":"start",null),p)){s.page.title=A(u);continue}s.pieces.push(i)}for(const i of a){const u=i.pieces.filter(y=>y.block).map(y=>y.block),{blocks:p,notes:m}=te(u),d=new Set(p),h=i.pieces.filter(y=>y.marker||d.has(y.block)),g=$t(h,n);i.page.blocks=xt(g.blocks),m!==void 0&&(i.page.notes=m);const f=i.attrs??g.page;f&&(f.eyebrow&&(i.page.eyebrow=String(f.eyebrow)),f.subtitle&&(i.page.subtitle=String(f.subtitle)),f.accent&&(i.page.accent=String(f.accent)),f.src&&(i.page.accentSrc=String(f.src)),f.alt&&(i.page.accentAlt=String(f.alt)),f.surface&&(i.page.surface=String(f.surface)),f.mode&&(i.page.mode=String(f.mode)))}return a.map(i=>i.page)}const ye=(t,e)=>xt($t(ee(t).flatMap(G),e).blocks),ee=t=>(lt().parse(t).content??[]).map(R).filter(Boolean),wt=t=>ne(re(t)),xt=t=>t.map(e=>e.type!=="layout"?wt(e):{...e,content:(e.content??[]).map(n=>({...n,content:(n.content??[]).map(wt)}))});function ne(t){if(t.type!=="codeBlock"||String(t.attrs?.language??"").trim().toLowerCase()!=="mermaid")return t;const e=A(t).trim();if(!e)return t;const{width:n,place:r}=t.attrs??{};return{type:"diagram",attrs:{id:w("diagram"),source:e,...n?{width:n}:{},...r?{place:r}:{}}}}function re(t){const e=t.type==="inlineImage"?t:(t.content??[]).length===1?t.content[0]:null;return t.type!=="paragraph"&&t.type!=="inlineImage"?t:e?.type!=="inlineImage"||!e.attrs?.src?t.type==="inlineImage"?{type:"paragraph",attrs:{id:w("paragraph")},content:[t]}:t:{type:"imageBlock",attrs:{id:w("imageBlock")},content:[{type:"mediaItem",attrs:{id:w("mediaItem"),src:e.attrs.src,alt:e.attrs.alt??null}}]}}function A(t){return t.type==="text"?t.text??"":(t.content??[]).map(A).join("")}export{ct as MARKDOWN_SPECS,ye as blocksFromMarkdown,ee as blocksOnly,he as directiveProblem,Kt as markdownExtensions,lt as markdownManager,ge as pagesFromMarkdown};
@@ -28,4 +28,4 @@ The document LINTS ITSELF and there is no tool to call for it. Diagnostics ride
28
28
  names what is wrong, where, and the change that fixes it. Act before moving on \u2014 nothing else raises
29
29
  it again. Pages are a fixed size, so text CAN overflow one; there is no character budget to write
30
30
  against, because a page is measured after you change it. A reply carries a fit field only when that
31
- check could NOT be made. No fit field means the page was measured and fits.`;function zn(n,s=ln){const w=new St({name:"decaframe",version:"0.1.0"},{instructions:Dn}),v=w.close.bind(w);w.close=async()=>{await rn(),await v()};const $=new Map,O=e=>[...$].filter(([,t])=>t.includes(e)).map(([t])=>t),f=(e,t,a)=>{const r=t.inputSchema??{},i=Object.keys(r);$.set(e,i),w.registerTool(e,{...t,inputSchema:o.looseObject(r)},(async c=>{const l=Object.keys(c??{}).filter(h=>!i.includes(h));if(l.length){const h=l.map(u=>[u,O(u)]).filter(([,u])=>u.length).map(([u,m])=>`'${u}' is an argument of ${m.join(" and ")}`);return d("unknownArgument",`${e} has no argument ${l.map(u=>`'${u}'`).join(", ")}. Nothing was written. It takes exactly: ${i.length?i.join(", "):"no arguments"}.`+(h.length?` ${h.join("; ")}.`:""))}return a(c)}))},x=(e,t,a)=>d("missingArgument",`${e} needs '${t}' and you sent none. Nothing was written. It takes: ${a}.`),y=()=>(n.sync(),n.editor.state.doc),k=e=>{const t=ae(y(),e);return t&&t.node.type.name==="page"?t.node:null},_=e=>{const t=ae(y(),e);return t&&W(t.node.type.name)?t.node:null},R=e=>{const t=ae(y(),e);if(!t)return null;const a=t.node.type.name;return a==="page"||a==="layoutRegion"?t.node:null},Z=e=>{const t=R(e);return t?t.type.name==="page"?t:H(y(),e):null},he=e=>{const t=[];return e.forEach(a=>{a.type.name==="layout"&&t.push(a.attrs.id)}),t},Ee=(e,t)=>{const a={};return t&&e.forEach(r=>{r.type.name!=="layout"||r.attrs.id!==t||r.forEach(i=>{a[i.attrs.region]=i.attrs.id})}),a},Oe=e=>d("unknownLayout",`No layout '${e}'. This document's layouts: ${ke.map(t=>t.name).join(", ")}.`),ee=e=>d("unknownPage",`No page or layout region '${e}'. Pages in this document: ${V(y()).join(", ")}. A layout's region ids come back from add_layout and from get_document. Call get_document for the current ids.`),Y=e=>d("unknownPage",`No page '${e}'. Pages in this document: ${V(y()).join(", ")}. Call get_document for the current ids.`),N=e=>{const t=[];return y().forEach(a=>t.push(...j(a),...he(a))),d("unknownBlock",`No block '${e}'. Blocks in this document: ${t.join(", ")}. Call get_document for the current ids.`)},q=()=>Xe(y().attrs.theme).theme,z=e=>d("unknownType",`No block type '${e}'. Available: ${In()}.`),F=(e,t)=>{if(e==null||e==="")return null;const a=ce(t.variantSlots,q(),{core:!0});return a.includes(e)?null:`'${t.type}' has no variant '${e}'. It offers: ${a.join(", ")}. Styles differ per block \u2014 a block only offers one it has somewhere to put, so a paragraph offers none and a callout page is a box of one entry wearing an accented edge. list_blocks gives each block its own list. A theme may also add names for people editing by hand; those are not on this surface, because they change with the theme and these do not.`},xe=e=>({arrangements:e.arrangements.map(t=>t.name),figures:(e.figures??[]).map(t=>t.name)}),te=(e,t)=>{if(e==null||e==="")return null;const{arrangements:a,figures:r}=xe(t);if(a.includes(e)||r.includes(e))return null;const i=[...a,...r];return`'${t.type}' has no shape '${e}'.`+(i.length?` It offers: ${i.join(", ")}. Each has its own count range \u2014 list_blocks names them, because a shape changes how much text fits.`+(r.length?` ${r.join(", ")} ${r.length>1?"are figures":"is a figure"} \u2014 an illustrated shape whose count range is a LIMIT, not guidance: a write outside it is refused. describe_block gives the range.`:""):" It has one shape, so omit the field.")},ue=(e,t,a)=>{if(e==null||e==="")return null;const r=(t.figures??[]).find(l=>l.name===e);if(!r)return null;const[i,c]=r.countRange;return a>=i&&a<=c?null:`'${e}' is a figure and holds ${i}\u2013${c} items, not ${a}. A figure draws a fixed number of places to stand, so unlike an arrangement \u2014 whose count range is guidance \u2014 this is refused rather than reported. Send a count in range, or pick a plain arrangement from describe_block.`},pe=(e,t,a)=>xe(a).figures.includes(t)?n.editor.commands.setFigure(e,t):n.editor.commands.setArrangement(e,t),me=(e,t)=>e==null||e===""?null:ie.includes(e)?t.slots.includes("icon")?t.marks.includes("icon")?`'${t.type}' puts each item's icon in its MARK \u2014 the numeral's own place \u2014 so there is no gutter to move it to. Use \`mark: 'icon'\` to turn the glyphs on, and set each entry's \`icon\`. Omit this field.`:null:`'${t.type}' draws no icon, so there is nowhere to put one \u2014 it stores an icon on an entry and never renders it. Blocks that do: ${T().filter(a=>a.slots.includes("icon")).map(a=>a.type).join(", ")}. Omit the field, or use set_block_type to swap to one of those.`:`There is no icon position '${e}'. The positions are: ${ie.join(", ")}. Pass '' to go back to whatever this block's shape does by default.`,ge=(e,t)=>{if(e==null||e===""||t.marks.includes(e)&&e!==t.marks[0])return null;if(!t.marks.length){const a=T().filter(r=>r.marks.length>0).map(r=>r.type);return`'${t.type}' marks its items with nothing, so there is nothing to change. Blocks that do: ${a.join(", ")}. Omit the field.`}return e===t.marks[0]?`'${e}' is what '${t.type}' already does when nothing is stored, so storing it would give one state two spellings. Pass '' to go back to it, or omit the field.`:`'${t.type}' has no mark '${e}'. It offers: ${t.marks.join(", ")}. '${t.marks[0]}' is the default and needs no field.`},A=(e,t)=>d("refused",`${e} was refused and the document is unchanged. Arguments: ${JSON.stringify(t)}. Commands refuse rather than corrupt: check that ids exist and belong where you used them, that entry ids came from this block, that a type swap stays inside one archetype, and that the change would not leave a page or the document empty.`),ft="not measured: fix the problems above first, then this page is checked against its frame.",Re="not measured: this document is fluid, so its pages grow with their content and none of them can overflow. Nothing here will tell you a page is too long \u2014 set a fixed format (16:9, 4:3 or A4) if you want that check back.",bt=async e=>{const t=Yt(e),a=e.attrs.id;if(t.length)return{diagnostics:t,fit:ft};if(!a)return{fit:"this page carries no id, so it could not be measured."};const r=await s(n.editor.getJSON(),a);if(r.problem)return{fit:r.problem};const i=hn(r,a);if(!i)return{fit:`page '${a}' did not render, so fit was not measured.`};if(!i.bounded)return{fit:Re};const l=jt.includes(String(y().attrs.format??""))?void 0:cn(dn(r,a)),h=un(r,a),u=h?{...i,over:h.over}:i,m=Je(u,n.fitSeen(a,u.over)),g=pn(h?.scale,!!m);return{...m?{diagnostics:[m]}:{},...l?{fill:l}:{},...g?{scale:g}:{}}},S=async(e,t={})=>K({...t,page:vn(e),...await bt(e)}),re=async(e={})=>{const t=y(),a=Kt(t),r=new Set(a.map(u=>u.pageId)),c=t.content.childCount>r.size?await s(n.editor.getJSON()):{},l=[];let h=!1;for(const u of c.pages??[]){if(r.has(u.id))continue;if(!u.bounded){h=!0;continue}const m=Je(u,n.fitSeen(u.id,u.over));m&&l.push(m)}return Q({...e,...kn(t),diagnostics:[...a,...l],...c.problem?{fit:c.problem}:h?{fit:Re}:{}})};f("list_blocks",{title:"List block types",description:"The block menu. One short row per block: what it is, when to pick it, how many entries its layout is designed for, and which entry slots it draws. Start here \u2014 block types are discovered, never memorised. For a worked example of one block, call describe_block. There is no per-block color, font or size on this surface: those values are the theme's and the theme is the account's. Change how a block LOOKS with its `variant`."},async()=>Q({variantsNote:"Each row carries the styles THAT block offers, because a block only offers one it has somewhere to put \u2014 a paragraph offers none. The theme decides what each name looks like, so a name means the same thing in every theme. Pass one as `variant`.",blocks:T().map(e=>({type:e.type,archetype:e.archetype,description:e.description,whenToUse:e.whenToUse,countRange:e.countRange,...e.colRange?{colRange:e.colRange}:{},renders:e.slots,variants:ce(e.variantSlots,q(),{core:!0}),...e.arrangements.length?{arrangements:e.arrangements.map(t=>t.name)}:{},...e.figures?.length?{figures:e.figures.map(t=>({name:t.name,countRange:t.countRange}))}:{},...e.marks.length?{marks:e.marks}:{}}))})),f("describe_block",{title:"Describe one block",description:"A worked example for one block type, ready to copy. Does NOT return a schema \u2014 the entry schema belongs to the archetype and is already on that archetype's add_ tool, identical for every block that shares it. Call this when you have chosen a block and want to see what good content looks like in it.",inputSchema:{type:o.string().describe('A block type from list_blocks, e.g. "timeline".')}},async({type:e})=>{const t=G(e);return t?Q({type:t.type,archetype:t.archetype,countRange:t.countRange,...t.colRange?{colRange:t.colRange}:{},renders:t.slots,whenToUse:t.whenToUse,...t.slots.includes("icon")?{icons:Nt}:{},...t.marks.length?{marks:t.marks}:{},variants:ce(t.variantSlots,q(),{core:!0}),...t.arrangements.length?{arrangements:t.arrangements.map(a=>({name:a.name,whenToUse:a.whenToUse,countRange:a.countRange}))}:{},...t.figures?.length?{figures:t.figures.map(a=>({name:a.name,whenToUse:`${a.whenToUse} It holds ${$n(a.countRange)} and a write outside that range is refused, unlike an arrangement whose range is guidance.`,countRange:a.countRange}))}:{},example:{tool:`add_${t.archetype}`,arguments:{pageId:"<a page id from get_document>",...$.get(`add_${t.archetype}`)?.includes("type")?{type:t.type}:{},[Ln[t.archetype]]:t.example}}}):z(e)}),f("get_document",{title:"Read the document",description:"The whole document: every page with its id, heading and blocks, and every block with its id, type and entries. Entry ids are what the update_ tools match on \u2014 echo one back to change that entry, omit it to add a new one, leave it out of the array to remove it. The reply also names the FILE this document is saved in, which defaults to the folder this server was started in. If the person named a file, or wants more than one document in that folder, call open_document BEFORE authoring."},async()=>re({file:n.path})),f("list_templates",{title:"List the templates",description:"The page designs available, each with the content it suits \u2014 the shelf to choose from before adding any page. Every one ADDS a single composed page; none replaces or alters what is already in the document. Add one with add_template_page.",inputSchema:{}},async()=>Q({templates:gt().map(e=>({id:e.id,name:e.name,whenToUse:e.whenToUse,look:Dt(e)}))})),f("add_template_page",{title:"Add a page from a template",description:"ADD ONE COMPOSED PAGE \u2014 heading, blocks, shape and design already chosen. This is how a page should normally be added; add_blank_page is for when no template suits. It INSERTS a page of its own, so do not add a page first. The text it arrives with is PLACEHOLDER, not content: fill every slot with set_page, which takes the whole page in one call.",inputSchema:{templateId:o.string().describe("A template id from list_templates."),before:o.string().optional().describe("Page id to insert before, for a PAGE template. Omit to append. Never an index.")}},async({templateId:e,before:t})=>{const a=ze(e);if(!a.ok)return d("unknownTemplate",`No template '${e}'. Templates are: ${gt().map(h=>h.id).join(", ")}. Call list_templates for the current ids.`);const r=a.value;if(t&&!k(t))return Y(t);const i=V(y());if(!n.editor.commands.applyTemplate(e,{before:t}))return A("apply_template",{templateId:e,before:t});n.save();const c=J(i,V(y())),l=c?k(c):null;return l?S(l,{pageId:c}):K({pageId:c})}),f("remove_page",{title:"Remove a page",description:"Delete a page and everything on it. Use it to clear a page you no longer want \u2014 including the blank one a document starts with, if you added your own pages instead of filling it. A document must keep at least one page, so removing the last one is refused.",inputSchema:{pageId:o.string().describe("A page id from get_document.")}},async({pageId:e})=>k(e)?V(y()).length===1?d("lastPage",`page '${e}' is the only page in this document and a document must hold at least one. Nothing was written. Put content on it instead, or add another page first.`):n.editor.commands.removePage(e)?(n.save(),re()):A("remove_page",{pageId:e}):Y(e)),f("open_document",{title:"Open or name the document file",description:"Choose the file this document is saved in. If the file exists it is OPENED and becomes the document you are working on; if it does not, it is where this document will be saved. Call it BEFORE authoring when the person names a file or wants more than one document in a folder \u2014 otherwise everything lands in the default the reply from get_document names. Pass a full path when you know the folder you are working in; a bare name is resolved against the folder this server was started in, which is not the same for every client.",inputSchema:{file:o.string().describe("Path to a .json document \u2014 absolute, or relative to the folder this server started in. The folder must already exist; this creates no directories.")}},async({file:e})=>{const t=fn(e);if(!t.ok)return d(t.code,t.message);try{n.openAt(t.path)}catch(a){return d("unreadableDocument",`${yn(e,a)} Nothing was opened; you are still working on the document you were before.`)}return console.error(`decaframe mcp \xB7 document: ${t.path}`),re({file:t.path,opened:t.existed})}),f("set_document",{title:"Set the document's title, shape or palette",description:"Change facts that belong to the WHOLE document rather than to one page: its title, its frame shape, where content sits in each frame, and which palette it is designed in. Omitted fields are left alone. Frame shape and content position live here, not on set_page, because a document is one shape and keeps one rhythm.",inputSchema:{title:o.string().optional().describe("The document's name. Not a heading on any page."),format:o.enum([...xt]).optional().describe("Frame shape for every page. Defaults to 16:9. A4 is a portrait sheet."),align:o.enum([...Et]).optional().describe("Where blocks sit in the leftover vertical space on EVERY page: top, middle or bottom. Defaults to middle."),mode:o.enum(["light","dark"]).optional().describe("Which palette the document is designed in: light or dark. A page may override it.")}},async e=>n.editor.commands.setDocument(e)?(n.save(),re()):A("set_document",e)),f("add_blank_page",{title:"Add a blank page",description:`Append a BLANK page, or insert one before an existing page. Reach for this only when no template suits the content \u2014 add_template_page adds a page that is already composed, and is the normal way to add one. A blank page holds nothing until you put blocks on it with ${we.map(e=>`add_${e}`).join(", ")}; set_page then fills every slot in one call.`,inputSchema:{...ut,...pt,before:o.string().optional().describe("Page id to insert before. Omit to append at the end. Never an index.")}},async e=>{if(e.before&&!k(e.before))return Y(e.before);const t=V(y());if(!n.editor.commands.addPage(e))return A("add_page",e);n.save();const a=J(t,V(y())),r=a?k(a):null;return r?S(r,{pageId:a}):K({pageId:a})}),f("import_markdown",{title:"Import a markdown document",description:'Write a whole markdown document at once \u2014 the fast path for anything already in markdown, and the ONE call that lays down a whole deck: pages, their look, and this catalogue\'s own blocks. WITHOUT pageId a NEW PAGE starts at every heading of `splitOn` (1 by default), that heading its title, and at every `::page\u2026::` line, so a page needs no heading; WITH pageId everything lands on that page and nothing splits. Ordinary markdown arrives as itself \u2014 paragraphs, headings, lists, tables, fenced code; a line that is only an image becomes a picture block; a ```mermaid fence becomes a DIAGRAM, and one that does not parse refuses the whole import. MARKERS say what markdown cannot, one per line, `::name: key=value \u2026::`, keys being this surface\'s own argument names: `::page: eyebrow="\u2026" subtitle="\u2026" accent=lead src=https://\u2026 alt="\u2026" surface=muted mode=dark::` right under a page\'s heading; `::layout: two-cols::` then `::col-1::`, `::col-2::` before each column; and a BLOCK marker over what builds it. `::box: variant=outlined arrangement=grid::`, `::steps: arrangement=funnel::`, `::timeline::`: the ITEMS are the headings under it, `### Title {icon=zap meta="\u2026" emphasis}`, the item\'s body beneath. `::stat-row::`: `### 84% {label="\u2026"}`, the note beneath. `::image::`, `::image-grid::`, `::people::`, `::image-with-text::`: one `![alt](https://\u2026) {caption="\u2026"}` line per picture, a heading after it its title, paragraphs its words; `::youtube::` over a bare address. `::chart: arrangement=line stack=stacked title="\u2026" axisX="\u2026" axisY="\u2026"::` over a table \u2014 header = series names, each row a category and its values, an empty cell a gap. `::text: arrangement=xl::` or `::table: variant=banded::` over the block beneath. `::end::` closes a block; the next marker or page closes it too, and nothing nests. A wrong name is REFUSED by name and nothing is written. It only creates: to change what exists, read it back and use the update_ tools.',inputSchema:{markdown:o.string().describe("The markdown document, as text."),pageId:o.string().optional().describe("A page id from get_document. Everything lands on that page and NOTHING is split. Omit to make a page per section instead."),splitOn:o.number().int().optional().describe("Which heading level starts a new page, 1 to 6. Default 1. Ignored when pageId is set."),before:o.string().optional().describe("Insert before this id \u2014 a block id when pageId is set, a page id otherwise. Omit to append at the end.")}},async({markdown:e,pageId:t,splitOn:a,before:r})=>{if(t&&!k(t))return Y(t);if(a!==void 0&&(a<1||a>6))return d("invalidLine",`splitOn is ${a}; markdown has six heading levels, so it must be 1 to 6. Nothing was written.`);const i=Ut(Ft(e),{theme:q(),pageless:!!t});if(i)return d("invalidLine",`${i} Nothing was written.`);const c=t?[{blocks:Ht(e,q())}]:Vt(e,a??1,q()),l=p=>!!p.text?.trim()||!!p.attrs?.src||!!p.attrs?.source||(p.content??[]).some(l);if(!c.some(p=>p.title?.trim()||p.blocks.some(l)))return d("invalidLine","that markdown draws nothing \u2014 no text, no pictures, no headings. Nothing was written. An empty document, one that is only whitespace, or one that is only HTML comments has nothing this can add.");const h=p=>p.flatMap(b=>b.type==="diagram"?[String(b.attrs?.source??"")]:h(b.content??[]));for(const p of c.flatMap(b=>h(b.blocks))){const b=await ye(p);if(b)return d("invalidSource",`In a \`\`\`mermaid fence: ${b} Nothing was written.`)}const u=V(y()),m=t?j(k(t)):[];if(!n.editor.commands.importMarkdown(c,t,r))return A("import_markdown",{pageId:t,before:r});if(n.save(),t){const p=j(k(t)),b=new Set(m);return S(k(t),{blockIds:p.filter(E=>!b.has(E))})}const g=new Set(u);return K({pageIds:V(y()).filter(p=>!g.has(p))})}),f("add_layout",{title:"Add a layout",description:`Put a LAYOUT on a page \u2014 a container that arranges other blocks side by side. It comes back with a region id per column; pass one of those to ${we.map(e=>`add_${e}`).join(", ")} instead of a page id to put a block inside that column. The layout holds no text of its own. For a picture running down one side of the page, this is NOT the tool \u2014 that is an ACCENT and it belongs to the page: use set_page.`,inputSchema:{pageId:o.string().describe("A page id from get_document. A layout only goes on a page."),layout:o.string().describe(ke.map(e=>`${e.name} \u2014 ${e.whenToUse}`).join(" | ")),before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,layout:t,before:a})=>{const r=k(e);if(!r)return Y(e);if(!et(t))return Oe(t);if(a&&!_(a))return N(a);const i=he(r);if(!n.editor.commands.addLayout(e,t,a))return A("add_layout",{pageId:e,layout:t,before:a});n.save();const c=k(e),l=he(c).find(h=>!i.includes(h));return S(c,{blockId:l,regions:Ee(c,l)})}),f("set_layout",{title:"Change which layout a container uses",description:"Re-arrange a layout that already exists. Everything inside it is KEPT \u2014 every block and every region id survives, so this is safe to call on a full layout. If the new layout has fewer regions than the old one, the leftover blocks move into the last region rather than being deleted.",inputSchema:{blockId:o.string().describe("A layout id from get_document or add_layout."),layout:o.string().describe(`One of: ${ke.map(e=>e.name).join(", ")}.`)}},async({blockId:e,layout:t})=>{const a=ae(y(),e);if(!a||a.node.type.name!=="layout")return d("unknownBlock",`'${e}' is not a layout. set_layout takes the id add_layout returned; use set_block_type for an ordinary block.`);if(!et(t))return Oe(t);if(!n.editor.commands.setLayout(e,t))return A("set_layout",{blockId:e,layout:t});n.save();const r=H(y(),e);return S(r,{blockId:e,regions:Ee(r,e)})});const yt=async(e,t)=>{const a="Nothing was written \u2014 the whole call is refused so the page is never left half-filled.",r=c=>`blocks[${c}]`,i=[];for(const[c,l]of t.entries()){const h=String(l.blockId??""),u=_(h);if(!u)return{refusal:d("unknownBlock",`${r(c)} names block '${h}', which is not in this document. ${a} Block ids come back from apply_template and get_document.`)};const m=H(y(),h);if(m&&m.attrs.id!==e)return{refusal:d("wrongPage",`${r(c)} names block '${h}', which is on page '${m.attrs.id}', not the page '${e}' you are setting. ${a} Call set_page once per page.`)};const g=W(u.type.name),p=l.entries,b=l.content;if(g.archetype==="prose"){if(p!==void 0)return{refusal:d("wrongArchetype",`${r(c)} sent 'entries' to '${g.type}', which is a 'prose' block and takes 'content' \u2014 the same field update_prose takes. ${a}`)};if(b===void 0)return{refusal:d("invalidLine",`${r(c)} names no 'content'. ${a}`)};const X=Se(b,{kind:!1,type:g.type});if(X)return{refusal:d("invalidLine",`${r(c)}: ${X}`)};if(!Ke.has(u.type.name)&&b.length!==1)return{refusal:d("invalidLine",`${r(c)}: '${g.type}' holds exactly one line and you sent ${b.length}. ${a} Use add_prose to put the others on the page as their own blocks.`)};i.push(()=>n.editor.commands.updateBlock(h,{content:b}));continue}if(!ve[g.archetype])return{refusal:d("wrongArchetype",`${r(c)} names '${g.type}', a '${g.archetype}' block, which this array cannot fill. ${a} Use update_${g.archetype} for it.`)};if(b!==void 0)return{refusal:d("wrongArchetype",`${r(c)} sent 'content' to '${g.type}', which is a '${g.archetype}' block and takes 'entries'. ${a}`)};if(p===void 0)return{refusal:d("invalidEntry",`${r(c)} names no 'entries'. ${a}`)};const E=l.variant,P=l.arrangement,L=l.iconPosition,D=l.mark,M=_e(g,p),I=M??F(E,g)??te(P,g)??ue(P,g,p.length)??me(L,g)??ge(D,g);if(I)return{refusal:d(I===M?"invalidEntry":"invalidBlock",`${r(c)}: ${I} ${a}`)};i.push(()=>n.editor.commands.updateBlock(h,Te(g.archetype,Ae(p)))?(E!==void 0&&n.editor.commands.setVariant(h,E),P!==void 0&&pe(h,P??"",g),L!==void 0&&n.editor.commands.setIconPosition(h,L),D!==void 0&&n.editor.commands.setBlockMark(h,D),!0):!1)}return{run:i}};f("set_page",{title:"Set a page's heading, look or block content",description:"Change a page's own heading and look, and FILL ITS BLOCKS IN ONE CALL. Omitted fields are left alone; an empty string removes that heading slot. `blocks` takes the payload each block's archetype takes, so a template applied and then filled costs two calls rather than one per block; every block is checked before any is written, so a refusal changes nothing. The frame's SHAPE and where content sits in it are the document's \u2014 set_document.",inputSchema:{pageId:o.string().describe("A page id from get_document."),...ut,...pt,...jn,blocks:o.array(o.looseObject({blockId:o.string().describe("A block id, from add_template_page or get_document."),entries:o.array(o.looseObject({})).optional().describe("items, stats or media \u2014 what update_<archetype> takes."),content:o.array($e({existing:!0})).optional().describe("prose \u2014 what update_prose takes."),variant:o.string().nullable().optional().describe(`The block's style \u2014 a core name such as ${Ge.slice(1,4).join(", ")}, each glossed on add_items' own \`variant\`. Choose one; omit to keep the template's.`),arrangement:o.string().nullable().optional().describe("Its shape or figure \u2014 the names the archetype's add_ tool takes on `arrangement`. Omit to keep the template's."),iconPosition:o.string().nullable().optional().describe(`Where its icons sit: ${ie.join(", ")}. Only a block that draws icons takes it.`),mark:o.string().nullable().optional().describe(`What its items are marked with \u2014 ${lt("items")} \u2014 as add_items takes it.`)})).optional().describe("THE FAST PATH AFTER add_template_page \u2014 the page arrives composed and placeholder-written, and this replaces every slot in ONE call.")}},async({pageId:e,accent:t,accentSrc:a,accentAlt:r,blocks:i,...c})=>{if(!k(e))return Y(e);let l=[];if(i!==void 0){const u=await yt(e,i);if("refusal"in u)return u.refusal;l=u.run}if(t!==void 0||a!==void 0||r!==void 0){const u=a?Qe(a,"image"):null;if(u)return d("invalidSrc",u);if(!n.editor.commands.setAccent(e,{...t!==void 0?{position:t}:{},...a!==void 0?{src:a}:{},...r!==void 0?{alt:r}:{}}))return A("set_page",{pageId:e,accent:t,accentSrc:a})}if(Object.keys(c).length&&!n.editor.commands.setPage(e,c))return A("set_page",{pageId:e,...c});for(const[u,m]of l.entries())if(!m())return A("set_page",{pageId:e,block:`blocks[${u}]`});return n.save(),S(k(e))});const wt=e=>{const t=ve[e],a=T().filter(i=>i.archetype===e).map(i=>i.type),r=i=>d("wrongArchetype",`'${i.type}' is a '${i.archetype}' block, not '${e}'. Use add_${i.archetype} / update_${i.archetype} for it. This tool takes: ${a.join(", ")}.`);f(`add_${e}`,{title:`Add a ${e} block`,description:`Put ${/^[aeiou]/.test(e)?"an":"a"} '${e}' block on a page \u2014 ${a.join(", ")}. Every type here takes the same entries, so switching later with set_block_type keeps what you wrote; \`type\` names what each one is for.`,inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(at(e)),entries:o.array(t).optional().describe(ot(e)),...B(e)?C():{},...de(e)?rt(e):{},...ct(e)?st:{},...ht(e)?dt(e):{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:i,type:c,entries:l,variant:h,arrangement:u,iconPosition:m,mark:g,before:p})=>{if(l===void 0)return x(`add_${e}`,"entries",$.get(`add_${e}`).join(", "));const b=G(c);if(!b)return z(c);if(b.archetype!==e)return r(b);const E=R(i);if(!E)return ee(i);if(p&&!_(p))return N(p);const P=_e(b,l);if(P)return d("invalidEntry",P);const L=F(h,b);if(L)return d("unknownVariant",L);const D=te(u,b);if(D)return d("unknownArrangement",D);const M=ue(u,b,l.length);if(M)return d("countOutOfRange",M);const I=me(m,b);if(I)return d("unknownIconPosition",I);const X=ge(g,b);if(X)return d("unknownMark",X);const $t=j(E);if(!n.editor.commands.addBlock(i,c,Te(e,Ae(l)),p))return A(`add_${e}`,{pageId:i,type:c,before:p});const _t=R(i),U=J($t,j(_t));h!==void 0&&U&&n.editor.commands.setVariant(U,h),u!==void 0&&U&&pe(U,u??"",b),m!==void 0&&U&&n.editor.commands.setIconPosition(U,m),g!==void 0&&U&&n.editor.commands.setBlockMark(U,g),n.save();const At=Z(i);return S(At,{blockId:U})}),f(`update_${e}`,{title:`Replace a ${e} block's entries`,description:"Replace a block's content, matching entries by id: an entry carrying an id from get_document is updated in place, one with no id is added, and an id you leave out is removed. Slots you omit from an entry are cleared, so echo back the ones you want kept.",inputSchema:{blockId:o.string().describe(`A block id from get_document, of archetype '${e}'.`),entries:o.array(t).optional().describe(ot(e)),...B(e)?C():{},...de(e)?rt(e):{},...ct(e)?st:{},...ht(e)?dt(e):{}}},async({blockId:i,entries:c,variant:l,arrangement:h,iconPosition:u,mark:m})=>{if(c===void 0)return x(`update_${e}`,"entries",$.get(`update_${e}`).join(", "));const g=_(i);if(!g)return N(i);const p=W(g.type.name);if(p.archetype!==e)return r(p);const b=_e(p,c);if(b)return d("invalidEntry",b);const E=F(l,p);if(E)return d("unknownVariant",E);const P=te(h,p);if(P)return d("unknownArrangement",P);const L=ue(h,p,c.length);if(L)return d("countOutOfRange",L);const D=me(u,p);if(D)return d("unknownIconPosition",D);const M=ge(m,p);return M?d("unknownMark",M):n.editor.commands.updateBlock(i,Te(e,Ae(c)))?(l!==void 0&&n.editor.commands.setVariant(i,l),h!==void 0&&pe(i,h??"",p),u!==void 0&&n.editor.commands.setIconPosition(i,u),m!==void 0&&n.editor.commands.setBlockMark(i,m),n.save(),S(H(y(),i))):A(`update_${e}`,{blockId:i})})};for(const e of we)ve[e]&&wt(e);const kt=T().filter(e=>e.archetype==="prose").map(e=>e.type),vt=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not 'prose'. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${kt.join(", ")}.`);f("add_prose",{title:"Add text to a page",description:"Write a passage onto a page \u2014 paragraphs, headings, lists, a quote and its source \u2014 in ONE call. Each line becomes its own block, so they can afterwards be reordered, restyled or deleted independently. This is how ordinary text gets onto a page.",inputSchema:{pageId:o.string().describe(oe),content:o.array($e()).optional().describe(Sn),...B("prose")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,content:t,before:a})=>{if(t===void 0)return x("add_prose","content",$.get("add_prose").join(", "));const r=R(e);if(!r)return ee(e);if(a&&!_(a))return N(a);const i=Se(t);if(i)return d("invalidLine",i);const c=j(r);for(const m of Gt(t))if(!n.editor.commands.addBlock(e,m.kind,{content:m.lines},a))return A("add_prose",{pageId:e,kind:m.kind,before:a});const l=R(e),h=new Set(c),u=j(l).filter(m=>!h.has(m));return n.save(),S(Z(e),{blockIds:u})}),f("update_prose",{title:"Replace a prose block's text",description:"Rewrite one block's text. A line whose text you echo back exactly is not rewritten at all, so any formatting a person applied to it survives \u2014 read the block first and send back what you want kept. Adding a paragraph is add_prose; changing what a block IS is set_block_type.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'prose'."),content:o.array($e({existing:!0})).optional().describe(En),...B("prose")?C():{}}},async({blockId:e,content:t})=>{if(t===void 0)return x("update_prose","content",$.get("update_prose").join(", "));const a=_(e);if(!a)return N(e);const r=W(a.type.name);if(r.archetype!=="prose")return vt(r);const i=Se(t,{kind:!1,type:r?.type});return i?d("invalidLine",i):!Ke.has(a.type.name)&&t.length!==1?d("invalidLine",`'${r.type}' holds exactly one line and you sent ${t.length}. Nothing was written. Send one line here, and use add_prose to put the others on the page as their own blocks.`):n.editor.commands.updateBlock(e,{content:t})?(n.save(),S(H(y(),e))):A("update_prose",{blockId:e})});const fe=T().filter(e=>e.archetype==="table").map(e=>e.type),Ne=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not 'table'. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${fe.join(", ")}.`),Pe=o.looseObject({headers:o.array(o.string()).optional().describe("The top row of column headings, left to right. Send an empty array for a table with no header row. There is no header COLUMN here \u2014 a person can make the first column headings in the editor, but this payload has one axis for them, so write those cells as ordinary text in each row."),rows:o.array(o.array(o.string())).optional().describe("The body rows, each an array of cell text in the SAME ORDER as `headers`. Every row needs the same number of cells; use '' for a blank one. Plain text only \u2014 no markdown.")}),je="The whole table as {headers, rows}. `rows` is a list of rows, each a list of cell text. Echo back the shape get_document gave you and change the cells you mean: a cell whose text is unchanged is not rewritten, so formatting a person applied to it survives. Adding or removing a row or a column is just sending a longer or shorter list.",Ie=(e,t)=>{const a=ne("table");for(const r of Object.keys(e??{}))if(!a.includes(r))return`a table has no field '${r}'. It takes exactly: ${a.join(", ")}. \`headers\` is the top row of headings and \`rows\` is a list of rows of cell text.`;return Qt(e,t)};f("add_table",{title:"Add a table block",description:`Put a 'table' block on a page \u2014 ${fe.join(", ")}. Use it for values that only mean something at the crossing of two things. `+(fe.length>1?"Every type here takes the same {headers, rows}, so the choice is about how it looks: see list_blocks.":"It takes {headers, rows}."),inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(at("table")),table:Pe.describe(je),...B("table")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,type:t,table:a,variant:r,before:i})=>{const c=G(t);if(!c)return z(t);if(c.archetype!=="table")return Ne(c);const l=R(e);if(!l)return ee(e);if(i&&!_(i))return N(i);const h=Ie(a);if(h)return d("invalidTable",h);const u=F(r,c);if(u)return d("unknownVariant",u);const m=j(l);if(!n.editor.commands.addBlock(e,t,{table:a},i))return A("add_table",{pageId:e,type:t,before:i});const g=R(e),p=J(m,j(g));return r!==void 0&&p&&n.editor.commands.setVariant(p,r),n.save(),S(Z(e),{blockId:p})}),f("update_table",{title:"Replace a table's contents",description:"Rewrite a table. It is patched cell by cell rather than rebuilt, so a cell whose text you echo back unchanged keeps any formatting a person applied to it \u2014 and a person may have JOINED two cells into one, which this cannot describe and does not disturb. A joined cell's text belongs to the row and column it starts at; leave the cells it covers empty.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'table'."),table:Pe.describe(je),...B("table")?C():{}}},async({blockId:e,table:t,variant:a})=>{const r=_(e);if(!r)return N(e);const i=W(r.type.name);if(i.archetype!=="table")return Ne(i);const c=Ie(t,r);if(c)return d("invalidTable",c);const l=F(a,i);return l?d("unknownVariant",l):n.editor.commands.updateBlock(e,{table:t})?(a!==void 0&&n.editor.commands.setVariant(e,a),n.save(),S(H(y(),e))):A("update_table",{blockId:e})});const be=T().filter(e=>e.archetype==="chart").map(e=>e.type),Le=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not a chart. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${be.join(", ")}.`),De=o.looseObject({categories:o.array(o.string()).optional().describe("The labels along the axis, in the order they should read \u2014 one per column of numbers. These are what a reader compares: product lines, quarters, teams."),series:o.array(o.looseObject({name:o.string().optional().describe('What this line of numbers IS. It labels the series in the legend, so name the measure and its unit \u2014 "Revenue ($M)", not "Series 1".'),values:o.array(o.number().nullable()).optional().describe('The numbers, in the SAME ORDER as `categories` and EXACTLY as many. Use null for a gap \u2014 a short list is refused, because "the rest are missing" and "I mis-pasted a column" cannot be told apart.'),as:o.literal("line").optional().describe("Draw this one series as a line over the bars. That combination IS a combo chart; omit it for the block's own drawing.")})).optional().describe(`At most ${en} \u2014 that is how many colours a theme is required to answer for, so a seventh would repeat one.`)}),Me={title:o.string().optional().describe("The chart's title, as ordinary editable text above the plot. Omit to leave it alone; pass '' to clear it."),axisX:o.string().optional().describe("What the categories ARE \u2014 the label under the axis, like 'Product line'. Omit to leave it alone; '' to clear."),axisY:o.string().optional().describe("What the numbers ARE, including the unit \u2014 like 'Revenue ($M)'. Reads up the side of the plot. Omit to leave it alone; '' to clear.")},Ce={stack:o.enum(tn).nullable().optional().describe("How several series share a bar. 'none' puts them side by side; 'stacked' puts them in one bar so the height is the total; 'proportional' makes every bar the same height so what is read is the SHARE rather than the total. It is a SEPARATE axis from the drawing itself, so it combines with whatever `arrangement` names. Omit to leave it alone; 'none' for side by side.")},Be="The whole chart as {categories, series}. `categories` are the axis labels and each entry in `series` is one named line of numbers lining up with them exactly. Echo back the shape get_document gave you and change what you mean. The chart's TITLE and its two AXIS TITLES are not in here \u2014 they are three arguments of their own on this same tool.",He=e=>{const t=ne("chart");for(const a of Object.keys(e??{}))if(!t.includes(a))return`a chart has no field '${a}'. It takes exactly: ${t.join(", ")}. \`categories\` are the axis labels and \`series\` is a list of {name, values}.`;return Jt(e)};f("add_chart",{title:"Add a chart block",description:`Put a chart on a page \u2014 ${be.join(", ")}. Use it for numbers that are COMPARED rather than listed: a trend, a split, a ranking. It takes {categories, series}. For numbers that are only being listed use add_table; for two or three headline figures use add_stats.`,inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(`One of: ${be.join(", ")}.`),chart:De.describe(Be),...Me,...de("chart")?it():{},...Ce,...B("chart")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,type:t,chart:a,title:r,axisX:i,axisY:c,arrangement:l,stack:h,variant:u,before:m})=>{const g=G(t);if(!g)return z(t);if(g.archetype!=="chart")return Le(g);const p=R(e);if(!p)return ee(e);if(m&&!_(m))return N(m);const b=He(a);if(b)return d("invalidChart",b);const E=F(u,g);if(E)return d("unknownVariant",E);const P=te(l,g);if(P)return d("unknownArrangement",P);const L=j(p),D={chart:a,chartText:{title:r,axisX:i,axisY:c}};if(!n.editor.commands.addBlock(e,t,D,m))return A("add_chart",{pageId:e,type:t,before:m});const M=R(e),I=J(L,j(M));return I&&(u!==void 0&&n.editor.commands.setVariant(I,u),l!==void 0&&n.editor.commands.setArrangement(I,l||null),h!==void 0&&n.editor.commands.setStack(I,h||null)),n.save(),S(Z(e),{blockId:I})}),f("update_chart",{title:"Replace a chart's numbers",description:"Rewrite the whole payload of a chart. The three text slots \u2014 its title and both axis titles \u2014 are left alone unless you pass them, and every design choice on the block survives, so a person who restyled the chart keeps their work. To change the DRAWING rather than the numbers, pass `arrangement` or `stack` here.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'chart'."),chart:De.describe(Be),...Me,...de("chart")?it():{},...Ce,...B("chart")?C():{}}},async({blockId:e,chart:t,title:a,axisX:r,axisY:i,arrangement:c,stack:l,variant:h})=>{const u=_(e);if(!u)return N(e);const m=W(u.type.name);if(m.archetype!=="chart")return Le(m);const g=He(t);if(g)return d("invalidChart",g);const p=F(h,m);if(p)return d("unknownVariant",p);const b=te(c,m);if(b)return d("unknownArrangement",b);const E={chart:t,chartText:{title:a,axisX:r,axisY:i}};return n.editor.commands.updateBlock(e,E)?(h!==void 0&&n.editor.commands.setVariant(e,h),c!==void 0&&n.editor.commands.setArrangement(e,c||null),l!==void 0&&n.editor.commands.setStack(e,l||null),n.save(),S(H(y(),e))):A("update_chart",{blockId:e})});const Fe=T().filter(e=>e.archetype==="diagram").map(e=>e.type),Ue=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not a diagram. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${Fe.join(", ")}.`),Ve=o.looseObject({source:o.string().optional().describe("The mermaid text. The whole diagram, as one string with real newlines.")}),We=`Mermaid text. The FIRST LINE names the kind of diagram and is the commonest thing to get wrong: ${nn}. After that, write the RELATIONSHIPS and let the layout engine place everything: there are no coordinates to set. Labels are plain TEXT, with no HTML tags and nothing that loads a picture. Newlines are real, so send the source exactly as it should read.`,ye=async e=>{if(typeof e!="string")return"a diagram's 'source' must be a string of mermaid text.";const t=on(e);if(t)return t;const a=await sn(e);return!a||a.ok?null:`mermaid could not parse this source${a.line?` (line ${a.line})`:""}. ${an(a.message??"",a.line??null)} Fix that line rather than rewriting the diagram; if the message mentions the very first line, the opening keyword is probably wrong \u2014 describe_block lists the kinds.`};return f("add_diagram",{title:"Add a diagram block",description:"Put a diagram on a page \u2014 a flowchart, an org chart, a sequence, a state machine, an entity diagram and about a dozen more. Use it when the meaning is in WHAT CONNECTS TO WHAT. The payload is one string of mermaid text and you place nothing: the layout engine decides where every box goes. For a chart of NUMBERS use add_chart; for a handful of steps with real prose in them use add_items with type `steps`, which is themed and reads better on a slide than a flowchart of the same three boxes. A title or caption is an ordinary `heading` or `text` block above or below it \u2014 this block is only the drawing.",inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(`One of: ${Fe.join(", ")}.`),diagram:Ve.describe(We),...B("diagram")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,type:t,diagram:a,variant:r,before:i})=>{const c=a?.source,l=G(t);if(!l)return z(t);if(l.archetype!=="diagram")return Ue(l);const h=R(e);if(!h)return ee(e);if(i&&!_(i))return N(i);const u=await ye(c);if(u)return d("invalidSource",u);const m=F(r,l);if(m)return d("unknownVariant",m);const g=j(h);if(!n.editor.commands.addBlock(e,t,{diagram:{source:c}},i))return A("add_diagram",{pageId:e,type:t,before:i});const p=J(g,j(R(e)));p&&r!==void 0&&n.editor.commands.setVariant(p,r),n.save();const b=tt(c);return S(Z(e),b?{blockId:p,hint:b}:{blockId:p})}),f("update_diagram",{title:"Replace a diagram's source",description:"Rewrite the mermaid text of a diagram. The whole source at once \u2014 there is no partial edit, because a diagram is one string. Every design choice on the block survives, so a person who resized or magnified it keeps their work.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'diagram'."),diagram:Ve.describe(We),...B("diagram")?C():{}}},async({blockId:e,diagram:t,variant:a})=>{const r=t?.source,i=_(e);if(!i)return N(e);const c=W(i.type.name);if(c.archetype!=="diagram")return Ue(c);const l=await ye(r);if(l)return d("invalidSource",l);const h=F(a,c);if(h)return d("unknownVariant",h);if(!n.editor.commands.updateBlock(e,{diagram:{source:r}}))return A("update_diagram",{blockId:e});a!==void 0&&n.editor.commands.setVariant(e,a),n.save();const u=tt(r);return S(H(y(),e),u?{hint:u}:{})}),f("set_block_type",{title:"Swap a block's rendering",description:"Render the same content as a different block. Free WITHIN an archetype \u2014 a timeline becomes a box keeping every entry and its id. Across archetypes there is no meaning-preserving mapping, so it is refused; remove the block and add the other instead.",inputSchema:{blockId:o.string().describe("A block id from get_document."),type:o.string().describe("The block type to switch to. Must share the current archetype.")}},async({blockId:e,type:t})=>{const a=_(e);if(!a)return N(e);const r=G(t);if(!r)return z(t);const i=W(a.type.name);if(i.archetype!==r.archetype)return d("archetypeMismatch",`Cannot turn '${i.type}' (${i.archetype}) into '${t}' (${r.archetype}) \u2014 the entries mean different things. Types you can swap to: ${T().filter(h=>h.archetype===i.archetype&&h.type!==i.type).map(h=>h.type).join(", ")||"none yet"}.`);const c=H(y(),e);if(!n.editor.commands.setBlockType(e,t))return A("set_block_type",{blockId:e,type:t});n.save();const l=c?k(c.attrs.id):null;return l?S(l):K()}),f("remove_block",{title:"Remove a block",description:"Delete a block and everything in it \u2014 a layout included, with every block in its columns. A page may be left empty \u2014 add its replacement afterwards rather than before.",inputSchema:{blockId:o.string().describe("A block id from get_document.")}},async({blockId:e})=>{const t=ae(y(),e);if(!t||!Mt(t.node.type.name))return N(e);const a=H(y(),e);if(!n.editor.commands.removeBlock(e))return A("remove_block",{blockId:e});n.save();const r=a?k(a.attrs.id):null;return r?S(r):K()}),f("export_html",{title:"Export the document as one HTML file",description:"Write the document to a single HTML file that opens in any browser by double-click, no hosting. Styles and every diagram's drawing are carried inside it; pictures, fonts and a video's player are LINKS (only a video's poster is embedded), so the file expects the internet and degrades without it \u2014 a system font, a glyph where a picture was, the video's still frame. It is a presentation viewer \u2014 swipe or use the arrow keys to move between pages. Returns the path, not the file; the path must be an .html file in a folder that exists. Export reflects the document as it is now, so fix any diagnostics first, and read the reply: it names any picture address the file could not use.",inputSchema:{path:o.string().optional().describe("Where to write it: an .html path in a folder that exists. Defaults to the document's own path with a .html suffix.")}},async({path:e})=>{const t=bn(e??n.path.replace(/\.json$/,"")+".html");if(!t.ok)return d(t.code,t.message);const a=t.path;let r;try{r=await gn(y().toJSON(),wn(n.path))}catch(i){return d("exportFailed",`Export aborted, nothing was written: ${i.message}`)}return Tt(a,r),K({path:a,pages:y().childCount,bytes:Buffer.byteLength(r),...mn(r)})}),w}function H(n,s){let w=null;return n.forEach(v=>{if(!w){if(v.attrs.id===s){w=v;return}v.descendants($=>w?!1:$.attrs?.id===s?(w=v,!1):!0)}}),w}export{Dn as INSTRUCTIONS,zn as createServer};
31
+ check could NOT be made. No fit field means the page was measured and fits.`;function zn(n,s=ln){const w=new St({name:"decaframe",version:"0.1.1"},{instructions:Dn}),v=w.close.bind(w);w.close=async()=>{await rn(),await v()};const $=new Map,O=e=>[...$].filter(([,t])=>t.includes(e)).map(([t])=>t),f=(e,t,a)=>{const r=t.inputSchema??{},i=Object.keys(r);$.set(e,i),w.registerTool(e,{...t,inputSchema:o.looseObject(r)},(async c=>{const l=Object.keys(c??{}).filter(h=>!i.includes(h));if(l.length){const h=l.map(u=>[u,O(u)]).filter(([,u])=>u.length).map(([u,m])=>`'${u}' is an argument of ${m.join(" and ")}`);return d("unknownArgument",`${e} has no argument ${l.map(u=>`'${u}'`).join(", ")}. Nothing was written. It takes exactly: ${i.length?i.join(", "):"no arguments"}.`+(h.length?` ${h.join("; ")}.`:""))}return a(c)}))},x=(e,t,a)=>d("missingArgument",`${e} needs '${t}' and you sent none. Nothing was written. It takes: ${a}.`),y=()=>(n.sync(),n.editor.state.doc),k=e=>{const t=ae(y(),e);return t&&t.node.type.name==="page"?t.node:null},_=e=>{const t=ae(y(),e);return t&&W(t.node.type.name)?t.node:null},R=e=>{const t=ae(y(),e);if(!t)return null;const a=t.node.type.name;return a==="page"||a==="layoutRegion"?t.node:null},Z=e=>{const t=R(e);return t?t.type.name==="page"?t:H(y(),e):null},he=e=>{const t=[];return e.forEach(a=>{a.type.name==="layout"&&t.push(a.attrs.id)}),t},Ee=(e,t)=>{const a={};return t&&e.forEach(r=>{r.type.name!=="layout"||r.attrs.id!==t||r.forEach(i=>{a[i.attrs.region]=i.attrs.id})}),a},Oe=e=>d("unknownLayout",`No layout '${e}'. This document's layouts: ${ke.map(t=>t.name).join(", ")}.`),ee=e=>d("unknownPage",`No page or layout region '${e}'. Pages in this document: ${V(y()).join(", ")}. A layout's region ids come back from add_layout and from get_document. Call get_document for the current ids.`),Y=e=>d("unknownPage",`No page '${e}'. Pages in this document: ${V(y()).join(", ")}. Call get_document for the current ids.`),N=e=>{const t=[];return y().forEach(a=>t.push(...j(a),...he(a))),d("unknownBlock",`No block '${e}'. Blocks in this document: ${t.join(", ")}. Call get_document for the current ids.`)},q=()=>Xe(y().attrs.theme).theme,z=e=>d("unknownType",`No block type '${e}'. Available: ${In()}.`),F=(e,t)=>{if(e==null||e==="")return null;const a=ce(t.variantSlots,q(),{core:!0});return a.includes(e)?null:`'${t.type}' has no variant '${e}'. It offers: ${a.join(", ")}. Styles differ per block \u2014 a block only offers one it has somewhere to put, so a paragraph offers none and a callout page is a box of one entry wearing an accented edge. list_blocks gives each block its own list. A theme may also add names for people editing by hand; those are not on this surface, because they change with the theme and these do not.`},xe=e=>({arrangements:e.arrangements.map(t=>t.name),figures:(e.figures??[]).map(t=>t.name)}),te=(e,t)=>{if(e==null||e==="")return null;const{arrangements:a,figures:r}=xe(t);if(a.includes(e)||r.includes(e))return null;const i=[...a,...r];return`'${t.type}' has no shape '${e}'.`+(i.length?` It offers: ${i.join(", ")}. Each has its own count range \u2014 list_blocks names them, because a shape changes how much text fits.`+(r.length?` ${r.join(", ")} ${r.length>1?"are figures":"is a figure"} \u2014 an illustrated shape whose count range is a LIMIT, not guidance: a write outside it is refused. describe_block gives the range.`:""):" It has one shape, so omit the field.")},ue=(e,t,a)=>{if(e==null||e==="")return null;const r=(t.figures??[]).find(l=>l.name===e);if(!r)return null;const[i,c]=r.countRange;return a>=i&&a<=c?null:`'${e}' is a figure and holds ${i}\u2013${c} items, not ${a}. A figure draws a fixed number of places to stand, so unlike an arrangement \u2014 whose count range is guidance \u2014 this is refused rather than reported. Send a count in range, or pick a plain arrangement from describe_block.`},pe=(e,t,a)=>xe(a).figures.includes(t)?n.editor.commands.setFigure(e,t):n.editor.commands.setArrangement(e,t),me=(e,t)=>e==null||e===""?null:ie.includes(e)?t.slots.includes("icon")?t.marks.includes("icon")?`'${t.type}' puts each item's icon in its MARK \u2014 the numeral's own place \u2014 so there is no gutter to move it to. Use \`mark: 'icon'\` to turn the glyphs on, and set each entry's \`icon\`. Omit this field.`:null:`'${t.type}' draws no icon, so there is nowhere to put one \u2014 it stores an icon on an entry and never renders it. Blocks that do: ${T().filter(a=>a.slots.includes("icon")).map(a=>a.type).join(", ")}. Omit the field, or use set_block_type to swap to one of those.`:`There is no icon position '${e}'. The positions are: ${ie.join(", ")}. Pass '' to go back to whatever this block's shape does by default.`,ge=(e,t)=>{if(e==null||e===""||t.marks.includes(e)&&e!==t.marks[0])return null;if(!t.marks.length){const a=T().filter(r=>r.marks.length>0).map(r=>r.type);return`'${t.type}' marks its items with nothing, so there is nothing to change. Blocks that do: ${a.join(", ")}. Omit the field.`}return e===t.marks[0]?`'${e}' is what '${t.type}' already does when nothing is stored, so storing it would give one state two spellings. Pass '' to go back to it, or omit the field.`:`'${t.type}' has no mark '${e}'. It offers: ${t.marks.join(", ")}. '${t.marks[0]}' is the default and needs no field.`},A=(e,t)=>d("refused",`${e} was refused and the document is unchanged. Arguments: ${JSON.stringify(t)}. Commands refuse rather than corrupt: check that ids exist and belong where you used them, that entry ids came from this block, that a type swap stays inside one archetype, and that the change would not leave a page or the document empty.`),ft="not measured: fix the problems above first, then this page is checked against its frame.",Re="not measured: this document is fluid, so its pages grow with their content and none of them can overflow. Nothing here will tell you a page is too long \u2014 set a fixed format (16:9, 4:3 or A4) if you want that check back.",bt=async e=>{const t=Yt(e),a=e.attrs.id;if(t.length)return{diagnostics:t,fit:ft};if(!a)return{fit:"this page carries no id, so it could not be measured."};const r=await s(n.editor.getJSON(),a);if(r.problem)return{fit:r.problem};const i=hn(r,a);if(!i)return{fit:`page '${a}' did not render, so fit was not measured.`};if(!i.bounded)return{fit:Re};const l=jt.includes(String(y().attrs.format??""))?void 0:cn(dn(r,a)),h=un(r,a),u=h?{...i,over:h.over}:i,m=Je(u,n.fitSeen(a,u.over)),g=pn(h?.scale,!!m);return{...m?{diagnostics:[m]}:{},...l?{fill:l}:{},...g?{scale:g}:{}}},S=async(e,t={})=>K({...t,page:vn(e),...await bt(e)}),re=async(e={})=>{const t=y(),a=Kt(t),r=new Set(a.map(u=>u.pageId)),c=t.content.childCount>r.size?await s(n.editor.getJSON()):{},l=[];let h=!1;for(const u of c.pages??[]){if(r.has(u.id))continue;if(!u.bounded){h=!0;continue}const m=Je(u,n.fitSeen(u.id,u.over));m&&l.push(m)}return Q({...e,...kn(t),diagnostics:[...a,...l],...c.problem?{fit:c.problem}:h?{fit:Re}:{}})};f("list_blocks",{title:"List block types",description:"The block menu. One short row per block: what it is, when to pick it, how many entries its layout is designed for, and which entry slots it draws. Start here \u2014 block types are discovered, never memorised. For a worked example of one block, call describe_block. There is no per-block color, font or size on this surface: those values are the theme's and the theme is the account's. Change how a block LOOKS with its `variant`."},async()=>Q({variantsNote:"Each row carries the styles THAT block offers, because a block only offers one it has somewhere to put \u2014 a paragraph offers none. The theme decides what each name looks like, so a name means the same thing in every theme. Pass one as `variant`.",blocks:T().map(e=>({type:e.type,archetype:e.archetype,description:e.description,whenToUse:e.whenToUse,countRange:e.countRange,...e.colRange?{colRange:e.colRange}:{},renders:e.slots,variants:ce(e.variantSlots,q(),{core:!0}),...e.arrangements.length?{arrangements:e.arrangements.map(t=>t.name)}:{},...e.figures?.length?{figures:e.figures.map(t=>({name:t.name,countRange:t.countRange}))}:{},...e.marks.length?{marks:e.marks}:{}}))})),f("describe_block",{title:"Describe one block",description:"A worked example for one block type, ready to copy. Does NOT return a schema \u2014 the entry schema belongs to the archetype and is already on that archetype's add_ tool, identical for every block that shares it. Call this when you have chosen a block and want to see what good content looks like in it.",inputSchema:{type:o.string().describe('A block type from list_blocks, e.g. "timeline".')}},async({type:e})=>{const t=G(e);return t?Q({type:t.type,archetype:t.archetype,countRange:t.countRange,...t.colRange?{colRange:t.colRange}:{},renders:t.slots,whenToUse:t.whenToUse,...t.slots.includes("icon")?{icons:Nt}:{},...t.marks.length?{marks:t.marks}:{},variants:ce(t.variantSlots,q(),{core:!0}),...t.arrangements.length?{arrangements:t.arrangements.map(a=>({name:a.name,whenToUse:a.whenToUse,countRange:a.countRange}))}:{},...t.figures?.length?{figures:t.figures.map(a=>({name:a.name,whenToUse:`${a.whenToUse} It holds ${$n(a.countRange)} and a write outside that range is refused, unlike an arrangement whose range is guidance.`,countRange:a.countRange}))}:{},example:{tool:`add_${t.archetype}`,arguments:{pageId:"<a page id from get_document>",...$.get(`add_${t.archetype}`)?.includes("type")?{type:t.type}:{},[Ln[t.archetype]]:t.example}}}):z(e)}),f("get_document",{title:"Read the document",description:"The whole document: every page with its id, heading and blocks, and every block with its id, type and entries. Entry ids are what the update_ tools match on \u2014 echo one back to change that entry, omit it to add a new one, leave it out of the array to remove it. The reply also names the FILE this document is saved in, which defaults to the folder this server was started in. If the person named a file, or wants more than one document in that folder, call open_document BEFORE authoring."},async()=>re({file:n.path})),f("list_templates",{title:"List the templates",description:"The page designs available, each with the content it suits \u2014 the shelf to choose from before adding any page. Every one ADDS a single composed page; none replaces or alters what is already in the document. Add one with add_template_page.",inputSchema:{}},async()=>Q({templates:gt().map(e=>({id:e.id,name:e.name,whenToUse:e.whenToUse,look:Dt(e)}))})),f("add_template_page",{title:"Add a page from a template",description:"ADD ONE COMPOSED PAGE \u2014 heading, blocks, shape and design already chosen. This is how a page should normally be added; add_blank_page is for when no template suits. It INSERTS a page of its own, so do not add a page first. The text it arrives with is PLACEHOLDER, not content: fill every slot with set_page, which takes the whole page in one call.",inputSchema:{templateId:o.string().describe("A template id from list_templates."),before:o.string().optional().describe("Page id to insert before, for a PAGE template. Omit to append. Never an index.")}},async({templateId:e,before:t})=>{const a=ze(e);if(!a.ok)return d("unknownTemplate",`No template '${e}'. Templates are: ${gt().map(h=>h.id).join(", ")}. Call list_templates for the current ids.`);const r=a.value;if(t&&!k(t))return Y(t);const i=V(y());if(!n.editor.commands.applyTemplate(e,{before:t}))return A("apply_template",{templateId:e,before:t});n.save();const c=J(i,V(y())),l=c?k(c):null;return l?S(l,{pageId:c}):K({pageId:c})}),f("remove_page",{title:"Remove a page",description:"Delete a page and everything on it. Use it to clear a page you no longer want \u2014 including the blank one a document starts with, if you added your own pages instead of filling it. A document must keep at least one page, so removing the last one is refused.",inputSchema:{pageId:o.string().describe("A page id from get_document.")}},async({pageId:e})=>k(e)?V(y()).length===1?d("lastPage",`page '${e}' is the only page in this document and a document must hold at least one. Nothing was written. Put content on it instead, or add another page first.`):n.editor.commands.removePage(e)?(n.save(),re()):A("remove_page",{pageId:e}):Y(e)),f("open_document",{title:"Open or name the document file",description:"Choose the file this document is saved in. If the file exists it is OPENED and becomes the document you are working on; if it does not, it is where this document will be saved. Call it BEFORE authoring when the person names a file or wants more than one document in a folder \u2014 otherwise everything lands in the default the reply from get_document names. Pass a full path when you know the folder you are working in; a bare name is resolved against the folder this server was started in, which is not the same for every client.",inputSchema:{file:o.string().describe("Path to a .json document \u2014 absolute, or relative to the folder this server started in. The folder must already exist; this creates no directories.")}},async({file:e})=>{const t=fn(e);if(!t.ok)return d(t.code,t.message);try{n.openAt(t.path)}catch(a){return d("unreadableDocument",`${yn(e,a)} Nothing was opened; you are still working on the document you were before.`)}return console.error(`decaframe mcp \xB7 document: ${t.path}`),re({file:t.path,opened:t.existed})}),f("set_document",{title:"Set the document's title, shape or palette",description:"Change facts that belong to the WHOLE document rather than to one page: its title, its frame shape, where content sits in each frame, and which palette it is designed in. Omitted fields are left alone. Frame shape and content position live here, not on set_page, because a document is one shape and keeps one rhythm.",inputSchema:{title:o.string().optional().describe("The document's name. Not a heading on any page."),format:o.enum([...xt]).optional().describe("Frame shape for every page. Defaults to 16:9. A4 is a portrait sheet."),align:o.enum([...Et]).optional().describe("Where blocks sit in the leftover vertical space on EVERY page: top, middle or bottom. Defaults to middle."),mode:o.enum(["light","dark"]).optional().describe("Which palette the document is designed in: light or dark. A page may override it.")}},async e=>n.editor.commands.setDocument(e)?(n.save(),re()):A("set_document",e)),f("add_blank_page",{title:"Add a blank page",description:`Append a BLANK page, or insert one before an existing page. Reach for this only when no template suits the content \u2014 add_template_page adds a page that is already composed, and is the normal way to add one. A blank page holds nothing until you put blocks on it with ${we.map(e=>`add_${e}`).join(", ")}; set_page then fills every slot in one call.`,inputSchema:{...ut,...pt,before:o.string().optional().describe("Page id to insert before. Omit to append at the end. Never an index.")}},async e=>{if(e.before&&!k(e.before))return Y(e.before);const t=V(y());if(!n.editor.commands.addPage(e))return A("add_page",e);n.save();const a=J(t,V(y())),r=a?k(a):null;return r?S(r,{pageId:a}):K({pageId:a})}),f("import_markdown",{title:"Import a markdown document",description:'Write a whole markdown document at once \u2014 the fast path for anything already in markdown, and the ONE call that lays down a whole deck: pages, their look, and this catalogue\'s own blocks. WITHOUT pageId a NEW PAGE starts at every heading of `splitOn` (1 by default), that heading its title, and at every `::page\u2026::` line, so a page needs no heading; WITH pageId everything lands on that page and nothing splits. Ordinary markdown arrives as itself \u2014 paragraphs, headings, lists, tables, fenced code; a line that is only an image becomes a picture block; a ```mermaid fence becomes a DIAGRAM, and one that does not parse refuses the whole import. MARKERS say what markdown cannot, one per line, `::name: key=value \u2026::`, keys being this surface\'s own argument names: `::page: eyebrow="\u2026" subtitle="\u2026" accent=lead src=https://\u2026 alt="\u2026" surface=muted mode=dark::` right under a page\'s heading; `::layout: two-cols::` then `::col-1::`, `::col-2::` before each column; and a BLOCK marker over what builds it. `::box: variant=outlined arrangement=grid::`, `::steps: arrangement=funnel::`, `::timeline::`: the ITEMS are the headings under it, `### Title {icon=zap meta="\u2026" emphasis}`, the item\'s body beneath. `::stat-row::`: `### 84% {label="\u2026"}`, the note beneath. `::image::`, `::image-grid::`, `::people::`, `::image-with-text::`: one `![alt](https://\u2026) {caption="\u2026"}` line per picture, a heading after it its title, paragraphs its words; `::youtube::` over a bare address. `::chart: arrangement=line stack=stacked title="\u2026" axisX="\u2026" axisY="\u2026"::` over a table \u2014 header = series names, each row a category and its values, an empty cell a gap. `::text: arrangement=xl::` or `::table: variant=banded::` over the block beneath. `::end::` closes a block; the next marker or page closes it too, and nothing nests. A wrong name is REFUSED by name and nothing is written. It only creates: to change what exists, read it back and use the update_ tools.',inputSchema:{markdown:o.string().describe("The markdown document, as text."),pageId:o.string().optional().describe("A page id from get_document. Everything lands on that page and NOTHING is split. Omit to make a page per section instead."),splitOn:o.number().int().optional().describe("Which heading level starts a new page, 1 to 6. Default 1. Ignored when pageId is set."),before:o.string().optional().describe("Insert before this id \u2014 a block id when pageId is set, a page id otherwise. Omit to append at the end.")}},async({markdown:e,pageId:t,splitOn:a,before:r})=>{if(t&&!k(t))return Y(t);if(a!==void 0&&(a<1||a>6))return d("invalidLine",`splitOn is ${a}; markdown has six heading levels, so it must be 1 to 6. Nothing was written.`);const i=Ut(Ft(e),{theme:q(),pageless:!!t});if(i)return d("invalidLine",`${i} Nothing was written.`);const c=t?[{blocks:Ht(e,q())}]:Vt(e,a??1,q()),l=p=>!!p.text?.trim()||!!p.attrs?.src||!!p.attrs?.source||(p.content??[]).some(l);if(!c.some(p=>p.title?.trim()||p.blocks.some(l)))return d("invalidLine","that markdown draws nothing \u2014 no text, no pictures, no headings. Nothing was written. An empty document, one that is only whitespace, or one that is only HTML comments has nothing this can add.");const h=p=>p.flatMap(b=>b.type==="diagram"?[String(b.attrs?.source??"")]:h(b.content??[]));for(const p of c.flatMap(b=>h(b.blocks))){const b=await ye(p);if(b)return d("invalidSource",`In a \`\`\`mermaid fence: ${b} Nothing was written.`)}const u=V(y()),m=t?j(k(t)):[];if(!n.editor.commands.importMarkdown(c,t,r))return A("import_markdown",{pageId:t,before:r});if(n.save(),t){const p=j(k(t)),b=new Set(m);return S(k(t),{blockIds:p.filter(E=>!b.has(E))})}const g=new Set(u);return K({pageIds:V(y()).filter(p=>!g.has(p))})}),f("add_layout",{title:"Add a layout",description:`Put a LAYOUT on a page \u2014 a container that arranges other blocks side by side. It comes back with a region id per column; pass one of those to ${we.map(e=>`add_${e}`).join(", ")} instead of a page id to put a block inside that column. The layout holds no text of its own. For a picture running down one side of the page, this is NOT the tool \u2014 that is an ACCENT and it belongs to the page: use set_page.`,inputSchema:{pageId:o.string().describe("A page id from get_document. A layout only goes on a page."),layout:o.string().describe(ke.map(e=>`${e.name} \u2014 ${e.whenToUse}`).join(" | ")),before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,layout:t,before:a})=>{const r=k(e);if(!r)return Y(e);if(!et(t))return Oe(t);if(a&&!_(a))return N(a);const i=he(r);if(!n.editor.commands.addLayout(e,t,a))return A("add_layout",{pageId:e,layout:t,before:a});n.save();const c=k(e),l=he(c).find(h=>!i.includes(h));return S(c,{blockId:l,regions:Ee(c,l)})}),f("set_layout",{title:"Change which layout a container uses",description:"Re-arrange a layout that already exists. Everything inside it is KEPT \u2014 every block and every region id survives, so this is safe to call on a full layout. If the new layout has fewer regions than the old one, the leftover blocks move into the last region rather than being deleted.",inputSchema:{blockId:o.string().describe("A layout id from get_document or add_layout."),layout:o.string().describe(`One of: ${ke.map(e=>e.name).join(", ")}.`)}},async({blockId:e,layout:t})=>{const a=ae(y(),e);if(!a||a.node.type.name!=="layout")return d("unknownBlock",`'${e}' is not a layout. set_layout takes the id add_layout returned; use set_block_type for an ordinary block.`);if(!et(t))return Oe(t);if(!n.editor.commands.setLayout(e,t))return A("set_layout",{blockId:e,layout:t});n.save();const r=H(y(),e);return S(r,{blockId:e,regions:Ee(r,e)})});const yt=async(e,t)=>{const a="Nothing was written \u2014 the whole call is refused so the page is never left half-filled.",r=c=>`blocks[${c}]`,i=[];for(const[c,l]of t.entries()){const h=String(l.blockId??""),u=_(h);if(!u)return{refusal:d("unknownBlock",`${r(c)} names block '${h}', which is not in this document. ${a} Block ids come back from apply_template and get_document.`)};const m=H(y(),h);if(m&&m.attrs.id!==e)return{refusal:d("wrongPage",`${r(c)} names block '${h}', which is on page '${m.attrs.id}', not the page '${e}' you are setting. ${a} Call set_page once per page.`)};const g=W(u.type.name),p=l.entries,b=l.content;if(g.archetype==="prose"){if(p!==void 0)return{refusal:d("wrongArchetype",`${r(c)} sent 'entries' to '${g.type}', which is a 'prose' block and takes 'content' \u2014 the same field update_prose takes. ${a}`)};if(b===void 0)return{refusal:d("invalidLine",`${r(c)} names no 'content'. ${a}`)};const X=Se(b,{kind:!1,type:g.type});if(X)return{refusal:d("invalidLine",`${r(c)}: ${X}`)};if(!Ke.has(u.type.name)&&b.length!==1)return{refusal:d("invalidLine",`${r(c)}: '${g.type}' holds exactly one line and you sent ${b.length}. ${a} Use add_prose to put the others on the page as their own blocks.`)};i.push(()=>n.editor.commands.updateBlock(h,{content:b}));continue}if(!ve[g.archetype])return{refusal:d("wrongArchetype",`${r(c)} names '${g.type}', a '${g.archetype}' block, which this array cannot fill. ${a} Use update_${g.archetype} for it.`)};if(b!==void 0)return{refusal:d("wrongArchetype",`${r(c)} sent 'content' to '${g.type}', which is a '${g.archetype}' block and takes 'entries'. ${a}`)};if(p===void 0)return{refusal:d("invalidEntry",`${r(c)} names no 'entries'. ${a}`)};const E=l.variant,P=l.arrangement,L=l.iconPosition,D=l.mark,M=_e(g,p),I=M??F(E,g)??te(P,g)??ue(P,g,p.length)??me(L,g)??ge(D,g);if(I)return{refusal:d(I===M?"invalidEntry":"invalidBlock",`${r(c)}: ${I} ${a}`)};i.push(()=>n.editor.commands.updateBlock(h,Te(g.archetype,Ae(p)))?(E!==void 0&&n.editor.commands.setVariant(h,E),P!==void 0&&pe(h,P??"",g),L!==void 0&&n.editor.commands.setIconPosition(h,L),D!==void 0&&n.editor.commands.setBlockMark(h,D),!0):!1)}return{run:i}};f("set_page",{title:"Set a page's heading, look or block content",description:"Change a page's own heading and look, and FILL ITS BLOCKS IN ONE CALL. Omitted fields are left alone; an empty string removes that heading slot. `blocks` takes the payload each block's archetype takes, so a template applied and then filled costs two calls rather than one per block; every block is checked before any is written, so a refusal changes nothing. The frame's SHAPE and where content sits in it are the document's \u2014 set_document.",inputSchema:{pageId:o.string().describe("A page id from get_document."),...ut,...pt,...jn,blocks:o.array(o.looseObject({blockId:o.string().describe("A block id, from add_template_page or get_document."),entries:o.array(o.looseObject({})).optional().describe("items, stats or media \u2014 what update_<archetype> takes."),content:o.array($e({existing:!0})).optional().describe("prose \u2014 what update_prose takes."),variant:o.string().nullable().optional().describe(`The block's style \u2014 a core name such as ${Ge.slice(1,4).join(", ")}, each glossed on add_items' own \`variant\`. Choose one; omit to keep the template's.`),arrangement:o.string().nullable().optional().describe("Its shape or figure \u2014 the names the archetype's add_ tool takes on `arrangement`. Omit to keep the template's."),iconPosition:o.string().nullable().optional().describe(`Where its icons sit: ${ie.join(", ")}. Only a block that draws icons takes it.`),mark:o.string().nullable().optional().describe(`What its items are marked with \u2014 ${lt("items")} \u2014 as add_items takes it.`)})).optional().describe("THE FAST PATH AFTER add_template_page \u2014 the page arrives composed and placeholder-written, and this replaces every slot in ONE call.")}},async({pageId:e,accent:t,accentSrc:a,accentAlt:r,blocks:i,...c})=>{if(!k(e))return Y(e);let l=[];if(i!==void 0){const u=await yt(e,i);if("refusal"in u)return u.refusal;l=u.run}if(t!==void 0||a!==void 0||r!==void 0){const u=a?Qe(a,"image"):null;if(u)return d("invalidSrc",u);if(!n.editor.commands.setAccent(e,{...t!==void 0?{position:t}:{},...a!==void 0?{src:a}:{},...r!==void 0?{alt:r}:{}}))return A("set_page",{pageId:e,accent:t,accentSrc:a})}if(Object.keys(c).length&&!n.editor.commands.setPage(e,c))return A("set_page",{pageId:e,...c});for(const[u,m]of l.entries())if(!m())return A("set_page",{pageId:e,block:`blocks[${u}]`});return n.save(),S(k(e))});const wt=e=>{const t=ve[e],a=T().filter(i=>i.archetype===e).map(i=>i.type),r=i=>d("wrongArchetype",`'${i.type}' is a '${i.archetype}' block, not '${e}'. Use add_${i.archetype} / update_${i.archetype} for it. This tool takes: ${a.join(", ")}.`);f(`add_${e}`,{title:`Add a ${e} block`,description:`Put ${/^[aeiou]/.test(e)?"an":"a"} '${e}' block on a page \u2014 ${a.join(", ")}. Every type here takes the same entries, so switching later with set_block_type keeps what you wrote; \`type\` names what each one is for.`,inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(at(e)),entries:o.array(t).optional().describe(ot(e)),...B(e)?C():{},...de(e)?rt(e):{},...ct(e)?st:{},...ht(e)?dt(e):{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:i,type:c,entries:l,variant:h,arrangement:u,iconPosition:m,mark:g,before:p})=>{if(l===void 0)return x(`add_${e}`,"entries",$.get(`add_${e}`).join(", "));const b=G(c);if(!b)return z(c);if(b.archetype!==e)return r(b);const E=R(i);if(!E)return ee(i);if(p&&!_(p))return N(p);const P=_e(b,l);if(P)return d("invalidEntry",P);const L=F(h,b);if(L)return d("unknownVariant",L);const D=te(u,b);if(D)return d("unknownArrangement",D);const M=ue(u,b,l.length);if(M)return d("countOutOfRange",M);const I=me(m,b);if(I)return d("unknownIconPosition",I);const X=ge(g,b);if(X)return d("unknownMark",X);const $t=j(E);if(!n.editor.commands.addBlock(i,c,Te(e,Ae(l)),p))return A(`add_${e}`,{pageId:i,type:c,before:p});const _t=R(i),U=J($t,j(_t));h!==void 0&&U&&n.editor.commands.setVariant(U,h),u!==void 0&&U&&pe(U,u??"",b),m!==void 0&&U&&n.editor.commands.setIconPosition(U,m),g!==void 0&&U&&n.editor.commands.setBlockMark(U,g),n.save();const At=Z(i);return S(At,{blockId:U})}),f(`update_${e}`,{title:`Replace a ${e} block's entries`,description:"Replace a block's content, matching entries by id: an entry carrying an id from get_document is updated in place, one with no id is added, and an id you leave out is removed. Slots you omit from an entry are cleared, so echo back the ones you want kept.",inputSchema:{blockId:o.string().describe(`A block id from get_document, of archetype '${e}'.`),entries:o.array(t).optional().describe(ot(e)),...B(e)?C():{},...de(e)?rt(e):{},...ct(e)?st:{},...ht(e)?dt(e):{}}},async({blockId:i,entries:c,variant:l,arrangement:h,iconPosition:u,mark:m})=>{if(c===void 0)return x(`update_${e}`,"entries",$.get(`update_${e}`).join(", "));const g=_(i);if(!g)return N(i);const p=W(g.type.name);if(p.archetype!==e)return r(p);const b=_e(p,c);if(b)return d("invalidEntry",b);const E=F(l,p);if(E)return d("unknownVariant",E);const P=te(h,p);if(P)return d("unknownArrangement",P);const L=ue(h,p,c.length);if(L)return d("countOutOfRange",L);const D=me(u,p);if(D)return d("unknownIconPosition",D);const M=ge(m,p);return M?d("unknownMark",M):n.editor.commands.updateBlock(i,Te(e,Ae(c)))?(l!==void 0&&n.editor.commands.setVariant(i,l),h!==void 0&&pe(i,h??"",p),u!==void 0&&n.editor.commands.setIconPosition(i,u),m!==void 0&&n.editor.commands.setBlockMark(i,m),n.save(),S(H(y(),i))):A(`update_${e}`,{blockId:i})})};for(const e of we)ve[e]&&wt(e);const kt=T().filter(e=>e.archetype==="prose").map(e=>e.type),vt=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not 'prose'. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${kt.join(", ")}.`);f("add_prose",{title:"Add text to a page",description:"Write a passage onto a page \u2014 paragraphs, headings, lists, a quote and its source \u2014 in ONE call. Each line becomes its own block, so they can afterwards be reordered, restyled or deleted independently. This is how ordinary text gets onto a page.",inputSchema:{pageId:o.string().describe(oe),content:o.array($e()).optional().describe(Sn),...B("prose")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,content:t,before:a})=>{if(t===void 0)return x("add_prose","content",$.get("add_prose").join(", "));const r=R(e);if(!r)return ee(e);if(a&&!_(a))return N(a);const i=Se(t);if(i)return d("invalidLine",i);const c=j(r);for(const m of Gt(t))if(!n.editor.commands.addBlock(e,m.kind,{content:m.lines},a))return A("add_prose",{pageId:e,kind:m.kind,before:a});const l=R(e),h=new Set(c),u=j(l).filter(m=>!h.has(m));return n.save(),S(Z(e),{blockIds:u})}),f("update_prose",{title:"Replace a prose block's text",description:"Rewrite one block's text. A line whose text you echo back exactly is not rewritten at all, so any formatting a person applied to it survives \u2014 read the block first and send back what you want kept. Adding a paragraph is add_prose; changing what a block IS is set_block_type.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'prose'."),content:o.array($e({existing:!0})).optional().describe(En),...B("prose")?C():{}}},async({blockId:e,content:t})=>{if(t===void 0)return x("update_prose","content",$.get("update_prose").join(", "));const a=_(e);if(!a)return N(e);const r=W(a.type.name);if(r.archetype!=="prose")return vt(r);const i=Se(t,{kind:!1,type:r?.type});return i?d("invalidLine",i):!Ke.has(a.type.name)&&t.length!==1?d("invalidLine",`'${r.type}' holds exactly one line and you sent ${t.length}. Nothing was written. Send one line here, and use add_prose to put the others on the page as their own blocks.`):n.editor.commands.updateBlock(e,{content:t})?(n.save(),S(H(y(),e))):A("update_prose",{blockId:e})});const fe=T().filter(e=>e.archetype==="table").map(e=>e.type),Ne=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not 'table'. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${fe.join(", ")}.`),Pe=o.looseObject({headers:o.array(o.string()).optional().describe("The top row of column headings, left to right. Send an empty array for a table with no header row. There is no header COLUMN here \u2014 a person can make the first column headings in the editor, but this payload has one axis for them, so write those cells as ordinary text in each row."),rows:o.array(o.array(o.string())).optional().describe("The body rows, each an array of cell text in the SAME ORDER as `headers`. Every row needs the same number of cells; use '' for a blank one. Plain text only \u2014 no markdown.")}),je="The whole table as {headers, rows}. `rows` is a list of rows, each a list of cell text. Echo back the shape get_document gave you and change the cells you mean: a cell whose text is unchanged is not rewritten, so formatting a person applied to it survives. Adding or removing a row or a column is just sending a longer or shorter list.",Ie=(e,t)=>{const a=ne("table");for(const r of Object.keys(e??{}))if(!a.includes(r))return`a table has no field '${r}'. It takes exactly: ${a.join(", ")}. \`headers\` is the top row of headings and \`rows\` is a list of rows of cell text.`;return Qt(e,t)};f("add_table",{title:"Add a table block",description:`Put a 'table' block on a page \u2014 ${fe.join(", ")}. Use it for values that only mean something at the crossing of two things. `+(fe.length>1?"Every type here takes the same {headers, rows}, so the choice is about how it looks: see list_blocks.":"It takes {headers, rows}."),inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(at("table")),table:Pe.describe(je),...B("table")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,type:t,table:a,variant:r,before:i})=>{const c=G(t);if(!c)return z(t);if(c.archetype!=="table")return Ne(c);const l=R(e);if(!l)return ee(e);if(i&&!_(i))return N(i);const h=Ie(a);if(h)return d("invalidTable",h);const u=F(r,c);if(u)return d("unknownVariant",u);const m=j(l);if(!n.editor.commands.addBlock(e,t,{table:a},i))return A("add_table",{pageId:e,type:t,before:i});const g=R(e),p=J(m,j(g));return r!==void 0&&p&&n.editor.commands.setVariant(p,r),n.save(),S(Z(e),{blockId:p})}),f("update_table",{title:"Replace a table's contents",description:"Rewrite a table. It is patched cell by cell rather than rebuilt, so a cell whose text you echo back unchanged keeps any formatting a person applied to it \u2014 and a person may have JOINED two cells into one, which this cannot describe and does not disturb. A joined cell's text belongs to the row and column it starts at; leave the cells it covers empty.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'table'."),table:Pe.describe(je),...B("table")?C():{}}},async({blockId:e,table:t,variant:a})=>{const r=_(e);if(!r)return N(e);const i=W(r.type.name);if(i.archetype!=="table")return Ne(i);const c=Ie(t,r);if(c)return d("invalidTable",c);const l=F(a,i);return l?d("unknownVariant",l):n.editor.commands.updateBlock(e,{table:t})?(a!==void 0&&n.editor.commands.setVariant(e,a),n.save(),S(H(y(),e))):A("update_table",{blockId:e})});const be=T().filter(e=>e.archetype==="chart").map(e=>e.type),Le=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not a chart. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${be.join(", ")}.`),De=o.looseObject({categories:o.array(o.string()).optional().describe("The labels along the axis, in the order they should read \u2014 one per column of numbers. These are what a reader compares: product lines, quarters, teams."),series:o.array(o.looseObject({name:o.string().optional().describe('What this line of numbers IS. It labels the series in the legend, so name the measure and its unit \u2014 "Revenue ($M)", not "Series 1".'),values:o.array(o.number().nullable()).optional().describe('The numbers, in the SAME ORDER as `categories` and EXACTLY as many. Use null for a gap \u2014 a short list is refused, because "the rest are missing" and "I mis-pasted a column" cannot be told apart.'),as:o.literal("line").optional().describe("Draw this one series as a line over the bars. That combination IS a combo chart; omit it for the block's own drawing.")})).optional().describe(`At most ${en} \u2014 that is how many colours a theme is required to answer for, so a seventh would repeat one.`)}),Me={title:o.string().optional().describe("The chart's title, as ordinary editable text above the plot. Omit to leave it alone; pass '' to clear it."),axisX:o.string().optional().describe("What the categories ARE \u2014 the label under the axis, like 'Product line'. Omit to leave it alone; '' to clear."),axisY:o.string().optional().describe("What the numbers ARE, including the unit \u2014 like 'Revenue ($M)'. Reads up the side of the plot. Omit to leave it alone; '' to clear.")},Ce={stack:o.enum(tn).nullable().optional().describe("How several series share a bar. 'none' puts them side by side; 'stacked' puts them in one bar so the height is the total; 'proportional' makes every bar the same height so what is read is the SHARE rather than the total. It is a SEPARATE axis from the drawing itself, so it combines with whatever `arrangement` names. Omit to leave it alone; 'none' for side by side.")},Be="The whole chart as {categories, series}. `categories` are the axis labels and each entry in `series` is one named line of numbers lining up with them exactly. Echo back the shape get_document gave you and change what you mean. The chart's TITLE and its two AXIS TITLES are not in here \u2014 they are three arguments of their own on this same tool.",He=e=>{const t=ne("chart");for(const a of Object.keys(e??{}))if(!t.includes(a))return`a chart has no field '${a}'. It takes exactly: ${t.join(", ")}. \`categories\` are the axis labels and \`series\` is a list of {name, values}.`;return Jt(e)};f("add_chart",{title:"Add a chart block",description:`Put a chart on a page \u2014 ${be.join(", ")}. Use it for numbers that are COMPARED rather than listed: a trend, a split, a ranking. It takes {categories, series}. For numbers that are only being listed use add_table; for two or three headline figures use add_stats.`,inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(`One of: ${be.join(", ")}.`),chart:De.describe(Be),...Me,...de("chart")?it():{},...Ce,...B("chart")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,type:t,chart:a,title:r,axisX:i,axisY:c,arrangement:l,stack:h,variant:u,before:m})=>{const g=G(t);if(!g)return z(t);if(g.archetype!=="chart")return Le(g);const p=R(e);if(!p)return ee(e);if(m&&!_(m))return N(m);const b=He(a);if(b)return d("invalidChart",b);const E=F(u,g);if(E)return d("unknownVariant",E);const P=te(l,g);if(P)return d("unknownArrangement",P);const L=j(p),D={chart:a,chartText:{title:r,axisX:i,axisY:c}};if(!n.editor.commands.addBlock(e,t,D,m))return A("add_chart",{pageId:e,type:t,before:m});const M=R(e),I=J(L,j(M));return I&&(u!==void 0&&n.editor.commands.setVariant(I,u),l!==void 0&&n.editor.commands.setArrangement(I,l||null),h!==void 0&&n.editor.commands.setStack(I,h||null)),n.save(),S(Z(e),{blockId:I})}),f("update_chart",{title:"Replace a chart's numbers",description:"Rewrite the whole payload of a chart. The three text slots \u2014 its title and both axis titles \u2014 are left alone unless you pass them, and every design choice on the block survives, so a person who restyled the chart keeps their work. To change the DRAWING rather than the numbers, pass `arrangement` or `stack` here.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'chart'."),chart:De.describe(Be),...Me,...de("chart")?it():{},...Ce,...B("chart")?C():{}}},async({blockId:e,chart:t,title:a,axisX:r,axisY:i,arrangement:c,stack:l,variant:h})=>{const u=_(e);if(!u)return N(e);const m=W(u.type.name);if(m.archetype!=="chart")return Le(m);const g=He(t);if(g)return d("invalidChart",g);const p=F(h,m);if(p)return d("unknownVariant",p);const b=te(c,m);if(b)return d("unknownArrangement",b);const E={chart:t,chartText:{title:a,axisX:r,axisY:i}};return n.editor.commands.updateBlock(e,E)?(h!==void 0&&n.editor.commands.setVariant(e,h),c!==void 0&&n.editor.commands.setArrangement(e,c||null),l!==void 0&&n.editor.commands.setStack(e,l||null),n.save(),S(H(y(),e))):A("update_chart",{blockId:e})});const Fe=T().filter(e=>e.archetype==="diagram").map(e=>e.type),Ue=e=>d("wrongArchetype",`'${e.type}' is a '${e.archetype}' block, not a diagram. Use add_${e.archetype} / update_${e.archetype} for it. This tool takes: ${Fe.join(", ")}.`),Ve=o.looseObject({source:o.string().optional().describe("The mermaid text. The whole diagram, as one string with real newlines.")}),We=`Mermaid text. The FIRST LINE names the kind of diagram and is the commonest thing to get wrong: ${nn}. After that, write the RELATIONSHIPS and let the layout engine place everything: there are no coordinates to set. Labels are plain TEXT, with no HTML tags and nothing that loads a picture. Newlines are real, so send the source exactly as it should read.`,ye=async e=>{if(typeof e!="string")return"a diagram's 'source' must be a string of mermaid text.";const t=on(e);if(t)return t;const a=await sn(e);return!a||a.ok?null:`mermaid could not parse this source${a.line?` (line ${a.line})`:""}. ${an(a.message??"",a.line??null)} Fix that line rather than rewriting the diagram; if the message mentions the very first line, the opening keyword is probably wrong \u2014 describe_block lists the kinds.`};return f("add_diagram",{title:"Add a diagram block",description:"Put a diagram on a page \u2014 a flowchart, an org chart, a sequence, a state machine, an entity diagram and about a dozen more. Use it when the meaning is in WHAT CONNECTS TO WHAT. The payload is one string of mermaid text and you place nothing: the layout engine decides where every box goes. For a chart of NUMBERS use add_chart; for a handful of steps with real prose in them use add_items with type `steps`, which is themed and reads better on a slide than a flowchart of the same three boxes. A title or caption is an ordinary `heading` or `text` block above or below it \u2014 this block is only the drawing.",inputSchema:{pageId:o.string().describe(oe),type:o.string().describe(`One of: ${Fe.join(", ")}.`),diagram:Ve.describe(We),...B("diagram")?C():{},before:o.string().optional().describe("Block id on that page to insert before. Omit to append.")}},async({pageId:e,type:t,diagram:a,variant:r,before:i})=>{const c=a?.source,l=G(t);if(!l)return z(t);if(l.archetype!=="diagram")return Ue(l);const h=R(e);if(!h)return ee(e);if(i&&!_(i))return N(i);const u=await ye(c);if(u)return d("invalidSource",u);const m=F(r,l);if(m)return d("unknownVariant",m);const g=j(h);if(!n.editor.commands.addBlock(e,t,{diagram:{source:c}},i))return A("add_diagram",{pageId:e,type:t,before:i});const p=J(g,j(R(e)));p&&r!==void 0&&n.editor.commands.setVariant(p,r),n.save();const b=tt(c);return S(Z(e),b?{blockId:p,hint:b}:{blockId:p})}),f("update_diagram",{title:"Replace a diagram's source",description:"Rewrite the mermaid text of a diagram. The whole source at once \u2014 there is no partial edit, because a diagram is one string. Every design choice on the block survives, so a person who resized or magnified it keeps their work.",inputSchema:{blockId:o.string().describe("A block id from get_document, of archetype 'diagram'."),diagram:Ve.describe(We),...B("diagram")?C():{}}},async({blockId:e,diagram:t,variant:a})=>{const r=t?.source,i=_(e);if(!i)return N(e);const c=W(i.type.name);if(c.archetype!=="diagram")return Ue(c);const l=await ye(r);if(l)return d("invalidSource",l);const h=F(a,c);if(h)return d("unknownVariant",h);if(!n.editor.commands.updateBlock(e,{diagram:{source:r}}))return A("update_diagram",{blockId:e});a!==void 0&&n.editor.commands.setVariant(e,a),n.save();const u=tt(r);return S(H(y(),e),u?{hint:u}:{})}),f("set_block_type",{title:"Swap a block's rendering",description:"Render the same content as a different block. Free WITHIN an archetype \u2014 a timeline becomes a box keeping every entry and its id. Across archetypes there is no meaning-preserving mapping, so it is refused; remove the block and add the other instead.",inputSchema:{blockId:o.string().describe("A block id from get_document."),type:o.string().describe("The block type to switch to. Must share the current archetype.")}},async({blockId:e,type:t})=>{const a=_(e);if(!a)return N(e);const r=G(t);if(!r)return z(t);const i=W(a.type.name);if(i.archetype!==r.archetype)return d("archetypeMismatch",`Cannot turn '${i.type}' (${i.archetype}) into '${t}' (${r.archetype}) \u2014 the entries mean different things. Types you can swap to: ${T().filter(h=>h.archetype===i.archetype&&h.type!==i.type).map(h=>h.type).join(", ")||"none yet"}.`);const c=H(y(),e);if(!n.editor.commands.setBlockType(e,t))return A("set_block_type",{blockId:e,type:t});n.save();const l=c?k(c.attrs.id):null;return l?S(l):K()}),f("remove_block",{title:"Remove a block",description:"Delete a block and everything in it \u2014 a layout included, with every block in its columns. A page may be left empty \u2014 add its replacement afterwards rather than before.",inputSchema:{blockId:o.string().describe("A block id from get_document.")}},async({blockId:e})=>{const t=ae(y(),e);if(!t||!Mt(t.node.type.name))return N(e);const a=H(y(),e);if(!n.editor.commands.removeBlock(e))return A("remove_block",{blockId:e});n.save();const r=a?k(a.attrs.id):null;return r?S(r):K()}),f("export_html",{title:"Export the document as one HTML file",description:"Write the document to a single HTML file that opens in any browser by double-click, no hosting. Styles and every diagram's drawing are carried inside it; pictures, fonts and a video's player are LINKS (only a video's poster is embedded), so the file expects the internet and degrades without it \u2014 a system font, a glyph where a picture was, the video's still frame. It is a presentation viewer \u2014 swipe or use the arrow keys to move between pages. Returns the path, not the file; the path must be an .html file in a folder that exists. Export reflects the document as it is now, so fix any diagnostics first, and read the reply: it names any picture address the file could not use.",inputSchema:{path:o.string().optional().describe("Where to write it: an .html path in a folder that exists. Defaults to the document's own path with a .html suffix.")}},async({path:e})=>{const t=bn(e??n.path.replace(/\.json$/,"")+".html");if(!t.ok)return d(t.code,t.message);const a=t.path;let r;try{r=await gn(y().toJSON(),wn(n.path))}catch(i){return d("exportFailed",`Export aborted, nothing was written: ${i.message}`)}return Tt(a,r),K({path:a,pages:y().childCount,bytes:Buffer.byteLength(r),...mn(r)})}),w}function H(n,s){let w=null;return n.forEach(v=>{if(!w){if(v.attrs.id===s){w=v;return}v.descendants($=>w?!1:$.attrs?.id===s?(w=v,!1):!0)}}),w}export{Dn as INSTRUCTIONS,zn as createServer};
package/dist/mcp/stdio.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{resolve as t}from"node:path";import{StdioServerTransport as c}from"@modelcontextprotocol/sdk/server/stdio.js";import{startChannel as s}from"./channel.js";import{createServer as a}from"./server.js";import{Session as m}from"./session.js";const n=t(process.env.DECAFRAME_DOC??"document.json"),o=new m(n),i=a(o);await i.connect(new c),console.error(`decaframe mcp \xB7 document: ${n}`);const e=Number(process.env.DECAFRAME_CHANNEL_PORT??"");if(Number.isInteger(e)&&e>0)try{const r=await s(o,e);o.watchFile(),console.error(`decaframe mcp \xB7 change channel: http://127.0.0.1:${r.port}`)}catch(r){console.error(`decaframe mcp \xB7 change channel NOT started on ${e}: ${r.message}. The agent surface is unaffected; the browser cannot commit edits.`)}
2
+ import{resolve as a}from"node:path";import{StdioServerTransport as i}from"@modelcontextprotocol/sdk/server/stdio.js";import{startChannel as m}from"./channel.js";import{createServer as l}from"./server.js";import{Session as f}from"./session.js";const t=a(process.env.DECAFRAME_DOC??"document.json"),r=new f(t),n=l(r);await n.connect(new i);let c=!1;const s=()=>{c||(c=!0,n.close().catch(()=>{}).finally(()=>process.exit(0)))};for(const e of["SIGTERM","SIGINT","SIGHUP"])process.on(e,s);n.server.onclose=s,console.error(`decaframe mcp \xB7 document: ${t}`);const o=Number(process.env.DECAFRAME_CHANNEL_PORT??"");if(Number.isInteger(o)&&o>0)try{const e=await m(r,o);r.watchFile(),console.error(`decaframe mcp \xB7 change channel: http://127.0.0.1:${e.port}`)}catch(e){console.error(`decaframe mcp \xB7 change channel NOT started on ${o}: ${e.message}. The agent surface is unaffected; the browser cannot commit edits.`)}
@@ -1 +1 @@
1
- {"id":"decaframe","name":"Deca","dictionary":7,"fonts":["geist","geist-mono"],"tokens":{"font-sans":"'Geist', ui-sans-serif, system-ui, sans-serif","font-mono":"'Geist Mono', ui-monospace, monospace","font-display":"'Geist', ui-sans-serif, system-ui, sans-serif","weight-display":700,"track-tight":1.15,"text-base":16,"display-scale":1.15,"display-case":"none","bullet-glyph":"'\\2014'","check-corner":2,"frame-pad-x":68,"frame-pad-t":50,"frame-pad-b":54,"corner-sm":2,"corner-md":4,"corner-lg":10,"border-sm":1,"border-md":2,"border-lg":3,"title-rule-weight":3,"title-rule-length":1},"variants":{"plain":{"divider":"rule","border-weight":"border-sm","divider-col":"rule","title-rule":"accent","footer":"fg-muted"},"accent-bar":{"border-lead":"accent","border-weight":"border-md"},"soft-fill":{"fill":"bg-subtle"},"outlined":{"border-top":"rule-strong","border-lead":"rule-strong","border-trail":"rule-strong","border-bottom":"rule-strong","border-weight":"border-sm"},"top-rule":{"border-top":"accent","border-weight":"border-md"},"side-rule":{"fill":"bg-subtle","border-top":"rule","border-trail":"rule","border-bottom":"rule","border-lead":"accent","border-weight":"border-md"},"raised-card":{"fill":"bg","border-top":"rule","border-lead":"rule","border-trail":"rule","border-bottom":"rule","border-weight":"border-sm","corner":"corner-md","elevation":"shadow-box"},"gradient-card":{"fill":"bg-muted","corner":"corner-md","elevation":"shadow-box"},"solid-accent":{"fill":"accent","on-fill":"bg"},"leaf-corner":{"fill":"bg-subtle","corner":"corner-lg"},"banded":{"band-fill":"bg-subtle","divider":"rule"},"header-band":{"head-fill":"bg-muted","divider":"rule"},"accent-header":{"head-fill":"accent","on-head-fill":"bg","border-top":"rule","border-lead":"rule","border-trail":"rule","border-bottom":"rule","border-weight":"border-sm","corner":"corner-md"},"marked":{"marker":"bg","marker-fill":"accent","divider":"rule"}},"palettes":{"light":{"bg":{"srgb":"#fcfcfd","p3":"color(display-p3 0.988 0.988 0.992)","from":"slate-1"},"bg-subtle":"oklch(0.959 0.004 264)","bg-muted":"linear-gradient(150deg, oklch(0.926 0.010 258), oklch(0.951 0.004 264) 68%)","fg":{"srgb":"#1c2024","p3":"color(display-p3 0.113 0.125 0.14)","from":"slate-12"},"fg-muted":{"srgb":"#60646c","p3":"color(display-p3 0.379 0.392 0.421)","from":"slate-11"},"fg-faint":{"srgb":"#8b8d98","p3":"color(display-p3 0.547 0.553 0.592)","from":"slate-9"},"rule":{"srgb":"#d9d9e0","p3":"color(display-p3 0.85 0.852 0.876)","from":"slate-6"},"rule-strong":{"srgb":"#b9bbc6","p3":"color(display-p3 0.727 0.733 0.773)","from":"slate-8"},"accent":{"srgb":"#000000","p3":"color(display-p3 0 0 0)"},"bg-accent":{"srgb":"#000000","p3":"color(display-p3 0 0 0)"},"on-bg-accent":{"srgb":"#fcfcfd","p3":"color(display-p3 0.988 0.988 0.992)"},"accent-fill":"radial-gradient(120% 110% at 0% 0%, color-mix(in oklab, var(--accent) 20%, transparent), transparent 60%), radial-gradient(115% 95% at 100% 100%, transparent 0 18%, color-mix(in oklab, var(--fg) 4%, var(--bg)) 66%), repeating-linear-gradient(90deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), repeating-linear-gradient(0deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), color-mix(in oklab, var(--fg) 4%, var(--bg))","shadow":"0 1px 2px rgba(28, 32, 36, 0.05), 0 12px 32px -8px rgba(28, 32, 36, 0.13)","shadow-box":"0 1px 2px oklch(0.28 0.02 264 / 0.06), 0 7px 18px -7px oklch(0.28 0.02 264 / 0.16)","c-grey":{"srgb":"#60646c","p3":"color(display-p3 0.379 0.392 0.421)","from":"slate-11"},"c-red":{"srgb":"#ce2c31","p3":"color(display-p3 0.744 0.234 0.222)","from":"red-11"},"c-orange":{"srgb":"#cc4e00","p3":"color(display-p3 0.76 0.34 0)","from":"orange-11"},"c-yellow":{"srgb":"#ab6400","p3":"color(display-p3 0.64 0.4 0)","from":"amber-11"},"c-green":{"srgb":"#218358","p3":"color(display-p3 0.19 0.5 0.32)","from":"green-11"},"c-teal":{"srgb":"#008573","p3":"color(display-p3 0.08 0.5 0.43)","from":"teal-11"},"c-blue":{"srgb":"#0d74ce","p3":"color(display-p3 0.15 0.44 0.84)","from":"blue-11"},"c-purple":{"srgb":"#6550b9","p3":"color(display-p3 0.383 0.317 0.702)","from":"violet-11"},"c-pink":{"srgb":"#c2298a","p3":"color(display-p3 0.698 0.219 0.528)","from":"pink-11"},"chart-series-1":"oklch(0.30 0.006 264)","chart-series-2":"oklch(0.42 0.006 264)","chart-series-3":"oklch(0.54 0.006 264)","chart-series-4":"oklch(0.66 0.006 264)","chart-series-5":"oklch(0.77 0.006 264)","chart-series-6":"oklch(0.87 0.006 264)"},"dark":{"bg":{"srgb":"#18191b","p3":"color(display-p3 0.095 0.098 0.105)","from":"slate-2"},"bg-subtle":"oklch(0.260 0.006 264)","bg-muted":"linear-gradient(150deg, oklch(0.318 0.011 258), oklch(0.268 0.006 264) 68%)","fg":{"srgb":"#edeef0","p3":"color(display-p3 0.93 0.933 0.94)","from":"slate-12"},"fg-muted":{"srgb":"#b0b4ba","p3":"color(display-p3 0.692 0.704 0.728)","from":"slate-11"},"fg-faint":{"srgb":"#696e77","p3":"color(display-p3 0.415 0.431 0.463)","from":"slate-9"},"rule":{"srgb":"#363a3f","p3":"color(display-p3 0.215 0.226 0.244)","from":"slate-6"},"rule-strong":{"srgb":"#5a6169","p3":"color(display-p3 0.357 0.381 0.409)","from":"slate-8"},"accent":{"srgb":"#ffffff","p3":"color(display-p3 1 1 1)"},"bg-accent":{"srgb":"#ffffff","p3":"color(display-p3 1 1 1)"},"on-bg-accent":{"srgb":"#18191b","p3":"color(display-p3 0.095 0.098 0.105)"},"accent-fill":"radial-gradient(120% 110% at 0% 0%, color-mix(in oklab, var(--accent) 20%, transparent), transparent 60%), radial-gradient(115% 95% at 100% 100%, transparent 0 18%, color-mix(in oklab, var(--fg) 4%, var(--bg)) 66%), repeating-linear-gradient(90deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), repeating-linear-gradient(0deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), color-mix(in oklab, var(--fg) 4%, var(--bg))","shadow":"0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 32px -8px rgba(0, 0, 0, 0.55)","shadow-box":"0 1px 2px oklch(0 0 0 / 0.5), 0 8px 20px -7px oklch(0 0 0 / 0.66)","c-grey":{"srgb":"#b0b4ba","p3":"color(display-p3 0.692 0.704 0.728)","from":"slate-11"},"c-red":{"srgb":"#ff9592","p3":"color(display-p3 1 0.57 0.55)","from":"red-11"},"c-orange":{"srgb":"#ffa057","p3":"color(display-p3 1 0.63 0.38)","from":"orange-11"},"c-yellow":{"srgb":"#ffca16","p3":"color(display-p3 1 0.8 0.29)","from":"amber-11"},"c-green":{"srgb":"#3dd68c","p3":"color(display-p3 0.434 0.828 0.573)","from":"green-11"},"c-teal":{"srgb":"#0bd8b6","p3":"color(display-p3 0.388 0.835 0.719)","from":"teal-11"},"c-blue":{"srgb":"#70b8ff","p3":"color(display-p3 0.49 0.72 1)","from":"blue-11"},"c-purple":{"srgb":"#baa7ff","p3":"color(display-p3 0.72 0.65 1)","from":"violet-11"},"c-pink":{"srgb":"#ff8dcc","p3":"color(display-p3 1 0.535 0.78)","from":"pink-11"},"chart-series-1":"oklch(0.85 0.006 264)","chart-series-2":"oklch(0.74 0.006 264)","chart-series-3":"oklch(0.63 0.006 264)","chart-series-4":"oklch(0.53 0.006 264)","chart-series-5":"oklch(0.43 0.006 264)","chart-series-6":"oklch(0.34 0.006 264)"}}}
1
+ {"id":"decaframe","name":"Deca","dictionary":7,"fonts":["geist","geist-mono"],"tokens":{"font-sans":"'Geist', ui-sans-serif, system-ui, sans-serif","font-mono":"'Geist Mono', ui-monospace, monospace","font-display":"'Geist', ui-sans-serif, system-ui, sans-serif","weight-display":700,"track-tight":1.15,"text-base":16,"display-scale":1.15,"display-case":"none","bullet-glyph":"'\\2014'","check-corner":2,"frame-pad-x":68,"frame-pad-t":50,"frame-pad-b":54,"corner-sm":2,"corner-md":4,"corner-lg":10,"border-sm":1,"border-md":2,"border-lg":3,"title-rule-weight":3,"title-rule-length":1},"variants":{"plain":{"divider":"rule","border-weight":"border-sm","divider-col":"rule","title-rule":"accent","footer":"fg-muted"},"accent-bar":{"border-lead":"accent","border-weight":"border-md"},"soft-fill":{"fill":"bg-subtle"},"outlined":{"border-top":"rule-strong","border-lead":"rule-strong","border-trail":"rule-strong","border-bottom":"rule-strong","border-weight":"border-sm"},"top-rule":{"border-top":"accent","border-weight":"border-md"},"side-rule":{"fill":"bg-subtle","border-top":"rule","border-trail":"rule","border-bottom":"rule","border-lead":"accent","border-weight":"border-md"},"raised-card":{"fill":"bg","border-top":"rule","border-lead":"rule","border-trail":"rule","border-bottom":"rule","border-weight":"border-sm","corner":"corner-md","elevation":"shadow-box"},"gradient-card":{"fill":"bg-muted","corner":"corner-md","elevation":"shadow-box"},"solid-accent":{"fill":"accent","on-fill":"bg"},"leaf-corner":{"fill":"bg-subtle","corner":"corner-lg"},"banded":{"band-fill":"bg-subtle","divider":"rule"},"header-band":{"head-fill":"bg-muted","divider":"rule"},"accent-header":{"head-fill":"accent","on-head-fill":"bg","border-top":"rule","border-lead":"rule","border-trail":"rule","border-bottom":"rule","border-weight":"border-sm","corner":"corner-md"},"marked":{"marker":"bg","marker-fill":"accent","divider":"rule"}},"palettes":{"light":{"bg":{"srgb":"#fcfcfd","p3":"color(display-p3 0.988 0.988 0.992)","from":"slate-1"},"bg-subtle":"oklch(0.959 0.004 264)","bg-muted":"linear-gradient(150deg, oklch(0.926 0.010 258), oklch(0.951 0.004 264) 68%)","fg":{"srgb":"#1c2024","p3":"color(display-p3 0.113 0.125 0.14)","from":"slate-12"},"fg-muted":{"srgb":"#60646c","p3":"color(display-p3 0.379 0.392 0.421)","from":"slate-11"},"fg-faint":{"srgb":"#8b8d98","p3":"color(display-p3 0.547 0.553 0.592)","from":"slate-9"},"rule":{"srgb":"#d9d9e0","p3":"color(display-p3 0.85 0.852 0.876)","from":"slate-6"},"rule-strong":{"srgb":"#b9bbc6","p3":"color(display-p3 0.727 0.733 0.773)","from":"slate-8"},"accent":{"srgb":"#000000","p3":"color(display-p3 0 0 0)"},"bg-accent":{"srgb":"#000000","p3":"color(display-p3 0 0 0)"},"on-bg-accent":{"srgb":"#fcfcfd","p3":"color(display-p3 0.988 0.988 0.992)"},"accent-fill":"radial-gradient(120% 110% at 0% 0%, color-mix(in oklab, var(--accent) 20%, transparent), transparent 60%), radial-gradient(115% 95% at 100% 100%, transparent 0 18%, color-mix(in oklab, var(--fg) 4%, var(--bg)) 66%), repeating-linear-gradient(90deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), repeating-linear-gradient(0deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), color-mix(in oklab, var(--fg) 4%, var(--bg))","shadow":"0 1px 2px rgba(28, 32, 36, 0.05), 0 12px 32px -8px rgba(28, 32, 36, 0.13)","shadow-box":"0 1px 2px oklch(0.28 0.02 264 / 0.06), 0 7px 18px -7px oklch(0.28 0.02 264 / 0.16)","c-grey":{"srgb":"#60646c","p3":"color(display-p3 0.379 0.392 0.421)","from":"slate-11"},"c-red":{"srgb":"#ce2c31","p3":"color(display-p3 0.744 0.234 0.222)","from":"red-11"},"c-orange":{"srgb":"#cc4e00","p3":"color(display-p3 0.76 0.34 0)","from":"orange-11"},"c-yellow":{"srgb":"#ab6400","p3":"color(display-p3 0.64 0.4 0)","from":"amber-11"},"c-green":{"srgb":"#218358","p3":"color(display-p3 0.19 0.5 0.32)","from":"green-11"},"c-teal":{"srgb":"#008573","p3":"color(display-p3 0.08 0.5 0.43)","from":"teal-11"},"c-blue":{"srgb":"#0d74ce","p3":"color(display-p3 0.15 0.44 0.84)","from":"blue-11"},"c-purple":{"srgb":"#6550b9","p3":"color(display-p3 0.383 0.317 0.702)","from":"violet-11"},"c-pink":{"srgb":"#c2298a","p3":"color(display-p3 0.698 0.219 0.528)","from":"pink-11"},"chart-series-1":"oklch(0.30 0.006 264)","chart-series-2":"oklch(0.42 0.006 264)","chart-series-3":"oklch(0.54 0.006 264)","chart-series-4":"oklch(0.66 0.006 264)","chart-series-5":"oklch(0.77 0.006 264)","chart-series-6":"oklch(0.87 0.006 264)"},"dark":{"bg":{"srgb":"#18191b","p3":"color(display-p3 0.095 0.098 0.105)","from":"slate-2"},"bg-subtle":"oklch(0.260 0.006 264)","bg-muted":"linear-gradient(150deg, oklch(0.318 0.011 258), oklch(0.268 0.006 264) 68%)","fg":{"srgb":"#edeef0","p3":"color(display-p3 0.93 0.933 0.94)","from":"slate-12"},"fg-muted":{"srgb":"#b0b4ba","p3":"color(display-p3 0.692 0.704 0.728)","from":"slate-11"},"fg-faint":{"srgb":"#696e77","p3":"color(display-p3 0.415 0.431 0.463)","from":"slate-9"},"rule":{"srgb":"#363a3f","p3":"color(display-p3 0.215 0.226 0.244)","from":"slate-6"},"rule-strong":{"srgb":"#5a6169","p3":"color(display-p3 0.357 0.381 0.409)","from":"slate-8"},"accent":{"srgb":"#ffffff","p3":"color(display-p3 1 1 1)"},"bg-accent":{"srgb":"#48494d","p3":"color(display-p3 0.283 0.286 0.301)"},"on-bg-accent":{"srgb":"#fcfcfd","p3":"color(display-p3 0.988 0.988 0.992)"},"accent-fill":"radial-gradient(120% 110% at 0% 0%, color-mix(in oklab, var(--accent) 20%, transparent), transparent 60%), radial-gradient(115% 95% at 100% 100%, transparent 0 18%, color-mix(in oklab, var(--fg) 4%, var(--bg)) 66%), repeating-linear-gradient(90deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), repeating-linear-gradient(0deg, color-mix(in oklab, var(--fg) 16%, transparent) 0 1px, transparent 1px calc(26px * var(--s))), color-mix(in oklab, var(--fg) 4%, var(--bg))","shadow":"0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 32px -8px rgba(0, 0, 0, 0.55)","shadow-box":"0 1px 2px oklch(0 0 0 / 0.5), 0 8px 20px -7px oklch(0 0 0 / 0.66)","c-grey":{"srgb":"#b0b4ba","p3":"color(display-p3 0.692 0.704 0.728)","from":"slate-11"},"c-red":{"srgb":"#ff9592","p3":"color(display-p3 1 0.57 0.55)","from":"red-11"},"c-orange":{"srgb":"#ffa057","p3":"color(display-p3 1 0.63 0.38)","from":"orange-11"},"c-yellow":{"srgb":"#ffca16","p3":"color(display-p3 1 0.8 0.29)","from":"amber-11"},"c-green":{"srgb":"#3dd68c","p3":"color(display-p3 0.434 0.828 0.573)","from":"green-11"},"c-teal":{"srgb":"#0bd8b6","p3":"color(display-p3 0.388 0.835 0.719)","from":"teal-11"},"c-blue":{"srgb":"#70b8ff","p3":"color(display-p3 0.49 0.72 1)","from":"blue-11"},"c-purple":{"srgb":"#baa7ff","p3":"color(display-p3 0.72 0.65 1)","from":"violet-11"},"c-pink":{"srgb":"#ff8dcc","p3":"color(display-p3 1 0.535 0.78)","from":"pink-11"},"chart-series-1":"oklch(0.85 0.006 264)","chart-series-2":"oklch(0.74 0.006 264)","chart-series-3":"oklch(0.63 0.006 264)","chart-series-4":"oklch(0.53 0.006 264)","chart-series-5":"oklch(0.43 0.006 264)","chart-series-6":"oklch(0.34 0.006 264)"}}}