@djvlc/sandbox 1.0.2 → 1.0.3

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
- var t=(t=>(t.READY="ready",t.INIT="init",t.DESTROY="destroy",t.LOAD_PAGE="load_page",t.UPDATE_SCHEMA="update_schema",t.REFRESH="refresh",t.SELECT_COMPONENT="select_component",t.HOVER_COMPONENT="hover_component",t.UPDATE_COMPONENT="update_component",t.DELETE_COMPONENT="delete_component",t.ADD_COMPONENT="add_component",t.MOVE_COMPONENT="move_component",t.SYNC_STATE="sync_state",t.SYNC_VARIABLES="sync_variables",t.COMPONENT_CLICK="component_click",t.COMPONENT_HOVER="component_hover",t.COMPONENT_CONTEXT_MENU="component_context_menu",t.COMPONENT_DRAG_START="component_drag_start",t.COMPONENT_DRAG_END="component_drag_end",t.COMPONENT_DROP="component_drop",t.ERROR="error",t.LOG="log",t))(t||{});function e(){return`msg_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function n(t,n){return{type:t,id:e(),data:n,timestamp:Date.now()}}function o(t,n,o,i,s){return{type:n,id:e(),requestId:t,success:o,data:i,error:s,timestamp:Date.now()}}function i(t){if(!t||"object"!=typeof t)return!1;const e=t;return"string"==typeof e.type&&"string"==typeof e.id&&"number"==typeof e.timestamp}exports.MessageType=t,exports.SandboxClient=class{constructor(t={}){this.runtime=null,this.selectedComponents=new Set,this.hoveredComponent=null,this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:null},this.handleMessage=async t=>{const e=t.data;if(i(e)){this.log("debug","Received message:",e.type,e);try{let t;switch(e.type){case"init":t=await this.handleInit(e.data);break;case"load_page":t=await this.handleLoadPage(e.data);break;case"update_schema":t=await this.handleUpdateSchema(e.data);break;case"refresh":t=await this.handleRefresh();break;case"select_component":t=this.handleSelectComponent(e.data);break;case"hover_component":t=this.handleHoverComponent(e.data);break;case"update_component":t=this.handleUpdateComponent(e.data);break;case"add_component":t=this.handleAddComponent(e.data);break;case"move_component":t=this.handleMoveComponent(e.data);break;case"delete_component":t=this.handleDeleteComponent(e.data);break;case"sync_variables":t=this.handleSyncVariables(e.data);break;default:return void this.log("warn","Unknown message type:",e.type)}this.sendResponse(e.id,e.type,!0,t)}catch(t){this.sendResponse(e.id,e.type,!1,void 0,t instanceof Error?t.message:"Unknown error")}}},this.options={targetOrigin:"*",debug:!1,enableDrag:!0,dragOptions:{dragOpacity:.5,showDropIndicator:!0,dropIndicatorColor:"#1890ff"},...t}}setRuntime(t){this.runtime=t}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxClient connected"),this.sendReady()}disconnect(){window.removeEventListener("message",this.handleMessage),this.log("debug","SandboxClient disconnected")}initComponentEventListeners(){document.addEventListener("click",t=>{const e=t.target,n=this.findComponentElement(e);if(n){t.preventDefault(),t.stopPropagation();const e=n.getAttribute("data-component-id"),o=n.getAttribute("data-component-type");e&&this.sendComponentEvent("component_click",{componentId:e,componentType:o||"unknown",bounds:n.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}},!0),document.addEventListener("mouseover",t=>{const e=t.target,n=this.findComponentElement(e);if(n){const t=n.getAttribute("data-component-id"),e=n.getAttribute("data-component-type");t&&t!==this.hoveredComponent&&(this.hoveredComponent=t,this.sendComponentEvent("component_hover",{componentId:t,componentType:e||"unknown",bounds:n.getBoundingClientRect()}))}}),document.addEventListener("mouseout",t=>{const e=t.relatedTarget;e&&this.findComponentElement(e)||this.hoveredComponent&&(this.hoveredComponent=null,this.sendComponentEvent("component_hover",{componentId:"",componentType:""}))}),document.addEventListener("contextmenu",t=>{const e=t.target,n=this.findComponentElement(e);if(n){t.preventDefault();const e=n.getAttribute("data-component-id"),o=n.getAttribute("data-component-type");e&&this.sendComponentEvent("component_context_menu",{componentId:e,componentType:o||"unknown",bounds:n.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}})}injectEditorStyles(){const t=this.options.dragOptions?.dropIndicatorColor||"#1890ff",e=document.createElement("style");e.id="djvlc-editor-styles",e.textContent=`\n /* 选中状态 */\n [data-component-id].djvlc-selected {\n outline: 2px solid ${t} !important;\n outline-offset: 2px;\n }\n\n /* 悬停状态 */\n [data-component-id].djvlc-hovered {\n outline: 1px dashed ${t} !important;\n outline-offset: 1px;\n }\n\n /* 禁用交互 */\n [data-component-id] * {\n pointer-events: none;\n }\n\n [data-component-id] {\n cursor: pointer;\n pointer-events: auto;\n }\n\n /* 可拖拽状态 */\n [data-component-id][draggable="true"] {\n cursor: grab;\n }\n\n [data-component-id][draggable="true"]:active {\n cursor: grabbing;\n }\n\n /* 拖拽中状态 */\n [data-component-id].djvlc-dragging {\n opacity: ${this.options.dragOptions?.dragOpacity||.5};\n }\n\n /* 放置目标状态 */\n [data-component-id].djvlc-drop-target {\n outline: 2px dashed ${t} !important;\n outline-offset: 4px;\n }\n\n /* 拖拽占位符 */\n .djvlc-drop-indicator {\n position: absolute;\n background: ${t};\n opacity: 0.8;\n pointer-events: none;\n z-index: 10000;\n transition: all 0.1s ease;\n }\n\n .djvlc-drop-indicator.horizontal {\n height: 4px;\n width: 100%;\n border-radius: 2px;\n }\n\n .djvlc-drop-indicator.vertical {\n width: 4px;\n height: 100%;\n border-radius: 2px;\n }\n\n /* 放置提示箭头 */\n .djvlc-drop-indicator::before {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n left: -4px;\n top: -2px;\n }\n\n .djvlc-drop-indicator::after {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n right: -4px;\n top: -2px;\n }\n `,document.head.appendChild(e)}initDragEventListeners(){this.options.enableDrag&&(this.makeComponentsDraggable(),document.addEventListener("dragstart",t=>{const e=t.target,n=this.findComponentElement(e);if(!n)return;const o=n.getAttribute("data-component-id");o&&(this.dragState.isDragging=!0,this.dragState.draggedComponentId=o,this.dragState.draggedElement=n,n.classList.add("djvlc-dragging"),t.dataTransfer?.setData("text/plain",o),t.dataTransfer?.setData("application/json",JSON.stringify({componentId:o,componentType:n.getAttribute("data-component-type")})),t.dataTransfer&&(t.dataTransfer.effectAllowed="move"),this.log("debug","Drag start:",o))}),document.addEventListener("dragover",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const e=t.target,n=this.findComponentElement(e);if(!n||n===this.dragState.draggedElement)return void this.hideDropIndicator();if(!n.getAttribute("data-component-id"))return;const o=n.getBoundingClientRect(),i=t.clientY,s=o.height/3;let a;a=i<o.top+s?"before":i>o.bottom-s?"after":"inside",this.dragState.dropTarget===n&&this.dragState.dropPosition===a||(this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.dragState.dropTarget=n,this.dragState.dropPosition=a,"inside"===a?(n.classList.add("djvlc-drop-target"),this.hideDropIndicator()):(n.classList.remove("djvlc-drop-target"),this.showDropIndicator(n,a)))}),document.addEventListener("dragleave",t=>{const e=t.target,n=this.findComponentElement(e);if(n&&n===this.dragState.dropTarget){const e=t.relatedTarget;e&&n.contains(e)||n.classList.remove("djvlc-drop-target")}}),document.addEventListener("drop",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const{draggedComponentId:e,dropTarget:n,dropPosition:o}=this.dragState;if(e&&n&&o){const t=n.getAttribute("data-component-id");t&&t!==e&&(this.log("info","Drop:",e,o,t),this.send("component_drop",{componentId:e,targetComponentId:t,position:o}))}this.endDrag()}),document.addEventListener("dragend",()=>{this.endDrag()}))}makeComponentsDraggable(){document.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}),new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof HTMLElement&&(t.hasAttribute("data-component-id")&&(t.draggable=!0),t.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}))})})}).observe(document.body,{childList:!0,subtree:!0})}showDropIndicator(t,e){if(!this.options.dragOptions?.showDropIndicator)return;let n=this.dragState.dropIndicator;n||(n=document.createElement("div"),n.className="djvlc-drop-indicator horizontal",document.body.appendChild(n),this.dragState.dropIndicator=n);const o=t.getBoundingClientRect(),i=window.scrollY||document.documentElement.scrollTop,s=window.scrollX||document.documentElement.scrollLeft;n.style.left=`${o.left+s}px`,n.style.width=`${o.width}px`,n.style.top="before"===e?o.top+i-2+"px":o.bottom+i-2+"px",n.style.display="block"}hideDropIndicator(){this.dragState.dropIndicator&&(this.dragState.dropIndicator.style.display="none")}endDrag(){this.dragState.draggedElement?.classList.remove("djvlc-dragging"),this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.hideDropIndicator(),this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:this.dragState.dropIndicator}}async handleInit(t){if(this.log("info","Init with config:",t),this.runtime)try{await this.runtime.init()}catch(t){throw this.log("error","Failed to init runtime:",t),t}}async handleLoadPage(t){if(this.log("info","Load page:",t.pageUid),!this.runtime)throw new Error("Runtime not set");try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to load page:",t),t}}async handleUpdateSchema(t){if(this.log("info","Update schema",t.fullUpdate?"(full)":"(partial)"),!this.runtime)throw new Error("Runtime not set");try{t.fullUpdate,await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to update schema:",t),t}}async handleRefresh(){if(this.log("info","Refresh page"),this.runtime)try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){this.log("error","Failed to refresh:",t),window.location.reload()}else window.location.reload()}handleSelectComponent(t){t.componentId?t.multi?this.selectedComponents.has(t.componentId)?this.selectedComponents.delete(t.componentId):this.selectedComponents.add(t.componentId):(this.selectedComponents.clear(),this.selectedComponents.add(t.componentId)):this.selectedComponents.clear(),this.updateSelectionStyles(),this.syncState()}handleHoverComponent(t){if(document.querySelectorAll(".djvlc-hovered").forEach(t=>{t.classList.remove("djvlc-hovered")}),t.componentId){const e=document.querySelector(`[data-component-id="${t.componentId}"]`);e&&!this.selectedComponents.has(t.componentId)&&e.classList.add("djvlc-hovered")}}handleUpdateComponent(t){this.log("info","Update component:",t.componentId),this.runtime?.updateComponent(t.componentId,t.props||{})}handleAddComponent(t){this.log("info","Add component:",t.component.componentType),this.log("warn","addComponent not supported, need to reload page")}handleMoveComponent(t){this.log("info","Move component:",t.componentId,"to",t.targetIndex),this.log("warn","moveComponent not supported, need to reload page")}handleDeleteComponent(t){this.log("info","Delete component:",t.componentId),this.log("warn","deleteComponent not supported, need to reload page"),this.selectedComponents.delete(t.componentId),this.hoveredComponent===t.componentId&&(this.hoveredComponent=null)}handleSyncVariables(t){Object.entries(t.variables).forEach(([t,e])=>{this.runtime?.setVariable(t,e)})}updateSelectionStyles(){document.querySelectorAll(".djvlc-selected").forEach(t=>{t.classList.remove("djvlc-selected")}),this.selectedComponents.forEach(t=>{const e=document.querySelector(`[data-component-id="${t}"]`);e&&e.classList.add("djvlc-selected")})}syncState(){const t=this.runtime?.getState(),e={phase:t?.phase||"idle",page:t?.page?{pageUid:t.page.pageUid,pageVersionId:t.page.pageVersionId}:void 0,selectedComponents:Array.from(this.selectedComponents),hoveredComponent:this.hoveredComponent};this.send("sync_state",e)}sendReady(){this.send("ready",{runtimeVersion:"0.1.0",capabilities:["select","hover","update","add","move","delete","drag"]})}sendComponentEvent(t,e){this.send(t,e)}send(t,e){const o=n(t,e);this.log("debug","Sending message:",t,o),window.parent.postMessage(o,this.options.targetOrigin||"*")}sendResponse(t,e,n,i,s){const a=o(t,e,n,i,s);this.log("debug","Sending response:",e,a),window.parent.postMessage(a,this.options.targetOrigin||"*")}findComponentElement(t){let e=t;for(;e;){if(e.hasAttribute("data-component-id"))return e;e=e.parentElement}return null}log(t,e,...n){this.options.debug}},exports.SandboxHost=class{constructor(t){this.pendingRequests=new Map,this.isReady=!1,this.handleMessage=t=>{if(t.source!==this.options.iframe.contentWindow)return;if("*"!==this.options.targetOrigin&&t.origin!==this.options.targetOrigin)return;const e=t.data;if(i(e)){if(this.log("debug","Received message:",e.type,e),"requestId"in e){const t=this.pendingRequests.get(e.requestId);if(t)return clearTimeout(t.timer),this.pendingRequests.delete(e.requestId),void(e.success?t.resolve(e.data):t.reject(new Error(e.error||"Unknown error")))}switch(e.type){case"ready":this.isReady=!0,this.readyResolve(),this.options.onReady?.();break;case"component_click":this.options.onComponentClick?.(e.data.componentId,e.data);break;case"component_hover":this.options.onComponentHover?.(e.data.componentId,e.data);break;case"component_context_menu":this.options.onComponentContextMenu?.(e.data.componentId,e.data);break;case"sync_state":this.options.onSyncState?.(e.data);break;case"error":this.options.onError?.(e.data);break;case"log":{const t=e.data;this.options.onLog?.(t.level,t.message,...t.args||[]);break}}}},this.options={timeout:3e4,debug:!1,...t},this.readyPromise=new Promise(t=>{this.readyResolve=t})}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxHost connected")}disconnect(){window.removeEventListener("message",this.handleMessage),this.pendingRequests.forEach(({timer:t})=>clearTimeout(t)),this.pendingRequests.clear(),this.isReady=!1,this.log("debug","SandboxHost disconnected")}async waitReady(){return this.readyPromise}get ready(){return this.isReady}async init(t){await this.send("init",t)}async loadPage(t){await this.send("load_page",t)}async updateSchema(t){await this.send("update_schema",t)}async refresh(){await this.send("refresh",{})}async selectComponent(t,e=!1){await this.send("select_component",{componentId:t,multi:e})}async hoverComponent(t){await this.send("hover_component",{componentId:t})}async updateComponent(t){await this.send("update_component",t)}async addComponent(t){await this.send("add_component",t)}async moveComponent(t){await this.send("move_component",t)}async deleteComponent(t){await this.send("delete_component",{componentId:t})}async syncVariables(t){await this.send("sync_variables",{variables:t})}send(t,e){return new Promise((o,i)=>{const s=n(t,e),a=setTimeout(()=>{this.pendingRequests.delete(s.id),i(new Error(`Request timeout: ${t}`))},this.options.timeout);this.pendingRequests.set(s.id,{resolve:o,reject:i,timer:a}),this.log("debug","Sending message:",t,s),this.options.iframe.contentWindow?.postMessage(s,this.options.targetOrigin)})}log(t,e,...n){this.options.debug}},exports.createMessage=n,exports.createResponse=o,exports.generateMessageId=e,exports.isDjvlcMessage=i;
1
+ "use strict";var t=(t=>(t.READY="ready",t.INIT="init",t.DESTROY="destroy",t.LOAD_PAGE="load_page",t.UPDATE_SCHEMA="update_schema",t.REFRESH="refresh",t.SELECT_COMPONENT="select_component",t.HOVER_COMPONENT="hover_component",t.UPDATE_COMPONENT="update_component",t.DELETE_COMPONENT="delete_component",t.ADD_COMPONENT="add_component",t.MOVE_COMPONENT="move_component",t.SYNC_STATE="sync_state",t.SYNC_VARIABLES="sync_variables",t.COMPONENT_CLICK="component_click",t.COMPONENT_HOVER="component_hover",t.COMPONENT_CONTEXT_MENU="component_context_menu",t.COMPONENT_DRAG_START="component_drag_start",t.COMPONENT_DRAG_END="component_drag_end",t.COMPONENT_DROP="component_drop",t.ERROR="error",t.LOG="log",t))(t||{});function e(){return`msg_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function n(t,n){return{type:t,id:e(),data:n,timestamp:Date.now()}}function o(t,n,o,i,s){return{type:n,id:e(),requestId:t,success:o,data:i,error:s,timestamp:Date.now()}}function i(t){if(!t||"object"!=typeof t)return!1;const e=t;return"string"==typeof e.type&&"string"==typeof e.id&&"number"==typeof e.timestamp}exports.MessageType=t,exports.SandboxClient=class{constructor(t={}){this.runtime=null,this.selectedComponents=new Set,this.hoveredComponent=null,this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:null},this.handleMessage=async t=>{const e=t.data;if(i(e)){this.log("debug","Received message:",e.type,e);try{let t;switch(e.type){case"init":t=await this.handleInit(e.data);break;case"load_page":t=await this.handleLoadPage(e.data);break;case"update_schema":t=await this.handleUpdateSchema(e.data);break;case"refresh":t=await this.handleRefresh();break;case"select_component":t=this.handleSelectComponent(e.data);break;case"hover_component":t=this.handleHoverComponent(e.data);break;case"update_component":t=this.handleUpdateComponent(e.data);break;case"add_component":t=this.handleAddComponent(e.data);break;case"move_component":t=this.handleMoveComponent(e.data);break;case"delete_component":t=this.handleDeleteComponent(e.data);break;case"sync_variables":t=this.handleSyncVariables(e.data);break;default:return void this.log("warn","Unknown message type:",e.type)}this.sendResponse(e.id,e.type,!0,t)}catch(t){this.sendResponse(e.id,e.type,!1,void 0,t instanceof Error?t.message:"Unknown error")}}},this.options={targetOrigin:"*",debug:!1,enableDrag:!0,dragOptions:{dragOpacity:.5,showDropIndicator:!0,dropIndicatorColor:"#1890ff"},...t}}setRuntime(t){this.runtime=t}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxClient connected"),this.sendReady()}disconnect(){window.removeEventListener("message",this.handleMessage),this.log("debug","SandboxClient disconnected")}initComponentEventListeners(){document.addEventListener("click",t=>{const e=t.target,n=this.findComponentElement(e);if(n){t.preventDefault(),t.stopPropagation();const e=n.getAttribute("data-component-id"),o=n.getAttribute("data-component-type");e&&this.sendComponentEvent("component_click",{componentId:e,componentType:o||"unknown",bounds:n.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}},!0),document.addEventListener("mouseover",t=>{const e=t.target,n=this.findComponentElement(e);if(n){const t=n.getAttribute("data-component-id"),e=n.getAttribute("data-component-type");t&&t!==this.hoveredComponent&&(this.hoveredComponent=t,this.sendComponentEvent("component_hover",{componentId:t,componentType:e||"unknown",bounds:n.getBoundingClientRect()}))}}),document.addEventListener("mouseout",t=>{const e=t.relatedTarget;e&&this.findComponentElement(e)||this.hoveredComponent&&(this.hoveredComponent=null,this.sendComponentEvent("component_hover",{componentId:"",componentType:""}))}),document.addEventListener("contextmenu",t=>{const e=t.target,n=this.findComponentElement(e);if(n){t.preventDefault();const e=n.getAttribute("data-component-id"),o=n.getAttribute("data-component-type");e&&this.sendComponentEvent("component_context_menu",{componentId:e,componentType:o||"unknown",bounds:n.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}})}injectEditorStyles(){const t=this.options.dragOptions?.dropIndicatorColor||"#1890ff",e=document.createElement("style");e.id="djvlc-editor-styles",e.textContent=`\n /* 选中状态 */\n [data-component-id].djvlc-selected {\n outline: 2px solid ${t} !important;\n outline-offset: 2px;\n }\n\n /* 悬停状态 */\n [data-component-id].djvlc-hovered {\n outline: 1px dashed ${t} !important;\n outline-offset: 1px;\n }\n\n /* 禁用交互 */\n [data-component-id] * {\n pointer-events: none;\n }\n\n [data-component-id] {\n cursor: pointer;\n pointer-events: auto;\n }\n\n /* 可拖拽状态 */\n [data-component-id][draggable="true"] {\n cursor: grab;\n }\n\n [data-component-id][draggable="true"]:active {\n cursor: grabbing;\n }\n\n /* 拖拽中状态 */\n [data-component-id].djvlc-dragging {\n opacity: ${this.options.dragOptions?.dragOpacity||.5};\n }\n\n /* 放置目标状态 */\n [data-component-id].djvlc-drop-target {\n outline: 2px dashed ${t} !important;\n outline-offset: 4px;\n }\n\n /* 拖拽占位符 */\n .djvlc-drop-indicator {\n position: absolute;\n background: ${t};\n opacity: 0.8;\n pointer-events: none;\n z-index: 10000;\n transition: all 0.1s ease;\n }\n\n .djvlc-drop-indicator.horizontal {\n height: 4px;\n width: 100%;\n border-radius: 2px;\n }\n\n .djvlc-drop-indicator.vertical {\n width: 4px;\n height: 100%;\n border-radius: 2px;\n }\n\n /* 放置提示箭头 */\n .djvlc-drop-indicator::before {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n left: -4px;\n top: -2px;\n }\n\n .djvlc-drop-indicator::after {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n right: -4px;\n top: -2px;\n }\n `,document.head.appendChild(e)}initDragEventListeners(){this.options.enableDrag&&(this.makeComponentsDraggable(),document.addEventListener("dragstart",t=>{const e=t.target,n=this.findComponentElement(e);if(!n)return;const o=n.getAttribute("data-component-id");o&&(this.dragState.isDragging=!0,this.dragState.draggedComponentId=o,this.dragState.draggedElement=n,n.classList.add("djvlc-dragging"),t.dataTransfer?.setData("text/plain",o),t.dataTransfer?.setData("application/json",JSON.stringify({componentId:o,componentType:n.getAttribute("data-component-type")})),t.dataTransfer&&(t.dataTransfer.effectAllowed="move"),this.log("debug","Drag start:",o))}),document.addEventListener("dragover",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const e=t.target,n=this.findComponentElement(e);if(!n||n===this.dragState.draggedElement)return void this.hideDropIndicator();if(!n.getAttribute("data-component-id"))return;const o=n.getBoundingClientRect(),i=t.clientY,s=o.height/3;let a;a=i<o.top+s?"before":i>o.bottom-s?"after":"inside",this.dragState.dropTarget===n&&this.dragState.dropPosition===a||(this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.dragState.dropTarget=n,this.dragState.dropPosition=a,"inside"===a?(n.classList.add("djvlc-drop-target"),this.hideDropIndicator()):(n.classList.remove("djvlc-drop-target"),this.showDropIndicator(n,a)))}),document.addEventListener("dragleave",t=>{const e=t.target,n=this.findComponentElement(e);if(n&&n===this.dragState.dropTarget){const e=t.relatedTarget;e&&n.contains(e)||n.classList.remove("djvlc-drop-target")}}),document.addEventListener("drop",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const{draggedComponentId:e,dropTarget:n,dropPosition:o}=this.dragState;if(e&&n&&o){const t=n.getAttribute("data-component-id");t&&t!==e&&(this.log("info","Drop:",e,o,t),this.send("component_drop",{componentId:e,targetComponentId:t,position:o}))}this.endDrag()}),document.addEventListener("dragend",()=>{this.endDrag()}))}makeComponentsDraggable(){document.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}),new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof HTMLElement&&(t.hasAttribute("data-component-id")&&(t.draggable=!0),t.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}))})})}).observe(document.body,{childList:!0,subtree:!0})}showDropIndicator(t,e){if(!this.options.dragOptions?.showDropIndicator)return;let n=this.dragState.dropIndicator;n||(n=document.createElement("div"),n.className="djvlc-drop-indicator horizontal",document.body.appendChild(n),this.dragState.dropIndicator=n);const o=t.getBoundingClientRect(),i=window.scrollY||document.documentElement.scrollTop,s=window.scrollX||document.documentElement.scrollLeft;n.style.left=`${o.left+s}px`,n.style.width=`${o.width}px`,n.style.top="before"===e?o.top+i-2+"px":o.bottom+i-2+"px",n.style.display="block"}hideDropIndicator(){this.dragState.dropIndicator&&(this.dragState.dropIndicator.style.display="none")}endDrag(){this.dragState.draggedElement?.classList.remove("djvlc-dragging"),this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.hideDropIndicator(),this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:this.dragState.dropIndicator}}async handleInit(t){if(this.log("info","Init with config:",t),this.runtime)try{await this.runtime.init()}catch(t){throw this.log("error","Failed to init runtime:",t),t}}async handleLoadPage(t){if(this.log("info","Load page:",t.pageId),!this.runtime)throw new Error("Runtime not set");try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to load page:",t),t}}async handleUpdateSchema(t){if(this.log("info","Update schema",t.fullUpdate?"(full)":"(partial)"),!this.runtime)throw new Error("Runtime not set");try{t.fullUpdate,await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to update schema:",t),t}}async handleRefresh(){if(this.log("info","Refresh page"),this.runtime)try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){this.log("error","Failed to refresh:",t),window.location.reload()}else window.location.reload()}handleSelectComponent(t){t.componentId?t.multi?this.selectedComponents.has(t.componentId)?this.selectedComponents.delete(t.componentId):this.selectedComponents.add(t.componentId):(this.selectedComponents.clear(),this.selectedComponents.add(t.componentId)):this.selectedComponents.clear(),this.updateSelectionStyles(),this.syncState()}handleHoverComponent(t){if(document.querySelectorAll(".djvlc-hovered").forEach(t=>{t.classList.remove("djvlc-hovered")}),t.componentId){const e=document.querySelector(`[data-component-id="${t.componentId}"]`);e&&!this.selectedComponents.has(t.componentId)&&e.classList.add("djvlc-hovered")}}handleUpdateComponent(t){this.log("info","Update component:",t.componentId),this.runtime?.updateComponent(t.componentId,t.props||{})}handleAddComponent(t){this.log("info","Add component:",t.component.componentType),this.log("warn","addComponent not supported, need to reload page")}handleMoveComponent(t){this.log("info","Move component:",t.componentId,"to",t.targetIndex),this.log("warn","moveComponent not supported, need to reload page")}handleDeleteComponent(t){this.log("info","Delete component:",t.componentId),this.log("warn","deleteComponent not supported, need to reload page"),this.selectedComponents.delete(t.componentId),this.hoveredComponent===t.componentId&&(this.hoveredComponent=null)}handleSyncVariables(t){Object.entries(t.variables).forEach(([t,e])=>{this.runtime?.setVariable(t,e)})}updateSelectionStyles(){document.querySelectorAll(".djvlc-selected").forEach(t=>{t.classList.remove("djvlc-selected")}),this.selectedComponents.forEach(t=>{const e=document.querySelector(`[data-component-id="${t}"]`);e&&e.classList.add("djvlc-selected")})}syncState(){const t=this.runtime?.getState(),e={phase:t?.phase||"idle",page:t?.page?{pageId:t.page.pageId,pageVersionId:t.page.pageVersionId}:void 0,selectedComponents:Array.from(this.selectedComponents),hoveredComponent:this.hoveredComponent};this.send("sync_state",e)}sendReady(){this.send("ready",{runtimeVersion:"0.1.0",capabilities:["select","hover","update","add","move","delete","drag"]})}sendComponentEvent(t,e){this.send(t,e)}send(t,e){const o=n(t,e);this.log("debug","Sending message:",t,o),window.parent.postMessage(o,this.options.targetOrigin||"*")}sendResponse(t,e,n,i,s){const a=o(t,e,n,i,s);this.log("debug","Sending response:",e,a),window.parent.postMessage(a,this.options.targetOrigin||"*")}findComponentElement(t){let e=t;for(;e;){if(e.hasAttribute("data-component-id"))return e;e=e.parentElement}return null}log(t,e,...n){this.options.debug}},exports.SandboxHost=class{constructor(t){this.pendingRequests=new Map,this.isReady=!1,this.handleMessage=t=>{if(t.source!==this.options.iframe.contentWindow)return;if("*"!==this.options.targetOrigin&&t.origin!==this.options.targetOrigin)return;const e=t.data;if(i(e)){if(this.log("debug","Received message:",e.type,e),"requestId"in e){const t=this.pendingRequests.get(e.requestId);if(t)return clearTimeout(t.timer),this.pendingRequests.delete(e.requestId),void(e.success?t.resolve(e.data):t.reject(new Error(e.error||"Unknown error")))}switch(e.type){case"ready":this.isReady=!0,this.readyResolve(),this.options.onReady?.();break;case"component_click":this.options.onComponentClick?.(e.data.componentId,e.data);break;case"component_hover":this.options.onComponentHover?.(e.data.componentId,e.data);break;case"component_context_menu":this.options.onComponentContextMenu?.(e.data.componentId,e.data);break;case"sync_state":this.options.onSyncState?.(e.data);break;case"error":this.options.onError?.(e.data);break;case"log":{const t=e.data;this.options.onLog?.(t.level,t.message,...t.args||[]);break}}}},this.options={timeout:3e4,debug:!1,...t},this.readyPromise=new Promise(t=>{this.readyResolve=t})}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxHost connected")}disconnect(){window.removeEventListener("message",this.handleMessage),this.pendingRequests.forEach(({timer:t})=>clearTimeout(t)),this.pendingRequests.clear(),this.isReady=!1,this.log("debug","SandboxHost disconnected")}async waitReady(){return this.readyPromise}get ready(){return this.isReady}async init(t){await this.send("init",t)}async loadPage(t){await this.send("load_page",t)}async updateSchema(t){await this.send("update_schema",t)}async refresh(){await this.send("refresh",{})}async selectComponent(t,e=!1){await this.send("select_component",{componentId:t,multi:e})}async hoverComponent(t){await this.send("hover_component",{componentId:t})}async updateComponent(t){await this.send("update_component",t)}async addComponent(t){await this.send("add_component",t)}async moveComponent(t){await this.send("move_component",t)}async deleteComponent(t){await this.send("delete_component",{componentId:t})}async syncVariables(t){await this.send("sync_variables",{variables:t})}send(t,e){return new Promise((o,i)=>{const s=n(t,e),a=setTimeout(()=>{this.pendingRequests.delete(s.id),i(new Error(`Request timeout: ${t}`))},this.options.timeout);this.pendingRequests.set(s.id,{resolve:o,reject:i,timer:a}),this.log("debug","Sending message:",t,s),this.options.iframe.contentWindow?.postMessage(s,this.options.targetOrigin)})}log(t,e,...n){this.options.debug}},exports.createMessage=n,exports.createResponse=o,exports.generateMessageId=e,exports.isDjvlcMessage=i;
package/dist/index.d.cts CHANGED
@@ -70,8 +70,8 @@ interface ReadyMessageData {
70
70
  * Init 消息数据
71
71
  */
72
72
  interface InitMessageData {
73
- /** 页面 UID */
74
- pageUid?: string;
73
+ /** 页面 ID */
74
+ pageId?: string;
75
75
  /** API 基础 URL */
76
76
  apiBaseUrl: string;
77
77
  /** CDN 基础 URL */
@@ -85,8 +85,8 @@ interface InitMessageData {
85
85
  * Load Page 消息数据
86
86
  */
87
87
  interface LoadPageMessageData {
88
- /** 页面 UID */
89
- pageUid: string;
88
+ /** 页面 ID */
89
+ pageId: string;
90
90
  /** 预览 Token */
91
91
  previewToken?: string;
92
92
  }
@@ -181,7 +181,7 @@ interface SyncStateMessageData {
181
181
  phase: string;
182
182
  /** 页面信息 */
183
183
  page?: {
184
- pageUid: string;
184
+ pageId: string;
185
185
  pageVersionId: string;
186
186
  };
187
187
  /** 已选中组件 */
package/dist/index.d.ts CHANGED
@@ -70,8 +70,8 @@ interface ReadyMessageData {
70
70
  * Init 消息数据
71
71
  */
72
72
  interface InitMessageData {
73
- /** 页面 UID */
74
- pageUid?: string;
73
+ /** 页面 ID */
74
+ pageId?: string;
75
75
  /** API 基础 URL */
76
76
  apiBaseUrl: string;
77
77
  /** CDN 基础 URL */
@@ -85,8 +85,8 @@ interface InitMessageData {
85
85
  * Load Page 消息数据
86
86
  */
87
87
  interface LoadPageMessageData {
88
- /** 页面 UID */
89
- pageUid: string;
88
+ /** 页面 ID */
89
+ pageId: string;
90
90
  /** 预览 Token */
91
91
  previewToken?: string;
92
92
  }
@@ -181,7 +181,7 @@ interface SyncStateMessageData {
181
181
  phase: string;
182
182
  /** 页面信息 */
183
183
  page?: {
184
- pageUid: string;
184
+ pageId: string;
185
185
  pageVersionId: string;
186
186
  };
187
187
  /** 已选中组件 */
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var t=(t=>(t.READY="ready",t.INIT="init",t.DESTROY="destroy",t.LOAD_PAGE="load_page",t.UPDATE_SCHEMA="update_schema",t.REFRESH="refresh",t.SELECT_COMPONENT="select_component",t.HOVER_COMPONENT="hover_component",t.UPDATE_COMPONENT="update_component",t.DELETE_COMPONENT="delete_component",t.ADD_COMPONENT="add_component",t.MOVE_COMPONENT="move_component",t.SYNC_STATE="sync_state",t.SYNC_VARIABLES="sync_variables",t.COMPONENT_CLICK="component_click",t.COMPONENT_HOVER="component_hover",t.COMPONENT_CONTEXT_MENU="component_context_menu",t.COMPONENT_DRAG_START="component_drag_start",t.COMPONENT_DRAG_END="component_drag_end",t.COMPONENT_DROP="component_drop",t.ERROR="error",t.LOG="log",t))(t||{});function n(){return`msg_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function e(t,e){return{type:t,id:n(),data:e,timestamp:Date.now()}}function o(t,e,o,i,s){return{type:e,id:n(),requestId:t,success:o,data:i,error:s,timestamp:Date.now()}}function i(t){if(!t||"object"!=typeof t)return!1;const n=t;return"string"==typeof n.type&&"string"==typeof n.id&&"number"==typeof n.timestamp}var s=class{constructor(t){this.pendingRequests=new Map,this.isReady=!1,this.handleMessage=t=>{if(t.source!==this.options.iframe.contentWindow)return;if("*"!==this.options.targetOrigin&&t.origin!==this.options.targetOrigin)return;const n=t.data;if(i(n)){if(this.log("debug","Received message:",n.type,n),"requestId"in n){const t=this.pendingRequests.get(n.requestId);if(t)return clearTimeout(t.timer),this.pendingRequests.delete(n.requestId),void(n.success?t.resolve(n.data):t.reject(new Error(n.error||"Unknown error")))}switch(n.type){case"ready":this.isReady=!0,this.readyResolve(),this.options.onReady?.();break;case"component_click":this.options.onComponentClick?.(n.data.componentId,n.data);break;case"component_hover":this.options.onComponentHover?.(n.data.componentId,n.data);break;case"component_context_menu":this.options.onComponentContextMenu?.(n.data.componentId,n.data);break;case"sync_state":this.options.onSyncState?.(n.data);break;case"error":this.options.onError?.(n.data);break;case"log":{const t=n.data;this.options.onLog?.(t.level,t.message,...t.args||[]);break}}}},this.options={timeout:3e4,debug:!1,...t},this.readyPromise=new Promise(t=>{this.readyResolve=t})}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxHost connected")}disconnect(){window.removeEventListener("message",this.handleMessage),this.pendingRequests.forEach(({timer:t})=>clearTimeout(t)),this.pendingRequests.clear(),this.isReady=!1,this.log("debug","SandboxHost disconnected")}async waitReady(){return this.readyPromise}get ready(){return this.isReady}async init(t){await this.send("init",t)}async loadPage(t){await this.send("load_page",t)}async updateSchema(t){await this.send("update_schema",t)}async refresh(){await this.send("refresh",{})}async selectComponent(t,n=!1){await this.send("select_component",{componentId:t,multi:n})}async hoverComponent(t){await this.send("hover_component",{componentId:t})}async updateComponent(t){await this.send("update_component",t)}async addComponent(t){await this.send("add_component",t)}async moveComponent(t){await this.send("move_component",t)}async deleteComponent(t){await this.send("delete_component",{componentId:t})}async syncVariables(t){await this.send("sync_variables",{variables:t})}send(t,n){return new Promise((o,i)=>{const s=e(t,n),a=setTimeout(()=>{this.pendingRequests.delete(s.id),i(new Error(`Request timeout: ${t}`))},this.options.timeout);this.pendingRequests.set(s.id,{resolve:o,reject:i,timer:a}),this.log("debug","Sending message:",t,s),this.options.iframe.contentWindow?.postMessage(s,this.options.targetOrigin)})}log(t,n,...e){this.options.debug}},a=class{constructor(t={}){this.runtime=null,this.selectedComponents=new Set,this.hoveredComponent=null,this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:null},this.handleMessage=async t=>{const n=t.data;if(i(n)){this.log("debug","Received message:",n.type,n);try{let t;switch(n.type){case"init":t=await this.handleInit(n.data);break;case"load_page":t=await this.handleLoadPage(n.data);break;case"update_schema":t=await this.handleUpdateSchema(n.data);break;case"refresh":t=await this.handleRefresh();break;case"select_component":t=this.handleSelectComponent(n.data);break;case"hover_component":t=this.handleHoverComponent(n.data);break;case"update_component":t=this.handleUpdateComponent(n.data);break;case"add_component":t=this.handleAddComponent(n.data);break;case"move_component":t=this.handleMoveComponent(n.data);break;case"delete_component":t=this.handleDeleteComponent(n.data);break;case"sync_variables":t=this.handleSyncVariables(n.data);break;default:return void this.log("warn","Unknown message type:",n.type)}this.sendResponse(n.id,n.type,!0,t)}catch(t){this.sendResponse(n.id,n.type,!1,void 0,t instanceof Error?t.message:"Unknown error")}}},this.options={targetOrigin:"*",debug:!1,enableDrag:!0,dragOptions:{dragOpacity:.5,showDropIndicator:!0,dropIndicatorColor:"#1890ff"},...t}}setRuntime(t){this.runtime=t}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxClient connected"),this.sendReady()}disconnect(){window.removeEventListener("message",this.handleMessage),this.log("debug","SandboxClient disconnected")}initComponentEventListeners(){document.addEventListener("click",t=>{const n=t.target,e=this.findComponentElement(n);if(e){t.preventDefault(),t.stopPropagation();const n=e.getAttribute("data-component-id"),o=e.getAttribute("data-component-type");n&&this.sendComponentEvent("component_click",{componentId:n,componentType:o||"unknown",bounds:e.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}},!0),document.addEventListener("mouseover",t=>{const n=t.target,e=this.findComponentElement(n);if(e){const t=e.getAttribute("data-component-id"),n=e.getAttribute("data-component-type");t&&t!==this.hoveredComponent&&(this.hoveredComponent=t,this.sendComponentEvent("component_hover",{componentId:t,componentType:n||"unknown",bounds:e.getBoundingClientRect()}))}}),document.addEventListener("mouseout",t=>{const n=t.relatedTarget;n&&this.findComponentElement(n)||this.hoveredComponent&&(this.hoveredComponent=null,this.sendComponentEvent("component_hover",{componentId:"",componentType:""}))}),document.addEventListener("contextmenu",t=>{const n=t.target,e=this.findComponentElement(n);if(e){t.preventDefault();const n=e.getAttribute("data-component-id"),o=e.getAttribute("data-component-type");n&&this.sendComponentEvent("component_context_menu",{componentId:n,componentType:o||"unknown",bounds:e.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}})}injectEditorStyles(){const t=this.options.dragOptions?.dropIndicatorColor||"#1890ff",n=document.createElement("style");n.id="djvlc-editor-styles",n.textContent=`\n /* 选中状态 */\n [data-component-id].djvlc-selected {\n outline: 2px solid ${t} !important;\n outline-offset: 2px;\n }\n\n /* 悬停状态 */\n [data-component-id].djvlc-hovered {\n outline: 1px dashed ${t} !important;\n outline-offset: 1px;\n }\n\n /* 禁用交互 */\n [data-component-id] * {\n pointer-events: none;\n }\n\n [data-component-id] {\n cursor: pointer;\n pointer-events: auto;\n }\n\n /* 可拖拽状态 */\n [data-component-id][draggable="true"] {\n cursor: grab;\n }\n\n [data-component-id][draggable="true"]:active {\n cursor: grabbing;\n }\n\n /* 拖拽中状态 */\n [data-component-id].djvlc-dragging {\n opacity: ${this.options.dragOptions?.dragOpacity||.5};\n }\n\n /* 放置目标状态 */\n [data-component-id].djvlc-drop-target {\n outline: 2px dashed ${t} !important;\n outline-offset: 4px;\n }\n\n /* 拖拽占位符 */\n .djvlc-drop-indicator {\n position: absolute;\n background: ${t};\n opacity: 0.8;\n pointer-events: none;\n z-index: 10000;\n transition: all 0.1s ease;\n }\n\n .djvlc-drop-indicator.horizontal {\n height: 4px;\n width: 100%;\n border-radius: 2px;\n }\n\n .djvlc-drop-indicator.vertical {\n width: 4px;\n height: 100%;\n border-radius: 2px;\n }\n\n /* 放置提示箭头 */\n .djvlc-drop-indicator::before {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n left: -4px;\n top: -2px;\n }\n\n .djvlc-drop-indicator::after {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n right: -4px;\n top: -2px;\n }\n `,document.head.appendChild(n)}initDragEventListeners(){this.options.enableDrag&&(this.makeComponentsDraggable(),document.addEventListener("dragstart",t=>{const n=t.target,e=this.findComponentElement(n);if(!e)return;const o=e.getAttribute("data-component-id");o&&(this.dragState.isDragging=!0,this.dragState.draggedComponentId=o,this.dragState.draggedElement=e,e.classList.add("djvlc-dragging"),t.dataTransfer?.setData("text/plain",o),t.dataTransfer?.setData("application/json",JSON.stringify({componentId:o,componentType:e.getAttribute("data-component-type")})),t.dataTransfer&&(t.dataTransfer.effectAllowed="move"),this.log("debug","Drag start:",o))}),document.addEventListener("dragover",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const n=t.target,e=this.findComponentElement(n);if(!e||e===this.dragState.draggedElement)return void this.hideDropIndicator();if(!e.getAttribute("data-component-id"))return;const o=e.getBoundingClientRect(),i=t.clientY,s=o.height/3;let a;a=i<o.top+s?"before":i>o.bottom-s?"after":"inside",this.dragState.dropTarget===e&&this.dragState.dropPosition===a||(this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.dragState.dropTarget=e,this.dragState.dropPosition=a,"inside"===a?(e.classList.add("djvlc-drop-target"),this.hideDropIndicator()):(e.classList.remove("djvlc-drop-target"),this.showDropIndicator(e,a)))}),document.addEventListener("dragleave",t=>{const n=t.target,e=this.findComponentElement(n);if(e&&e===this.dragState.dropTarget){const n=t.relatedTarget;n&&e.contains(n)||e.classList.remove("djvlc-drop-target")}}),document.addEventListener("drop",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const{draggedComponentId:n,dropTarget:e,dropPosition:o}=this.dragState;if(n&&e&&o){const t=e.getAttribute("data-component-id");t&&t!==n&&(this.log("info","Drop:",n,o,t),this.send("component_drop",{componentId:n,targetComponentId:t,position:o}))}this.endDrag()}),document.addEventListener("dragend",()=>{this.endDrag()}))}makeComponentsDraggable(){document.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}),new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof HTMLElement&&(t.hasAttribute("data-component-id")&&(t.draggable=!0),t.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}))})})}).observe(document.body,{childList:!0,subtree:!0})}showDropIndicator(t,n){if(!this.options.dragOptions?.showDropIndicator)return;let e=this.dragState.dropIndicator;e||(e=document.createElement("div"),e.className="djvlc-drop-indicator horizontal",document.body.appendChild(e),this.dragState.dropIndicator=e);const o=t.getBoundingClientRect(),i=window.scrollY||document.documentElement.scrollTop,s=window.scrollX||document.documentElement.scrollLeft;e.style.left=`${o.left+s}px`,e.style.width=`${o.width}px`,e.style.top="before"===n?o.top+i-2+"px":o.bottom+i-2+"px",e.style.display="block"}hideDropIndicator(){this.dragState.dropIndicator&&(this.dragState.dropIndicator.style.display="none")}endDrag(){this.dragState.draggedElement?.classList.remove("djvlc-dragging"),this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.hideDropIndicator(),this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:this.dragState.dropIndicator}}async handleInit(t){if(this.log("info","Init with config:",t),this.runtime)try{await this.runtime.init()}catch(t){throw this.log("error","Failed to init runtime:",t),t}}async handleLoadPage(t){if(this.log("info","Load page:",t.pageUid),!this.runtime)throw new Error("Runtime not set");try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to load page:",t),t}}async handleUpdateSchema(t){if(this.log("info","Update schema",t.fullUpdate?"(full)":"(partial)"),!this.runtime)throw new Error("Runtime not set");try{t.fullUpdate,await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to update schema:",t),t}}async handleRefresh(){if(this.log("info","Refresh page"),this.runtime)try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){this.log("error","Failed to refresh:",t),window.location.reload()}else window.location.reload()}handleSelectComponent(t){t.componentId?t.multi?this.selectedComponents.has(t.componentId)?this.selectedComponents.delete(t.componentId):this.selectedComponents.add(t.componentId):(this.selectedComponents.clear(),this.selectedComponents.add(t.componentId)):this.selectedComponents.clear(),this.updateSelectionStyles(),this.syncState()}handleHoverComponent(t){if(document.querySelectorAll(".djvlc-hovered").forEach(t=>{t.classList.remove("djvlc-hovered")}),t.componentId){const n=document.querySelector(`[data-component-id="${t.componentId}"]`);n&&!this.selectedComponents.has(t.componentId)&&n.classList.add("djvlc-hovered")}}handleUpdateComponent(t){this.log("info","Update component:",t.componentId),this.runtime?.updateComponent(t.componentId,t.props||{})}handleAddComponent(t){this.log("info","Add component:",t.component.componentType),this.log("warn","addComponent not supported, need to reload page")}handleMoveComponent(t){this.log("info","Move component:",t.componentId,"to",t.targetIndex),this.log("warn","moveComponent not supported, need to reload page")}handleDeleteComponent(t){this.log("info","Delete component:",t.componentId),this.log("warn","deleteComponent not supported, need to reload page"),this.selectedComponents.delete(t.componentId),this.hoveredComponent===t.componentId&&(this.hoveredComponent=null)}handleSyncVariables(t){Object.entries(t.variables).forEach(([t,n])=>{this.runtime?.setVariable(t,n)})}updateSelectionStyles(){document.querySelectorAll(".djvlc-selected").forEach(t=>{t.classList.remove("djvlc-selected")}),this.selectedComponents.forEach(t=>{const n=document.querySelector(`[data-component-id="${t}"]`);n&&n.classList.add("djvlc-selected")})}syncState(){const t=this.runtime?.getState(),n={phase:t?.phase||"idle",page:t?.page?{pageUid:t.page.pageUid,pageVersionId:t.page.pageVersionId}:void 0,selectedComponents:Array.from(this.selectedComponents),hoveredComponent:this.hoveredComponent};this.send("sync_state",n)}sendReady(){this.send("ready",{runtimeVersion:"0.1.0",capabilities:["select","hover","update","add","move","delete","drag"]})}sendComponentEvent(t,n){this.send(t,n)}send(t,n){const o=e(t,n);this.log("debug","Sending message:",t,o),window.parent.postMessage(o,this.options.targetOrigin||"*")}sendResponse(t,n,e,i,s){const a=o(t,n,e,i,s);this.log("debug","Sending response:",n,a),window.parent.postMessage(a,this.options.targetOrigin||"*")}findComponentElement(t){let n=t;for(;n;){if(n.hasAttribute("data-component-id"))return n;n=n.parentElement}return null}log(t,n,...e){this.options.debug}};export{t as MessageType,a as SandboxClient,s as SandboxHost,e as createMessage,o as createResponse,n as generateMessageId,i as isDjvlcMessage};
1
+ var t=(t=>(t.READY="ready",t.INIT="init",t.DESTROY="destroy",t.LOAD_PAGE="load_page",t.UPDATE_SCHEMA="update_schema",t.REFRESH="refresh",t.SELECT_COMPONENT="select_component",t.HOVER_COMPONENT="hover_component",t.UPDATE_COMPONENT="update_component",t.DELETE_COMPONENT="delete_component",t.ADD_COMPONENT="add_component",t.MOVE_COMPONENT="move_component",t.SYNC_STATE="sync_state",t.SYNC_VARIABLES="sync_variables",t.COMPONENT_CLICK="component_click",t.COMPONENT_HOVER="component_hover",t.COMPONENT_CONTEXT_MENU="component_context_menu",t.COMPONENT_DRAG_START="component_drag_start",t.COMPONENT_DRAG_END="component_drag_end",t.COMPONENT_DROP="component_drop",t.ERROR="error",t.LOG="log",t))(t||{});function n(){return`msg_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function e(t,e){return{type:t,id:n(),data:e,timestamp:Date.now()}}function o(t,e,o,i,s){return{type:e,id:n(),requestId:t,success:o,data:i,error:s,timestamp:Date.now()}}function i(t){if(!t||"object"!=typeof t)return!1;const n=t;return"string"==typeof n.type&&"string"==typeof n.id&&"number"==typeof n.timestamp}var s=class{constructor(t){this.pendingRequests=new Map,this.isReady=!1,this.handleMessage=t=>{if(t.source!==this.options.iframe.contentWindow)return;if("*"!==this.options.targetOrigin&&t.origin!==this.options.targetOrigin)return;const n=t.data;if(i(n)){if(this.log("debug","Received message:",n.type,n),"requestId"in n){const t=this.pendingRequests.get(n.requestId);if(t)return clearTimeout(t.timer),this.pendingRequests.delete(n.requestId),void(n.success?t.resolve(n.data):t.reject(new Error(n.error||"Unknown error")))}switch(n.type){case"ready":this.isReady=!0,this.readyResolve(),this.options.onReady?.();break;case"component_click":this.options.onComponentClick?.(n.data.componentId,n.data);break;case"component_hover":this.options.onComponentHover?.(n.data.componentId,n.data);break;case"component_context_menu":this.options.onComponentContextMenu?.(n.data.componentId,n.data);break;case"sync_state":this.options.onSyncState?.(n.data);break;case"error":this.options.onError?.(n.data);break;case"log":{const t=n.data;this.options.onLog?.(t.level,t.message,...t.args||[]);break}}}},this.options={timeout:3e4,debug:!1,...t},this.readyPromise=new Promise(t=>{this.readyResolve=t})}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxHost connected")}disconnect(){window.removeEventListener("message",this.handleMessage),this.pendingRequests.forEach(({timer:t})=>clearTimeout(t)),this.pendingRequests.clear(),this.isReady=!1,this.log("debug","SandboxHost disconnected")}async waitReady(){return this.readyPromise}get ready(){return this.isReady}async init(t){await this.send("init",t)}async loadPage(t){await this.send("load_page",t)}async updateSchema(t){await this.send("update_schema",t)}async refresh(){await this.send("refresh",{})}async selectComponent(t,n=!1){await this.send("select_component",{componentId:t,multi:n})}async hoverComponent(t){await this.send("hover_component",{componentId:t})}async updateComponent(t){await this.send("update_component",t)}async addComponent(t){await this.send("add_component",t)}async moveComponent(t){await this.send("move_component",t)}async deleteComponent(t){await this.send("delete_component",{componentId:t})}async syncVariables(t){await this.send("sync_variables",{variables:t})}send(t,n){return new Promise((o,i)=>{const s=e(t,n),a=setTimeout(()=>{this.pendingRequests.delete(s.id),i(new Error(`Request timeout: ${t}`))},this.options.timeout);this.pendingRequests.set(s.id,{resolve:o,reject:i,timer:a}),this.log("debug","Sending message:",t,s),this.options.iframe.contentWindow?.postMessage(s,this.options.targetOrigin)})}log(t,n,...e){this.options.debug}},a=class{constructor(t={}){this.runtime=null,this.selectedComponents=new Set,this.hoveredComponent=null,this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:null},this.handleMessage=async t=>{const n=t.data;if(i(n)){this.log("debug","Received message:",n.type,n);try{let t;switch(n.type){case"init":t=await this.handleInit(n.data);break;case"load_page":t=await this.handleLoadPage(n.data);break;case"update_schema":t=await this.handleUpdateSchema(n.data);break;case"refresh":t=await this.handleRefresh();break;case"select_component":t=this.handleSelectComponent(n.data);break;case"hover_component":t=this.handleHoverComponent(n.data);break;case"update_component":t=this.handleUpdateComponent(n.data);break;case"add_component":t=this.handleAddComponent(n.data);break;case"move_component":t=this.handleMoveComponent(n.data);break;case"delete_component":t=this.handleDeleteComponent(n.data);break;case"sync_variables":t=this.handleSyncVariables(n.data);break;default:return void this.log("warn","Unknown message type:",n.type)}this.sendResponse(n.id,n.type,!0,t)}catch(t){this.sendResponse(n.id,n.type,!1,void 0,t instanceof Error?t.message:"Unknown error")}}},this.options={targetOrigin:"*",debug:!1,enableDrag:!0,dragOptions:{dragOpacity:.5,showDropIndicator:!0,dropIndicatorColor:"#1890ff"},...t}}setRuntime(t){this.runtime=t}connect(){window.addEventListener("message",this.handleMessage),this.log("debug","SandboxClient connected"),this.sendReady()}disconnect(){window.removeEventListener("message",this.handleMessage),this.log("debug","SandboxClient disconnected")}initComponentEventListeners(){document.addEventListener("click",t=>{const n=t.target,e=this.findComponentElement(n);if(e){t.preventDefault(),t.stopPropagation();const n=e.getAttribute("data-component-id"),o=e.getAttribute("data-component-type");n&&this.sendComponentEvent("component_click",{componentId:n,componentType:o||"unknown",bounds:e.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}},!0),document.addEventListener("mouseover",t=>{const n=t.target,e=this.findComponentElement(n);if(e){const t=e.getAttribute("data-component-id"),n=e.getAttribute("data-component-type");t&&t!==this.hoveredComponent&&(this.hoveredComponent=t,this.sendComponentEvent("component_hover",{componentId:t,componentType:n||"unknown",bounds:e.getBoundingClientRect()}))}}),document.addEventListener("mouseout",t=>{const n=t.relatedTarget;n&&this.findComponentElement(n)||this.hoveredComponent&&(this.hoveredComponent=null,this.sendComponentEvent("component_hover",{componentId:"",componentType:""}))}),document.addEventListener("contextmenu",t=>{const n=t.target,e=this.findComponentElement(n);if(e){t.preventDefault();const n=e.getAttribute("data-component-id"),o=e.getAttribute("data-component-type");n&&this.sendComponentEvent("component_context_menu",{componentId:n,componentType:o||"unknown",bounds:e.getBoundingClientRect(),position:{x:t.clientX,y:t.clientY}})}})}injectEditorStyles(){const t=this.options.dragOptions?.dropIndicatorColor||"#1890ff",n=document.createElement("style");n.id="djvlc-editor-styles",n.textContent=`\n /* 选中状态 */\n [data-component-id].djvlc-selected {\n outline: 2px solid ${t} !important;\n outline-offset: 2px;\n }\n\n /* 悬停状态 */\n [data-component-id].djvlc-hovered {\n outline: 1px dashed ${t} !important;\n outline-offset: 1px;\n }\n\n /* 禁用交互 */\n [data-component-id] * {\n pointer-events: none;\n }\n\n [data-component-id] {\n cursor: pointer;\n pointer-events: auto;\n }\n\n /* 可拖拽状态 */\n [data-component-id][draggable="true"] {\n cursor: grab;\n }\n\n [data-component-id][draggable="true"]:active {\n cursor: grabbing;\n }\n\n /* 拖拽中状态 */\n [data-component-id].djvlc-dragging {\n opacity: ${this.options.dragOptions?.dragOpacity||.5};\n }\n\n /* 放置目标状态 */\n [data-component-id].djvlc-drop-target {\n outline: 2px dashed ${t} !important;\n outline-offset: 4px;\n }\n\n /* 拖拽占位符 */\n .djvlc-drop-indicator {\n position: absolute;\n background: ${t};\n opacity: 0.8;\n pointer-events: none;\n z-index: 10000;\n transition: all 0.1s ease;\n }\n\n .djvlc-drop-indicator.horizontal {\n height: 4px;\n width: 100%;\n border-radius: 2px;\n }\n\n .djvlc-drop-indicator.vertical {\n width: 4px;\n height: 100%;\n border-radius: 2px;\n }\n\n /* 放置提示箭头 */\n .djvlc-drop-indicator::before {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n left: -4px;\n top: -2px;\n }\n\n .djvlc-drop-indicator::after {\n content: '';\n position: absolute;\n width: 8px;\n height: 8px;\n background: ${t};\n border-radius: 50%;\n right: -4px;\n top: -2px;\n }\n `,document.head.appendChild(n)}initDragEventListeners(){this.options.enableDrag&&(this.makeComponentsDraggable(),document.addEventListener("dragstart",t=>{const n=t.target,e=this.findComponentElement(n);if(!e)return;const o=e.getAttribute("data-component-id");o&&(this.dragState.isDragging=!0,this.dragState.draggedComponentId=o,this.dragState.draggedElement=e,e.classList.add("djvlc-dragging"),t.dataTransfer?.setData("text/plain",o),t.dataTransfer?.setData("application/json",JSON.stringify({componentId:o,componentType:e.getAttribute("data-component-type")})),t.dataTransfer&&(t.dataTransfer.effectAllowed="move"),this.log("debug","Drag start:",o))}),document.addEventListener("dragover",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const n=t.target,e=this.findComponentElement(n);if(!e||e===this.dragState.draggedElement)return void this.hideDropIndicator();if(!e.getAttribute("data-component-id"))return;const o=e.getBoundingClientRect(),i=t.clientY,s=o.height/3;let a;a=i<o.top+s?"before":i>o.bottom-s?"after":"inside",this.dragState.dropTarget===e&&this.dragState.dropPosition===a||(this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.dragState.dropTarget=e,this.dragState.dropPosition=a,"inside"===a?(e.classList.add("djvlc-drop-target"),this.hideDropIndicator()):(e.classList.remove("djvlc-drop-target"),this.showDropIndicator(e,a)))}),document.addEventListener("dragleave",t=>{const n=t.target,e=this.findComponentElement(n);if(e&&e===this.dragState.dropTarget){const n=t.relatedTarget;n&&e.contains(n)||e.classList.remove("djvlc-drop-target")}}),document.addEventListener("drop",t=>{if(!this.dragState.isDragging)return;t.preventDefault();const{draggedComponentId:n,dropTarget:e,dropPosition:o}=this.dragState;if(n&&e&&o){const t=e.getAttribute("data-component-id");t&&t!==n&&(this.log("info","Drop:",n,o,t),this.send("component_drop",{componentId:n,targetComponentId:t,position:o}))}this.endDrag()}),document.addEventListener("dragend",()=>{this.endDrag()}))}makeComponentsDraggable(){document.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}),new MutationObserver(t=>{t.forEach(t=>{t.addedNodes.forEach(t=>{t instanceof HTMLElement&&(t.hasAttribute("data-component-id")&&(t.draggable=!0),t.querySelectorAll("[data-component-id]").forEach(t=>{t.draggable=!0}))})})}).observe(document.body,{childList:!0,subtree:!0})}showDropIndicator(t,n){if(!this.options.dragOptions?.showDropIndicator)return;let e=this.dragState.dropIndicator;e||(e=document.createElement("div"),e.className="djvlc-drop-indicator horizontal",document.body.appendChild(e),this.dragState.dropIndicator=e);const o=t.getBoundingClientRect(),i=window.scrollY||document.documentElement.scrollTop,s=window.scrollX||document.documentElement.scrollLeft;e.style.left=`${o.left+s}px`,e.style.width=`${o.width}px`,e.style.top="before"===n?o.top+i-2+"px":o.bottom+i-2+"px",e.style.display="block"}hideDropIndicator(){this.dragState.dropIndicator&&(this.dragState.dropIndicator.style.display="none")}endDrag(){this.dragState.draggedElement?.classList.remove("djvlc-dragging"),this.dragState.dropTarget?.classList.remove("djvlc-drop-target"),this.hideDropIndicator(),this.dragState={isDragging:!1,draggedComponentId:null,draggedElement:null,dropTarget:null,dropPosition:null,dropIndicator:this.dragState.dropIndicator}}async handleInit(t){if(this.log("info","Init with config:",t),this.runtime)try{await this.runtime.init()}catch(t){throw this.log("error","Failed to init runtime:",t),t}}async handleLoadPage(t){if(this.log("info","Load page:",t.pageId),!this.runtime)throw new Error("Runtime not set");try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to load page:",t),t}}async handleUpdateSchema(t){if(this.log("info","Update schema",t.fullUpdate?"(full)":"(partial)"),!this.runtime)throw new Error("Runtime not set");try{t.fullUpdate,await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){throw this.log("error","Failed to update schema:",t),t}}async handleRefresh(){if(this.log("info","Refresh page"),this.runtime)try{await this.runtime.load(),await this.runtime.render(),this.syncState()}catch(t){this.log("error","Failed to refresh:",t),window.location.reload()}else window.location.reload()}handleSelectComponent(t){t.componentId?t.multi?this.selectedComponents.has(t.componentId)?this.selectedComponents.delete(t.componentId):this.selectedComponents.add(t.componentId):(this.selectedComponents.clear(),this.selectedComponents.add(t.componentId)):this.selectedComponents.clear(),this.updateSelectionStyles(),this.syncState()}handleHoverComponent(t){if(document.querySelectorAll(".djvlc-hovered").forEach(t=>{t.classList.remove("djvlc-hovered")}),t.componentId){const n=document.querySelector(`[data-component-id="${t.componentId}"]`);n&&!this.selectedComponents.has(t.componentId)&&n.classList.add("djvlc-hovered")}}handleUpdateComponent(t){this.log("info","Update component:",t.componentId),this.runtime?.updateComponent(t.componentId,t.props||{})}handleAddComponent(t){this.log("info","Add component:",t.component.componentType),this.log("warn","addComponent not supported, need to reload page")}handleMoveComponent(t){this.log("info","Move component:",t.componentId,"to",t.targetIndex),this.log("warn","moveComponent not supported, need to reload page")}handleDeleteComponent(t){this.log("info","Delete component:",t.componentId),this.log("warn","deleteComponent not supported, need to reload page"),this.selectedComponents.delete(t.componentId),this.hoveredComponent===t.componentId&&(this.hoveredComponent=null)}handleSyncVariables(t){Object.entries(t.variables).forEach(([t,n])=>{this.runtime?.setVariable(t,n)})}updateSelectionStyles(){document.querySelectorAll(".djvlc-selected").forEach(t=>{t.classList.remove("djvlc-selected")}),this.selectedComponents.forEach(t=>{const n=document.querySelector(`[data-component-id="${t}"]`);n&&n.classList.add("djvlc-selected")})}syncState(){const t=this.runtime?.getState(),n={phase:t?.phase||"idle",page:t?.page?{pageId:t.page.pageId,pageVersionId:t.page.pageVersionId}:void 0,selectedComponents:Array.from(this.selectedComponents),hoveredComponent:this.hoveredComponent};this.send("sync_state",n)}sendReady(){this.send("ready",{runtimeVersion:"0.1.0",capabilities:["select","hover","update","add","move","delete","drag"]})}sendComponentEvent(t,n){this.send(t,n)}send(t,n){const o=e(t,n);this.log("debug","Sending message:",t,o),window.parent.postMessage(o,this.options.targetOrigin||"*")}sendResponse(t,n,e,i,s){const a=o(t,n,e,i,s);this.log("debug","Sending response:",n,a),window.parent.postMessage(a,this.options.targetOrigin||"*")}findComponentElement(t){let n=t;for(;n;){if(n.hasAttribute("data-component-id"))return n;n=n.parentElement}return null}log(t,n,...e){this.options.debug}};export{t as MessageType,a as SandboxClient,s as SandboxHost,e as createMessage,o as createResponse,n as generateMessageId,i as isDjvlcMessage};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djvlc/sandbox",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "DJV 低代码平台沙箱通信模块",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@djvlc/contracts-types": "1.8.3",
30
- "@djvlc/runtime-core": "1.0.2"
30
+ "@djvlc/runtime-core": "1.0.3"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^20.10.0",