@craftile/preview-client-html 0.5.0 → 0.6.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/html.cdn.js CHANGED
@@ -1 +1 @@
1
- var Te=Object.defineProperty;var Pe=(S,k,m)=>k in S?Te(S,k,{enumerable:!0,configurable:!0,writable:!0,value:m}):S[k]=m;var A=(S,k,m)=>Pe(S,typeof k!="symbol"?k+"":k,m);var HtmlPreviewClient=(function(){"use strict";var S=Object.defineProperty,k=(o,e,t)=>e in o?S(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,m=(o,e,t)=>k(o,typeof e!="symbol"?e+"":e,t);class re{constructor(e,t="*"){m(this,"handlers",new Map),m(this,"targetWindow"),m(this,"targetOrigin"),m(this,"fallbackHandler"),this.targetWindow=e,this.targetOrigin=t,window.addEventListener("message",this.handleMessage.bind(this))}handleMessage(e){const t=e.data;if(!t.type)return;const n=this.handlers.get(t.type);n?n.forEach(i=>i(t.payload,e)):this.fallbackHandler&&this.fallbackHandler(e.data,e)}send(e,t){const n={type:e,payload:t};this.targetWindow.postMessage(n,this.targetOrigin)}listen(e,t){return this.handlers.has(e)||this.handlers.set(e,[]),this.handlers.get(e).push(t),()=>{const n=this.handlers.get(e);if(n){const i=n.indexOf(t);i>-1&&n.splice(i,1)}}}registerFallbackHandler(e){this.fallbackHandler=e}}function se(o="*"){if(window.parent===window)throw new Error("Not inside an iframe");return new re(window.parent,o)}class oe{constructor(){m(this,"listeners",new Map)}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{this.off(e,t)}}off(e,t){const n=this.listeners.get(e);n&&(n.delete(t),n.size===0&&this.listeners.delete(e))}once(e,t){const n=i=>{t(i),this.off(e,n)};this.on(e,n)}emit(e,...t){const n=this.listeners.get(e);if(n){const i=t[0];n.forEach(r=>{try{r(i)}catch(s){console.error(`Error in event listener for '${String(e)}':`,s)}})}}removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}}class le{constructor(e){m(this,"active",!0),m(this,"messenger"),m(this,"currentHoveredBlock",null),m(this,"currentSelectedBlock",null),m(this,"overlayButtonHovered",!1),m(this,"resizeObserver",null),m(this,"mutationObserver",null),this.messenger=e,this.messenger.listen("craftile.inspector.enable",()=>this.enable()),this.messenger.listen("craftile.inspector.disable",()=>this.disable()),this.messenger.listen("craftile.inspector.overlay-button-enter",this.handleOverlayButtonEnter.bind(this)),this.messenger.listen("craftile.inspector.overlay-button-leave",this.handleOverlayButtonLeave.bind(this)),this.messenger.listen("craftile.editor.select-block",this.handleEditorSelectBlock.bind(this)),this.messenger.listen("craftile.editor.deselect-block",this.handleEditorDeselectBlock.bind(this)),window.addEventListener("scroll",this.handleScroll.bind(this),{passive:!0}),this.setupGlobalEventListeners()}updateTrackedElement(e,t){this.currentHoveredBlock?.dataset.block===e&&(this.currentHoveredBlock=t,this.sendHoveredBlockPosition()),this.currentSelectedBlock?.dataset.block===e&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(!0),this.trackSelectedBlock())}enable(){this.active=!0,document.body.classList.add("craftile-inspector-active")}disable(){this.active=!1,document.body.classList.remove("craftile-inspector-active"),this.currentHoveredBlock=null,this.currentSelectedBlock=null,this.overlayButtonHovered=!1}handleOverlayButtonEnter(){this.overlayButtonHovered=!0}handleOverlayButtonLeave(e){this.overlayButtonHovered=!1,this.currentHoveredBlock||this.messenger.send("craftile.preview.block-leave")}handleEditorSelectBlock(e){const t=document.querySelector(`[data-block="${e.blockId}"]`);t&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(),this.trackSelectedBlock(),t.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"}))}handleEditorDeselectBlock(){this.currentSelectedBlock=null,this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null)}handleScroll(){this.currentSelectedBlock&&this.sendSelectedBlockPosition(),this.currentHoveredBlock&&this.sendHoveredBlockPosition()}setupGlobalEventListeners(){document.addEventListener("mouseover",this.handleGlobalHover.bind(this)),document.addEventListener("mouseout",this.handleGlobalLeave.bind(this)),document.addEventListener("click",this.handleGlobalClick.bind(this))}handleGlobalHover(e){if(!this.active)return;const t=e.target.closest("[data-block]");t&&(e.stopPropagation(),this.currentHoveredBlock=t,this.sendHoveredBlockPosition())}handleGlobalLeave(e){!this.active||!e.target.closest("[data-block]")||this.overlayButtonHovered||(this.currentHoveredBlock=null,this.messenger.send("craftile.preview.block-leave"))}handleGlobalClick(e){if(this.messenger.send("craftile.preview.click"),!this.active)return;const t=e.target.closest("[data-block]");if(!t)return;e.stopPropagation(),t.getAttribute("data-block")&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(),this.trackSelectedBlock())}sendHoveredBlockPosition(){const e=this.computeElementPositioning(this.currentHoveredBlock);this.messenger.send("craftile.preview.block-hover",{blockRect:e.rect,parentRect:e.parentRect,scrollTop:e.scrollTop,scrollLeft:e.scrollLeft,blockId:this.currentHoveredBlock.dataset.block,parentFlexDirection:e.parentFlexDirection})}sendSelectedBlockPosition(e=!1){const t=this.computeElementPositioning(this.currentSelectedBlock);this.messenger.send(e?"craftile.preview.update-selected-block":"craftile.preview.block-select",{blockRect:t.rect,scrollTop:t.scrollTop,scrollLeft:t.scrollLeft,blockId:this.currentSelectedBlock.dataset.block})}trackSelectedBlock(){if(!this.currentSelectedBlock)return;this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null),this.resizeObserver=new ResizeObserver(()=>{this.sendSelectedBlockPosition(!0)}),this.resizeObserver.observe(this.currentSelectedBlock),this.mutationObserver=new MutationObserver(()=>{this.sendSelectedBlockPosition(!0)}),this.mutationObserver.observe(this.currentSelectedBlock,{attributes:!0,subtree:!1});let e=this.currentSelectedBlock.parentElement;for(;e&&(this.mutationObserver.observe(e,{childList:!0,subtree:!1}),e!==document.body);)e=e.parentElement}computeElementPositioning(e){const t=e.getBoundingClientRect(),n=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r=this.findEldestParentBlock(e),s=r&&r!==e?r.getBoundingClientRect():void 0,l=this.getElementFlexDirection(e.parentElement);return{rect:t,parentRect:s,scrollTop:n,scrollLeft:i,parentFlexDirection:l}}findEldestParentBlock(e){let t=null,n=e.parentElement;for(;n&&n!==document.body;)n.hasAttribute("data-block")&&(t=n),n=n.parentElement;return t}getElementFlexDirection(e){if(!e)return"column";const t=window.getComputedStyle(e),n=t.display.includes("flex")?t.flexDirection:"column";return n==="row"||n==="row-reverse"?"row":"column"}}class ae extends oe{constructor(){super(),m(this,"messenger"),m(this,"inspector"),this.messenger=se(window.origin),this.inspector=new le(this.messenger),this.initialize()}initialize(){window.addEventListener("load",()=>{this.messenger.send("craftile.preview.ready",{}),setTimeout(()=>{this.sendPageData()},0)}),this.messenger.registerFallbackHandler(e=>{const{type:t,payload:n}=e;this.emit(t,n)})}sendPageData(){const e=document.getElementById("page-data");if(e)try{const t=JSON.parse(e.textContent||"{}");this.messenger.send("craftile.preview.page-data",{pageData:t})}catch(t){console.error("Failed to parse page data:",t)}}}var q=11;function ce(o,e){var t=e.attributes,n,i,r,s,l;if(!(e.nodeType===q||o.nodeType===q)){for(var f=t.length-1;f>=0;f--)n=t[f],i=n.name,r=n.namespaceURI,s=n.value,r?(i=n.localName||i,l=o.getAttributeNS(r,i),l!==s&&(n.prefix==="xmlns"&&(i=n.name),o.setAttributeNS(r,i,s))):(l=o.getAttribute(i),l!==s&&o.setAttribute(i,s));for(var p=o.attributes,v=p.length-1;v>=0;v--)n=p[v],i=n.name,r=n.namespaceURI,r?(i=n.localName||i,e.hasAttributeNS(r,i)||o.removeAttributeNS(r,i)):e.hasAttribute(i)||o.removeAttribute(i)}}var H,de="http://www.w3.org/1999/xhtml",b=typeof document>"u"?void 0:document,he=!!b&&"content"in b.createElement("template"),fe=!!b&&b.createRange&&"createContextualFragment"in b.createRange();function ue(o){var e=b.createElement("template");return e.innerHTML=o,e.content.childNodes[0]}function ve(o){H||(H=b.createRange(),H.selectNode(b.body));var e=H.createContextualFragment(o);return e.childNodes[0]}function me(o){var e=b.createElement("body");return e.innerHTML=o,e.childNodes[0]}function pe(o){return o=o.trim(),he?ue(o):fe?ve(o):me(o)}function I(o,e){var t=o.nodeName,n=e.nodeName,i,r;return t===n?!0:(i=t.charCodeAt(0),r=n.charCodeAt(0),i<=90&&r>=97?t===n.toUpperCase():r<=90&&i>=97?n===t.toUpperCase():!1)}function ge(o,e){return!e||e===de?b.createElement(o):b.createElementNS(e,o)}function be(o,e){for(var t=o.firstChild;t;){var n=t.nextSibling;e.appendChild(t),t=n}return e}function _(o,e,t){o[t]!==e[t]&&(o[t]=e[t],o[t]?o.setAttribute(t,""):o.removeAttribute(t))}var Y={OPTION:function(o,e){var t=o.parentNode;if(t){var n=t.nodeName.toUpperCase();n==="OPTGROUP"&&(t=t.parentNode,n=t&&t.nodeName.toUpperCase()),n==="SELECT"&&!t.hasAttribute("multiple")&&(o.hasAttribute("selected")&&!e.selected&&(o.setAttribute("selected","selected"),o.removeAttribute("selected")),t.selectedIndex=-1)}_(o,e,"selected")},INPUT:function(o,e){_(o,e,"checked"),_(o,e,"disabled"),o.value!==e.value&&(o.value=e.value),e.hasAttribute("value")||o.removeAttribute("value")},TEXTAREA:function(o,e){var t=e.value;o.value!==t&&(o.value=t);var n=o.firstChild;if(n){var i=n.nodeValue;if(i==t||!t&&i==o.placeholder)return;n.nodeValue=t}},SELECT:function(o,e){if(!e.hasAttribute("multiple")){for(var t=-1,n=0,i=o.firstChild,r,s;i;)if(s=i.nodeName&&i.nodeName.toUpperCase(),s==="OPTGROUP")r=i,i=r.firstChild,i||(i=r.nextSibling,r=null);else{if(s==="OPTION"){if(i.hasAttribute("selected")){t=n;break}n++}i=i.nextSibling,!i&&r&&(i=r.nextSibling,r=null)}o.selectedIndex=t}}},P=1,J=11,Q=3,Z=8;function w(){}function ke(o){if(o)return o.getAttribute&&o.getAttribute("id")||o.id}function Be(o){return function(t,n,i){if(i||(i={}),typeof n=="string")if(t.nodeName==="#document"||t.nodeName==="HTML"||t.nodeName==="BODY"){var r=n;n=b.createElement("html"),n.innerHTML=r}else n=pe(n);else n.nodeType===J&&(n=n.firstElementChild);var s=i.getNodeKey||ke,l=i.onBeforeNodeAdded||w,f=i.onNodeAdded||w,p=i.onBeforeElUpdated||w,v=i.onElUpdated||w,z=i.onBeforeNodeDiscarded||w,N=i.onNodeDiscarded||w,Ee=i.onBeforeElChildrenUpdated||w,Ce=i.skipFromChildren||w,ee=i.addChild||function(a,c){return a.appendChild(c)},V=i.childrenOnly===!0,y=Object.create(null),L=[];function M(a){L.push(a)}function te(a,c){if(a.nodeType===P)for(var u=a.firstChild;u;){var d=void 0;c&&(d=s(u))?M(d):(N(u),u.firstChild&&te(u,c)),u=u.nextSibling}}function x(a,c,u){z(a)!==!1&&(c&&c.removeChild(a),N(a),te(a,u))}function G(a){if(a.nodeType===P||a.nodeType===J)for(var c=a.firstChild;c;){var u=s(c);u&&(y[u]=c),G(c),c=c.nextSibling}}G(t);function W(a){f(a);for(var c=a.firstChild;c;){var u=c.nextSibling,d=s(c);if(d){var h=y[d];h&&I(c,h)?(c.parentNode.replaceChild(h,c),D(h,c)):W(c)}else W(c);c=u}}function Se(a,c,u){for(;c;){var d=c.nextSibling;(u=s(c))?M(u):x(c,a,!0),c=d}}function D(a,c,u){var d=s(c);if(d&&delete y[d],!u){var h=p(a,c);if(h===!1||(h instanceof HTMLElement&&(a=h,G(a)),o(a,c),v(a),Ee(a,c)===!1))return}a.nodeName!=="TEXTAREA"?ye(a,c):Y.TEXTAREA(a,c)}function ye(a,c){var u=Ce(a,c),d=c.firstChild,h=a.firstChild,O,B,T,$,E;e:for(;d;){for($=d.nextSibling,O=s(d);!u&&h;){if(T=h.nextSibling,d.isSameNode&&d.isSameNode(h)){d=$,h=T;continue e}B=s(h);var U=h.nodeType,C=void 0;if(U===d.nodeType&&(U===P?(O?O!==B&&((E=y[O])?T===E?C=!1:(a.insertBefore(E,h),B?M(B):x(h,a,!0),h=E,B=s(h)):C=!1):B&&(C=!1),C=C!==!1&&I(h,d),C&&D(h,d)):(U===Q||U==Z)&&(C=!0,h.nodeValue!==d.nodeValue&&(h.nodeValue=d.nodeValue))),C){d=$,h=T;continue e}B?M(B):x(h,a,!0),h=T}if(O&&(E=y[O])&&I(E,d))u||ee(a,E),D(E,d);else{var j=l(d);j!==!1&&(j&&(d=j),d.actualize&&(d=d.actualize(a.ownerDocument||b)),ee(a,d),W(d))}d=$,h=T}Se(a,h,B);var ie=Y[a.nodeName];ie&&ie(a,c)}var g=t,R=g.nodeType,ne=n.nodeType;if(!V){if(R===P)ne===P?I(t,n)||(N(t),g=be(t,ge(n.nodeName,n.namespaceURI))):g=n;else if(R===Q||R===Z){if(ne===R)return g.nodeValue!==n.nodeValue&&(g.nodeValue=n.nodeValue),g;g=n}}if(g===n)N(t);else{if(n.isSameNode&&n.isSameNode(g))return;if(D(g,n,V),L)for(var X=0,Oe=L.length;X<Oe;X++){var K=y[L[X]];K&&x(K,K.parentNode,!1)}}return!V&&g!==t&&t.parentNode&&(g.actualize&&(g=g.actualize(t.ownerDocument||b)),t.parentNode.replaceChild(g,t)),g}}var we=Be(ce);class F{constructor(e,t){A(this,"previewClient");A(this,"morphdomOptions");A(this,"elementCache",new Map);A(this,"regionCommentsCache",new Map);this.previewClient=e||new ae,this.morphdomOptions=t?.morphdom||{},this.previewClient.on("updates.effects",this.handleEffects.bind(this)),this.previewClient.on("craftile.editor.updates",this.handleDirectUpdates.bind(this)),this.cacheRegionComments()}static init(e,t){return new F(e,t)}getElementCached(e){if(this.elementCache.has(e)){const n=this.elementCache.get(e);if(n.isConnected)return n;this.elementCache.delete(e)}const t=document.querySelector(`[data-block="${e}"]`);return t&&this.elementCache.set(e,t),t}cacheRegionComments(){const e=document.createTreeWalker(document.body,NodeFilter.SHOW_COMMENT,null);let t;const n=new Map;for(;t=e.nextNode();){const i=t.textContent?.trim();if(i){if(i.startsWith("BEGIN region: ")){const r=i.substring(14);n.set(r,t)}else if(i.startsWith("END region: ")){const r=i.substring(12),s=n.get(r);s&&(this.regionCommentsCache.set(r,{begin:s,end:t}),n.delete(r))}}}}invalidateCache(e){[...e.updated,...e.removed].forEach(t=>{this.elementCache.delete(t)})}handleEffects(e){const{effects:t,blocks:n,regions:i,changes:r}=e;t&&(this.invalidateCache(r),t.html&&this.handleHtmlEffects(t.html,{blocks:n,regions:i,changes:r}))}handleDirectUpdates(e){const{changes:t}=e;t.moved&&this.handleMoves(e),t.updated.length>0&&this.handleDisabledBlocks(e),t.removed.length>0&&this.handleRemoves(e)}handleMoves(e){const{blocks:t,changes:n}=e;for(const[i,r]of Object.entries(e.changes.moved)){if(r?.toParent&&n.updated?.includes(r?.toParent))continue;if(!this.isValidMoveInstruction(r)){console.warn(`Invalid move instruction for block ${i}:`,r);continue}const s=t[i];this.moveBlockUsingDOM(s,r)}}handleDisabledBlocks(e){const{blocks:t,changes:n}=e;for(const i of n.updated){const r=t[i];if(!r)continue;const l=!!this.getElementCached(i);r.disabled&&l?this.removeBlock(r.id):r.disabled}}handleRemoves(e){const{changes:t}=e;for(const n of t.removed)this.removeBlock(n)}handleHtmlEffects(e,t){const{blocks:n,regions:i,changes:r}=t;for(const s of r.added){const l=e[s];if(!l)continue;const f=n[s];if(!f){console.warn(`Block data not found for ${s}`);continue}if(f.disabled){console.debug(`Skipping disabled block ${s} during add operation`);continue}const p=this.calculatePosition(f,i,n);this.insertBlock(f,l,p)}for(const s of r.updated){const l=e[s];if(!l)continue;const f=n[s];if(!f){console.warn(`Block data not found for ${s}`);continue}if(f.disabled)continue;if(!!this.getElementCached(s))this.updateBlockHtml(f,l);else{const z=this.calculatePosition(f,i,n);this.insertBlock(f,l,z)}}}isValidMoveInstruction(e){const t=!!(e.toParent||e.toRegion),n=e.toIndex===void 0||e.toIndex>=0;return t&&n}removeBlock(e){const t=this.getElementCached(e);t?(this.previewClient.emit("block.remove.before",{blockId:e,element:t}),t.remove(),this.elementCache.delete(e),this.previewClient.emit("block.remove.after",{blockId:e,element:t})):console.warn(`Block ${e} not found for removal`)}moveBlockUsingDOM(e,t){const n=this.getElementCached(e.id),{toParent:i,toRegion:r,toIndex:s}=t;if(!n){console.warn(`Block element ${e.id} not found for move operation`);return}if(this.previewClient.emit("block.move.before",{blockId:e.id,blockType:e.type,block:e,element:n,toParent:i,toRegion:r,toIndex:s}),i){const l=this.getElementCached(i);if(!l){console.error(`Parent element ${i} not found for move operation`);return}this.insertElementInContainer(n,l,e.id,s)}else if(r){const l=this.findRegionInsertionPoint(r,s,void 0,void 0,e.id);if(!l){console.error(`Failed to find insertion point for region: ${r}`);return}l.parent.insertBefore(n,l.before)}this.previewClient.emit("block.move.after",{blockId:e.id,blockType:e.type,block:e,element:n,toParent:i,toRegion:r,toIndex:s})}insertElementInContainer(e,t,n,i){const s=Array.from(t.children).filter(f=>f.getAttribute("data-block")!==n),l=i!==void 0&&i<s.length?s[i]:null;t.insertBefore(e,l)}insertBlock(e,t,n){if(this.getElementCached(e.id))return;this.previewClient.emit("block.insert.before",{blockId:e.id,blockType:e.type,block:e,html:t,positionInfo:n});const i=this.parseHtmlElement(t,e.id);if(!i)return;const{parentId:r,regionName:s,position:l,afterId:f,beforeId:p}=n;if(r){const v=this.getElementCached(r);if(!v){console.error(`Parent element ${r} not found for block ${e.id}`);return}this.insertElementByPosition(i,v,l,f,p)}else if(s){const v=this.findRegionInsertionPoint(s,l,f,p);if(!v){console.error(`Failed to find insertion point for region: ${s}`);return}v.parent.insertBefore(i,v.before)}else console.warn(`No parent or region specified for block ${e.id}, inserting into body`),document.body.appendChild(i);this.elementCache.set(e.id,i),i.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"}),this.previewClient.emit("block.insert.after",{blockId:e.id,blockType:e.type,block:e,html:t,positionInfo:n})}updateBlockHtml(e,t){if(!t){console.warn(`No HTML provided for block ${e.id}`);return}const n=this.getElementCached(e.id);if(!n){console.warn(`Block element ${e.id} not found for HTML update`);return}this.previewClient.emit("block.update.before",{blockId:e.id,blockType:e.type,block:e,element:n,html:t}),we(n,t,this.morphdomOptions),this.elementCache.delete(e.id);const i=this.getElementCached(e.id);if(!i){console.error(`Block element ${e.id} not found after morphdom update`);return}i!==n&&this.previewClient.inspector.updateTrackedElement(e.id,i),this.previewClient.emit("block.update.after",{blockId:e.id,blockType:e.type,block:e,element:i,html:t})}insertElementByPosition(e,t,n,i,r){let s=null;if(r)s=this.getElementCached(r);else if(i){const l=this.getElementCached(i);l&&(s=l.nextElementSibling)}else typeof n=="number"&&(s=Array.from(t.children)[n]||null);t.insertBefore(e,s)}findRegionInsertionPoint(e,t,n,i,r){const s=this.regionCommentsCache.get(e);if(!s)return console.error(`Region comments not found for region: ${e}`),null;const{begin:l,end:f}=s;let p=null;if(i){const v=this.getElementCached(i);v&&(p=v)}else if(n){const v=this.getElementCached(n);v&&v.nextSibling&&(p=v.nextSibling)}else typeof t=="number"&&(p=this.findPositionInRegion(l,f,t,r));return p||(p=f),{parent:l.parentNode,before:p}}findPositionInRegion(e,t,n,i){const r=[];let s=e.nextSibling;for(;s&&s!==t;){if(s.nodeType===Node.ELEMENT_NODE&&s.hasAttribute("data-block")){const l=s.getAttribute("data-block");(!i||l!==i)&&r.push(s)}s=s.nextSibling}return n<r.length?r[n]:null}calculatePosition(e,t,n){if(e.parentId){const i=n[e.parentId];i||console.warn(`Block data not found for block ${e.id}`);const r=i.children.indexOf(e.id);if(r===-1)return console.warn(`Block ${e.id} not found in parent ${e.parentId} children`),{parentId:e.parentId};const{afterId:s,beforeId:l}=this.findAdjacentSiblings(i.children,r,n);return{parentId:e.parentId,position:r,afterId:s,beforeId:l}}else{for(const i of t){const r=i.blocks.indexOf(e.id);if(r!==-1){const{afterId:s,beforeId:l}=this.findAdjacentSiblings(i.blocks,r,n);return{regionName:i.name,position:r,afterId:s,beforeId:l}}}return{}}}findAdjacentSiblings(e,t,n){let i,r;for(let s=t-1;s>=0;s--){const l=n[e[s]];if(l&&!l.disabled){i=e[s];break}}for(let s=t+1;s<e.length;s++){const l=n[e[s]];if(l&&!l.disabled){r=e[s];break}}return{afterId:i,beforeId:r}}parseHtmlElement(e,t){if(!e.trim())return console.error(`Empty HTML provided for block ${t}`),null;const n=document.createElement("div");n.innerHTML=e;const i=n.firstElementChild;return i||(console.error(`Invalid HTML for block ${t}: no element found`),null)}}return F})();
1
+ var Te=Object.defineProperty;var Ne=(S,k,g)=>k in S?Te(S,k,{enumerable:!0,configurable:!0,writable:!0,value:g}):S[k]=g;var N=(S,k,g)=>Ne(S,typeof k!="symbol"?k+"":k,g);var HtmlPreviewClient=(function(){"use strict";var S=Object.defineProperty,k=(o,e,t)=>e in o?S(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,g=(o,e,t)=>k(o,typeof e!="symbol"?e+"":e,t);class re{constructor(e,t="*"){g(this,"handlers",new Map),g(this,"targetWindow"),g(this,"targetOrigin"),g(this,"fallbackHandler"),this.targetWindow=e,this.targetOrigin=t,window.addEventListener("message",this.handleMessage.bind(this))}handleMessage(e){const t=e.data;if(!t.type)return;const n=this.handlers.get(t.type);n?n.forEach(i=>i(t.payload,e)):this.fallbackHandler&&this.fallbackHandler(e.data,e)}send(e,t){const n={type:e,payload:t};this.targetWindow.postMessage(n,this.targetOrigin)}listen(e,t){return this.handlers.has(e)||this.handlers.set(e,[]),this.handlers.get(e).push(t),()=>{const n=this.handlers.get(e);if(n){const i=n.indexOf(t);i>-1&&n.splice(i,1)}}}registerFallbackHandler(e){this.fallbackHandler=e}}function se(o="*"){if(window.parent===window)throw new Error("Not inside an iframe");return new re(window.parent,o)}class oe{constructor(){g(this,"listeners",new Map)}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{this.off(e,t)}}off(e,t){const n=this.listeners.get(e);n&&(n.delete(t),n.size===0&&this.listeners.delete(e))}once(e,t){const n=i=>{t(i),this.off(e,n)};this.on(e,n)}emit(e,...t){const n=this.listeners.get(e);if(n){const i=t[0];n.forEach(r=>{try{r(i)}catch(s){console.error(`Error in event listener for '${String(e)}':`,s)}})}}removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}}class le{constructor(e){g(this,"active",!0),g(this,"messenger"),g(this,"currentHoveredBlock",null),g(this,"currentSelectedBlock",null),g(this,"overlayButtonHovered",!1),g(this,"resizeObserver",null),g(this,"mutationObserver",null),this.messenger=e,this.messenger.listen("craftile.inspector.enable",()=>this.enable()),this.messenger.listen("craftile.inspector.disable",()=>this.disable()),this.messenger.listen("craftile.inspector.overlay-button-enter",this.handleOverlayButtonEnter.bind(this)),this.messenger.listen("craftile.inspector.overlay-button-leave",this.handleOverlayButtonLeave.bind(this)),this.messenger.listen("craftile.editor.select-block",this.handleEditorSelectBlock.bind(this)),this.messenger.listen("craftile.editor.deselect-block",this.handleEditorDeselectBlock.bind(this)),window.addEventListener("scroll",this.handleScroll.bind(this),{passive:!0}),this.setupGlobalEventListeners()}updateTrackedElement(e,t){this.currentHoveredBlock?.dataset.block===e&&(this.currentHoveredBlock=t,this.sendHoveredBlockPosition()),this.currentSelectedBlock?.dataset.block===e&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(!0),this.trackSelectedBlock())}enable(){this.active=!0,document.body.classList.add("craftile-inspector-active")}disable(){this.active=!1,document.body.classList.remove("craftile-inspector-active"),this.currentHoveredBlock=null,this.currentSelectedBlock=null,this.overlayButtonHovered=!1}handleOverlayButtonEnter(){this.overlayButtonHovered=!0}handleOverlayButtonLeave(e){this.overlayButtonHovered=!1,this.currentHoveredBlock||this.messenger.send("craftile.preview.block-leave")}handleEditorSelectBlock(e){const t=document.querySelector(`[data-block="${e.blockId}"]`);t&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(),this.trackSelectedBlock(),t.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"}))}handleEditorDeselectBlock(){this.currentSelectedBlock=null,this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null)}handleScroll(){this.currentSelectedBlock&&this.sendSelectedBlockPosition(),this.currentHoveredBlock&&this.sendHoveredBlockPosition()}setupGlobalEventListeners(){document.addEventListener("mouseover",this.handleGlobalHover.bind(this)),document.addEventListener("mouseout",this.handleGlobalLeave.bind(this)),document.addEventListener("click",this.handleGlobalClick.bind(this))}handleGlobalHover(e){if(!this.active)return;const t=e.target.closest("[data-block]");t&&(e.stopPropagation(),this.currentHoveredBlock=t,this.sendHoveredBlockPosition())}handleGlobalLeave(e){!this.active||!e.target.closest("[data-block]")||this.overlayButtonHovered||(this.currentHoveredBlock=null,this.messenger.send("craftile.preview.block-leave"))}handleGlobalClick(e){if(this.messenger.send("craftile.preview.click"),!this.active)return;const t=e.target.closest("[data-block]");if(!t)return;e.stopPropagation(),t.getAttribute("data-block")&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(),this.trackSelectedBlock())}sendHoveredBlockPosition(){const e=this.computeElementPositioning(this.currentHoveredBlock);this.messenger.send("craftile.preview.block-hover",{blockRect:e.rect,parentRect:e.parentRect,scrollTop:e.scrollTop,scrollLeft:e.scrollLeft,blockId:this.currentHoveredBlock.dataset.block,parentFlexDirection:e.parentFlexDirection})}sendSelectedBlockPosition(e=!1){const t=this.computeElementPositioning(this.currentSelectedBlock);this.messenger.send(e?"craftile.preview.update-selected-block":"craftile.preview.block-select",{blockRect:t.rect,scrollTop:t.scrollTop,scrollLeft:t.scrollLeft,blockId:this.currentSelectedBlock.dataset.block})}trackSelectedBlock(){if(!this.currentSelectedBlock)return;this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null),this.resizeObserver=new ResizeObserver(()=>{this.sendSelectedBlockPosition(!0)}),this.resizeObserver.observe(this.currentSelectedBlock),this.mutationObserver=new MutationObserver(()=>{this.sendSelectedBlockPosition(!0)}),this.mutationObserver.observe(this.currentSelectedBlock,{attributes:!0,subtree:!1});let e=this.currentSelectedBlock.parentElement;for(;e&&(this.mutationObserver.observe(e,{childList:!0,subtree:!1}),e!==document.body);)e=e.parentElement}computeElementPositioning(e){const t=e.getBoundingClientRect(),n=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r=this.findEldestParentBlock(e),s=r&&r!==e?r.getBoundingClientRect():void 0,l=this.getElementFlexDirection(e.parentElement);return{rect:t,parentRect:s,scrollTop:n,scrollLeft:i,parentFlexDirection:l}}findEldestParentBlock(e){let t=null,n=e.parentElement;for(;n&&n!==document.body;)n.hasAttribute("data-block")&&(t=n),n=n.parentElement;return t}getElementFlexDirection(e){if(!e)return"column";const t=window.getComputedStyle(e),n=t.display.includes("flex")?t.flexDirection:"column";return n==="row"||n==="row-reverse"?"row":"column"}}class ae extends oe{constructor(){super(),g(this,"messenger"),g(this,"inspector"),this.messenger=se(window.origin),this.inspector=new le(this.messenger),this.initialize()}initialize(){window.addEventListener("load",()=>{this.messenger.send("craftile.preview.ready",{}),setTimeout(()=>{this.sendPageData()},0)}),this.messenger.registerFallbackHandler(e=>{const{type:t,payload:n}=e;this.emit(t,n)})}sendPageData(){const e=document.getElementById("page-data");if(e)try{const t=JSON.parse(e.textContent||"{}");this.messenger.send("craftile.preview.page-data",{pageData:t})}catch(t){console.error("Failed to parse page data:",t)}}}var q=11;function ce(o,e){var t=e.attributes,n,i,r,s,l;if(!(e.nodeType===q||o.nodeType===q)){for(var d=t.length-1;d>=0;d--)n=t[d],i=n.name,r=n.namespaceURI,s=n.value,r?(i=n.localName||i,l=o.getAttributeNS(r,i),l!==s&&(n.prefix==="xmlns"&&(i=n.name),o.setAttributeNS(r,i,s))):(l=o.getAttribute(i),l!==s&&o.setAttribute(i,s));for(var f=o.attributes,m=f.length-1;m>=0;m--)n=f[m],i=n.name,r=n.namespaceURI,r?(i=n.localName||i,e.hasAttributeNS(r,i)||o.removeAttributeNS(r,i)):e.hasAttribute(i)||o.removeAttribute(i)}}var A,de="http://www.w3.org/1999/xhtml",b=typeof document>"u"?void 0:document,he=!!b&&"content"in b.createElement("template"),fe=!!b&&b.createRange&&"createContextualFragment"in b.createRange();function ue(o){var e=b.createElement("template");return e.innerHTML=o,e.content.childNodes[0]}function me(o){A||(A=b.createRange(),A.selectNode(b.body));var e=A.createContextualFragment(o);return e.childNodes[0]}function ve(o){var e=b.createElement("body");return e.innerHTML=o,e.childNodes[0]}function ge(o){return o=o.trim(),he?ue(o):fe?me(o):ve(o)}function H(o,e){var t=o.nodeName,n=e.nodeName,i,r;return t===n?!0:(i=t.charCodeAt(0),r=n.charCodeAt(0),i<=90&&r>=97?t===n.toUpperCase():r<=90&&i>=97?n===t.toUpperCase():!1)}function pe(o,e){return!e||e===de?b.createElement(o):b.createElementNS(e,o)}function be(o,e){for(var t=o.firstChild;t;){var n=t.nextSibling;e.appendChild(t),t=n}return e}function _(o,e,t){o[t]!==e[t]&&(o[t]=e[t],o[t]?o.setAttribute(t,""):o.removeAttribute(t))}var Y={OPTION:function(o,e){var t=o.parentNode;if(t){var n=t.nodeName.toUpperCase();n==="OPTGROUP"&&(t=t.parentNode,n=t&&t.nodeName.toUpperCase()),n==="SELECT"&&!t.hasAttribute("multiple")&&(o.hasAttribute("selected")&&!e.selected&&(o.setAttribute("selected","selected"),o.removeAttribute("selected")),t.selectedIndex=-1)}_(o,e,"selected")},INPUT:function(o,e){_(o,e,"checked"),_(o,e,"disabled"),o.value!==e.value&&(o.value=e.value),e.hasAttribute("value")||o.removeAttribute("value")},TEXTAREA:function(o,e){var t=e.value;o.value!==t&&(o.value=t);var n=o.firstChild;if(n){var i=n.nodeValue;if(i==t||!t&&i==o.placeholder)return;n.nodeValue=t}},SELECT:function(o,e){if(!e.hasAttribute("multiple")){for(var t=-1,n=0,i=o.firstChild,r,s;i;)if(s=i.nodeName&&i.nodeName.toUpperCase(),s==="OPTGROUP")r=i,i=r.firstChild,i||(i=r.nextSibling,r=null);else{if(s==="OPTION"){if(i.hasAttribute("selected")){t=n;break}n++}i=i.nextSibling,!i&&r&&(i=r.nextSibling,r=null)}o.selectedIndex=t}}},P=1,J=11,Q=3,Z=8;function B(){}function ke(o){if(o)return o.getAttribute&&o.getAttribute("id")||o.id}function Ce(o){return function(t,n,i){if(i||(i={}),typeof n=="string")if(t.nodeName==="#document"||t.nodeName==="HTML"||t.nodeName==="BODY"){var r=n;n=b.createElement("html"),n.innerHTML=r}else n=ge(n);else n.nodeType===J&&(n=n.firstElementChild);var s=i.getNodeKey||ke,l=i.onBeforeNodeAdded||B,d=i.onNodeAdded||B,f=i.onBeforeElUpdated||B,m=i.onElUpdated||B,G=i.onBeforeNodeDiscarded||B,M=i.onNodeDiscarded||B,we=i.onBeforeElChildrenUpdated||B,Ee=i.skipFromChildren||B,ee=i.addChild||function(a,c){return a.appendChild(c)},z=i.childrenOnly===!0,y=Object.create(null),x=[];function I(a){x.push(a)}function te(a,c){if(a.nodeType===P)for(var v=a.firstChild;v;){var h=void 0;c&&(h=s(v))?I(h):(M(v),v.firstChild&&te(v,c)),v=v.nextSibling}}function D(a,c,v){G(a)!==!1&&(c&&c.removeChild(a),M(a),te(a,v))}function V(a){if(a.nodeType===P||a.nodeType===J)for(var c=a.firstChild;c;){var v=s(c);v&&(y[v]=c),V(c),c=c.nextSibling}}V(t);function W(a){d(a);for(var c=a.firstChild;c;){var v=c.nextSibling,h=s(c);if(h){var u=y[h];u&&H(c,u)?(c.parentNode.replaceChild(u,c),L(u,c)):W(c)}else W(c);c=v}}function Se(a,c,v){for(;c;){var h=c.nextSibling;(v=s(c))?I(v):D(c,a,!0),c=h}}function L(a,c,v){var h=s(c);if(h&&delete y[h],!v){var u=f(a,c);if(u===!1||(u instanceof HTMLElement&&(a=u,V(a)),o(a,c),m(a),we(a,c)===!1))return}a.nodeName!=="TEXTAREA"?ye(a,c):Y.TEXTAREA(a,c)}function ye(a,c){var v=Ee(a,c),h=c.firstChild,u=a.firstChild,O,C,T,$,w;e:for(;h;){for($=h.nextSibling,O=s(h);!v&&u;){if(T=u.nextSibling,h.isSameNode&&h.isSameNode(u)){h=$,u=T;continue e}C=s(u);var F=u.nodeType,E=void 0;if(F===h.nodeType&&(F===P?(O?O!==C&&((w=y[O])?T===w?E=!1:(a.insertBefore(w,u),C?I(C):D(u,a,!0),u=w,C=s(u)):E=!1):C&&(E=!1),E=E!==!1&&H(u,h),E&&L(u,h)):(F===Q||F==Z)&&(E=!0,u.nodeValue!==h.nodeValue&&(u.nodeValue=h.nodeValue))),E){h=$,u=T;continue e}C?I(C):D(u,a,!0),u=T}if(O&&(w=y[O])&&H(w,h))v||ee(a,w),L(w,h);else{var j=l(h);j!==!1&&(j&&(h=j),h.actualize&&(h=h.actualize(a.ownerDocument||b)),ee(a,h),W(h))}h=$,u=T}Se(a,u,C);var ie=Y[a.nodeName];ie&&ie(a,c)}var p=t,R=p.nodeType,ne=n.nodeType;if(!z){if(R===P)ne===P?H(t,n)||(M(t),p=be(t,pe(n.nodeName,n.namespaceURI))):p=n;else if(R===Q||R===Z){if(ne===R)return p.nodeValue!==n.nodeValue&&(p.nodeValue=n.nodeValue),p;p=n}}if(p===n)M(t);else{if(n.isSameNode&&n.isSameNode(p))return;if(L(p,n,z),x)for(var X=0,Oe=x.length;X<Oe;X++){var K=y[x[X]];K&&D(K,K.parentNode,!1)}}return!z&&p!==t&&t.parentNode&&(p.actualize&&(p=p.actualize(t.ownerDocument||b)),t.parentNode.replaceChild(p,t)),p}}var Be=Ce(ce);class U{constructor(e,t){N(this,"previewClient");N(this,"morphdomOptions");N(this,"elementCache",new Map);N(this,"regionCommentsCache",new Map);N(this,"childrenCommentsCache",new Map);this.previewClient=e||new ae,this.morphdomOptions=t?.morphdom||{},this.previewClient.on("updates.effects",this.handleEffects.bind(this)),this.previewClient.on("craftile.editor.updates",this.handleDirectUpdates.bind(this)),this.cacheRegionComments(),this.cacheChildrenComments()}static init(e,t){return new U(e,t)}getElementCached(e){if(this.elementCache.has(e)){const n=this.elementCache.get(e);if(n.isConnected)return n;this.elementCache.delete(e)}const t=document.querySelector(`[data-block="${e}"]`);return t&&this.elementCache.set(e,t),t}cacheRegionComments(){const e=document.createTreeWalker(document.body,NodeFilter.SHOW_COMMENT,null);let t;const n=new Map;for(;t=e.nextNode();){const i=t.textContent?.trim();if(i){if(i.startsWith("BEGIN region: ")){const r=i.substring(14);n.set(r,t)}else if(i.startsWith("END region: ")){const r=i.substring(12),s=n.get(r);s&&(this.regionCommentsCache.set(r,{begin:s,end:t}),n.delete(r))}}}}cacheChildrenComments(){const e=document.createTreeWalker(document.body,NodeFilter.SHOW_COMMENT,null);let t;const n=new Map;for(;t=e.nextNode();){const i=t.textContent?.trim();if(i){if(i.startsWith("BEGIN children: ")){const r=i.substring(16);n.set(r,t)}else if(i.startsWith("END children: ")){const r=i.substring(14),s=n.get(r);s&&(this.childrenCommentsCache.set(r,{begin:s,end:t}),n.delete(r))}}}}cacheChildrenCommentsForBlock(e){const t=this.getElementCached(e);if(!t)return;this.childrenCommentsCache.delete(e);const n=document.createTreeWalker(t,NodeFilter.SHOW_COMMENT,null);let i,r=null;for(;i=n.nextNode();){const s=i.textContent?.trim();if(s){if(s===`BEGIN children: ${e}`)r=i;else if(s===`END children: ${e}`&&r){this.childrenCommentsCache.set(e,{begin:r,end:i});break}}}}invalidateCache(e){[...e.updated,...e.removed].forEach(t=>{this.elementCache.delete(t),this.childrenCommentsCache.delete(t)})}handleEffects(e){const{effects:t,blocks:n,regions:i,changes:r}=e;t&&(this.invalidateCache(r),t.html&&this.handleHtmlEffects(t.html,{blocks:n,regions:i,changes:r}))}handleDirectUpdates(e){const{changes:t}=e;t.moved&&this.handleMoves(e),t.updated.length>0&&this.handleDisabledBlocks(e),t.removed.length>0&&this.handleRemoves(e)}handleMoves(e){const{blocks:t,changes:n}=e;for(const[i,r]of Object.entries(e.changes.moved)){if(r?.toParent&&n.updated?.includes(r?.toParent))continue;if(!this.isValidMoveInstruction(r)){console.warn(`Invalid move instruction for block ${i}:`,r);continue}const s=t[i];this.moveBlockUsingDOM(s,r)}}handleDisabledBlocks(e){const{blocks:t,changes:n}=e;for(const i of n.updated){const r=t[i];if(!r)continue;const l=!!this.getElementCached(i);r.disabled&&l?this.removeBlock(r.id):r.disabled}}handleRemoves(e){const{changes:t}=e;for(const n of t.removed)this.removeBlock(n)}handleHtmlEffects(e,t){const{blocks:n,regions:i,changes:r}=t;for(const s of r.added){const l=e[s];if(!l)continue;const d=n[s];if(!d){console.warn(`Block data not found for ${s}`);continue}if(d.disabled){console.debug(`Skipping disabled block ${s} during add operation`);continue}const f=this.calculatePosition(d,i,n);this.insertBlock(d,l,f)}for(const s of r.updated){const l=e[s];if(!l)continue;const d=n[s];if(!d){console.warn(`Block data not found for ${s}`);continue}if(d.disabled)continue;if(!!this.getElementCached(s))this.updateBlockHtml(d,l);else{const G=this.calculatePosition(d,i,n);this.insertBlock(d,l,G)}}}isValidMoveInstruction(e){const t=!!(e.toParent||e.toRegion),n=e.toIndex===void 0||e.toIndex>=0;return t&&n}removeBlock(e){const t=this.getElementCached(e);t?(this.previewClient.emit("block.remove.before",{blockId:e,element:t}),t.remove(),this.elementCache.delete(e),this.previewClient.emit("block.remove.after",{blockId:e,element:t})):console.warn(`Block ${e} not found for removal`)}moveBlockUsingDOM(e,t){const n=this.getElementCached(e.id),{toParent:i,toRegion:r,toIndex:s}=t;if(!n){console.warn(`Block element ${e.id} not found for move operation`);return}if(this.previewClient.emit("block.move.before",{blockId:e.id,blockType:e.type,block:e,element:n,toParent:i,toRegion:r,toIndex:s}),i){const l=this.getElementCached(i);if(!l){console.error(`Parent element ${i} not found for move operation`);return}this.insertElementInContainer(n,l,e.id,s)}else if(r){const l=this.findRegionInsertionPoint(r,s,void 0,void 0,e.id);if(!l){console.error(`Failed to find insertion point for region: ${r}`);return}l.parent.insertBefore(n,l.before)}this.previewClient.emit("block.move.after",{blockId:e.id,blockType:e.type,block:e,element:n,toParent:i,toRegion:r,toIndex:s})}insertElementInContainer(e,t,n,i){const r=t.getAttribute("data-block"),s=r?this.childrenCommentsCache.get(r):null;if(s){const l=this.findPositionBetweenComments(s.begin,s.end,i,n);l.parent.insertBefore(e,l.before)}else{const d=Array.from(t.children).filter(m=>m.getAttribute("data-block")!==n),f=i!==void 0&&i<d.length?d[i]:null;t.insertBefore(e,f)}}insertBlock(e,t,n){if(this.getElementCached(e.id))return;this.previewClient.emit("block.insert.before",{blockId:e.id,blockType:e.type,block:e,html:t,positionInfo:n});const i=this.parseHtmlElement(t,e.id);if(!i)return;const{parentId:r,regionName:s,position:l,afterId:d,beforeId:f}=n;if(r){const m=this.getElementCached(r);if(!m){console.error(`Parent element ${r} not found for block ${e.id}`);return}this.insertElementByPosition(i,m,l,d,f)}else if(s){const m=this.findRegionInsertionPoint(s,l,d,f);if(!m){console.error(`Failed to find insertion point for region: ${s}`);return}m.parent.insertBefore(i,m.before)}else console.warn(`No parent or region specified for block ${e.id}, inserting into body`),document.body.appendChild(i);this.elementCache.set(e.id,i),this.cacheChildrenCommentsForBlock(e.id),i.scrollIntoView({behavior:"smooth",block:"nearest",inline:"nearest"}),this.previewClient.emit("block.insert.after",{blockId:e.id,blockType:e.type,block:e,html:t,positionInfo:n})}updateBlockHtml(e,t){if(!t){console.warn(`No HTML provided for block ${e.id}`);return}const n=this.getElementCached(e.id);if(!n){console.warn(`Block element ${e.id} not found for HTML update`);return}this.previewClient.emit("block.update.before",{blockId:e.id,blockType:e.type,block:e,element:n,html:t}),Be(n,t,this.morphdomOptions),this.elementCache.delete(e.id);const i=this.getElementCached(e.id);if(!i){console.error(`Block element ${e.id} not found after morphdom update`);return}i!==n&&this.previewClient.inspector.updateTrackedElement(e.id,i),this.cacheChildrenCommentsForBlock(e.id),this.previewClient.emit("block.update.after",{blockId:e.id,blockType:e.type,block:e,element:i,html:t})}insertElementByPosition(e,t,n,i,r){const s=t.getAttribute("data-block"),l=s?this.childrenCommentsCache.get(s):null;if(l){let d=null;if(r)d=this.getElementCached(r);else if(i){const f=this.getElementCached(i);f&&f.nextSibling&&(d=f.nextSibling)}else if(typeof n=="number"){const f=this.findPositionBetweenComments(l.begin,l.end,n);l.begin.parentNode.insertBefore(e,f.before);return}d||(d=l.end),l.begin.parentNode.insertBefore(e,d)}else{let d=null;if(r)d=this.getElementCached(r);else if(i){const f=this.getElementCached(i);f&&(d=f.nextElementSibling)}else typeof n=="number"&&(d=Array.from(t.children)[n]||null);t.insertBefore(e,d)}}findRegionInsertionPoint(e,t,n,i,r){const s=this.regionCommentsCache.get(e);if(!s)return console.error(`Region comments not found for region: ${e}`),null;const{begin:l,end:d}=s;let f=null;if(i){const m=this.getElementCached(i);m&&(f=m)}else if(n){const m=this.getElementCached(n);m&&m.nextSibling&&(f=m.nextSibling)}else typeof t=="number"&&(f=this.findPositionInRegion(l,d,t,r));return f||(f=d),{parent:l.parentNode,before:f}}findPositionInRegion(e,t,n,i){const r=[];let s=e.nextSibling;for(;s&&s!==t;){if(s.nodeType===Node.ELEMENT_NODE&&s.hasAttribute("data-block")){const l=s.getAttribute("data-block");(!i||l!==i)&&r.push(s)}s=s.nextSibling}return n<r.length?r[n]:null}findPositionBetweenComments(e,t,n,i){let r=null;if(typeof n=="number"){const s=[];let l=e.nextSibling;for(;l&&l!==t;){if(l.nodeType===Node.ELEMENT_NODE&&l.hasAttribute("data-block")){const d=l.getAttribute("data-block");(!i||d!==i)&&s.push(l)}l=l.nextSibling}r=n<s.length?s[n]:null}return r||(r=t),{parent:e.parentNode,before:r}}calculatePosition(e,t,n){if(e.parentId){const i=n[e.parentId];i||console.warn(`Block data not found for block ${e.id}`);const r=i.children.indexOf(e.id);if(r===-1)return console.warn(`Block ${e.id} not found in parent ${e.parentId} children`),{parentId:e.parentId};const{afterId:s,beforeId:l}=this.findAdjacentSiblings(i.children,r,n);return{parentId:e.parentId,position:r,afterId:s,beforeId:l}}else{for(const i of t){const r=i.blocks.indexOf(e.id);if(r!==-1){const{afterId:s,beforeId:l}=this.findAdjacentSiblings(i.blocks,r,n);return{regionName:i.name,position:r,afterId:s,beforeId:l}}}return{}}}findAdjacentSiblings(e,t,n){let i,r;for(let s=t-1;s>=0;s--){const l=n[e[s]];if(l&&!l.disabled){i=e[s];break}}for(let s=t+1;s<e.length;s++){const l=n[e[s]];if(l&&!l.disabled){r=e[s];break}}return{afterId:i,beforeId:r}}parseHtmlElement(e,t){if(!e.trim())return console.error(`Empty HTML provided for block ${t}`),null;const n=document.createElement("div");n.innerHTML=e;const i=n.firstElementChild;return i||(console.error(`Invalid HTML for block ${t}: no element found`),null)}}return U})();
package/dist/index.d.ts CHANGED
@@ -10,10 +10,13 @@ export default class RawHtmlRenderer {
10
10
  private morphdomOptions;
11
11
  private elementCache;
12
12
  private regionCommentsCache;
13
+ private childrenCommentsCache;
13
14
  constructor(previewClient?: PreviewClient, options?: HtmlPreviewClientOptions);
14
15
  static init(previewClient?: PreviewClient, options?: HtmlPreviewClientOptions): RawHtmlRenderer;
15
16
  private getElementCached;
16
17
  private cacheRegionComments;
18
+ private cacheChildrenComments;
19
+ private cacheChildrenCommentsForBlock;
17
20
  private invalidateCache;
18
21
  private handleEffects;
19
22
  private handleDirectUpdates;
@@ -30,6 +33,7 @@ export default class RawHtmlRenderer {
30
33
  private insertElementByPosition;
31
34
  private findRegionInsertionPoint;
32
35
  private findPositionInRegion;
36
+ private findPositionBetweenComments;
33
37
  private calculatePosition;
34
38
  private findAdjacentSiblings;
35
39
  private parseHtmlElement;
package/dist/index.js CHANGED
@@ -9,11 +9,13 @@ class RawHtmlRenderer {
9
9
  __publicField(this, "morphdomOptions");
10
10
  __publicField(this, "elementCache", /* @__PURE__ */ new Map());
11
11
  __publicField(this, "regionCommentsCache", /* @__PURE__ */ new Map());
12
+ __publicField(this, "childrenCommentsCache", /* @__PURE__ */ new Map());
12
13
  this.previewClient = previewClient || new PreviewClient();
13
14
  this.morphdomOptions = options?.morphdom || {};
14
15
  this.previewClient.on("updates.effects", this.handleEffects.bind(this));
15
16
  this.previewClient.on("craftile.editor.updates", this.handleDirectUpdates.bind(this));
16
17
  this.cacheRegionComments();
18
+ this.cacheChildrenComments();
17
19
  }
18
20
  static init(previewClient, options) {
19
21
  return new RawHtmlRenderer(previewClient, options);
@@ -57,9 +59,60 @@ class RawHtmlRenderer {
57
59
  }
58
60
  }
59
61
  }
62
+ cacheChildrenComments() {
63
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT, null);
64
+ let node;
65
+ const pendingChildren = /* @__PURE__ */ new Map();
66
+ while (node = walker.nextNode()) {
67
+ const text = node.textContent?.trim();
68
+ if (!text) {
69
+ continue;
70
+ }
71
+ if (text.startsWith("BEGIN children: ")) {
72
+ const blockId = text.substring("BEGIN children: ".length);
73
+ pendingChildren.set(blockId, node);
74
+ } else if (text.startsWith("END children: ")) {
75
+ const blockId = text.substring("END children: ".length);
76
+ const beginComment = pendingChildren.get(blockId);
77
+ if (beginComment) {
78
+ this.childrenCommentsCache.set(blockId, {
79
+ begin: beginComment,
80
+ end: node
81
+ });
82
+ pendingChildren.delete(blockId);
83
+ }
84
+ }
85
+ }
86
+ }
87
+ cacheChildrenCommentsForBlock(blockId) {
88
+ const blockElement = this.getElementCached(blockId);
89
+ if (!blockElement) {
90
+ return;
91
+ }
92
+ this.childrenCommentsCache.delete(blockId);
93
+ const walker = document.createTreeWalker(blockElement, NodeFilter.SHOW_COMMENT, null);
94
+ let node;
95
+ let beginComment = null;
96
+ while (node = walker.nextNode()) {
97
+ const text = node.textContent?.trim();
98
+ if (!text) {
99
+ continue;
100
+ }
101
+ if (text === `BEGIN children: ${blockId}`) {
102
+ beginComment = node;
103
+ } else if (text === `END children: ${blockId}` && beginComment) {
104
+ this.childrenCommentsCache.set(blockId, {
105
+ begin: beginComment,
106
+ end: node
107
+ });
108
+ break;
109
+ }
110
+ }
111
+ }
60
112
  invalidateCache(changes) {
61
113
  [...changes.updated, ...changes.removed].forEach((blockId) => {
62
114
  this.elementCache.delete(blockId);
115
+ this.childrenCommentsCache.delete(blockId);
63
116
  });
64
117
  }
65
118
  handleEffects(data) {
@@ -224,10 +277,17 @@ class RawHtmlRenderer {
224
277
  });
225
278
  }
226
279
  insertElementInContainer(element, container, elementId, index) {
227
- const children = Array.from(container.children);
228
- const otherChildren = children.filter((child) => child.getAttribute("data-block") !== elementId);
229
- const insertBefore = index !== void 0 && index < otherChildren.length ? otherChildren[index] : null;
230
- container.insertBefore(element, insertBefore);
280
+ const blockId = container.getAttribute("data-block");
281
+ const comments = blockId ? this.childrenCommentsCache.get(blockId) : null;
282
+ if (comments) {
283
+ const insertionPoint = this.findPositionBetweenComments(comments.begin, comments.end, index, elementId);
284
+ insertionPoint.parent.insertBefore(element, insertionPoint.before);
285
+ } else {
286
+ const children = Array.from(container.children);
287
+ const otherChildren = children.filter((child) => child.getAttribute("data-block") !== elementId);
288
+ const insertBefore = index !== void 0 && index < otherChildren.length ? otherChildren[index] : null;
289
+ container.insertBefore(element, insertBefore);
290
+ }
231
291
  }
232
292
  insertBlock(block, html, positionInfo) {
233
293
  if (this.getElementCached(block.id)) {
@@ -264,6 +324,7 @@ class RawHtmlRenderer {
264
324
  document.body.appendChild(newElement);
265
325
  }
266
326
  this.elementCache.set(block.id, newElement);
327
+ this.cacheChildrenCommentsForBlock(block.id);
267
328
  newElement.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
268
329
  this.previewClient.emit("block.insert.after", {
269
330
  blockId: block.id,
@@ -300,6 +361,7 @@ class RawHtmlRenderer {
300
361
  if (updatedElement !== blockElement) {
301
362
  this.previewClient.inspector.updateTrackedElement(block.id, updatedElement);
302
363
  }
364
+ this.cacheChildrenCommentsForBlock(block.id);
303
365
  this.previewClient.emit("block.update.after", {
304
366
  blockId: block.id,
305
367
  blockType: block.type,
@@ -309,19 +371,41 @@ class RawHtmlRenderer {
309
371
  });
310
372
  }
311
373
  insertElementByPosition(element, parentElement, position, afterId, beforeId) {
312
- let insertBefore = null;
313
- if (beforeId) {
314
- insertBefore = this.getElementCached(beforeId);
315
- } else if (afterId) {
316
- const afterElement = this.getElementCached(afterId);
317
- if (afterElement) {
318
- insertBefore = afterElement.nextElementSibling;
374
+ const blockId = parentElement.getAttribute("data-block");
375
+ const comments = blockId ? this.childrenCommentsCache.get(blockId) : null;
376
+ if (comments) {
377
+ let insertBefore = null;
378
+ if (beforeId) {
379
+ insertBefore = this.getElementCached(beforeId);
380
+ } else if (afterId) {
381
+ const afterElement = this.getElementCached(afterId);
382
+ if (afterElement && afterElement.nextSibling) {
383
+ insertBefore = afterElement.nextSibling;
384
+ }
385
+ } else if (typeof position === "number") {
386
+ const insertionPoint = this.findPositionBetweenComments(comments.begin, comments.end, position);
387
+ comments.begin.parentNode.insertBefore(element, insertionPoint.before);
388
+ return;
319
389
  }
320
- } else if (typeof position === "number") {
321
- const children = Array.from(parentElement.children);
322
- insertBefore = children[position] || null;
390
+ if (!insertBefore) {
391
+ insertBefore = comments.end;
392
+ }
393
+ comments.begin.parentNode.insertBefore(element, insertBefore);
394
+ } else {
395
+ let insertBefore = null;
396
+ if (beforeId) {
397
+ insertBefore = this.getElementCached(beforeId);
398
+ } else if (afterId) {
399
+ const afterElement = this.getElementCached(afterId);
400
+ if (afterElement) {
401
+ insertBefore = afterElement.nextElementSibling;
402
+ }
403
+ } else if (typeof position === "number") {
404
+ const children = Array.from(parentElement.children);
405
+ insertBefore = children[position] || null;
406
+ }
407
+ parentElement.insertBefore(element, insertBefore);
323
408
  }
324
- parentElement.insertBefore(element, insertBefore);
325
409
  }
326
410
  findRegionInsertionPoint(regionName, position, afterId, beforeId, excludeBlockId) {
327
411
  const regionComments = this.regionCommentsCache.get(regionName);
@@ -366,6 +450,30 @@ class RawHtmlRenderer {
366
450
  }
367
451
  return position < regionBlocks.length ? regionBlocks[position] : null;
368
452
  }
453
+ findPositionBetweenComments(beginComment, endComment, index, excludeBlockId) {
454
+ let insertBefore = null;
455
+ if (typeof index === "number") {
456
+ const blocks = [];
457
+ let currentNode = beginComment.nextSibling;
458
+ while (currentNode && currentNode !== endComment) {
459
+ if (currentNode.nodeType === Node.ELEMENT_NODE && currentNode.hasAttribute("data-block")) {
460
+ const blockId = currentNode.getAttribute("data-block");
461
+ if (!excludeBlockId || blockId !== excludeBlockId) {
462
+ blocks.push(currentNode);
463
+ }
464
+ }
465
+ currentNode = currentNode.nextSibling;
466
+ }
467
+ insertBefore = index < blocks.length ? blocks[index] : null;
468
+ }
469
+ if (!insertBefore) {
470
+ insertBefore = endComment;
471
+ }
472
+ return {
473
+ parent: beginComment.parentNode,
474
+ before: insertBefore
475
+ };
476
+ }
369
477
  calculatePosition(block, regions, blocks) {
370
478
  if (block.parentId) {
371
479
  const parentBlock = blocks[block.parentId];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craftile/preview-client-html",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "HTML preview client for static block rendering in Craftile",
5
5
  "keywords": [
6
6
  "craftile",
@@ -39,8 +39,8 @@
39
39
  "vite": "^7.0.0",
40
40
  "vite-plugin-dts": "^4.5.4",
41
41
  "vitest": "^3.2.4",
42
- "@craftile/preview-client": "0.5.0",
43
- "@craftile/types": "0.5.0"
42
+ "@craftile/preview-client": "0.6.0",
43
+ "@craftile/types": "0.6.0"
44
44
  },
45
45
  "repository": {
46
46
  "type": "git",