@combos-fun/plugin-development-tool 0.0.22 → 0.0.24

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.
@@ -1 +1 @@
1
- "use strict";var e=require("tslib"),t=require("@combos-fun/engine"),n=require("@combos-fun/plugin-renderer"),o=require("@combos-fun/plugin-renderer-graphics"),s=require("@combos-fun/plugin-renderer-event");const i="combos-development-tool:set",r="combos-development-tool:set-success",a="combos-development-tool:refresh",c="combos-development-tool:gameobject-selected",l="combos-development-tool:apply-property";class h extends t.Component{constructor(){super(...arguments),this.payload={}}static{this.componentName="CombosDevelopmentToolTarget"}init(e){this.payload=e?.payload?{...e.payload}:{}}}const d=new Set(["gameObject","name","started","__componentDefaultParams","destroyed","inScene","worldTransform","children","_parent"]),u={Graphics:new Set(["graphics"]),Physics:new Set(["body","Body","PhysicsEngine","World","Constraint","mouseConstraint","bodyParams"]),Event:new Set(["hitArea"])};function p(e,t=0){if(null==e)return e;if("number"==typeof e||"string"==typeof e||"boolean"==typeof e)return e;if(!(t>6)){if(Array.isArray(e))return e.map(e=>p(e,t+1));if("object"==typeof e){const n={};for(const[o,s]of Object.entries(e)){if("function"==typeof s)continue;const e=p(s,t+1);void 0!==e&&(n[o]=e)}return n}}}function f(e){const t={},n=u[e.name];for(const o of Object.keys(e)){if(d.has(o)||o.startsWith("_"))continue;if(n?.has(o))continue;const s=e[o];if("function"==typeof s)continue;const i=p(s);void 0!==i&&(t[o]=i)}return t}function m(e){const t=e.getComponent(h),n=t?.payload?.source;var o;if((o=n)&&"object"==typeof o&&"string"==typeof o.file)return{file:n.file,anchor:n.anchor??e.name}}function g(e){const t=e.components.filter(e=>"CombosDevelopmentToolTarget"!==e.name).map(e=>({componentName:e.name,fields:f(e)}));return{id:e.id,name:e.name,components:t,source:m(e)}}function y(e,t,n,o){const s=e.getComponent(t);if(!s||!n.length)return!1;let i=s;for(let e=0;e<n.length-1;e++){const t=n[e];void 0!==i[t]&&null!==i[t]||(i[t]={}),i=i[t]}return i[n[n.length-1]]=o,!0}const b=new Set(["position","size","origin","anchor","scale","skew"]);function w(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e&&""!==e.trim()){const t=Number(e);return Number.isFinite(t)?t:void 0}}const O={Transform:function(e,t,n,o){const s=e.transform??t,i=s;if(1===n.length&&"rotation"===n[0]){const e=w(o);return void 0!==e&&(s.rotation=e,!0)}if(2===n.length&&b.has(n[0])){const t=n[0],s=n[1],r=w(o);if(void 0===r)return!1;const a=i[t];return!(!a||"object"!=typeof a)&&(a[s]=r,"position"===t&&(function(e){const t=e.getComponent("Physics");if(!t?.body)return;const{x:n,y:o}=e.transform.position;t.Body?.setPosition?t.Body.setPosition(t.body,{x:n,y:o}):(t.body.position.x=n,t.body.position.y=o)}(e),function(e){const t=e.getComponent("PlatformerMover");t&&(t.originX=e.transform.position.x,t.originY=e.transform.position.y)}(e)),!0)}return!1},Text:function(e,t,n,o){const s=t;if(1===n.length&&"text"===n[0])return s.text=String(o??""),!0;if(2===n.length&&"style"===n[0]){const e=n[1],t={...s.style??{},[e]:o};return s.style=t,!0}return!1},Sound:function(e,t,n,o){const s=t;if(1===n.length&&"volume"===n[0]||2===n.length&&"config"===n[0]&&"volume"===n[1]){const e=w(o);return void 0!==e&&(s.volume=e,!0)}return!1}};function v(e,t,n,o){const s=e.getComponent(t);if(!s||!n.length)return!1;const i=O[t];return!!i?.(e,s,n,o)||y(e,t,n,o)}const E="__combosDevelopmentToolOutline";let G=class extends t.System{constructor(){super(...arguments),this.postMessageOrigin="*",this.enabled=!1,this.outlineGo=null,this.selected=null,this.tapHandlers=new Map,this.tapOwners=new Map,this.disabledGameEventGoIds=new Set,this.pickEvents=new Set,this.needsRescan=!1,this.lastOutlineBounds=null,this.onWindowSet=e=>{const t=e.detail;t&&"boolean"==typeof t.enabled&&this.setEnabled(t.enabled)},this.onWindowMessage=e=>{const t=e.data;t&&"object"==typeof t&&(t.type===i&&"boolean"==typeof t.enabled?this.setEnabled(t.enabled):t.type===a?this.requestSceneRescan():t.type===l&&this.handleApplyProperty(t))},this.onGameSet=e=>{e&&"boolean"==typeof e.enabled&&this.setEnabled(e.enabled)},this.onGameRefresh=()=>{this.requestSceneRescan()},this.onWindowRefresh=()=>{this.requestSceneRescan()}}static{this.systemName="CombosDevelopmentToolSystem"}init(e){this.postMessageOrigin=e?.postMessageOrigin??"*","undefined"!=typeof window&&(window.addEventListener(i,this.onWindowSet),window.addEventListener(a,this.onWindowRefresh),window.addEventListener("message",this.onWindowMessage)),this.game.on(i,this.onGameSet),this.game.on(a,this.onGameRefresh),this.enabled&&(this.needsRescan=!0)}onDestroy(){"undefined"!=typeof window&&(window.removeEventListener(i,this.onWindowSet),window.removeEventListener(a,this.onWindowRefresh),window.removeEventListener("message",this.onWindowMessage)),this.game.off(i,this.onGameSet),this.game.off(a,this.onGameRefresh),this.detachAllPicks(),this.restoreDisabledGameEvents(),this.clearSelection()}setEnabled(e){this.enabled!==e&&(this.enabled=e,e?this.needsRescan=!0:(this.clearSelection(),this.detachAllPicks(),this.restoreDisabledGameEvents(),this.postSetSuccess(!1)))}get isEnabled(){return this.enabled}update(e){this.enabled&&this.needsRescan&&(this.needsRescan=!1,this.attachEditMode(),this.postSetSuccess(!0));for(const e of[...this.tapOwners.values()])e.destroyed&&this.releasePick(e);this.enabled&&this.selected&&this.outlineGo&&this.updateSelectionOutline()}componentChanged(e){if(!this.enabled)return;const{type:n,gameObject:o,componentName:s}=e;o&&"CombosDevelopmentToolTarget"===s&&(n===t.OBSERVER_TYPE.ADD?(this.stripGameEvent(o),this.ensurePick(o)):n===t.OBSERVER_TYPE.REMOVE&&(this.releasePick(o),this.selected===o&&this.clearSelection()))}requestSceneRescan(){this.enabled&&(this.needsRescan=!0)}attachEditMode(){this.detachAllPicks();const e=[];for(const t of this.game.scene.transform.children)this.collectGameObjects(t.gameObject,e);for(const t of e)this.isIgnoredPickGo(t)||this.stripGameEvent(t);for(const t of e)this.isIgnoredPickGo(t)||t.getComponent(h)&&this.ensurePick(t)}collectGameObjects(e,t){t.push(e);for(const n of e.transform.children)this.collectGameObjects(n.gameObject,t)}isIgnoredPickGo(e){return e.name===E||e===this.outlineGo}stripGameEvent(e){if(this.disabledGameEventGoIds.has(e.id))return;if(!e.getComponent(s.Event))return;const t=this.getRendererContainer(e);if(t)try{t.interactive=!1,this.disabledGameEventGoIds.add(e.id)}catch{}}restoreDisabledGameEvents(){for(const e of[...this.disabledGameEventGoIds]){const t=this.findGameObjectById(e);if(!t||t.destroyed){this.disabledGameEventGoIds.delete(e);continue}const n=this.getRendererContainer(t);if(n)try{n.interactive=!0}catch{}this.disabledGameEventGoIds.delete(e)}}detachAllPicks(){for(const e of[...this.tapOwners.values()])this.releasePick(e)}ensurePick(e){if(this.tapHandlers.has(e.id))return;const t=this.getRendererContainer(e);if(t)try{t.interactive=!0}catch{}let n=e.getComponent(s.Event);n||(n=e.addComponent(this.createPickEvent(e)),this.pickEvents.add(e.id));const o=t=>this.onSelect(e,t);this.tapHandlers.set(e.id,o),this.tapOwners.set(e.id,e),n.on("tap",o)}createPickEvent(e){const t=this.resolvePickBounds(e);return t.width>0&&t.height>0?new s.Event({hitArea:{type:s.HIT_AREA_TYPE.Rect,style:{x:t.x,y:t.y,width:t.width,height:t.height}}}):new s.Event}resolvePickBounds(e){if(this.shouldPreferRenderedBounds(e)){const t=this.resolveOwnGraphicsLocalBounds(e);if(t)return t;const n=this.resolveContainerLocalBounds(e);if(n)return n}const{width:t,height:n}=e.transform.size;if(t>0&&n>0)return{x:0,y:0,width:t,height:n};const o=this.resolveContainerLocalBounds(e);return o||{x:0,y:0,width:1,height:1}}shouldPreferRenderedBounds(e){return null!=e.getComponent(o.Graphics)}resolveOwnGraphicsLocalBounds(e){const t=e.getComponent(o.Graphics);if(!t?.graphics)return null;try{const e=t.graphics.getLocalBounds();if(e.width>0&&e.height>0)return{x:e.x,y:e.y,width:e.width,height:e.height}}catch{}return null}resolveContainerLocalBounds(e){const t=this.getRendererContainer(e);if(!t)return null;const n=this.getOutlineContainerIfChildOf(e);let o=!1;n?.parent===t&&(t.removeChild(n),o=!0);try{const e=t.getLocalBounds();if(e.width>0&&e.height>0)return{x:e.x,y:e.y,width:e.width,height:e.height}}catch{}finally{o&&n&&t.addChild(n)}return null}getOutlineContainerIfChildOf(e){return this.outlineGo&&this.outlineGo.parent===e?this.getRendererContainer(this.outlineGo):null}getRendererContainer(e){const t=this.game.getSystem(n.RendererSystem);if(!t?.containerManager)return null;try{return t.containerManager.getContainer(e.id)}catch{return null}}releasePick(e){const t=this.tapHandlers.get(e.id);if(!t)return;const n=e.getComponent(s.Event);if(n?.off("tap",t),this.tapHandlers.delete(e.id),this.tapOwners.delete(e.id),this.pickEvents.has(e.id)){try{e.removeComponent(s.Event)}catch{}this.pickEvents.delete(e.id)}}postSetSuccess(e){const t={type:r,enabled:e};"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage(t,this.postMessageOrigin),this.game.emit(r,{enabled:e})}ensureOutline(){if(this.outlineGo)return;const e=new t.GameObject(E,{size:{width:1,height:1},position:{x:0,y:0},origin:{x:0,y:0}});e.addComponent(new o.Graphics),this.outlineGo=e}onSelect(e,t){if(!this.enabled)return;if(t?.stopPropagation?.(),this.ensureOutline(),!this.outlineGo)return;this.selected=e,this.outlineGo.parent&&this.outlineGo.remove(),e.addChild(this.outlineGo),this.invalidateOutlineBounds(),this.updateSelectionOutline();const n=g(e),o={type:c,snapshot:n,pointer:t?.data?.position??null};"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage(o,this.postMessageOrigin)}findGameObjectById(e){const t=[];for(const e of this.game.scene.transform.children)this.collectGameObjects(e.gameObject,t);return t.find(t=>t.id===e)??null}findGameObjectBySource(e){const t=[];for(const e of this.game.scene.transform.children)this.collectGameObjects(e.gameObject,t);const n=e.file.trim(),o=(e.anchor??"").trim();if(!o)return null;for(const e of t){const t=m(e);if(!t||t.file!==n)continue;if((t.anchor??e.name)===o)return e}return null}resolveApplyTarget(e){if("number"==typeof e.gameObjectId){const t=this.findGameObjectById(e.gameObjectId);if(t)return t}const t=e.source?.file?.trim(),n=e.source?.anchor?.trim();return t&&n?this.findGameObjectBySource({file:t,anchor:n}):null}handleApplyProperty(e){const t=e.componentName,n=e.path,o="number"==typeof e.gameObjectId,s=Boolean(e.source?.file?.trim())&&Boolean(e.source?.anchor?.trim());if(!o&&!s||"string"!=typeof t||!Array.isArray(n)||0===n.length)return;const i=this.resolveApplyTarget(e);if(!i)return;v(i,t,n,e.value)&&this.selected?.id===i.id&&(this.invalidateOutlineBounds(),this.updateSelectionOutline(),this.postSnapshotUpdate(i))}postSnapshotUpdate(e){const t=g(e);"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage({type:c,snapshot:t,pointer:null,source:"property-applied"},this.postMessageOrigin)}clearSelection(){this.selected=null,this.invalidateOutlineBounds(),this.outlineGo?.parent&&this.outlineGo.remove();const e=this.outlineGo?.getComponent(o.Graphics);e?.graphics&&e.graphics.clear()}updateSelectionOutline(){if(!this.enabled||!this.selected||!this.outlineGo)return;const e=this.outlineGo.getComponent(o.Graphics);if(!e?.graphics)return;const t=this.resolvePickBounds(this.selected);if(this.lastOutlineBounds&&this.pickBoundsEqual(this.lastOutlineBounds,t))return;this.lastOutlineBounds=t;const n=e.graphics;n.clear(),n.rect(t.x,t.y,t.width,t.height),n.stroke({width:4,color:5636010,alpha:1})}invalidateOutlineBounds(){this.lastOutlineBounds=null}pickBoundsEqual(e,t){const n=.01;return Math.abs(e.x-t.x)<n&&Math.abs(e.y-t.y)<n&&Math.abs(e.width-t.width)<n&&Math.abs(e.height-t.height)<n}};G=e.__decorate([t.decorators.componentObserver({CombosDevelopmentToolTarget:[]})],G);var S=G;Object.assign(S,{packageName:"@combos-fun/plugin-development-tool",packageVersion:"0.0.21"}),exports.COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY=l,exports.COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED=c,exports.COMBOS_DEVELOPMENT_TOOL_READY="combos-development-tool:ready",exports.COMBOS_DEVELOPMENT_TOOL_REFRESH=a,exports.COMBOS_DEVELOPMENT_TOOL_SET=i,exports.COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS=r,exports.CombosDevelopmentToolSystem=S,exports.CombosDevelopmentToolTarget=h,exports.applyPropertyValue=y,exports.applyPropertyWithHooks=v,exports.buildGameObjectSnapshot=g,exports.readPropertyValue=function(e,t,n){const o=e.getComponent(t);if(!o||!n.length)return;let s=o;for(const e of n){if(null==s||"object"!=typeof s)return;s=s[e]}return s},exports.readSourceAnchor=m;
1
+ "use strict";var e=require("tslib"),t=require("@combos-fun/engine"),n=require("@combos-fun/plugin-renderer"),o=require("@combos-fun/plugin-renderer-graphics"),s=require("@combos-fun/plugin-renderer-event");const i="combos-development-tool:set",r="combos-development-tool:set-success",a="combos-development-tool:refresh",d="combos-development-tool:gameobject-selected",c="combos-development-tool:apply-property",h="combos-development-tool:set-paused",l="combos-development-tool:set-muted",u="combos-development-tool:state-changed";class p extends t.Component{constructor(){super(...arguments),this.payload={}}static{this.componentName="CombosDevelopmentToolTarget"}init(e){this.payload=e?.payload?{...e.payload}:{}}}const m=["knoffice.tech","converge.ai"];function f(e,t){if(t.includes("*"))return!0;if(0===t.length)return!1;let n;try{n=new URL(e)}catch{return!1}const o=e.toLowerCase(),s=n.hostname.toLowerCase();for(const e of t){const t=e.trim();if(!t)continue;if(t.includes("://")){if(o===t.toLowerCase())return!0;continue}const n=t.toLowerCase().replace(/^\./,"");if(s===n||s.endsWith(`.${n}`))return!0}return!1}function g(e){if(!e?.length)return[...m];if(e.includes("*"))return["*"];const t=new Set,n=[];for(const o of[...m,...e]){const e=o.trim();e&&!t.has(e)&&(t.add(e),n.push(e))}return n}const y=new Set(["gameObject","name","started","__componentDefaultParams","destroyed","inScene","worldTransform","children","_parent"]),w={Graphics:new Set(["graphics"]),Physics:new Set(["body","Body","PhysicsEngine","World","Constraint","mouseConstraint","bodyParams"]),Event:new Set(["hitArea"])};function b(e,t=0){if(null==e)return e;if("number"==typeof e||"string"==typeof e||"boolean"==typeof e)return e;if(!(t>6)){if(Array.isArray(e))return e.map(e=>b(e,t+1));if("object"==typeof e){const n={};for(const[o,s]of Object.entries(e)){if("function"==typeof s)continue;const e=b(s,t+1);void 0!==e&&(n[o]=e)}return n}}}function O(e){const t={},n=w[e.name];for(const o of Object.keys(e)){if(y.has(o)||o.startsWith("_"))continue;if(n?.has(o))continue;const s=e[o];if("function"==typeof s)continue;const i=b(s);void 0!==i&&(t[o]=i)}return t}function E(e){const t=e.getComponent(p),n=t?.payload?.source;var o;if((o=n)&&"object"==typeof o&&"string"==typeof o.file)return{file:n.file,anchor:n.anchor??e.name}}function S(e){const t=e.components.filter(e=>"CombosDevelopmentToolTarget"!==e.name).map(e=>({componentName:e.name,fields:O(e)}));return{id:e.id,name:e.name,components:t,source:E(e)}}function v(e,t,n,o){const s=e.getComponent(t);if(!s||!n.length)return!1;let i=s;for(let e=0;e<n.length-1;e++){const t=n[e];void 0!==i[t]&&null!==i[t]||(i[t]={}),i=i[t]}return i[n[n.length-1]]=o,!0}const G=new Set(["position","size","origin","anchor","scale","skew"]);function C(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e&&""!==e.trim()){const t=Number(e);return Number.isFinite(t)?t:void 0}}const M={Transform:function(e,t,n,o){const s=e.transform??t,i=s;if(1===n.length&&"rotation"===n[0]){const e=C(o);return void 0!==e&&(s.rotation=e,!0)}if(2===n.length&&G.has(n[0])){const t=n[0],s=n[1],r=C(o);if(void 0===r)return!1;const a=i[t];return!(!a||"object"!=typeof a)&&(a[s]=r,"position"===t&&(function(e){const t=e.getComponent("Physics");if(!t?.body)return;const{x:n,y:o}=e.transform.position;t.Body?.setPosition?t.Body.setPosition(t.body,{x:n,y:o}):(t.body.position.x=n,t.body.position.y=o)}(e),function(e){const t=e.getComponent("PlatformerMover");t&&(t.originX=e.transform.position.x,t.originY=e.transform.position.y)}(e)),!0)}return!1},Text:function(e,t,n,o){const s=t;if(1===n.length&&"text"===n[0])return s.text=String(o??""),!0;if(2===n.length&&"style"===n[0]){const e=n[1],t={...s.style??{},[e]:o};return s.style=t,!0}return!1},Sound:function(e,t,n,o){const s=t;if(1===n.length&&"volume"===n[0]||2===n.length&&"config"===n[0]&&"volume"===n[1]){const e=C(o);return void 0!==e&&(s.volume=e,!0)}return!1}};function P(e,t,n,o){const s=e.getComponent(t);if(!s||!n.length)return!1;const i=M[t];return!!i?.(e,s,n,o)||v(e,t,n,o)}const T="__combosDevelopmentToolOutline";let _=class extends t.System{constructor(){super(...arguments),this.postMessageOrigin="*",this.allowedMessageOrigins=g(),this.enabled=!1,this.outlineGo=null,this.selected=null,this.tapHandlers=new Map,this.tapOwners=new Map,this.disabledGameEventGoIds=new Set,this.pickEvents=new Set,this.needsRescan=!1,this.lastOutlineBounds=null,this.mutedWithoutSoundSystem=!1,this.onWindowSet=e=>{const t=e.detail;t&&"boolean"==typeof t.enabled&&this.setEnabled(t.enabled)},this.onWindowSetPaused=e=>{const t=e.detail;t&&"boolean"==typeof t.paused&&this.setPaused(t.paused)},this.onWindowSetMuted=e=>{const t=e.detail;t&&"boolean"==typeof t.muted&&this.setMuted(t.muted)},this.onWindowMessage=e=>{if(!f(e.origin,this.allowedMessageOrigins))return;const t=e.data;t&&"object"==typeof t&&(t.type===i&&"boolean"==typeof t.enabled?this.setEnabled(t.enabled):t.type===h&&"boolean"==typeof t.paused?this.setPaused(t.paused):t.type===l&&"boolean"==typeof t.muted?this.setMuted(t.muted):t.type===a?this.requestSceneRescan():t.type===c&&this.handleApplyProperty(t))},this.onGameSet=e=>{e&&"boolean"==typeof e.enabled&&this.setEnabled(e.enabled)},this.onGameRefresh=()=>{this.requestSceneRescan()},this.onGameSetPaused=e=>{e&&"boolean"==typeof e.paused&&this.setPaused(e.paused)},this.onGameSetMuted=e=>{e&&"boolean"==typeof e.muted&&this.setMuted(e.muted)},this.onWindowRefresh=()=>{this.requestSceneRescan()}}static{this.systemName="CombosDevelopmentToolSystem"}init(e){this.postMessageOrigin=e?.postMessageOrigin??"*",this.allowedMessageOrigins=g(e?.allowedMessageOrigins),"undefined"!=typeof window&&(window.addEventListener(i,this.onWindowSet),window.addEventListener(h,this.onWindowSetPaused),window.addEventListener(l,this.onWindowSetMuted),window.addEventListener(a,this.onWindowRefresh),window.addEventListener("message",this.onWindowMessage)),this.game.on(i,this.onGameSet),this.game.on(h,this.onGameSetPaused),this.game.on(l,this.onGameSetMuted),this.game.on(a,this.onGameRefresh),this.enabled&&(this.needsRescan=!0)}onDestroy(){"undefined"!=typeof window&&(window.removeEventListener(i,this.onWindowSet),window.removeEventListener(h,this.onWindowSetPaused),window.removeEventListener(l,this.onWindowSetMuted),window.removeEventListener(a,this.onWindowRefresh),window.removeEventListener("message",this.onWindowMessage)),this.game.off(i,this.onGameSet),this.game.off(h,this.onGameSetPaused),this.game.off(l,this.onGameSetMuted),this.game.off(a,this.onGameRefresh),this.detachAllPicks(),this.restoreDisabledGameEvents(),this.clearSelection()}setEnabled(e){this.enabled!==e&&(this.enabled=e,e?this.needsRescan=!0:(this.clearSelection(),this.detachAllPicks(),this.restoreDisabledGameEvents(),this.postSetSuccess(!1)))}get isEnabled(){return this.enabled}get isPaused(){return!this.game.playing}get isMuted(){const e=this.getSoundSystem();return e?e.muted:this.mutedWithoutSoundSystem}setPaused(e){e?this.game.playing&&this.game.pause():this.game.playing||this.game.resume(),this.postStateChanged()}setMuted(e){const t=this.getSoundSystem();t?t.muted=e:this.mutedWithoutSoundSystem=e,this.postStateChanged()}update(e){this.enabled&&this.needsRescan&&(this.needsRescan=!1,this.attachEditMode(),this.postSetSuccess(!0));for(const e of[...this.tapOwners.values()])e.destroyed&&this.releasePick(e);this.enabled&&this.selected&&this.outlineGo&&this.updateSelectionOutline()}componentChanged(e){if(!this.enabled)return;const{type:n,gameObject:o,componentName:s}=e;o&&"CombosDevelopmentToolTarget"===s&&(n===t.OBSERVER_TYPE.ADD?(this.stripGameEvent(o),this.ensurePick(o)):n===t.OBSERVER_TYPE.REMOVE&&(this.releasePick(o),this.selected===o&&this.clearSelection()))}requestSceneRescan(){this.enabled&&(this.needsRescan=!0)}attachEditMode(){this.detachAllPicks();const e=[];for(const t of this.game.scene.transform.children)this.collectGameObjects(t.gameObject,e);for(const t of e)this.isIgnoredPickGo(t)||this.stripGameEvent(t);for(const t of e)this.isIgnoredPickGo(t)||t.getComponent(p)&&this.ensurePick(t)}collectGameObjects(e,t){t.push(e);for(const n of e.transform.children)this.collectGameObjects(n.gameObject,t)}isIgnoredPickGo(e){return e.name===T||e===this.outlineGo}stripGameEvent(e){if(this.disabledGameEventGoIds.has(e.id))return;if(!e.getComponent(s.Event))return;const t=this.getRendererContainer(e);if(t)try{t.interactive=!1,this.disabledGameEventGoIds.add(e.id)}catch{}}restoreDisabledGameEvents(){for(const e of[...this.disabledGameEventGoIds]){const t=this.findGameObjectById(e);if(!t||t.destroyed){this.disabledGameEventGoIds.delete(e);continue}const n=this.getRendererContainer(t);if(n)try{n.interactive=!0}catch{}this.disabledGameEventGoIds.delete(e)}}detachAllPicks(){for(const e of[...this.tapOwners.values()])this.releasePick(e)}ensurePick(e){if(this.tapHandlers.has(e.id))return;const t=this.getRendererContainer(e);if(t)try{t.interactive=!0}catch{}let n=e.getComponent(s.Event);n||(n=e.addComponent(this.createPickEvent(e)),this.pickEvents.add(e.id));const o=t=>this.onSelect(e,t);this.tapHandlers.set(e.id,o),this.tapOwners.set(e.id,e),n.on("tap",o)}createPickEvent(e){const t=this.resolvePickBounds(e);return t.width>0&&t.height>0?new s.Event({hitArea:{type:s.HIT_AREA_TYPE.Rect,style:{x:t.x,y:t.y,width:t.width,height:t.height}}}):new s.Event}resolvePickBounds(e){if(this.shouldPreferRenderedBounds(e)){const t=this.resolveOwnGraphicsLocalBounds(e);if(t)return t;const n=this.resolveContainerLocalBounds(e);if(n)return n}const{width:t,height:n}=e.transform.size;if(t>0&&n>0)return{x:0,y:0,width:t,height:n};const o=this.resolveContainerLocalBounds(e);return o||{x:0,y:0,width:1,height:1}}shouldPreferRenderedBounds(e){return null!=e.getComponent(o.Graphics)}resolveOwnGraphicsLocalBounds(e){const t=e.getComponent(o.Graphics);if(!t?.graphics)return null;try{const e=t.graphics.getLocalBounds();if(e.width>0&&e.height>0)return{x:e.x,y:e.y,width:e.width,height:e.height}}catch{}return null}resolveContainerLocalBounds(e){const t=this.getRendererContainer(e);if(!t)return null;const n=this.getOutlineContainerIfChildOf(e);let o=!1;n?.parent===t&&(t.removeChild(n),o=!0);try{const e=t.getLocalBounds();if(e.width>0&&e.height>0)return{x:e.x,y:e.y,width:e.width,height:e.height}}catch{}finally{o&&n&&t.addChild(n)}return null}getOutlineContainerIfChildOf(e){return this.outlineGo&&this.outlineGo.parent===e?this.getRendererContainer(this.outlineGo):null}getRendererContainer(e){const t=this.game.getSystem(n.RendererSystem);if(!t?.containerManager)return null;try{return t.containerManager.getContainer(e.id)}catch{return null}}releasePick(e){const t=this.tapHandlers.get(e.id);if(!t)return;const n=e.getComponent(s.Event);if(n?.off("tap",t),this.tapHandlers.delete(e.id),this.tapOwners.delete(e.id),this.pickEvents.has(e.id)){try{e.removeComponent(s.Event)}catch{}this.pickEvents.delete(e.id)}}postSetSuccess(e){const t={type:r,enabled:e};"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage(t,this.postMessageOrigin),this.game.emit(r,{enabled:e})}postStateChanged(){const e={paused:this.isPaused,muted:this.isMuted},t={type:u,...e};"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage(t,this.postMessageOrigin),this.game.emit(u,e)}getSoundSystem(){const e=this.game.getSystem("SoundSystem");return e&&"object"==typeof e&&"muted"in e&&"boolean"==typeof e.muted?e:null}ensureOutline(){if(this.outlineGo)return;const e=new t.GameObject(T,{size:{width:1,height:1},position:{x:0,y:0},origin:{x:0,y:0}});e.addComponent(new o.Graphics),this.outlineGo=e}onSelect(e,t){if(!this.enabled)return;if(t?.stopPropagation?.(),this.ensureOutline(),!this.outlineGo)return;this.selected=e,this.outlineGo.parent&&this.outlineGo.remove(),e.addChild(this.outlineGo),this.invalidateOutlineBounds(),this.updateSelectionOutline();const n=S(e),o={type:d,snapshot:n,pointer:t?.data?.position??null};"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage(o,this.postMessageOrigin)}findGameObjectById(e){const t=[];for(const e of this.game.scene.transform.children)this.collectGameObjects(e.gameObject,t);return t.find(t=>t.id===e)??null}findGameObjectBySource(e){const t=[];for(const e of this.game.scene.transform.children)this.collectGameObjects(e.gameObject,t);const n=e.file.trim(),o=(e.anchor??"").trim();if(!o)return null;for(const e of t){const t=E(e);if(!t||t.file!==n)continue;if((t.anchor??e.name)===o)return e}return null}resolveApplyTarget(e){if("number"==typeof e.gameObjectId){const t=this.findGameObjectById(e.gameObjectId);if(t)return t}const t=e.source?.file?.trim(),n=e.source?.anchor?.trim();return t&&n?this.findGameObjectBySource({file:t,anchor:n}):null}handleApplyProperty(e){const t=e.componentName,n=e.path,o="number"==typeof e.gameObjectId,s=Boolean(e.source?.file?.trim())&&Boolean(e.source?.anchor?.trim());if(!o&&!s||"string"!=typeof t||!Array.isArray(n)||0===n.length)return;const i=this.resolveApplyTarget(e);if(!i)return;P(i,t,n,e.value)&&this.selected?.id===i.id&&(this.invalidateOutlineBounds(),this.updateSelectionOutline(),this.postSnapshotUpdate(i))}postSnapshotUpdate(e){const t=S(e);"undefined"!=typeof window&&window.parent&&window.parent!==window&&window.parent.postMessage({type:d,snapshot:t,pointer:null,source:"property-applied"},this.postMessageOrigin)}clearSelection(){this.selected=null,this.invalidateOutlineBounds(),this.outlineGo?.parent&&this.outlineGo.remove();const e=this.outlineGo?.getComponent(o.Graphics);e?.graphics&&e.graphics.clear()}updateSelectionOutline(){if(!this.enabled||!this.selected||!this.outlineGo)return;const e=this.outlineGo.getComponent(o.Graphics);if(!e?.graphics)return;const t=this.resolvePickBounds(this.selected);if(this.lastOutlineBounds&&this.pickBoundsEqual(this.lastOutlineBounds,t))return;this.lastOutlineBounds=t;const n=e.graphics;n.clear(),n.rect(t.x,t.y,t.width,t.height),n.stroke({width:4,color:5636010,alpha:1})}invalidateOutlineBounds(){this.lastOutlineBounds=null}pickBoundsEqual(e,t){const n=.01;return Math.abs(e.x-t.x)<n&&Math.abs(e.y-t.y)<n&&Math.abs(e.width-t.width)<n&&Math.abs(e.height-t.height)<n}};_=e.__decorate([t.decorators.componentObserver({CombosDevelopmentToolTarget:[]})],_);var B=_;Object.assign(B,{packageName:"@combos-fun/plugin-development-tool",packageVersion:"0.0.23"}),exports.COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY=c,exports.COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED=d,exports.COMBOS_DEVELOPMENT_TOOL_READY="combos-development-tool:ready",exports.COMBOS_DEVELOPMENT_TOOL_REFRESH=a,exports.COMBOS_DEVELOPMENT_TOOL_SET=i,exports.COMBOS_DEVELOPMENT_TOOL_SET_MUTED=l,exports.COMBOS_DEVELOPMENT_TOOL_SET_PAUSED=h,exports.COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS=r,exports.COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED=u,exports.CombosDevelopmentToolSystem=B,exports.CombosDevelopmentToolTarget=p,exports.DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES=m,exports.applyPropertyValue=v,exports.applyPropertyWithHooks=P,exports.buildGameObjectSnapshot=S,exports.isAllowedMessageOrigin=f,exports.mergeAllowedMessageOrigins=g,exports.readPropertyValue=function(e,t,n){const o=e.getComponent(t);if(!o||!n.length)return;let s=o;for(const e of n){if(null==s||"object"!=typeof s)return;s=s[e]}return s},exports.readSourceAnchor=E;
@@ -12,6 +12,26 @@ declare const COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED: "combos-development-t
12
12
  declare const COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY: "combos-development-tool:apply-property";
13
13
  /** iframe → parent: game scene bootstrap finished; parent may enable pick mode. */
14
14
  declare const COMBOS_DEVELOPMENT_TOOL_READY: "combos-development-tool:ready";
15
+ /** Parent → iframe: pause or resume the game loop. Payload: `{ paused: boolean }`. */
16
+ declare const COMBOS_DEVELOPMENT_TOOL_SET_PAUSED: "combos-development-tool:set-paused";
17
+ /** Parent → iframe: mute or unmute audio when `SoundSystem` is registered. Payload: `{ muted: boolean }`. */
18
+ declare const COMBOS_DEVELOPMENT_TOOL_SET_MUTED: "combos-development-tool:set-muted";
19
+ /** iframe → parent (and `game.emit`): current pause/mute snapshot after a successful change. */
20
+ declare const COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED: "combos-development-tool:state-changed";
21
+
22
+ /** Default inbound postMessage host suffixes (host + all subdomains). */
23
+ declare const DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES: readonly ["knoffice.tech", "converge.ai"];
24
+ /**
25
+ * Returns whether `origin` is allowed by `allowed`.
26
+ *
27
+ * Each entry in `allowed` is either:
28
+ * - a **host suffix** (e.g. `knoffice.tech`) — matches that host and `*.knoffice.tech`;
29
+ * - a **full origin** (e.g. `https://creator.knoffice.tech`) — exact match;
30
+ * - `'*'` — accept any origin (escape hatch).
31
+ */
32
+ declare function isAllowedMessageOrigin(origin: string, allowed: readonly string[]): boolean;
33
+ /** Defaults plus any extra entries (deduped). `['*']` alone accepts any origin. */
34
+ declare function mergeAllowedMessageOrigins(extra?: string[]): string[];
15
35
 
16
36
  interface CombosDevelopmentToolTargetParams extends ComponentParams {
17
37
  /** Extra JSON-serializable fields included in postMessage to parent. */
@@ -31,6 +51,11 @@ declare class CombosDevelopmentToolTarget extends Component<CombosDevelopmentToo
31
51
  interface CombosDevelopmentToolSystemParams {
32
52
  /** postMessage `targetOrigin` when notifying parent (default `'*'`). */
33
53
  postMessageOrigin?: string;
54
+ /**
55
+ * Extra inbound `postMessage` origins merged with {@link DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES}.
56
+ * Each entry is a host suffix (e.g. `localhost`) or full origin. Pass `['*']` to accept any origin.
57
+ */
58
+ allowedMessageOrigins?: string[];
34
59
  }
35
60
  /**
36
61
  * **Off** at start. While enabled:
@@ -42,12 +67,20 @@ interface CombosDevelopmentToolSystemParams {
42
67
  * - `window.postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET, enabled: true }, targetOrigin)` (e.g. from parent iframe)
43
68
  * - `game.emit(COMBOS_DEVELOPMENT_TOOL_SET, { enabled: true })` or `getSystem(CombosDevelopmentToolSystem).setEnabled(true)`
44
69
  *
70
+ * Pause / mute (independent of pick mode; parent toolbar or in-game debug UI):
71
+ * - `postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, paused: true })`
72
+ * - `postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET_MUTED, muted: true })`
73
+ * - `game.emit(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, { paused: true })` or `.setPaused(true)`
74
+ * Mute applies when `SoundSystem` is registered; otherwise the desired value is remembered until it appears.
75
+ * Both emit / post `COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED` with `{ paused, muted }`.
76
+ *
45
77
  * While enabled: `game.emit(COMBOS_DEVELOPMENT_TOOL_REFRESH)`, `window` event `combos-development-tool:refresh`,
46
78
  * or `postMessage({ type: 'combos-development-tool:refresh' })` to re-bind after adding/removing objects.
47
79
  */
48
80
  declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSystemParams> {
49
81
  static systemName: string;
50
82
  private postMessageOrigin;
83
+ private allowedMessageOrigins;
51
84
  private enabled;
52
85
  private outlineGo;
53
86
  private selected;
@@ -59,16 +92,33 @@ declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSy
59
92
  private readonly pickEvents;
60
93
  private needsRescan;
61
94
  private lastOutlineBounds;
95
+ /** Remembered mute when `SoundSystem` is not registered yet. */
96
+ private mutedWithoutSoundSystem;
62
97
  private readonly onWindowSet;
98
+ private readonly onWindowSetPaused;
99
+ private readonly onWindowSetMuted;
63
100
  private readonly onWindowMessage;
64
101
  private readonly onGameSet;
65
102
  private readonly onGameRefresh;
103
+ private readonly onGameSetPaused;
104
+ private readonly onGameSetMuted;
66
105
  private readonly onWindowRefresh;
67
106
  init(params?: CombosDevelopmentToolSystemParams): void;
68
107
  onDestroy(): void;
69
108
  /** Programmatic toggle (same effect as events). */
70
109
  setEnabled(on: boolean): void;
71
110
  get isEnabled(): boolean;
111
+ /** Whether the game loop is paused (`!game.playing`). */
112
+ get isPaused(): boolean;
113
+ /** Master mute when `SoundSystem` exists; otherwise the last requested value. */
114
+ get isMuted(): boolean;
115
+ /** Pause or resume the game loop (same as `game.pause()` / `game.resume()`). */
116
+ setPaused(paused: boolean): void;
117
+ /**
118
+ * Mute or unmute audio via `SoundSystem` when registered.
119
+ * No-op on the audio graph if `SoundSystem` is missing; value is remembered for `isMuted`.
120
+ */
121
+ setMuted(muted: boolean): void;
72
122
  update(e: UpdateParams): void;
73
123
  componentChanged(changed: ComponentChanged): void;
74
124
  /** Re-bind pick targets after dynamic scene changes while enabled. */
@@ -91,6 +141,8 @@ declare class CombosDevelopmentToolSystem extends System<CombosDevelopmentToolSy
91
141
  private getRendererContainer;
92
142
  private releasePick;
93
143
  private postSetSuccess;
144
+ private postStateChanged;
145
+ private getSoundSystem;
94
146
  private ensureOutline;
95
147
  private onSelect;
96
148
  private findGameObjectById;
@@ -136,5 +188,5 @@ declare function readPropertyValue(go: GameObject, componentName: string, path:
136
188
 
137
189
  declare function applyPropertyWithHooks(go: GameObject, componentName: string, path: string[], value: unknown): boolean;
138
190
 
139
- export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, applyPropertyValue, applyPropertyWithHooks, buildGameObjectSnapshot, readPropertyValue, readSourceAnchor };
191
+ export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, applyPropertyValue, applyPropertyWithHooks, buildGameObjectSnapshot, isAllowedMessageOrigin, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor };
140
192
  export type { CombosDevelopmentToolSystemParams, CombosDevelopmentToolTargetParams, SceneEditComponentSnapshot, SceneEditGameObjectSnapshot, SceneEditPropertyRef, SceneSourceAnchor };
@@ -16,6 +16,12 @@ const COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED = 'combos-development-tool:gam
16
16
  const COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY = 'combos-development-tool:apply-property';
17
17
  /** iframe → parent: game scene bootstrap finished; parent may enable pick mode. */
18
18
  const COMBOS_DEVELOPMENT_TOOL_READY = 'combos-development-tool:ready';
19
+ /** Parent → iframe: pause or resume the game loop. Payload: `{ paused: boolean }`. */
20
+ const COMBOS_DEVELOPMENT_TOOL_SET_PAUSED = 'combos-development-tool:set-paused';
21
+ /** Parent → iframe: mute or unmute audio when `SoundSystem` is registered. Payload: `{ muted: boolean }`. */
22
+ const COMBOS_DEVELOPMENT_TOOL_SET_MUTED = 'combos-development-tool:set-muted';
23
+ /** iframe → parent (and `game.emit`): current pause/mute snapshot after a successful change. */
24
+ const COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED = 'combos-development-tool:state-changed';
19
25
 
20
26
  /**
21
27
  * Marks a `GameObject` as selectable in editor pick mode. While the development tool is enabled,
@@ -33,6 +39,74 @@ class CombosDevelopmentToolTarget extends Component {
33
39
  }
34
40
  }
35
41
 
42
+ /** Default inbound postMessage host suffixes (host + all subdomains). */
43
+ const DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES = [
44
+ 'knoffice.tech',
45
+ 'converge.ai',
46
+ ];
47
+ /**
48
+ * Returns whether `origin` is allowed by `allowed`.
49
+ *
50
+ * Each entry in `allowed` is either:
51
+ * - a **host suffix** (e.g. `knoffice.tech`) — matches that host and `*.knoffice.tech`;
52
+ * - a **full origin** (e.g. `https://creator.knoffice.tech`) — exact match;
53
+ * - `'*'` — accept any origin (escape hatch).
54
+ */
55
+ function isAllowedMessageOrigin(origin, allowed) {
56
+ if (allowed.includes('*')) {
57
+ return true;
58
+ }
59
+ if (allowed.length === 0) {
60
+ return false;
61
+ }
62
+ let parsed;
63
+ try {
64
+ parsed = new URL(origin);
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ const originLower = origin.toLowerCase();
70
+ const hostLower = parsed.hostname.toLowerCase();
71
+ for (const entry of allowed) {
72
+ const trimmed = entry.trim();
73
+ if (!trimmed) {
74
+ continue;
75
+ }
76
+ if (trimmed.includes('://')) {
77
+ if (originLower === trimmed.toLowerCase()) {
78
+ return true;
79
+ }
80
+ continue;
81
+ }
82
+ const suffix = trimmed.toLowerCase().replace(/^\./, '');
83
+ if (hostLower === suffix || hostLower.endsWith(`.${suffix}`)) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ /** Defaults plus any extra entries (deduped). `['*']` alone accepts any origin. */
90
+ function mergeAllowedMessageOrigins(extra) {
91
+ if (!extra?.length) {
92
+ return [...DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES];
93
+ }
94
+ if (extra.includes('*')) {
95
+ return ['*'];
96
+ }
97
+ const seen = new Set();
98
+ const result = [];
99
+ for (const entry of [...DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, ...extra]) {
100
+ const trimmed = entry.trim();
101
+ if (!trimmed || seen.has(trimmed)) {
102
+ continue;
103
+ }
104
+ seen.add(trimmed);
105
+ result.push(trimmed);
106
+ }
107
+ return result;
108
+ }
109
+
36
110
  function isSceneSourceAnchor(v) {
37
111
  return (!!v &&
38
112
  typeof v === 'object' &&
@@ -282,6 +356,13 @@ const OUTLINE_GO_NAME = '__combosDevelopmentToolOutline';
282
356
  * - `window.postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET, enabled: true }, targetOrigin)` (e.g. from parent iframe)
283
357
  * - `game.emit(COMBOS_DEVELOPMENT_TOOL_SET, { enabled: true })` or `getSystem(CombosDevelopmentToolSystem).setEnabled(true)`
284
358
  *
359
+ * Pause / mute (independent of pick mode; parent toolbar or in-game debug UI):
360
+ * - `postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, paused: true })`
361
+ * - `postMessage({ type: COMBOS_DEVELOPMENT_TOOL_SET_MUTED, muted: true })`
362
+ * - `game.emit(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, { paused: true })` or `.setPaused(true)`
363
+ * Mute applies when `SoundSystem` is registered; otherwise the desired value is remembered until it appears.
364
+ * Both emit / post `COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED` with `{ paused, muted }`.
365
+ *
285
366
  * While enabled: `game.emit(COMBOS_DEVELOPMENT_TOOL_REFRESH)`, `window` event `combos-development-tool:refresh`,
286
367
  * or `postMessage({ type: 'combos-development-tool:refresh' })` to re-bind after adding/removing objects.
287
368
  */
@@ -289,6 +370,7 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
289
370
  constructor() {
290
371
  super(...arguments);
291
372
  this.postMessageOrigin = '*';
373
+ this.allowedMessageOrigins = mergeAllowedMessageOrigins();
292
374
  this.enabled = false;
293
375
  this.outlineGo = null;
294
376
  this.selected = null;
@@ -300,19 +382,42 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
300
382
  this.pickEvents = new Set();
301
383
  this.needsRescan = false;
302
384
  this.lastOutlineBounds = null;
385
+ /** Remembered mute when `SoundSystem` is not registered yet. */
386
+ this.mutedWithoutSoundSystem = false;
303
387
  this.onWindowSet = (e) => {
304
388
  const d = e.detail;
305
389
  if (d && typeof d.enabled === 'boolean') {
306
390
  this.setEnabled(d.enabled);
307
391
  }
308
392
  };
393
+ this.onWindowSetPaused = (e) => {
394
+ const d = e.detail;
395
+ if (d && typeof d.paused === 'boolean') {
396
+ this.setPaused(d.paused);
397
+ }
398
+ };
399
+ this.onWindowSetMuted = (e) => {
400
+ const d = e.detail;
401
+ if (d && typeof d.muted === 'boolean') {
402
+ this.setMuted(d.muted);
403
+ }
404
+ };
309
405
  this.onWindowMessage = (e) => {
406
+ if (!isAllowedMessageOrigin(e.origin, this.allowedMessageOrigins)) {
407
+ return;
408
+ }
310
409
  const d = e.data;
311
410
  if (!d || typeof d !== 'object')
312
411
  return;
313
412
  if (d.type === COMBOS_DEVELOPMENT_TOOL_SET && typeof d.enabled === 'boolean') {
314
413
  this.setEnabled(d.enabled);
315
414
  }
415
+ else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_PAUSED && typeof d.paused === 'boolean') {
416
+ this.setPaused(d.paused);
417
+ }
418
+ else if (d.type === COMBOS_DEVELOPMENT_TOOL_SET_MUTED && typeof d.muted === 'boolean') {
419
+ this.setMuted(d.muted);
420
+ }
316
421
  else if (d.type === COMBOS_DEVELOPMENT_TOOL_REFRESH) {
317
422
  this.requestSceneRescan();
318
423
  }
@@ -328,6 +433,16 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
328
433
  this.onGameRefresh = () => {
329
434
  this.requestSceneRescan();
330
435
  };
436
+ this.onGameSetPaused = (payload) => {
437
+ if (payload && typeof payload.paused === 'boolean') {
438
+ this.setPaused(payload.paused);
439
+ }
440
+ };
441
+ this.onGameSetMuted = (payload) => {
442
+ if (payload && typeof payload.muted === 'boolean') {
443
+ this.setMuted(payload.muted);
444
+ }
445
+ };
331
446
  this.onWindowRefresh = () => {
332
447
  this.requestSceneRescan();
333
448
  };
@@ -335,12 +450,17 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
335
450
  static { this.systemName = 'CombosDevelopmentToolSystem'; }
336
451
  init(params) {
337
452
  this.postMessageOrigin = params?.postMessageOrigin ?? '*';
453
+ this.allowedMessageOrigins = mergeAllowedMessageOrigins(params?.allowedMessageOrigins);
338
454
  if (typeof window !== 'undefined') {
339
455
  window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET, this.onWindowSet);
456
+ window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onWindowSetPaused);
457
+ window.addEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onWindowSetMuted);
340
458
  window.addEventListener(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onWindowRefresh);
341
459
  window.addEventListener('message', this.onWindowMessage);
342
460
  }
343
461
  this.game.on(COMBOS_DEVELOPMENT_TOOL_SET, this.onGameSet);
462
+ this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onGameSetPaused);
463
+ this.game.on(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onGameSetMuted);
344
464
  this.game.on(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
345
465
  if (this.enabled) {
346
466
  this.needsRescan = true;
@@ -349,10 +469,14 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
349
469
  onDestroy() {
350
470
  if (typeof window !== 'undefined') {
351
471
  window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET, this.onWindowSet);
472
+ window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onWindowSetPaused);
473
+ window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onWindowSetMuted);
352
474
  window.removeEventListener(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onWindowRefresh);
353
475
  window.removeEventListener('message', this.onWindowMessage);
354
476
  }
355
477
  this.game.off(COMBOS_DEVELOPMENT_TOOL_SET, this.onGameSet);
478
+ this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, this.onGameSetPaused);
479
+ this.game.off(COMBOS_DEVELOPMENT_TOOL_SET_MUTED, this.onGameSetMuted);
356
480
  this.game.off(COMBOS_DEVELOPMENT_TOOL_REFRESH, this.onGameRefresh);
357
481
  this.detachAllPicks();
358
482
  this.restoreDisabledGameEvents();
@@ -376,6 +500,44 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
376
500
  get isEnabled() {
377
501
  return this.enabled;
378
502
  }
503
+ /** Whether the game loop is paused (`!game.playing`). */
504
+ get isPaused() {
505
+ return !this.game.playing;
506
+ }
507
+ /** Master mute when `SoundSystem` exists; otherwise the last requested value. */
508
+ get isMuted() {
509
+ const sound = this.getSoundSystem();
510
+ if (sound) {
511
+ return sound.muted;
512
+ }
513
+ return this.mutedWithoutSoundSystem;
514
+ }
515
+ /** Pause or resume the game loop (same as `game.pause()` / `game.resume()`). */
516
+ setPaused(paused) {
517
+ if (paused) {
518
+ if (this.game.playing) {
519
+ this.game.pause();
520
+ }
521
+ }
522
+ else if (!this.game.playing) {
523
+ this.game.resume();
524
+ }
525
+ this.postStateChanged();
526
+ }
527
+ /**
528
+ * Mute or unmute audio via `SoundSystem` when registered.
529
+ * No-op on the audio graph if `SoundSystem` is missing; value is remembered for `isMuted`.
530
+ */
531
+ setMuted(muted) {
532
+ const sound = this.getSoundSystem();
533
+ if (sound) {
534
+ sound.muted = muted;
535
+ }
536
+ else {
537
+ this.mutedWithoutSoundSystem = muted;
538
+ }
539
+ this.postStateChanged();
540
+ }
379
541
  update(e) {
380
542
  if (this.enabled && this.needsRescan) {
381
543
  this.needsRescan = false;
@@ -644,6 +806,30 @@ let CombosDevelopmentToolSystem = class CombosDevelopmentToolSystem extends Syst
644
806
  }
645
807
  this.game.emit(COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, { enabled });
646
808
  }
809
+ postStateChanged() {
810
+ const state = {
811
+ paused: this.isPaused,
812
+ muted: this.isMuted,
813
+ };
814
+ const payload = {
815
+ type: COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED,
816
+ ...state,
817
+ };
818
+ if (typeof window !== 'undefined' && window.parent && window.parent !== window) {
819
+ window.parent.postMessage(payload, this.postMessageOrigin);
820
+ }
821
+ this.game.emit(COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, state);
822
+ }
823
+ getSoundSystem() {
824
+ const system = this.game.getSystem('SoundSystem');
825
+ if (!system ||
826
+ typeof system !== 'object' ||
827
+ !('muted' in system) ||
828
+ typeof system.muted !== 'boolean') {
829
+ return null;
830
+ }
831
+ return system;
832
+ }
647
833
  ensureOutline() {
648
834
  if (this.outlineGo)
649
835
  return;
@@ -805,8 +991,8 @@ var CombosDevelopmentToolSystem$1 = CombosDevelopmentToolSystem;
805
991
  /** Auto-generated by scripts/build-package.mjs — do not edit. */
806
992
  Object.assign(CombosDevelopmentToolSystem$1, {
807
993
  packageName: "@combos-fun/plugin-development-tool",
808
- packageVersion: "0.0.21",
994
+ packageVersion: "0.0.23",
809
995
  });
810
996
 
811
- export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, CombosDevelopmentToolSystem$1 as CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, applyPropertyValue, applyPropertyWithHooks, buildGameObjectSnapshot, readPropertyValue, readSourceAnchor };
997
+ export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_PAUSED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem$1 as CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, applyPropertyValue, applyPropertyWithHooks, buildGameObjectSnapshot, isAllowedMessageOrigin, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor };
812
998
  //# sourceMappingURL=plugin-development-tool.esm.js.map