@lit-pigeon/core 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=require("immer"),j=require("nanoid");class ${constructor(t){this._steps=[],this._selection=null,this._selectionSet=!1,this._meta=new Map,this._doc=t}get steps(){return this._steps}get selection(){return this._selection}get selectionSet(){return this._selectionSet}get meta(){return this._meta}get doc(){return this._doc}addStep(t){return this._doc=t.apply(this._doc),this._steps.push(t),this}setSelection(t){return this._selection=t,this._selectionSet=!0,this}setMeta(t,e){return this._meta.set(t,e),this}getMeta(t){return this._meta.get(t)}}function m(o,t,e,n){return{type:o,path:t,apply(r){return B.produce(r,e)},invert(r){return m(`${o}:invert`,t,n,e)}}}function w(){return j.nanoid(10)}function g(o=0){return{top:o,right:o,bottom:o,left:o}}function z(){return{content:"<p>Enter your text here</p>",padding:g(10),lineHeight:"1.5",textAlign:"left"}}function q(){return{src:"",alt:"",width:"auto",padding:g(10),alignment:"center"}}function N(){return{content:"<p>Click me</p>",href:"#",backgroundColor:"#3b82f6",textColor:"#ffffff",borderRadius:4,padding:g(10),innerPadding:{top:12,right:24,bottom:12,left:24},fontSize:16,fontWeight:"600",alignment:"center",fullWidth:!1}}function U(){return{borderColor:"#e2e8f0",borderWidth:1,borderStyle:"solid",padding:g(10),width:"100%"}}function F(){return{height:20}}function K(){return{icons:[],iconSize:32,spacing:8,alignment:"center",padding:g(10)}}function G(){return{content:"",padding:g(0)}}function Y(){return{backgroundUrl:"",backgroundPosition:"center center",mode:"fluid-height",width:600,height:400,verticalAlign:"middle",padding:g(0),innerPadding:g(20),backgroundColor:"#ffffff",content:'<p style="color:#ffffff;font-size:24px;">Hero Title</p>'}}function X(){return{links:[{href:"#",text:"Home"},{href:"#",text:"About"},{href:"#",text:"Contact"}],hamburger:"hamburger",alignment:"center",padding:g(10),linkColor:"#000000",linkFontSize:14,linkPadding:"10px 15px"}}const M={text:z,image:q,button:N,divider:U,spacer:F,social:K,html:G,hero:Y,navbar:X};function h(o){const t=M[o];if(!t)throw new Error(`Unknown block type: ${o}`);return t()}let I=null;function Z(o){I=o}function J(o,t){const e=M[o];if(e)return{id:w(),type:o,values:{...e(),...t}};const n=I==null?void 0:I(o);if(n)return{id:w(),type:o,values:{...structuredClone(n),...t}};throw new Error(`Unknown block type: ${o}`)}function T(o=[]){return{id:w(),type:"column",attributes:{padding:g(0),verticalAlign:"top"},blocks:o}}function Q(o,t){const e=o??[T()],n=t??e.map(()=>Math.floor(12/e.length));return{id:w(),type:"row",attributes:{padding:g(0),fullWidth:!1},columns:e,columnRatios:n,locked:!1}}function H(o="Untitled"){const t=new Date().toISOString();return{version:"1.0",metadata:{name:o,createdAt:t,updatedAt:t},body:{attributes:{width:600,backgroundColor:"#f4f4f5",fontFamily:"Arial, Helvetica, sans-serif",contentAlignment:"center"},rows:[]}}}class S{constructor(t,e,n,r){this.doc=t,this.selection=e,this.plugins=n,this._pluginList=r}static create(t={}){const e=t.doc??H(),n=t.plugins??[],r=new Map,s=new S(e,null,r,n);for(const i of n)i.init&&r.set(i.name,i.init(s));return s}createTransaction(){return new $(this.doc)}apply(t){let e=t.doc;const n=t.selectionSet?t.selection:this.selection,r=new Map(this.plugins);for(const i of this._pluginList)if(i.apply){const l=r.get(i.name);r.set(i.name,i.apply(t,l))}t.steps.length>0&&!t.getMeta("skipTimestamp")&&(e=B.produce(e,i=>{i.metadata.updatedAt=new Date().toISOString()}));const s=new S(e,n,r,this._pluginList);for(const i of this._pluginList)i.onStateChange&&i.onStateChange(s,this);return s}getPluginState(t){return this.plugins.get(t)}}function v(o,t,e){return{type:"block",rowId:o,columnId:t,blockId:e}}function A(o){return{type:"row",rowId:o}}function tt(o,t){return{type:"column",rowId:o,columnId:t}}function et(){return{type:"body"}}function ot(o,t){return o===t?!0:!o||!t?!1:o.type===t.type&&o.rowId===t.rowId&&o.columnId===t.columnId&&o.blockId===t.blockId}function k(o,t,e){const n=o.body.rows.findIndex(s=>s.id===t);if(n===-1)return null;const r=o.body.rows[n].columns.findIndex(s=>s.id===e);return r===-1?null:{rowIndex:n,columnIndex:r}}function nt(o,t,e,n){return(r,s)=>{const i=k(r.doc,o,t);if(!i)return!1;const l=r.doc.body.rows[i.rowIndex].columns[i.columnIndex],c=n??l.blocks.length;if(s){const a=r.createTransaction(),u=m("insertBlock",`body.rows[${i.rowIndex}].columns[${i.columnIndex}].blocks`,d=>{d.body.rows[i.rowIndex].columns[i.columnIndex].blocks.splice(c,0,e)},d=>{d.body.rows[i.rowIndex].columns[i.columnIndex].blocks.splice(c,1)});a.addStep(u),a.setSelection(v(o,t,e.id)),s(a)}return!0}}function rt(o,t,e){return(n,r)=>{const s=k(n.doc,o,t);if(!s)return!1;const i=n.doc.body.rows[s.rowIndex].columns[s.columnIndex],l=i.blocks.findIndex(c=>c.id===e);if(l===-1)return!1;if(r){const c=i.blocks[l],a=n.createTransaction(),u=m("deleteBlock",`body.rows[${s.rowIndex}].columns[${s.columnIndex}].blocks`,d=>{d.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(l,1)},d=>{d.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(l,0,c)});a.addStep(u),a.setSelection(null),r(a)}return!0}}function st(o,t,e,n){return(r,s)=>{const i=k(r.doc,o,t);if(!i)return!1;const l=r.doc.body.rows[i.rowIndex].columns[i.columnIndex],c=l.blocks.findIndex(a=>a.id===e);if(c===-1)return!1;if(s){const a=l.blocks[c],u=r.createTransaction(),d=m("updateBlock",`body.rows[${i.rowIndex}].columns[${i.columnIndex}].blocks[${c}]`,p=>{const b=p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[c];Object.assign(b.values,n)},p=>{p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[c]=a});u.addStep(d),s(u)}return!0}}function it(o,t,e,n,r,s){return(i,l)=>{const c=k(i.doc,o,t);if(!c)return!1;const u=i.doc.body.rows[c.rowIndex].columns[c.columnIndex].blocks.findIndex(p=>p.id===e);if(u===-1)return!1;const d=k(i.doc,n,r);if(!d)return!1;if(l){const p=i.createTransaction(),b=m("moveBlock","body.rows",y=>{const[R]=y.body.rows[c.rowIndex].columns[c.columnIndex].blocks.splice(u,1);y.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s,0,R)},y=>{const[R]=y.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s,1);y.body.rows[c.rowIndex].columns[c.columnIndex].blocks.splice(u,0,R)});p.addStep(b),p.setSelection(v(n,r,e)),l(p)}return!0}}function lt(o,t,e){return(n,r)=>{const s=k(n.doc,o,t);if(!s)return!1;const i=n.doc.body.rows[s.rowIndex].columns[s.columnIndex],l=i.blocks.findIndex(c=>c.id===e);if(l===-1)return!1;if(r){const c=i.blocks[l],a=w(),u=structuredClone(c);u.id=a;const d=l+1,p=n.createTransaction(),b=m("duplicateBlock",`body.rows[${s.rowIndex}].columns[${s.columnIndex}].blocks`,y=>{y.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d,0,u)},y=>{y.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d,1)});p.addStep(b),p.setSelection(v(o,t,a)),r(p)}return!0}}function ct(o,t){return(e,n)=>{const r=t??e.doc.body.rows.length;if(n){const s=e.createTransaction(),i=m("insertRow","body.rows",l=>{l.body.rows.splice(r,0,o)},l=>{l.body.rows.splice(r,1)});s.addStep(i),s.setSelection(A(o.id)),n(s)}return!0}}function at(o){return(t,e)=>{const n=t.doc.body.rows.findIndex(r=>r.id===o);if(n===-1)return!1;if(e){const r=structuredClone(t.doc.body.rows[n]),s=t.createTransaction(),i=m("deleteRow","body.rows",l=>{l.body.rows.splice(n,1)},l=>{l.body.rows.splice(n,0,r)});s.addStep(i),s.setSelection(null),e(s)}return!0}}function dt(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(s=>s.id===o);if(r===-1||r===t)return!1;if(n){const s=e.createTransaction(),i=m("moveRow","body.rows",l=>{const[c]=l.body.rows.splice(r,1);l.body.rows.splice(t,0,c)},l=>{const[c]=l.body.rows.splice(t,1);l.body.rows.splice(r,0,c)});s.addStep(i),s.setSelection(A(o)),n(s)}return!0}}function ut(o){return(t,e)=>{const n=t.doc.body.rows.findIndex(r=>r.id===o);if(n===-1)return!1;if(e){const r=t.doc.body.rows[n],s=structuredClone(r);s.id=w(),s.columns.forEach(a=>{a.id=w(),a.blocks.forEach(u=>{u.id=w()})});const i=n+1,l=t.createTransaction(),c=m("duplicateRow","body.rows",a=>{a.body.rows.splice(i,0,s)},a=>{a.body.rows.splice(i,1)});l.addStep(c),l.setSelection(A(s.id)),e(l)}return!0}}function ft(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(s=>s.id===o);if(r===-1)return!1;if(n){const s={...e.doc.body.rows[r].attributes},i=e.createTransaction(),l=m("updateRowAttributes",`body.rows[${r}].attributes`,c=>{Object.assign(c.body.rows[r].attributes,t)},c=>{c.body.rows[r].attributes=s});i.addStep(l),n(i)}return!0}}function pt(o){return(t,e)=>{const n=t.doc.body.rows.findIndex(s=>s.id===o);if(n===-1)return!1;const r=t.doc.body.rows[n];if(r.locked||r.columns.length>=4)return!1;if(e){const s=T(),i=r.columns.length+1,l=D(i),c=[...r.columnRatios],a=t.createTransaction(),u=m("addColumn",`body.rows[${n}]`,d=>{d.body.rows[n].columns.push(s),d.body.rows[n].columnRatios=l},d=>{d.body.rows[n].columns.pop(),d.body.rows[n].columnRatios=c});a.addStep(u),e(a)}return!0}}function mt(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(l=>l.id===o);if(r===-1)return!1;const s=e.doc.body.rows[r];if(s.locked||s.columns.length<=1)return!1;const i=s.columns.findIndex(l=>l.id===t);if(i===-1)return!1;if(n){const l=structuredClone(s.columns[i]),c=[...s.columnRatios],a=s.columns.length-1,u=D(a),d=e.createTransaction(),p=m("removeColumn",`body.rows[${r}]`,b=>{b.body.rows[r].columns.splice(i,1),b.body.rows[r].columnRatios=u},b=>{b.body.rows[r].columns.splice(i,0,l),b.body.rows[r].columnRatios=c});d.addStep(p),n(d)}return!0}}function gt(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(l=>l.id===o);if(r===-1)return!1;const s=e.doc.body.rows[r];if(t.length!==s.columns.length||t.reduce((l,c)=>l+c,0)!==12)return!1;if(n){const l=[...s.columnRatios],c=e.createTransaction(),a=m("resizeColumns",`body.rows[${r}].columnRatios`,u=>{u.body.rows[r].columnRatios=t},u=>{u.body.rows[r].columnRatios=l});c.addStep(a),n(c)}return!0}}function D(o){const t=Math.floor(12/o),e=12-t*o,n=new Array(o).fill(t);for(let r=0;r<e;r++)n[r]+=1;return n}const bt=["text","image","button","divider","spacer","social","html","hero","navbar"];function W(o){const t=[];if(!o||typeof o!="object")return t.push({path:"",message:"Document must be an object"}),t;const e=o;if(e.version!=="1.0"&&t.push({path:"version",message:'Version must be "1.0"'}),!e.metadata||typeof e.metadata!="object"?t.push({path:"metadata",message:"Metadata is required"}):typeof e.metadata.name!="string"&&t.push({path:"metadata.name",message:"Name must be a string"}),!e.body||typeof e.body!="object")return t.push({path:"body",message:"Body is required"}),t;const n=e.body;return(!n.attributes||typeof n.attributes!="object")&&t.push({path:"body.attributes",message:"Body attributes are required"}),Array.isArray(n.rows)?(n.rows.forEach((r,s)=>{t.push(...ht(r,`body.rows[${s}]`))}),t):(t.push({path:"body.rows",message:"Rows must be an array"}),t)}function ht(o,t){const e=[];return o.id||e.push({path:`${t}.id`,message:"Row must have an id"}),o.type!=="row"&&e.push({path:`${t}.type`,message:'Row type must be "row"'}),Array.isArray(o.columns)?(Array.isArray(o.columnRatios)?o.columnRatios.length!==o.columns.length&&e.push({path:`${t}.columnRatios`,message:"Column ratios length must match columns length"}):e.push({path:`${t}.columnRatios`,message:"Column ratios must be an array"}),o.columns.forEach((n,r)=>{e.push(...yt(n,`${t}.columns[${r}]`))}),e):(e.push({path:`${t}.columns`,message:"Columns must be an array"}),e)}function yt(o,t){const e=[];return o.id||e.push({path:`${t}.id`,message:"Column must have an id"}),Array.isArray(o.blocks)?(o.blocks.forEach((n,r)=>{e.push(...wt(n,`${t}.blocks[${r}]`))}),e):(e.push({path:`${t}.blocks`,message:"Blocks must be an array"}),e)}function wt(o,t){const e=[];return o.id||e.push({path:`${t}.id`,message:"Block must have an id"}),bt.includes(o.type)||e.push({path:`${t}.type`,message:`Invalid block type: ${o.type}`}),(!o.values||typeof o.values!="object")&&e.push({path:`${t}.values`,message:"Block must have values"}),e}function kt(o){return W(o).length===0}const x=new Map,xt=[{type:"text",label:"Text",icon:"text",defaultValues:h("text")},{type:"image",label:"Image",icon:"image",defaultValues:h("image")},{type:"button",label:"Button",icon:"button",defaultValues:h("button")},{type:"divider",label:"Divider",icon:"divider",defaultValues:h("divider")},{type:"spacer",label:"Spacer",icon:"spacer",defaultValues:h("spacer")},{type:"social",label:"Social",icon:"social",defaultValues:h("social")},{type:"html",label:"HTML",icon:"html",defaultValues:h("html")},{type:"hero",label:"Hero",icon:"hero",defaultValues:h("hero")},{type:"navbar",label:"Navbar",icon:"navbar",defaultValues:h("navbar")}];function It(){for(const o of xt)x.set(o.type,o);Z(o=>{var t;return(t=x.get(o))==null?void 0:t.defaultValues})}function P(o){x.set(o.type,o)}function St(o){return x.get(o)}function vt(){return Array.from(x.values())}function At(o){return x.has(o)}It();const Ct=100;function Rt(){return{undoStack:[],redoStack:[]}}function _t(o,t){const e=[...o.undoStack,t];return e.length>Ct&&e.shift(),{undoStack:e,redoStack:[]}}function Tt(o){if(o.undoStack.length===0)return{history:o,entry:null};const t=[...o.undoStack],e=t.pop(),n=[...o.redoStack,e];return{history:{undoStack:t,redoStack:n},entry:e}}function Bt(o){if(o.redoStack.length===0)return{history:o,entry:null};const t=[...o.redoStack],e=t.pop();return{history:{undoStack:[...o.undoStack,e],redoStack:t},entry:e}}function V(o){return o.undoStack.length>0}function E(o){return o.redoStack.length>0}const C="history";function $t(){return{name:C,init(){return Rt()},apply(o,t){const e=t;if(o.meta.get("isUndo")||o.meta.get("isRedo"))return o.meta.get("newHistory")??e;if(o.steps.length===0||o.meta.get("skipHistory"))return e;const n={steps:[...o.steps],doc:o.doc,timestamp:Date.now()};return _t(e,n)}}}function Mt(o,t){const e=o.plugins.get(C);if(!e||!V(e))return!1;if(t){const{history:n,entry:r}=Tt(e);if(!r)return!1;const s=o.createTransaction();for(let i=r.steps.length-1;i>=0;i--){const l=r.steps[i].invert(s.doc);s.addStep(l)}s.setMeta("isUndo",!0),s.setMeta("newHistory",n),s.setMeta("skipTimestamp",!0),t(s)}return!0}function Ht(o,t){const e=o.plugins.get(C);if(!e||!E(e))return!1;if(t){const{history:n,entry:r}=Bt(e);if(!r)return!1;const s=o.createTransaction();for(const i of r.steps)s.addStep(i);s.setMeta("isRedo",!0),s.setMeta("newHistory",n),s.setMeta("skipTimestamp",!0),t(s)}return!0}class Dt{constructor(){this._plugins=new Map}register(t){if(this._plugins.has(t.name))throw new Error(`Plugin "${t.name}" is already registered`);if(this._plugins.set(t.name,t),t.blocks)for(const e of t.blocks)P(e)}unregister(t){this._plugins.delete(t)}get(t){return this._plugins.get(t)}getAll(){return Array.from(this._plugins.values())}getCommands(){const t={};for(const e of this._plugins.values())e.commands&&Object.assign(t,e.commands);return t}has(t){return this._plugins.has(t)}}function _(o,t){if(o===t)return!0;if(o==null||t==null||typeof o!=typeof t)return!1;if(Array.isArray(o))return!Array.isArray(t)||o.length!==t.length?!1:o.every((e,n)=>_(e,t[n]));if(typeof o=="object"){const e=Object.keys(o),n=Object.keys(t);return e.length!==n.length?!1:e.every(r=>_(o[r],t[r]))}return!1}const f="2026-05-26T10:00:00.000Z",Wt={id:"starter-welcome",name:"Welcome",description:"A clean welcome email with a hero, intro paragraph, and call-to-action.",category:"welcome",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Welcome",previewText:"We're glad you're here.",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#f5f7fb",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-hero",type:"row",locked:!1,attributes:{padding:{top:0,right:0,bottom:0,left:0},fullWidth:!0},columnRatios:[12],columns:[{id:"c-hero",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-hero",type:"hero",values:{backgroundUrl:"https://placehold.co/600x280/0f172a/ffffff?text=Welcome",backgroundPosition:"center center",mode:"fixed-height",width:600,height:280,verticalAlign:"middle",padding:{top:0,right:0,bottom:0,left:0},innerPadding:{top:40,right:40,bottom:40,left:40},backgroundColor:"#0f172a",content:'<h1 style="color:#ffffff;font-size:32px;margin:0;">Welcome, {{firstName}}</h1>'}}]}]},{id:"r-intro",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:32,right:24,bottom:8,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-intro",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-intro",type:"text",values:{content:"<p>Thanks for joining — we've put together a quick guide to help you get the most out of your account.</p>",padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.6",textAlign:"left"}}]}]},{id:"r-cta",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:16,right:24,bottom:32,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-cta",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-cta",type:"button",values:{content:"<p>Get started</p>",href:"https://example.com/start",backgroundColor:"#3b82f6",textColor:"#ffffff",borderRadius:6,padding:{top:8,right:8,bottom:8,left:8},innerPadding:{top:12,right:24,bottom:12,left:24},fontSize:16,fontWeight:"600",alignment:"center",fullWidth:!1}}]}]}]}}},Pt={id:"starter-newsletter",name:"Newsletter",description:"A simple 3-section newsletter — headline + body + footer.",category:"newsletter",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Newsletter",previewText:"This month's updates",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#ffffff",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-head",type:"row",locked:!1,attributes:{padding:{top:24,right:24,bottom:8,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-head",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-head",type:"text",values:{content:'<h1 style="font-size:24px;margin:0;">This month at Acme</h1>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.3",textAlign:"left"}}]}]},{id:"r-body",type:"row",locked:!1,attributes:{padding:{top:8,right:24,bottom:24,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-body",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-body",type:"text",values:{content:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sodales, mauris vel rhoncus sodales, eros lectus rutrum lectus.</p><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>",padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.6",textAlign:"left"}}]}]},{id:"r-foot",type:"row",locked:!1,attributes:{backgroundColor:"#f5f7fb",padding:{top:16,right:24,bottom:16,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-foot",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-foot",type:"text",values:{content:'<p style="color:#64748b;font-size:12px;">© Acme · <a href="https://example.com/unsubscribe">Unsubscribe</a></p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"center"}}]}]}]}}},Vt={id:"starter-transactional",name:"Transactional (order confirmation)",description:"Order confirmation pattern with summary and a primary button.",category:"transactional",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Order confirmation",previewText:"Order {{orderNumber}} is on its way.",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#ffffff",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-h",type:"row",locked:!1,attributes:{padding:{top:32,right:24,bottom:8,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-h",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-h",type:"text",values:{content:'<h1 style="font-size:24px;margin:0 0 8px;">Thanks for your order, {{firstName}}</h1><p style="margin:0;">Order <strong>{{orderNumber}}</strong> is on its way.</p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"left"}}]}]},{id:"r-summary",type:"row",locked:!1,attributes:{backgroundColor:"#f8fafc",padding:{top:16,right:24,bottom:16,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-summary",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-summary",type:"text",values:{content:'<p style="margin:0 0 8px;"><strong>Summary</strong></p><p style="margin:0;">{{itemCount}} item(s) · {{total}}</p><p style="margin:8px 0 0;color:#64748b;font-size:12px;">Estimated delivery: {{deliveryDate}}</p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"left"}}]}]},{id:"r-cta",type:"row",locked:!1,attributes:{padding:{top:24,right:24,bottom:24,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-cta",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-cta",type:"button",values:{content:"<p>View order</p>",href:"https://example.com/orders/{{orderNumber}}",backgroundColor:"#0f172a",textColor:"#ffffff",borderRadius:6,padding:{top:0,right:0,bottom:0,left:0},innerPadding:{top:12,right:24,bottom:12,left:24},fontSize:16,fontWeight:"600",alignment:"left",fullWidth:!1}}]}]}]}}},Et={id:"starter-promo",name:"Promo / Sale",description:"Two-column product grid with a primary CTA and a social-link footer.",category:"promo",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Spring sale",previewText:"Two days only — 30% off.",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#fdf6ee",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-hero",type:"row",locked:!1,attributes:{padding:{top:0,right:0,bottom:0,left:0},fullWidth:!0},columnRatios:[12],columns:[{id:"c-hero",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"middle"},blocks:[{id:"b-hero",type:"hero",values:{backgroundUrl:"https://placehold.co/600x240/e76f51/ffffff?text=30%25+off",backgroundPosition:"center center",mode:"fluid-height",width:600,height:240,verticalAlign:"middle",padding:{top:0,right:0,bottom:0,left:0},innerPadding:{top:32,right:32,bottom:32,left:32},backgroundColor:"#e76f51",content:'<h1 style="color:#ffffff;font-size:36px;margin:0 0 8px;">30% off, this weekend only</h1><p style="color:#ffffff;font-size:16px;margin:0;">Code <strong>SPRING30</strong> at checkout.</p>'}}]}]},{id:"r-grid",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:24,right:16,bottom:24,left:16},fullWidth:!1},columnRatios:[6,6],columns:[{id:"c-l",type:"column",attributes:{padding:{top:0,right:8,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-l-img",type:"image",values:{src:"https://placehold.co/280x200",alt:"Linen shirt",width:"auto",href:"https://example.com/p/shirt",padding:{top:0,right:0,bottom:8,left:0},alignment:"center",borderRadius:6}},{id:"b-l-txt",type:"text",values:{content:'<p style="text-align:center;margin:0;"><strong>Linen shirt</strong><br><span style="color:#64748b;">$48 · was $69</span></p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"center"}}]},{id:"c-r",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:8},verticalAlign:"top"},blocks:[{id:"b-r-img",type:"image",values:{src:"https://placehold.co/280x200",alt:"Wool jumper",width:"auto",href:"https://example.com/p/jumper",padding:{top:0,right:0,bottom:8,left:0},alignment:"center",borderRadius:6}},{id:"b-r-txt",type:"text",values:{content:'<p style="text-align:center;margin:0;"><strong>Wool jumper</strong><br><span style="color:#64748b;">$84 · was $120</span></p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"center"}}]}]},{id:"r-cta",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:8,right:16,bottom:24,left:16},fullWidth:!1},columnRatios:[12],columns:[{id:"c-cta",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-cta",type:"button",values:{content:"<p>Shop the sale</p>",href:"https://example.com/sale",backgroundColor:"#0f172a",textColor:"#ffffff",borderRadius:6,padding:{top:8,right:0,bottom:8,left:0},innerPadding:{top:14,right:28,bottom:14,left:28},fontSize:16,fontWeight:"600",alignment:"center",fullWidth:!0}}]}]}]}}},O=Object.freeze([Wt,Pt,Vt,Et]);function L(){return O.map(o=>structuredClone(o))}function Ot(o){const t=O.find(e=>e.id===o);return t?structuredClone(t):null}class Lt{constructor(t={}){if(this._byId=new Map,t.includeStarters!==!1)for(const e of L())this._byId.set(e.id,e);if(t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(){return Array.from(this._byId.values()).map(t=>structuredClone(t))}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("Template.id is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}}class jt{constructor(t={}){if(this._byId=new Map,t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(){return Array.from(this._byId.values()).map(t=>structuredClone(t))}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("BrandKit.id is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}}class zt{constructor(t={}){if(this._byId=new Map,t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(t={}){let n=Array.from(this._byId.values()).map(i=>structuredClone(i)).filter(i=>qt(i,t));n.sort((i,l)=>i.updatedAt<l.updatedAt?1:i.updatedAt>l.updatedAt?-1:0);const r=t.offset??0,s=t.limit;return n=n.slice(r,s!==void 0?r+s:void 0),n}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("Asset.id is required");if(!t.src)throw new Error("Asset.src is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),e.folder||(e.folder="/"),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}async listFolders(){const t=new Set;for(const e of this._byId.values())t.add(e.folder||"/");return Array.from(t).sort()}}function qt(o,t){if(t.folder&&(o.folder||"/")!==t.folder)return!1;if(t.tags&&t.tags.length>0){const e=new Set(o.tags??[]);if(!t.tags.every(n=>e.has(n)))return!1}if(t.search){const e=t.search.toLowerCase();if(![o.name,o.alt??"",...o.tags??[]].join(" ").toLowerCase().includes(e))return!1}return!0}exports.EditorState=S;exports.HISTORY_PLUGIN_NAME=C;exports.InMemoryAssetStorage=zt;exports.InMemoryBrandKitStorage=jt;exports.InMemoryTemplateStorage=Lt;exports.PluginRegistry=Dt;exports.Transaction=$;exports.addColumn=pt;exports.canRedo=E;exports.canUndo=V;exports.createBlock=J;exports.createBlockSelection=v;exports.createBodySelection=et;exports.createColumn=T;exports.createColumnSelection=tt;exports.createDefaultDocument=H;exports.createDocStep=m;exports.createHistoryPlugin=$t;exports.createRow=Q;exports.createRowSelection=A;exports.deepEqual=_;exports.defaultSpacing=g;exports.deleteBlock=rt;exports.deleteRow=at;exports.duplicateBlock=lt;exports.duplicateRow=ut;exports.generateId=w;exports.getAllBlockDefinitions=vt;exports.getBlockDefinition=St;exports.getDefaultValues=h;exports.getStarterTemplate=Ot;exports.getStarterTemplates=L;exports.insertBlock=nt;exports.insertRow=ct;exports.isKnownBlockType=At;exports.isValidDocument=kt;exports.moveBlock=it;exports.moveRow=dt;exports.redo=Ht;exports.registerBlock=P;exports.removeColumn=mt;exports.resizeColumns=gt;exports.selectionsEqual=ot;exports.undo=Mt;exports.updateBlock=st;exports.updateRowAttributes=ft;exports.validateDocument=W;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=require("immer"),N=require("nanoid");class M{constructor(t){this._steps=[],this._selection=null,this._selectionSet=!1,this._meta=new Map,this._doc=t}get steps(){return this._steps}get selection(){return this._selection}get selectionSet(){return this._selectionSet}get meta(){return this._meta}get doc(){return this._doc}addStep(t){return this._doc=t.apply(this._doc),this._steps.push(t),this}setSelection(t){return this._selection=t,this._selectionSet=!0,this}setMeta(t,e){return this._meta.set(t,e),this}getMeta(t){return this._meta.get(t)}}function m(o,t,e,n){return{type:o,path:t,apply(r){return B.produce(r,e)},invert(r){return m(`${o}:invert`,t,n,e)}}}function g(){return N.nanoid(10)}function b(o=0){return{top:o,right:o,bottom:o,left:o}}function j(){return{content:"<p>Enter your text here</p>",padding:b(10),lineHeight:"1.5",textAlign:"left"}}function z(){return{src:"",alt:"",width:"auto",padding:b(10),alignment:"center"}}function q(){return{content:"<p>Click me</p>",href:"#",backgroundColor:"#3b82f6",textColor:"#ffffff",borderRadius:4,padding:b(10),innerPadding:{top:12,right:24,bottom:12,left:24},fontSize:16,fontWeight:"600",alignment:"center",fullWidth:!1}}function U(){return{borderColor:"#e2e8f0",borderWidth:1,borderStyle:"solid",padding:b(10),width:"100%"}}function F(){return{height:20}}function K(){return{icons:[],iconSize:32,spacing:8,alignment:"center",padding:b(10)}}function Y(){return{content:"",padding:b(0)}}function G(){return{backgroundUrl:"",backgroundPosition:"center center",mode:"fluid-height",width:600,height:400,verticalAlign:"middle",padding:b(0),innerPadding:b(20),backgroundColor:"#ffffff",content:'<p style="color:#ffffff;font-size:24px;">Hero Title</p>'}}function X(){return{links:[{href:"#",text:"Home"},{href:"#",text:"About"},{href:"#",text:"Contact"}],hamburger:"hamburger",alignment:"center",padding:b(10),linkColor:"#000000",linkFontSize:14,linkPadding:"10px 15px"}}const $={text:j,image:z,button:q,divider:U,spacer:F,social:K,html:Y,hero:G,navbar:X};function y(o){const t=$[o];if(!t)throw new Error(`Unknown block type: ${o}`);return t()}let I=null;function Z(o){I=o}function J(o,t){const e=$[o];if(e)return{id:g(),type:o,values:{...e(),...t}};const n=I==null?void 0:I(o);if(n)return{id:g(),type:o,values:{...structuredClone(n),...t}};throw new Error(`Unknown block type: ${o}`)}function T(o=[]){return{id:g(),type:"column",attributes:{padding:b(0),verticalAlign:"top"},blocks:o}}function Q(o,t){const e=o??[T()],n=t??e.map(()=>Math.floor(12/e.length));return{id:g(),type:"row",attributes:{padding:b(0),fullWidth:!1},columns:e,columnRatios:n,locked:!1}}function E(o="Untitled"){const t=new Date().toISOString();return{version:"1.0",metadata:{name:o,createdAt:t,updatedAt:t},body:{attributes:{width:600,backgroundColor:"#f4f4f5",fontFamily:"Arial, Helvetica, sans-serif",contentAlignment:"center"},rows:[]}}}class S{constructor(t,e,n,r){this.doc=t,this.selection=e,this.plugins=n,this._pluginList=r}static create(t={}){const e=t.doc??E(),n=t.plugins??[],r=new Map,s=new S(e,null,r,n);for(const i of n)i.init&&r.set(i.name,i.init(s));return s}createTransaction(){return new M(this.doc)}apply(t){let e=t.doc;const n=t.selectionSet?t.selection:this.selection,r=new Map(this.plugins);for(const i of this._pluginList)if(i.apply){const l=r.get(i.name);r.set(i.name,i.apply(t,l))}t.steps.length>0&&!t.getMeta("skipTimestamp")&&(e=B.produce(e,i=>{i.metadata.updatedAt=new Date().toISOString()}));const s=new S(e,n,r,this._pluginList);for(const i of this._pluginList)i.onStateChange&&i.onStateChange(s,this);return s}getPluginState(t){return this.plugins.get(t)}}function v(o,t,e){return{type:"block",rowId:o,columnId:t,blockId:e}}function A(o){return{type:"row",rowId:o}}function tt(o,t){return{type:"column",rowId:o,columnId:t}}function et(){return{type:"body"}}function ot(o,t){return o===t?!0:!o||!t?!1:o.type===t.type&&o.rowId===t.rowId&&o.columnId===t.columnId&&o.blockId===t.blockId}function k(o,t,e){const n=o.body.rows.findIndex(s=>s.id===t);if(n===-1)return null;const r=o.body.rows[n].columns.findIndex(s=>s.id===e);return r===-1?null:{rowIndex:n,columnIndex:r}}function nt(o,t,e,n){return(r,s)=>{const i=k(r.doc,o,t);if(!i)return!1;const l=r.doc.body.rows[i.rowIndex].columns[i.columnIndex],a=n??l.blocks.length;if(s){const c=r.createTransaction(),u=m("insertBlock",`body.rows[${i.rowIndex}].columns[${i.columnIndex}].blocks`,d=>{d.body.rows[i.rowIndex].columns[i.columnIndex].blocks.splice(a,0,e)},d=>{d.body.rows[i.rowIndex].columns[i.columnIndex].blocks.splice(a,1)});c.addStep(u),c.setSelection(v(o,t,e.id)),s(c)}return!0}}function rt(o,t,e){return(n,r)=>{const s=k(n.doc,o,t);if(!s)return!1;const i=n.doc.body.rows[s.rowIndex].columns[s.columnIndex],l=i.blocks.findIndex(a=>a.id===e);if(l===-1)return!1;if(r){const a=i.blocks[l],c=n.createTransaction(),u=m("deleteBlock",`body.rows[${s.rowIndex}].columns[${s.columnIndex}].blocks`,d=>{d.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(l,1)},d=>{d.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(l,0,a)});c.addStep(u),c.setSelection(null),r(c)}return!0}}function st(o,t,e,n){return(r,s)=>{const i=k(r.doc,o,t);if(!i)return!1;const l=r.doc.body.rows[i.rowIndex].columns[i.columnIndex],a=l.blocks.findIndex(c=>c.id===e);if(a===-1)return!1;if(s){const c=l.blocks[a],u=r.createTransaction(),d=m("updateBlock",`body.rows[${i.rowIndex}].columns[${i.columnIndex}].blocks[${a}]`,p=>{const h=p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[a];Object.assign(h.values,n)},p=>{p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[a]=c});u.addStep(d),s(u)}return!0}}function it(o,t,e,n,r,s){return(i,l)=>{const a=k(i.doc,o,t);if(!a)return!1;const u=i.doc.body.rows[a.rowIndex].columns[a.columnIndex].blocks.findIndex(p=>p.id===e);if(u===-1)return!1;const d=k(i.doc,n,r);if(!d)return!1;if(l){const p=i.createTransaction(),h=m("moveBlock","body.rows",w=>{const[R]=w.body.rows[a.rowIndex].columns[a.columnIndex].blocks.splice(u,1);w.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s,0,R)},w=>{const[R]=w.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s,1);w.body.rows[a.rowIndex].columns[a.columnIndex].blocks.splice(u,0,R)});p.addStep(h),p.setSelection(v(n,r,e)),l(p)}return!0}}function lt(o,t,e){return(n,r)=>{const s=k(n.doc,o,t);if(!s)return!1;const i=n.doc.body.rows[s.rowIndex].columns[s.columnIndex],l=i.blocks.findIndex(a=>a.id===e);if(l===-1)return!1;if(r){const a=i.blocks[l],c=g(),u=structuredClone(a);u.id=c;const d=l+1,p=n.createTransaction(),h=m("duplicateBlock",`body.rows[${s.rowIndex}].columns[${s.columnIndex}].blocks`,w=>{w.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d,0,u)},w=>{w.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d,1)});p.addStep(h),p.setSelection(v(o,t,c)),r(p)}return!0}}function at(o,t){return(e,n)=>{const r=t??e.doc.body.rows.length;if(n){const s=e.createTransaction(),i=m("insertRow","body.rows",l=>{l.body.rows.splice(r,0,o)},l=>{l.body.rows.splice(r,1)});s.addStep(i),s.setSelection(A(o.id)),n(s)}return!0}}function ct(o){return(t,e)=>{const n=t.doc.body.rows.findIndex(r=>r.id===o);if(n===-1)return!1;if(e){const r=structuredClone(t.doc.body.rows[n]),s=t.createTransaction(),i=m("deleteRow","body.rows",l=>{l.body.rows.splice(n,1)},l=>{l.body.rows.splice(n,0,r)});s.addStep(i),s.setSelection(null),e(s)}return!0}}function dt(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(s=>s.id===o);if(r===-1||r===t)return!1;if(n){const s=e.createTransaction(),i=m("moveRow","body.rows",l=>{const[a]=l.body.rows.splice(r,1);l.body.rows.splice(t,0,a)},l=>{const[a]=l.body.rows.splice(t,1);l.body.rows.splice(r,0,a)});s.addStep(i),s.setSelection(A(o)),n(s)}return!0}}function ut(o){return(t,e)=>{const n=t.doc.body.rows.findIndex(r=>r.id===o);if(n===-1)return!1;if(e){const r=t.doc.body.rows[n],s=structuredClone(r);s.id=g(),s.columns.forEach(c=>{c.id=g(),c.blocks.forEach(u=>{u.id=g()})});const i=n+1,l=t.createTransaction(),a=m("duplicateRow","body.rows",c=>{c.body.rows.splice(i,0,s)},c=>{c.body.rows.splice(i,1)});l.addStep(a),l.setSelection(A(s.id)),e(l)}return!0}}function ft(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(s=>s.id===o);if(r===-1)return!1;if(n){const s={...e.doc.body.rows[r].attributes},i=e.createTransaction(),l=m("updateRowAttributes",`body.rows[${r}].attributes`,a=>{Object.assign(a.body.rows[r].attributes,t)},a=>{a.body.rows[r].attributes=s});i.addStep(l),n(i)}return!0}}function pt(o){return(t,e)=>{const n=t.doc.body.rows.findIndex(s=>s.id===o);if(n===-1)return!1;const r=t.doc.body.rows[n];if(r.locked||r.columns.length>=4)return!1;if(e){const s=T(),i=r.columns.length+1,l=H(i),a=[...r.columnRatios],c=t.createTransaction(),u=m("addColumn",`body.rows[${n}]`,d=>{d.body.rows[n].columns.push(s),d.body.rows[n].columnRatios=l},d=>{d.body.rows[n].columns.pop(),d.body.rows[n].columnRatios=a});c.addStep(u),e(c)}return!0}}function mt(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(l=>l.id===o);if(r===-1)return!1;const s=e.doc.body.rows[r];if(s.locked||s.columns.length<=1)return!1;const i=s.columns.findIndex(l=>l.id===t);if(i===-1)return!1;if(n){const l=structuredClone(s.columns[i]),a=[...s.columnRatios],c=s.columns.length-1,u=H(c),d=e.createTransaction(),p=m("removeColumn",`body.rows[${r}]`,h=>{h.body.rows[r].columns.splice(i,1),h.body.rows[r].columnRatios=u},h=>{h.body.rows[r].columns.splice(i,0,l),h.body.rows[r].columnRatios=a});d.addStep(p),n(d)}return!0}}function gt(o,t){return(e,n)=>{const r=e.doc.body.rows.findIndex(l=>l.id===o);if(r===-1)return!1;const s=e.doc.body.rows[r];if(t.length!==s.columns.length||t.reduce((l,a)=>l+a,0)!==12)return!1;if(n){const l=[...s.columnRatios],a=e.createTransaction(),c=m("resizeColumns",`body.rows[${r}].columnRatios`,u=>{u.body.rows[r].columnRatios=t},u=>{u.body.rows[r].columnRatios=l});a.addStep(c),n(a)}return!0}}function H(o){const t=Math.floor(12/o),e=12-t*o,n=new Array(o).fill(t);for(let r=0;r<e;r++)n[r]+=1;return n}const bt=["text","image","button","divider","spacer","social","html","hero","navbar"];function W(o){const t=[];if(!o||typeof o!="object")return t.push({path:"",message:"Document must be an object"}),t;const e=o;if(e.version!=="1.0"&&t.push({path:"version",message:'Version must be "1.0"'}),!e.metadata||typeof e.metadata!="object"?t.push({path:"metadata",message:"Metadata is required"}):typeof e.metadata.name!="string"&&t.push({path:"metadata.name",message:"Name must be a string"}),!e.body||typeof e.body!="object")return t.push({path:"body",message:"Body is required"}),t;const n=e.body;return(!n.attributes||typeof n.attributes!="object")&&t.push({path:"body.attributes",message:"Body attributes are required"}),Array.isArray(n.rows)?(n.rows.forEach((r,s)=>{t.push(...ht(r,`body.rows[${s}]`))}),t):(t.push({path:"body.rows",message:"Rows must be an array"}),t)}function ht(o,t){const e=[];return o.id||e.push({path:`${t}.id`,message:"Row must have an id"}),o.type!=="row"&&e.push({path:`${t}.type`,message:'Row type must be "row"'}),Array.isArray(o.columns)?(Array.isArray(o.columnRatios)?o.columnRatios.length!==o.columns.length&&e.push({path:`${t}.columnRatios`,message:"Column ratios length must match columns length"}):e.push({path:`${t}.columnRatios`,message:"Column ratios must be an array"}),o.columns.forEach((n,r)=>{e.push(...yt(n,`${t}.columns[${r}]`))}),e):(e.push({path:`${t}.columns`,message:"Columns must be an array"}),e)}function yt(o,t){const e=[];return o.id||e.push({path:`${t}.id`,message:"Column must have an id"}),Array.isArray(o.blocks)?(o.blocks.forEach((n,r)=>{e.push(...wt(n,`${t}.blocks[${r}]`))}),e):(e.push({path:`${t}.blocks`,message:"Blocks must be an array"}),e)}function wt(o,t){const e=[];return o.id||e.push({path:`${t}.id`,message:"Block must have an id"}),bt.includes(o.type)||e.push({path:`${t}.type`,message:`Invalid block type: ${o.type}`}),(!o.values||typeof o.values!="object")&&e.push({path:`${t}.values`,message:"Block must have values"}),e}function kt(o){return W(o).length===0}const x=new Map,xt=[{type:"text",label:"Text",icon:"text",defaultValues:y("text")},{type:"image",label:"Image",icon:"image",defaultValues:y("image")},{type:"button",label:"Button",icon:"button",defaultValues:y("button")},{type:"divider",label:"Divider",icon:"divider",defaultValues:y("divider")},{type:"spacer",label:"Spacer",icon:"spacer",defaultValues:y("spacer")},{type:"social",label:"Social",icon:"social",defaultValues:y("social")},{type:"html",label:"HTML",icon:"html",defaultValues:y("html")},{type:"hero",label:"Hero",icon:"hero",defaultValues:y("hero")},{type:"navbar",label:"Navbar",icon:"navbar",defaultValues:y("navbar")}];function It(){for(const o of xt)x.set(o.type,o);Z(o=>{var t;return(t=x.get(o))==null?void 0:t.defaultValues})}function D(o){x.set(o.type,o)}function St(o){return x.get(o)}function vt(){return Array.from(x.values())}function At(o){return x.has(o)}It();const Ct=100;function Rt(){return{undoStack:[],redoStack:[]}}function _t(o,t){const e=[...o.undoStack,t];return e.length>Ct&&e.shift(),{undoStack:e,redoStack:[]}}function Tt(o){if(o.undoStack.length===0)return{history:o,entry:null};const t=[...o.undoStack],e=t.pop(),n=[...o.redoStack,e];return{history:{undoStack:t,redoStack:n},entry:e}}function Bt(o){if(o.redoStack.length===0)return{history:o,entry:null};const t=[...o.redoStack],e=t.pop();return{history:{undoStack:[...o.undoStack,e],redoStack:t},entry:e}}function P(o){return o.undoStack.length>0}function V(o){return o.redoStack.length>0}const C="history";function Mt(){return{name:C,init(){return Rt()},apply(o,t){const e=t;if(o.meta.get("isUndo")||o.meta.get("isRedo"))return o.meta.get("newHistory")??e;if(o.steps.length===0||o.meta.get("skipHistory"))return e;const n={steps:[...o.steps],doc:o.doc,timestamp:Date.now()};return _t(e,n)}}}function $t(o,t){const e=o.plugins.get(C);if(!e||!P(e))return!1;if(t){const{history:n,entry:r}=Tt(e);if(!r)return!1;const s=o.createTransaction();for(let i=r.steps.length-1;i>=0;i--){const l=r.steps[i].invert(s.doc);s.addStep(l)}s.setMeta("isUndo",!0),s.setMeta("newHistory",n),s.setMeta("skipTimestamp",!0),t(s)}return!0}function Et(o,t){const e=o.plugins.get(C);if(!e||!V(e))return!1;if(t){const{history:n,entry:r}=Bt(e);if(!r)return!1;const s=o.createTransaction();for(const i of r.steps)s.addStep(i);s.setMeta("isRedo",!0),s.setMeta("newHistory",n),s.setMeta("skipTimestamp",!0),t(s)}return!0}class Ht{constructor(){this._plugins=new Map}register(t){if(this._plugins.has(t.name))throw new Error(`Plugin "${t.name}" is already registered`);if(this._plugins.set(t.name,t),t.blocks)for(const e of t.blocks)D(e)}unregister(t){this._plugins.delete(t)}get(t){return this._plugins.get(t)}getAll(){return Array.from(this._plugins.values())}getCommands(){const t={};for(const e of this._plugins.values())e.commands&&Object.assign(t,e.commands);return t}has(t){return this._plugins.has(t)}}function _(o,t){if(o===t)return!0;if(o==null||t==null||typeof o!=typeof t)return!1;if(Array.isArray(o))return!Array.isArray(t)||o.length!==t.length?!1:o.every((e,n)=>_(e,t[n]));if(typeof o=="object"){const e=Object.keys(o),n=Object.keys(t);return e.length!==n.length?!1:e.every(r=>_(o[r],t[r]))}return!1}const f="2026-05-26T10:00:00.000Z",Wt={id:"starter-welcome",name:"Welcome",description:"A clean welcome email with a hero, intro paragraph, and call-to-action.",category:"welcome",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Welcome",previewText:"We're glad you're here.",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#f5f7fb",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-hero",type:"row",locked:!1,attributes:{padding:{top:0,right:0,bottom:0,left:0},fullWidth:!0},columnRatios:[12],columns:[{id:"c-hero",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-hero",type:"hero",values:{backgroundUrl:"https://placehold.co/600x280/0f172a/ffffff?text=Welcome",backgroundPosition:"center center",mode:"fixed-height",width:600,height:280,verticalAlign:"middle",padding:{top:0,right:0,bottom:0,left:0},innerPadding:{top:40,right:40,bottom:40,left:40},backgroundColor:"#0f172a",content:'<h1 style="color:#ffffff;font-size:32px;margin:0;">Welcome, {{firstName}}</h1>'}}]}]},{id:"r-intro",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:32,right:24,bottom:8,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-intro",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-intro",type:"text",values:{content:"<p>Thanks for joining — we've put together a quick guide to help you get the most out of your account.</p>",padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.6",textAlign:"left"}}]}]},{id:"r-cta",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:16,right:24,bottom:32,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-cta",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-cta",type:"button",values:{content:"<p>Get started</p>",href:"https://example.com/start",backgroundColor:"#3b82f6",textColor:"#ffffff",borderRadius:6,padding:{top:8,right:8,bottom:8,left:8},innerPadding:{top:12,right:24,bottom:12,left:24},fontSize:16,fontWeight:"600",alignment:"center",fullWidth:!1}}]}]}]}}},Dt={id:"starter-newsletter",name:"Newsletter",description:"A simple 3-section newsletter — headline + body + footer.",category:"newsletter",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Newsletter",previewText:"This month's updates",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#ffffff",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-head",type:"row",locked:!1,attributes:{padding:{top:24,right:24,bottom:8,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-head",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-head",type:"text",values:{content:'<h1 style="font-size:24px;margin:0;">This month at Acme</h1>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.3",textAlign:"left"}}]}]},{id:"r-body",type:"row",locked:!1,attributes:{padding:{top:8,right:24,bottom:24,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-body",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-body",type:"text",values:{content:"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sodales, mauris vel rhoncus sodales, eros lectus rutrum lectus.</p><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>",padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.6",textAlign:"left"}}]}]},{id:"r-foot",type:"row",locked:!1,attributes:{backgroundColor:"#f5f7fb",padding:{top:16,right:24,bottom:16,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-foot",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-foot",type:"text",values:{content:'<p style="color:#64748b;font-size:12px;">© Acme · <a href="https://example.com/unsubscribe">Unsubscribe</a></p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"center"}}]}]}]}}},Pt={id:"starter-transactional",name:"Transactional (order confirmation)",description:"Order confirmation pattern with summary and a primary button.",category:"transactional",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Order confirmation",previewText:"Order {{orderNumber}} is on its way.",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#ffffff",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-h",type:"row",locked:!1,attributes:{padding:{top:32,right:24,bottom:8,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-h",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-h",type:"text",values:{content:'<h1 style="font-size:24px;margin:0 0 8px;">Thanks for your order, {{firstName}}</h1><p style="margin:0;">Order <strong>{{orderNumber}}</strong> is on its way.</p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"left"}}]}]},{id:"r-summary",type:"row",locked:!1,attributes:{backgroundColor:"#f8fafc",padding:{top:16,right:24,bottom:16,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-summary",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-summary",type:"text",values:{content:'<p style="margin:0 0 8px;"><strong>Summary</strong></p><p style="margin:0;">{{itemCount}} item(s) · {{total}}</p><p style="margin:8px 0 0;color:#64748b;font-size:12px;">Estimated delivery: {{deliveryDate}}</p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"left"}}]}]},{id:"r-cta",type:"row",locked:!1,attributes:{padding:{top:24,right:24,bottom:24,left:24},fullWidth:!1},columnRatios:[12],columns:[{id:"c-cta",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-cta",type:"button",values:{content:"<p>View order</p>",href:"https://example.com/orders/{{orderNumber}}",backgroundColor:"#0f172a",textColor:"#ffffff",borderRadius:6,padding:{top:0,right:0,bottom:0,left:0},innerPadding:{top:12,right:24,bottom:12,left:24},fontSize:16,fontWeight:"600",alignment:"left",fullWidth:!1}}]}]}]}}},Vt={id:"starter-promo",name:"Promo / Sale",description:"Two-column product grid with a primary CTA and a social-link footer.",category:"promo",createdAt:f,updatedAt:f,document:{version:"1.0",metadata:{name:"Spring sale",previewText:"Two days only — 30% off.",createdAt:f,updatedAt:f},body:{attributes:{width:600,backgroundColor:"#fdf6ee",fontFamily:"Inter, sans-serif",contentAlignment:"center"},rows:[{id:"r-hero",type:"row",locked:!1,attributes:{padding:{top:0,right:0,bottom:0,left:0},fullWidth:!0},columnRatios:[12],columns:[{id:"c-hero",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"middle"},blocks:[{id:"b-hero",type:"hero",values:{backgroundUrl:"https://placehold.co/600x240/e76f51/ffffff?text=30%25+off",backgroundPosition:"center center",mode:"fluid-height",width:600,height:240,verticalAlign:"middle",padding:{top:0,right:0,bottom:0,left:0},innerPadding:{top:32,right:32,bottom:32,left:32},backgroundColor:"#e76f51",content:'<h1 style="color:#ffffff;font-size:36px;margin:0 0 8px;">30% off, this weekend only</h1><p style="color:#ffffff;font-size:16px;margin:0;">Code <strong>SPRING30</strong> at checkout.</p>'}}]}]},{id:"r-grid",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:24,right:16,bottom:24,left:16},fullWidth:!1},columnRatios:[6,6],columns:[{id:"c-l",type:"column",attributes:{padding:{top:0,right:8,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-l-img",type:"image",values:{src:"https://placehold.co/280x200",alt:"Linen shirt",width:"auto",href:"https://example.com/p/shirt",padding:{top:0,right:0,bottom:8,left:0},alignment:"center",borderRadius:6}},{id:"b-l-txt",type:"text",values:{content:'<p style="text-align:center;margin:0;"><strong>Linen shirt</strong><br><span style="color:#64748b;">$48 · was $69</span></p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"center"}}]},{id:"c-r",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:8},verticalAlign:"top"},blocks:[{id:"b-r-img",type:"image",values:{src:"https://placehold.co/280x200",alt:"Wool jumper",width:"auto",href:"https://example.com/p/jumper",padding:{top:0,right:0,bottom:8,left:0},alignment:"center",borderRadius:6}},{id:"b-r-txt",type:"text",values:{content:'<p style="text-align:center;margin:0;"><strong>Wool jumper</strong><br><span style="color:#64748b;">$84 · was $120</span></p>',padding:{top:0,right:0,bottom:0,left:0},lineHeight:"1.5",textAlign:"center"}}]}]},{id:"r-cta",type:"row",locked:!1,attributes:{backgroundColor:"#ffffff",padding:{top:8,right:16,bottom:24,left:16},fullWidth:!1},columnRatios:[12],columns:[{id:"c-cta",type:"column",attributes:{padding:{top:0,right:0,bottom:0,left:0},verticalAlign:"top"},blocks:[{id:"b-cta",type:"button",values:{content:"<p>Shop the sale</p>",href:"https://example.com/sale",backgroundColor:"#0f172a",textColor:"#ffffff",borderRadius:6,padding:{top:8,right:0,bottom:8,left:0},innerPadding:{top:14,right:28,bottom:14,left:28},fontSize:16,fontWeight:"600",alignment:"center",fullWidth:!0}}]}]}]}}},L=Object.freeze([Wt,Dt,Pt,Vt]);function O(){return L.map(o=>structuredClone(o))}function Lt(o){const t=L.find(e=>e.id===o);return t?structuredClone(t):null}class Ot{constructor(t={}){if(this._byId=new Map,t.includeStarters!==!1)for(const e of O())this._byId.set(e.id,e);if(t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(){return Array.from(this._byId.values()).map(t=>structuredClone(t))}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("Template.id is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}}class Nt{constructor(t={}){if(this._byId=new Map,t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(){return Array.from(this._byId.values()).map(t=>structuredClone(t))}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("BrandKit.id is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}}class jt{constructor(t={}){if(this._byId=new Map,t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(t={}){let n=Array.from(this._byId.values()).map(i=>structuredClone(i)).filter(i=>zt(i,t));n.sort((i,l)=>i.updatedAt<l.updatedAt?1:i.updatedAt>l.updatedAt?-1:0);const r=t.offset??0,s=t.limit;return n=n.slice(r,s!==void 0?r+s:void 0),n}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("Asset.id is required");if(!t.src)throw new Error("Asset.src is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),e.folder||(e.folder="/"),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}async listFolders(){const t=new Set;for(const e of this._byId.values())t.add(e.folder||"/");return Array.from(t).sort()}}function zt(o,t){if(t.folder&&(o.folder||"/")!==t.folder)return!1;if(t.tags&&t.tags.length>0){const e=new Set(o.tags??[]);if(!t.tags.every(n=>e.has(n)))return!1}if(t.search){const e=t.search.toLowerCase();if(![o.name,o.alt??"",...o.tags??[]].join(" ").toLowerCase().includes(e))return!1}return!0}class qt{constructor(t={}){if(this._byId=new Map,t.seed)for(const e of t.seed)this._byId.set(e.id,e)}async list(){return Array.from(this._byId.values()).map(t=>structuredClone(t))}async get(t){const e=this._byId.get(t);return e?structuredClone(e):null}async save(t){if(!t.id)throw new Error("LibraryEntry.id is required");const e=structuredClone(t);e.updatedAt=new Date().toISOString(),this._byId.has(e.id)||(e.createdAt=e.updatedAt),this._byId.set(e.id,e)}async delete(t){this._byId.delete(t)}}function Ut(o){const t=structuredClone(o);return t.id=g(),t.columns.forEach(e=>{e.id=g(),e.blocks.forEach(n=>{n.id=g()})}),t}const Ft=[{id:"unsubscribe",label:"Unsubscribe",href:"{{unsubscribe_url}}"},{id:"view-in-browser",label:"View in browser",href:"{{view_in_browser_url}}"},{id:"email",label:"Email address",prompt:"email"},{id:"phone",label:"Phone number",prompt:"tel"}];exports.EditorState=S;exports.HISTORY_PLUGIN_NAME=C;exports.InMemoryAssetStorage=jt;exports.InMemoryBrandKitStorage=Nt;exports.InMemoryRowLibraryStorage=qt;exports.InMemoryTemplateStorage=Ot;exports.PluginRegistry=Ht;exports.SYSTEM_LINK_TYPES=Ft;exports.Transaction=M;exports.addColumn=pt;exports.canRedo=V;exports.canUndo=P;exports.cloneRowWithNewIds=Ut;exports.createBlock=J;exports.createBlockSelection=v;exports.createBodySelection=et;exports.createColumn=T;exports.createColumnSelection=tt;exports.createDefaultDocument=E;exports.createDocStep=m;exports.createHistoryPlugin=Mt;exports.createRow=Q;exports.createRowSelection=A;exports.deepEqual=_;exports.defaultSpacing=b;exports.deleteBlock=rt;exports.deleteRow=ct;exports.duplicateBlock=lt;exports.duplicateRow=ut;exports.generateId=g;exports.getAllBlockDefinitions=vt;exports.getBlockDefinition=St;exports.getDefaultValues=y;exports.getStarterTemplate=Lt;exports.getStarterTemplates=O;exports.insertBlock=nt;exports.insertRow=at;exports.isKnownBlockType=At;exports.isValidDocument=kt;exports.moveBlock=it;exports.moveRow=dt;exports.redo=Et;exports.registerBlock=D;exports.removeColumn=mt;exports.resizeColumns=gt;exports.selectionsEqual=ot;exports.undo=$t;exports.updateBlock=st;exports.updateRowAttributes=ft;exports.validateDocument=W;
package/dist/index.d.ts CHANGED
@@ -186,6 +186,13 @@ export declare function canRedo(history: HistoryState): boolean;
186
186
 
187
187
  export declare function canUndo(history: HistoryState): boolean;
188
188
 
189
+ /**
190
+ * Deep-clone a row and assign fresh ids to the row, every column, and every
191
+ * block. Used when inserting a saved library row so it never collides with
192
+ * existing node ids. (Same id-regeneration the `duplicateRow` command performs.)
193
+ */
194
+ export declare function cloneRowWithNewIds(row: RowNode): RowNode;
195
+
189
196
  export declare interface ColumnNode {
190
197
  id: string;
191
198
  type: 'column';
@@ -280,6 +287,31 @@ export declare interface EditorConfig {
280
287
  * directly; a `BrandKitStorage` is resolved lazily on demand.
281
288
  */
282
289
  brandKit?: BrandKit | BrandKitStorage;
290
+ /**
291
+ * Optional host-registered fonts. They appear as options in the editor font
292
+ * picker and, when a `url` is set, are emitted by the renderer as `<mj-font>`
293
+ * so email clients load them. Family stacks should include email-safe
294
+ * fallbacks.
295
+ */
296
+ fontConfig?: FontDefinition[];
297
+ /**
298
+ * Optional persistence for the user's saved-rows library. When unset, the
299
+ * editor falls back to an in-memory store so the "Saved" palette tab works
300
+ * in-session. A filesystem implementation ships in `@lit-pigeon/mcp-server`.
301
+ */
302
+ rowLibrary?: RowLibraryStorage;
303
+ /**
304
+ * Optional host-registered link types (label + href template) shown in the
305
+ * link editor and button panel alongside the built-in system links
306
+ * (`SYSTEM_LINK_TYPES`). The host/SSR resolves any `{{…}}` templates at send.
307
+ */
308
+ linkTypes?: LinkType[];
309
+ /** Active locale code (e.g. 'fr', 'ar'). Defaults to 'en'. */
310
+ locale?: string;
311
+ /** Host-supplied per-locale catalogs, merged over the built-in `en` baseline. */
312
+ messages?: Record<string, Record<string, string>>;
313
+ /** Explicit text direction. When unset, derived from `locale`. */
314
+ dir?: 'ltr' | 'rtl';
283
315
  }
284
316
 
285
317
  export declare class EditorState {
@@ -301,6 +333,19 @@ export declare interface EditorStateSnapshot {
301
333
  createTransaction(): TransactionSnapshot;
302
334
  }
303
335
 
336
+ /**
337
+ * A web/brand font a host registers so it appears in the editor font picker
338
+ * and (when `url` is set) is emitted by the renderer as `<mj-font>`.
339
+ */
340
+ export declare interface FontDefinition {
341
+ /** Display name shown in the font picker. */
342
+ name: string;
343
+ /** CSS font-family stack, including email-safe fallbacks (e.g. "Inter, Arial, sans-serif"). */
344
+ family: string;
345
+ /** Optional stylesheet URL that loads the web font (e.g. a Google Fonts CSS link). */
346
+ url?: string;
347
+ }
348
+
304
349
  export declare function generateId(): string;
305
350
 
306
351
  export declare function getAllBlockDefinitions(): BlockDefinition[];
@@ -404,6 +449,25 @@ export declare interface InMemoryBrandKitStorageOptions {
404
449
  seed?: BrandKit[];
405
450
  }
406
451
 
452
+ /**
453
+ * Default `RowLibraryStorage` — `Map`-backed, per-process. Mirrors
454
+ * `InMemoryTemplateStorage`: deep-clones on read so callers can't poison the
455
+ * store by mutating returned objects.
456
+ */
457
+ export declare class InMemoryRowLibraryStorage implements RowLibraryStorage {
458
+ private readonly _byId;
459
+ constructor(opts?: InMemoryRowLibraryStorageOptions);
460
+ list(): Promise<LibraryEntry[]>;
461
+ get(id: string): Promise<LibraryEntry | null>;
462
+ save(entry: LibraryEntry): Promise<void>;
463
+ delete(id: string): Promise<void>;
464
+ }
465
+
466
+ export declare interface InMemoryRowLibraryStorageOptions {
467
+ /** Entries to seed the store with on construction. */
468
+ seed?: LibraryEntry[];
469
+ }
470
+
407
471
  /**
408
472
  * Default `TemplateStorage` implementation — backs templates in a `Map`
409
473
  * inside a single process. Suitable for the editor's default config, the
@@ -436,6 +500,33 @@ export declare function isKnownBlockType(type: string): type is BlockType;
436
500
 
437
501
  export declare function isValidDocument(doc: unknown): doc is PigeonDocument;
438
502
 
503
+ /** A user-saved reusable row. */
504
+ export declare interface LibraryEntry {
505
+ /** Stable, URL-safe slug; doubles as filename when persisted. */
506
+ id: string;
507
+ /** Display name shown in the Saved tab. */
508
+ name: string;
509
+ /** What the entry holds. Only 'row' is supported now. */
510
+ kind: 'row';
511
+ /** The saved row (with its columns/blocks). */
512
+ node: RowNode;
513
+ /** ISO-8601 timestamps. */
514
+ createdAt: string;
515
+ updatedAt: string;
516
+ }
517
+
518
+ /** A selectable "special link" surfaced in the link editor and button panel. */
519
+ export declare interface LinkType {
520
+ /** Stable id (also the picker option value). */
521
+ id: string;
522
+ /** Label shown in the picker. */
523
+ label: string;
524
+ /** Href inserted on select (e.g. "{{unsubscribe_url}}"). Omitted for guided types. */
525
+ href?: string;
526
+ /** When set, the picker prompts for a value and builds `mailto:`/`tel:`. */
527
+ prompt?: 'email' | 'tel';
528
+ }
529
+
439
530
  export declare interface MergeTag {
440
531
  name: string;
441
532
  label: string;
@@ -581,6 +672,11 @@ export declare interface RenderOptions {
581
672
  * Set to false to opt out (useful for testing the bare renderer output).
582
673
  */
583
674
  outlookWorkarounds?: boolean;
675
+ /**
676
+ * Web fonts to emit as `<mj-font>` in the document head. Each font with a
677
+ * `url` produces one stylesheet link; URL-less fonts are ignored.
678
+ */
679
+ fonts?: FontDefinition[];
584
680
  }
585
681
 
586
682
  export declare interface RenderResult {
@@ -590,6 +686,14 @@ export declare interface RenderResult {
590
686
 
591
687
  export declare function resizeColumns(rowId: string, ratios: number[]): Command;
592
688
 
689
+ /** Pluggable persistence for the row library. Mirrors {@link TemplateStorage}. */
690
+ export declare interface RowLibraryStorage {
691
+ list(): Promise<LibraryEntry[]>;
692
+ get(id: string): Promise<LibraryEntry | null>;
693
+ save(entry: LibraryEntry): Promise<void>;
694
+ delete(id: string): Promise<void>;
695
+ }
696
+
593
697
  export declare interface RowNode {
594
698
  id: string;
595
699
  type: 'row';
@@ -662,6 +766,13 @@ export declare interface Step {
662
766
  invert(doc: PigeonDocument): Step;
663
767
  }
664
768
 
769
+ /**
770
+ * Built-in system link types. `unsubscribe`/`view-in-browser` emit fixed
771
+ * `{{…}}` placeholder hrefs (conventional ESP tokens, resolved by the host at
772
+ * send time); `email`/`phone` prompt for a value and build `mailto:`/`tel:`.
773
+ */
774
+ export declare const SYSTEM_LINK_TYPES: LinkType[];
775
+
665
776
  export declare interface Template {
666
777
  /** Stable, URL-safe slug. Doubles as filename when persisted. */
667
778
  id: string;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { produce as T } from "immer";
2
- import { nanoid as W } from "nanoid";
3
- class V {
2
+ import { nanoid as H } from "nanoid";
3
+ class E {
4
4
  constructor(t) {
5
5
  this._steps = [], this._selection = null, this._selectionSet = !1, this._meta = /* @__PURE__ */ new Map(), this._doc = t;
6
6
  }
@@ -44,37 +44,37 @@ function m(o, t, e, n) {
44
44
  }
45
45
  };
46
46
  }
47
- function w() {
48
- return W(10);
47
+ function g() {
48
+ return H(10);
49
49
  }
50
- function b(o = 0) {
50
+ function h(o = 0) {
51
51
  return { top: o, right: o, bottom: o, left: o };
52
52
  }
53
- function P() {
53
+ function V() {
54
54
  return {
55
55
  content: "<p>Enter your text here</p>",
56
- padding: b(10),
56
+ padding: h(10),
57
57
  lineHeight: "1.5",
58
58
  textAlign: "left"
59
59
  };
60
60
  }
61
- function D() {
61
+ function P() {
62
62
  return {
63
63
  src: "",
64
64
  alt: "",
65
65
  width: "auto",
66
- padding: b(10),
66
+ padding: h(10),
67
67
  alignment: "center"
68
68
  };
69
69
  }
70
- function E() {
70
+ function D() {
71
71
  return {
72
72
  content: "<p>Click me</p>",
73
73
  href: "#",
74
74
  backgroundColor: "#3b82f6",
75
75
  textColor: "#ffffff",
76
76
  borderRadius: 4,
77
- padding: b(10),
77
+ padding: h(10),
78
78
  innerPadding: { top: 12, right: 24, bottom: 12, left: 24 },
79
79
  fontSize: 16,
80
80
  fontWeight: "600",
@@ -87,7 +87,7 @@ function L() {
87
87
  borderColor: "#e2e8f0",
88
88
  borderWidth: 1,
89
89
  borderStyle: "solid",
90
- padding: b(10),
90
+ padding: h(10),
91
91
  width: "100%"
92
92
  };
93
93
  }
@@ -100,13 +100,13 @@ function j() {
100
100
  iconSize: 32,
101
101
  spacing: 8,
102
102
  alignment: "center",
103
- padding: b(10)
103
+ padding: h(10)
104
104
  };
105
105
  }
106
106
  function z() {
107
107
  return {
108
108
  content: "",
109
- padding: b(0)
109
+ padding: h(0)
110
110
  };
111
111
  }
112
112
  function N() {
@@ -117,8 +117,8 @@ function N() {
117
117
  width: 600,
118
118
  height: 400,
119
119
  verticalAlign: "middle",
120
- padding: b(0),
121
- innerPadding: b(20),
120
+ padding: h(0),
121
+ innerPadding: h(20),
122
122
  backgroundColor: "#ffffff",
123
123
  content: '<p style="color:#ffffff;font-size:24px;">Hero Title</p>'
124
124
  };
@@ -132,16 +132,16 @@ function q() {
132
132
  ],
133
133
  hamburger: "hamburger",
134
134
  alignment: "center",
135
- padding: b(10),
135
+ padding: h(10),
136
136
  linkColor: "#000000",
137
137
  linkFontSize: 14,
138
138
  linkPadding: "10px 15px"
139
139
  };
140
140
  }
141
141
  const $ = {
142
- text: P,
143
- image: D,
144
- button: E,
142
+ text: V,
143
+ image: P,
144
+ button: D,
145
145
  divider: L,
146
146
  spacer: O,
147
147
  social: j,
@@ -149,7 +149,7 @@ const $ = {
149
149
  hero: N,
150
150
  navbar: q
151
151
  };
152
- function y(o) {
152
+ function w(o) {
153
153
  const t = $[o];
154
154
  if (!t)
155
155
  throw new Error(`Unknown block type: ${o}`);
@@ -159,18 +159,18 @@ let I = null;
159
159
  function U(o) {
160
160
  I = o;
161
161
  }
162
- function bt(o, t) {
162
+ function gt(o, t) {
163
163
  const e = $[o];
164
164
  if (e)
165
165
  return {
166
- id: w(),
166
+ id: g(),
167
167
  type: o,
168
168
  values: { ...e(), ...t }
169
169
  };
170
170
  const n = I == null ? void 0 : I(o);
171
171
  if (n)
172
172
  return {
173
- id: w(),
173
+ id: g(),
174
174
  type: o,
175
175
  values: { ...structuredClone(n), ...t }
176
176
  };
@@ -178,10 +178,10 @@ function bt(o, t) {
178
178
  }
179
179
  function B(o = []) {
180
180
  return {
181
- id: w(),
181
+ id: g(),
182
182
  type: "column",
183
183
  attributes: {
184
- padding: b(0),
184
+ padding: h(0),
185
185
  verticalAlign: "top"
186
186
  },
187
187
  blocks: o
@@ -190,10 +190,10 @@ function B(o = []) {
190
190
  function ht(o, t) {
191
191
  const e = o ?? [B()], n = t ?? e.map(() => Math.floor(12 / e.length));
192
192
  return {
193
- id: w(),
193
+ id: g(),
194
194
  type: "row",
195
195
  attributes: {
196
- padding: b(0),
196
+ padding: h(0),
197
197
  fullWidth: !1
198
198
  },
199
199
  columns: e,
@@ -221,18 +221,18 @@ function F(o = "Untitled") {
221
221
  }
222
222
  };
223
223
  }
224
- class A {
224
+ class S {
225
225
  constructor(t, e, n, r) {
226
226
  this.doc = t, this.selection = e, this.plugins = n, this._pluginList = r;
227
227
  }
228
228
  static create(t = {}) {
229
- const e = t.doc ?? F(), n = t.plugins ?? [], r = /* @__PURE__ */ new Map(), s = new A(e, null, r, n);
229
+ const e = t.doc ?? F(), n = t.plugins ?? [], r = /* @__PURE__ */ new Map(), s = new S(e, null, r, n);
230
230
  for (const i of n)
231
231
  i.init && r.set(i.name, i.init(s));
232
232
  return s;
233
233
  }
234
234
  createTransaction() {
235
- return new V(this.doc);
235
+ return new E(this.doc);
236
236
  }
237
237
  apply(t) {
238
238
  let e = t.doc;
@@ -245,7 +245,7 @@ class A {
245
245
  t.steps.length > 0 && !t.getMeta("skipTimestamp") && (e = T(e, (i) => {
246
246
  i.metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
247
247
  }));
248
- const s = new A(e, n, r, this._pluginList);
248
+ const s = new S(e, n, r, this._pluginList);
249
249
  for (const i of this._pluginList)
250
250
  i.onStateChange && i.onStateChange(s, this);
251
251
  return s;
@@ -254,7 +254,7 @@ class A {
254
254
  return this.plugins.get(t);
255
255
  }
256
256
  }
257
- function S(o, t, e) {
257
+ function A(o, t, e) {
258
258
  return { type: "block", rowId: o, columnId: t, blockId: e };
259
259
  }
260
260
  function C(o) {
@@ -291,7 +291,7 @@ function xt(o, t, e, n) {
291
291
  d.body.rows[i.rowIndex].columns[i.columnIndex].blocks.splice(c, 1);
292
292
  }
293
293
  );
294
- a.addStep(u), a.setSelection(S(o, t, e.id)), s(a);
294
+ a.addStep(u), a.setSelection(A(o, t, e.id)), s(a);
295
295
  }
296
296
  return !0;
297
297
  };
@@ -333,8 +333,8 @@ function vt(o, t, e, n) {
333
333
  "updateBlock",
334
334
  `body.rows[${i.rowIndex}].columns[${i.columnIndex}].blocks[${c}]`,
335
335
  (p) => {
336
- const g = p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[c];
337
- Object.assign(g.values, n);
336
+ const b = p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[c];
337
+ Object.assign(b.values, n);
338
338
  },
339
339
  (p) => {
340
340
  p.body.rows[i.rowIndex].columns[i.columnIndex].blocks[c] = a;
@@ -345,7 +345,7 @@ function vt(o, t, e, n) {
345
345
  return !0;
346
346
  };
347
347
  }
348
- function At(o, t, e, n, r, s) {
348
+ function St(o, t, e, n, r, s) {
349
349
  return (i, l) => {
350
350
  const c = k(i.doc, o, t);
351
351
  if (!c) return !1;
@@ -354,43 +354,43 @@ function At(o, t, e, n, r, s) {
354
354
  const d = k(i.doc, n, r);
355
355
  if (!d) return !1;
356
356
  if (l) {
357
- const p = i.createTransaction(), g = m(
357
+ const p = i.createTransaction(), b = m(
358
358
  "moveBlock",
359
359
  "body.rows",
360
- (h) => {
361
- const [v] = h.body.rows[c.rowIndex].columns[c.columnIndex].blocks.splice(u, 1);
362
- h.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s, 0, v);
360
+ (y) => {
361
+ const [v] = y.body.rows[c.rowIndex].columns[c.columnIndex].blocks.splice(u, 1);
362
+ y.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s, 0, v);
363
363
  },
364
- (h) => {
365
- const [v] = h.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s, 1);
366
- h.body.rows[c.rowIndex].columns[c.columnIndex].blocks.splice(u, 0, v);
364
+ (y) => {
365
+ const [v] = y.body.rows[d.rowIndex].columns[d.columnIndex].blocks.splice(s, 1);
366
+ y.body.rows[c.rowIndex].columns[c.columnIndex].blocks.splice(u, 0, v);
367
367
  }
368
368
  );
369
- p.addStep(g), p.setSelection(S(n, r, e)), l(p);
369
+ p.addStep(b), p.setSelection(A(n, r, e)), l(p);
370
370
  }
371
371
  return !0;
372
372
  };
373
373
  }
374
- function St(o, t, e) {
374
+ function At(o, t, e) {
375
375
  return (n, r) => {
376
376
  const s = k(n.doc, o, t);
377
377
  if (!s) return !1;
378
378
  const i = n.doc.body.rows[s.rowIndex].columns[s.columnIndex], l = i.blocks.findIndex((c) => c.id === e);
379
379
  if (l === -1) return !1;
380
380
  if (r) {
381
- const c = i.blocks[l], a = w(), u = structuredClone(c);
381
+ const c = i.blocks[l], a = g(), u = structuredClone(c);
382
382
  u.id = a;
383
- const d = l + 1, p = n.createTransaction(), g = m(
383
+ const d = l + 1, p = n.createTransaction(), b = m(
384
384
  "duplicateBlock",
385
385
  `body.rows[${s.rowIndex}].columns[${s.columnIndex}].blocks`,
386
- (h) => {
387
- h.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d, 0, u);
386
+ (y) => {
387
+ y.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d, 0, u);
388
388
  },
389
- (h) => {
390
- h.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d, 1);
389
+ (y) => {
390
+ y.body.rows[s.rowIndex].columns[s.columnIndex].blocks.splice(d, 1);
391
391
  }
392
392
  );
393
- p.addStep(g), p.setSelection(S(o, t, a)), r(p);
393
+ p.addStep(b), p.setSelection(A(o, t, a)), r(p);
394
394
  }
395
395
  return !0;
396
396
  };
@@ -414,7 +414,7 @@ function Ct(o, t) {
414
414
  return !0;
415
415
  };
416
416
  }
417
- function Rt(o) {
417
+ function _t(o) {
418
418
  return (t, e) => {
419
419
  const n = t.doc.body.rows.findIndex((r) => r.id === o);
420
420
  if (n === -1) return !1;
@@ -434,7 +434,7 @@ function Rt(o) {
434
434
  return !0;
435
435
  };
436
436
  }
437
- function _t(o, t) {
437
+ function Rt(o, t) {
438
438
  return (e, n) => {
439
439
  const r = e.doc.body.rows.findIndex((s) => s.id === o);
440
440
  if (r === -1 || r === t) return !1;
@@ -462,9 +462,9 @@ function Tt(o) {
462
462
  if (n === -1) return !1;
463
463
  if (e) {
464
464
  const r = t.doc.body.rows[n], s = structuredClone(r);
465
- s.id = w(), s.columns.forEach((a) => {
466
- a.id = w(), a.blocks.forEach((u) => {
467
- u.id = w();
465
+ s.id = g(), s.columns.forEach((a) => {
466
+ a.id = g(), a.blocks.forEach((u) => {
467
+ u.id = g();
468
468
  });
469
469
  });
470
470
  const i = n + 1, l = t.createTransaction(), c = m(
@@ -509,7 +509,7 @@ function Bt(o) {
509
509
  const r = t.doc.body.rows[n];
510
510
  if (r.locked || r.columns.length >= 4) return !1;
511
511
  if (e) {
512
- const s = B(), i = r.columns.length + 1, l = H(i), c = [...r.columnRatios], a = t.createTransaction(), u = m(
512
+ const s = B(), i = r.columns.length + 1, l = M(i), c = [...r.columnRatios], a = t.createTransaction(), u = m(
513
513
  "addColumn",
514
514
  `body.rows[${n}]`,
515
515
  (d) => {
@@ -524,7 +524,7 @@ function Bt(o) {
524
524
  return !0;
525
525
  };
526
526
  }
527
- function Ht(o, t) {
527
+ function Mt(o, t) {
528
528
  return (e, n) => {
529
529
  const r = e.doc.body.rows.findIndex((l) => l.id === o);
530
530
  if (r === -1) return !1;
@@ -533,14 +533,14 @@ function Ht(o, t) {
533
533
  const i = s.columns.findIndex((l) => l.id === t);
534
534
  if (i === -1) return !1;
535
535
  if (n) {
536
- const l = structuredClone(s.columns[i]), c = [...s.columnRatios], a = s.columns.length - 1, u = H(a), d = e.createTransaction(), p = m(
536
+ const l = structuredClone(s.columns[i]), c = [...s.columnRatios], a = s.columns.length - 1, u = M(a), d = e.createTransaction(), p = m(
537
537
  "removeColumn",
538
538
  `body.rows[${r}]`,
539
- (g) => {
540
- g.body.rows[r].columns.splice(i, 1), g.body.rows[r].columnRatios = u;
539
+ (b) => {
540
+ b.body.rows[r].columns.splice(i, 1), b.body.rows[r].columnRatios = u;
541
541
  },
542
- (g) => {
543
- g.body.rows[r].columns.splice(i, 0, l), g.body.rows[r].columnRatios = c;
542
+ (b) => {
543
+ b.body.rows[r].columns.splice(i, 0, l), b.body.rows[r].columnRatios = c;
544
544
  }
545
545
  );
546
546
  d.addStep(p), n(d);
@@ -548,7 +548,7 @@ function Ht(o, t) {
548
548
  return !0;
549
549
  };
550
550
  }
551
- function Mt(o, t) {
551
+ function Wt(o, t) {
552
552
  return (e, n) => {
553
553
  const r = e.doc.body.rows.findIndex((l) => l.id === o);
554
554
  if (r === -1) return !1;
@@ -570,14 +570,14 @@ function Mt(o, t) {
570
570
  return !0;
571
571
  };
572
572
  }
573
- function H(o) {
573
+ function M(o) {
574
574
  const t = Math.floor(12 / o), e = 12 - t * o, n = new Array(o).fill(t);
575
575
  for (let r = 0; r < e; r++)
576
576
  n[r] += 1;
577
577
  return n;
578
578
  }
579
579
  const K = ["text", "image", "button", "divider", "spacer", "social", "html", "hero", "navbar"];
580
- function G(o) {
580
+ function Y(o) {
581
581
  const t = [];
582
582
  if (!o || typeof o != "object")
583
583
  return t.push({ path: "", message: "Document must be an object" }), t;
@@ -586,10 +586,10 @@ function G(o) {
586
586
  return t.push({ path: "body", message: "Body is required" }), t;
587
587
  const n = e.body;
588
588
  return (!n.attributes || typeof n.attributes != "object") && t.push({ path: "body.attributes", message: "Body attributes are required" }), Array.isArray(n.rows) ? (n.rows.forEach((r, s) => {
589
- t.push(...Y(r, `body.rows[${s}]`));
589
+ t.push(...G(r, `body.rows[${s}]`));
590
590
  }), t) : (t.push({ path: "body.rows", message: "Rows must be an array" }), t);
591
591
  }
592
- function Y(o, t) {
592
+ function G(o, t) {
593
593
  const e = [];
594
594
  return o.id || e.push({ path: `${t}.id`, message: "Row must have an id" }), o.type !== "row" && e.push({ path: `${t}.type`, message: 'Row type must be "row"' }), Array.isArray(o.columns) ? (Array.isArray(o.columnRatios) ? o.columnRatios.length !== o.columns.length && e.push({
595
595
  path: `${t}.columnRatios`,
@@ -608,63 +608,63 @@ function Z(o, t) {
608
608
  const e = [];
609
609
  return o.id || e.push({ path: `${t}.id`, message: "Block must have an id" }), K.includes(o.type) || e.push({ path: `${t}.type`, message: `Invalid block type: ${o.type}` }), (!o.values || typeof o.values != "object") && e.push({ path: `${t}.values`, message: "Block must have values" }), e;
610
610
  }
611
- function Wt(o) {
612
- return G(o).length === 0;
611
+ function Ht(o) {
612
+ return Y(o).length === 0;
613
613
  }
614
614
  const x = /* @__PURE__ */ new Map(), J = [
615
615
  {
616
616
  type: "text",
617
617
  label: "Text",
618
618
  icon: "text",
619
- defaultValues: y("text")
619
+ defaultValues: w("text")
620
620
  },
621
621
  {
622
622
  type: "image",
623
623
  label: "Image",
624
624
  icon: "image",
625
- defaultValues: y("image")
625
+ defaultValues: w("image")
626
626
  },
627
627
  {
628
628
  type: "button",
629
629
  label: "Button",
630
630
  icon: "button",
631
- defaultValues: y("button")
631
+ defaultValues: w("button")
632
632
  },
633
633
  {
634
634
  type: "divider",
635
635
  label: "Divider",
636
636
  icon: "divider",
637
- defaultValues: y("divider")
637
+ defaultValues: w("divider")
638
638
  },
639
639
  {
640
640
  type: "spacer",
641
641
  label: "Spacer",
642
642
  icon: "spacer",
643
- defaultValues: y("spacer")
643
+ defaultValues: w("spacer")
644
644
  },
645
645
  {
646
646
  type: "social",
647
647
  label: "Social",
648
648
  icon: "social",
649
- defaultValues: y("social")
649
+ defaultValues: w("social")
650
650
  },
651
651
  {
652
652
  type: "html",
653
653
  label: "HTML",
654
654
  icon: "html",
655
- defaultValues: y("html")
655
+ defaultValues: w("html")
656
656
  },
657
657
  {
658
658
  type: "hero",
659
659
  label: "Hero",
660
660
  icon: "hero",
661
- defaultValues: y("hero")
661
+ defaultValues: w("hero")
662
662
  },
663
663
  {
664
664
  type: "navbar",
665
665
  label: "Navbar",
666
666
  icon: "navbar",
667
- defaultValues: y("navbar")
667
+ defaultValues: w("navbar")
668
668
  }
669
669
  ];
670
670
  function Q() {
@@ -678,13 +678,13 @@ function Q() {
678
678
  function tt(o) {
679
679
  x.set(o.type, o);
680
680
  }
681
- function Vt(o) {
681
+ function Et(o) {
682
682
  return x.get(o);
683
683
  }
684
- function Pt() {
684
+ function Vt() {
685
685
  return Array.from(x.values());
686
686
  }
687
- function Dt(o) {
687
+ function Pt(o) {
688
688
  return x.has(o);
689
689
  }
690
690
  Q();
@@ -727,10 +727,10 @@ function it(o) {
727
727
  function lt(o) {
728
728
  return o.redoStack.length > 0;
729
729
  }
730
- const R = "history";
731
- function Et() {
730
+ const _ = "history";
731
+ function Dt() {
732
732
  return {
733
- name: R,
733
+ name: _,
734
734
  init() {
735
735
  return ot();
736
736
  },
@@ -750,7 +750,7 @@ function Et() {
750
750
  };
751
751
  }
752
752
  function Lt(o, t) {
753
- const e = o.plugins.get(R);
753
+ const e = o.plugins.get(_);
754
754
  if (!e || !it(e)) return !1;
755
755
  if (t) {
756
756
  const { history: n, entry: r } = rt(e);
@@ -765,7 +765,7 @@ function Lt(o, t) {
765
765
  return !0;
766
766
  }
767
767
  function Ot(o, t) {
768
- const e = o.plugins.get(R);
768
+ const e = o.plugins.get(_);
769
769
  if (!e || !lt(e)) return !1;
770
770
  if (t) {
771
771
  const { history: n, entry: r } = st(e);
@@ -807,15 +807,15 @@ class jt {
807
807
  return this._plugins.has(t);
808
808
  }
809
809
  }
810
- function _(o, t) {
810
+ function R(o, t) {
811
811
  if (o === t) return !0;
812
812
  if (o == null || t == null || typeof o != typeof t) return !1;
813
813
  if (Array.isArray(o))
814
- return !Array.isArray(t) || o.length !== t.length ? !1 : o.every((e, n) => _(e, t[n]));
814
+ return !Array.isArray(t) || o.length !== t.length ? !1 : o.every((e, n) => R(e, t[n]));
815
815
  if (typeof o == "object") {
816
816
  const e = Object.keys(o), n = Object.keys(t);
817
817
  return e.length !== n.length ? !1 : e.every(
818
- (r) => _(
818
+ (r) => R(
819
819
  o[r],
820
820
  t[r]
821
821
  )
@@ -1236,12 +1236,12 @@ const f = "2026-05-26T10:00:00.000Z", ct = {
1236
1236
  ]
1237
1237
  }
1238
1238
  }
1239
- }, M = Object.freeze([ct, at, dt, ut]);
1239
+ }, W = Object.freeze([ct, at, dt, ut]);
1240
1240
  function ft() {
1241
- return M.map((o) => structuredClone(o));
1241
+ return W.map((o) => structuredClone(o));
1242
1242
  }
1243
1243
  function zt(o) {
1244
- const t = M.find((e) => e.id === o);
1244
+ const t = W.find((e) => e.id === o);
1245
1245
  return t ? structuredClone(t) : null;
1246
1246
  }
1247
1247
  class Nt {
@@ -1331,52 +1331,90 @@ function pt(o, t) {
1331
1331
  }
1332
1332
  return !0;
1333
1333
  }
1334
+ class Ft {
1335
+ constructor(t = {}) {
1336
+ if (this._byId = /* @__PURE__ */ new Map(), t.seed)
1337
+ for (const e of t.seed) this._byId.set(e.id, e);
1338
+ }
1339
+ async list() {
1340
+ return Array.from(this._byId.values()).map((t) => structuredClone(t));
1341
+ }
1342
+ async get(t) {
1343
+ const e = this._byId.get(t);
1344
+ return e ? structuredClone(e) : null;
1345
+ }
1346
+ async save(t) {
1347
+ if (!t.id) throw new Error("LibraryEntry.id is required");
1348
+ const e = structuredClone(t);
1349
+ e.updatedAt = (/* @__PURE__ */ new Date()).toISOString(), this._byId.has(e.id) || (e.createdAt = e.updatedAt), this._byId.set(e.id, e);
1350
+ }
1351
+ async delete(t) {
1352
+ this._byId.delete(t);
1353
+ }
1354
+ }
1355
+ function Kt(o) {
1356
+ const t = structuredClone(o);
1357
+ return t.id = g(), t.columns.forEach((e) => {
1358
+ e.id = g(), e.blocks.forEach((n) => {
1359
+ n.id = g();
1360
+ });
1361
+ }), t;
1362
+ }
1363
+ const Yt = [
1364
+ { id: "unsubscribe", label: "Unsubscribe", href: "{{unsubscribe_url}}" },
1365
+ { id: "view-in-browser", label: "View in browser", href: "{{view_in_browser_url}}" },
1366
+ { id: "email", label: "Email address", prompt: "email" },
1367
+ { id: "phone", label: "Phone number", prompt: "tel" }
1368
+ ];
1334
1369
  export {
1335
- A as EditorState,
1336
- R as HISTORY_PLUGIN_NAME,
1370
+ S as EditorState,
1371
+ _ as HISTORY_PLUGIN_NAME,
1337
1372
  Ut as InMemoryAssetStorage,
1338
1373
  qt as InMemoryBrandKitStorage,
1374
+ Ft as InMemoryRowLibraryStorage,
1339
1375
  Nt as InMemoryTemplateStorage,
1340
1376
  jt as PluginRegistry,
1341
- V as Transaction,
1377
+ Yt as SYSTEM_LINK_TYPES,
1378
+ E as Transaction,
1342
1379
  Bt as addColumn,
1343
1380
  lt as canRedo,
1344
1381
  it as canUndo,
1345
- bt as createBlock,
1346
- S as createBlockSelection,
1382
+ Kt as cloneRowWithNewIds,
1383
+ gt as createBlock,
1384
+ A as createBlockSelection,
1347
1385
  wt as createBodySelection,
1348
1386
  B as createColumn,
1349
1387
  yt as createColumnSelection,
1350
1388
  F as createDefaultDocument,
1351
1389
  m as createDocStep,
1352
- Et as createHistoryPlugin,
1390
+ Dt as createHistoryPlugin,
1353
1391
  ht as createRow,
1354
1392
  C as createRowSelection,
1355
- _ as deepEqual,
1356
- b as defaultSpacing,
1393
+ R as deepEqual,
1394
+ h as defaultSpacing,
1357
1395
  It as deleteBlock,
1358
- Rt as deleteRow,
1359
- St as duplicateBlock,
1396
+ _t as deleteRow,
1397
+ At as duplicateBlock,
1360
1398
  Tt as duplicateRow,
1361
- w as generateId,
1362
- Pt as getAllBlockDefinitions,
1363
- Vt as getBlockDefinition,
1364
- y as getDefaultValues,
1399
+ g as generateId,
1400
+ Vt as getAllBlockDefinitions,
1401
+ Et as getBlockDefinition,
1402
+ w as getDefaultValues,
1365
1403
  zt as getStarterTemplate,
1366
1404
  ft as getStarterTemplates,
1367
1405
  xt as insertBlock,
1368
1406
  Ct as insertRow,
1369
- Dt as isKnownBlockType,
1370
- Wt as isValidDocument,
1371
- At as moveBlock,
1372
- _t as moveRow,
1407
+ Pt as isKnownBlockType,
1408
+ Ht as isValidDocument,
1409
+ St as moveBlock,
1410
+ Rt as moveRow,
1373
1411
  Ot as redo,
1374
1412
  tt as registerBlock,
1375
- Ht as removeColumn,
1376
- Mt as resizeColumns,
1413
+ Mt as removeColumn,
1414
+ Wt as resizeColumns,
1377
1415
  kt as selectionsEqual,
1378
1416
  Lt as undo,
1379
1417
  vt as updateBlock,
1380
1418
  $t as updateRowAttributes,
1381
- G as validateDocument
1419
+ Y as validateDocument
1382
1420
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lit-pigeon/core",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Pure TypeScript engine for the Pigeon email editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",