@juuno-sdk/cli 1.0.8 → 1.0.11

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.
@@ -0,0 +1 @@
1
+ import{defineComponent as D,ref as y,computed as A,createElementBlock as m,openBlock as d,normalizeClass as k,createElementVNode as t,createCommentVNode as R,toDisplayString as x,createTextVNode as U,withDirectives as P,createBlock as $,resolveDynamicComponent as B,vShow as O,shallowRef as N,reactive as F,onMounted as I,unref as V,markRaw as M,createApp as J}from"vue";import{provideAppConfigContext as q}from"@juuno-sdk/app-sdk";(function(){const p=document.createElement("link").relList;if(p&&p.supports&&p.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))g(o);new MutationObserver(o=>{for(const n of o)if(n.type==="childList")for(const l of n.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&g(l)}).observe(document,{childList:!0,subtree:!0});function u(o){const n={};return o.integrity&&(n.integrity=o.integrity),o.referrerPolicy&&(n.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?n.credentials="include":o.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function g(o){if(o.ep)return;o.ep=!0;const n=u(o);fetch(o.href,n)}})();const W="modulepreload",z=function(r){return"/"+r},T={},j=function(p,u,g){let o=Promise.resolve();if(u&&u.length>0){let l=function(a){return Promise.all(a.map(w=>Promise.resolve(w).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),C=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));o=l(u.map(a=>{if(a=z(a),a in T)return;T[a]=!0;const w=a.endsWith(".css"),h=w?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${a}"]${h}`))return;const v=document.createElement("link");if(v.rel=w?"stylesheet":W,w||(v.as="script"),v.crossOrigin="",v.href=a,C&&v.setAttribute("nonce",C),document.head.appendChild(v),w)return new Promise((E,_)=>{v.addEventListener("load",E),v.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${a}`)))})}))}function n(l){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=l,window.dispatchEvent(s),!s.defaultPrevented)throw l}return o.then(l=>{for(const s of l||[])s.status==="rejected"&&n(s.reason);return p().catch(n)})},H={class:"header"},K={class:"header-left"},X={class:"app-info"},G={class:"app-name"},Q={key:0,class:"app-version"},Y={class:"header-right"},Z={key:0,class:"status-badge live"},ee={key:0,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},te={key:1,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},oe={key:0,class:"loading-state"},ne={class:"url-info"},le={key:1,class:"error-state"},se={class:"error-message"},re={class:"error-details"},ae={key:2,class:"main-content"},ie={class:"tabs"},ce={key:0,class:"tab-controls"},ue={class:"tab-panels"},de={class:"player-frame"},pe={key:1,class:"placeholder"},ve={class:"tab-panel config-panel"},fe={key:1,class:"placeholder"},me={class:"tab-panel meta-panel"},ge={class:"meta-header"},he={class:"meta-code"},ye={name:"SimulatorLayout"},we=D({...ye,props:{playerComponent:{},configComponent:{},scene:{},appUrl:{},isLoading:{type:Boolean},error:{},manifest:{},liveReloadActive:{type:Boolean}},emits:["reload"],setup(r,{emit:p}){const u=r,g=p,o=y("player"),n=y("landscape"),l=y(!1),s=y(!1);y(!0);const C=A(()=>{var f;return((f=u.manifest)==null?void 0:f.name)||"External App"}),a=A(()=>{var f;return((f=u.manifest)==null?void 0:f.version)||""}),w=A(()=>n.value==="landscape"?"aspect-landscape":"aspect-portrait"),h=A(()=>{var f;try{return JSON.stringify(((f=u.scene)==null?void 0:f.meta)||{},null,2)}catch{return"{}"}});function v(){l.value=!l.value}function E(){s.value=!s.value}function _(){g("reload")}return(f,e)=>(d(),m("div",{class:k(["simulator-layout",{"dark-mode":s.value}])},[t("header",H,[t("div",K,[e[7]||(e[7]=t("h1",null,"Juuno Simulator",-1)),t("div",X,[t("span",G,x(C.value),1),a.value?(d(),m("span",Q,"v"+x(a.value),1)):R("",!0)])]),t("div",Y,[r.liveReloadActive?(d(),m("div",Z,[...e[8]||(e[8]=[t("span",{class:"status-dot"},null,-1),U(" Live ",-1)])])):R("",!0),t("button",{class:"icon-btn",title:"Reload app",onClick:_},[...e[9]||(e[9]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"})],-1)])]),t("button",{class:"icon-btn",title:"Toggle dark mode",onClick:E},[s.value?(d(),m("svg",te,[...e[11]||(e[11]=[t("path",{d:"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"},null,-1)])])):(d(),m("svg",ee,[...e[10]||(e[10]=[t("circle",{cx:"12",cy:"12",r:"5"},null,-1),t("path",{d:"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"},null,-1)])]))])])]),r.isLoading?(d(),m("div",oe,[e[12]||(e[12]=t("div",{class:"spinner"},null,-1)),e[13]||(e[13]=t("p",null,"Loading app...",-1)),t("p",ne,x(r.appUrl),1)])):r.error?(d(),m("div",le,[e[15]||(e[15]=t("div",{class:"error-icon"},"!",-1)),e[16]||(e[16]=t("h2",null,"Failed to Load App",-1)),t("p",se,x(r.error),1),t("div",re,[t("p",null,[e[14]||(e[14]=t("strong",null,"App URL:",-1)),U(" "+x(r.appUrl),1)]),t("button",{class:"retry-btn",onClick:_},"Try Again")])])):(d(),m("div",ae,[t("div",ie,[t("button",{class:k(["tab",{active:o.value==="player"}]),onClick:e[0]||(e[0]=i=>o.value="player")}," Player ",2),t("button",{class:k(["tab",{active:o.value==="config"}]),onClick:e[1]||(e[1]=i=>o.value="config")}," Config ",2),t("button",{class:k(["tab",{active:o.value==="meta"}]),onClick:e[2]||(e[2]=i=>o.value="meta")}," Scene Meta ",2),o.value==="player"?(d(),m("div",ce,[t("button",{class:k(["control-btn",{active:n.value==="landscape"}]),title:"Landscape (16:9)",onClick:e[3]||(e[3]=i=>n.value="landscape")},[...e[17]||(e[17]=[t("svg",{width:"18",height:"12",viewBox:"0 0 18 12",fill:"currentColor"},[t("rect",{x:"0.5",y:"0.5",width:"17",height:"11",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),t("button",{class:k(["control-btn",{active:n.value==="portrait"}]),title:"Portrait (9:16)",onClick:e[4]||(e[4]=i=>n.value="portrait")},[...e[18]||(e[18]=[t("svg",{width:"12",height:"18",viewBox:"0 0 12 18",fill:"currentColor"},[t("rect",{x:"0.5",y:"0.5",width:"11",height:"17",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),t("button",{class:k(["control-btn",{active:l.value}]),title:"Fullscreen",onClick:v},[...e[19]||(e[19]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3"})],-1)])],2)])):R("",!0)]),t("div",ue,[P(t("div",{class:k(["tab-panel player-panel",{fullscreen:l.value}])},[t("div",{class:k(["player-container",w.value])},[t("div",de,[r.playerComponent?(d(),$(B(r.playerComponent),{key:r.scene.idx,scene:r.scene,playing:!0,"is-visible":!0,"duration-ms":5e3},null,8,["scene"])):(d(),m("div",pe,"No player component"))])],2),l.value?(d(),m("button",{key:0,class:"exit-fullscreen",onClick:e[5]||(e[5]=i=>l.value=!1)}," Exit Fullscreen ")):R("",!0)],2),[[O,o.value==="player"]]),P(t("div",ve,[r.configComponent?(d(),$(B(r.configComponent),{key:"config-"+r.scene.idx})):(d(),m("div",fe,"No config component"))],512),[[O,o.value==="config"]]),P(t("div",me,[t("div",ge,[e[20]||(e[20]=t("h3",null,"scene.meta",-1)),t("button",{class:"copy-btn",onClick:e[6]||(e[6]=i=>f.navigator.clipboard.writeText(h.value))}," Copy ")]),t("pre",he,x(h.value),1)],512),[[O,o.value==="meta"]])])]))],2))}}),ke=(r,p)=>{const u=r.__vccOpts||r;for(const[g,o]of p)u[g]=o;return u},Ce=ke(we,[["__scopeId","data-v-2641604d"]]),be={name:"JuunoCLI"},_e=D({...be,setup(r){const p=y(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),u=y(window.__JUUNO_LIVE_RELOAD__||!1),g=y(null),o=[],n=N(null),l=N(null),s=y(!0),C=y(null),a=F({idx:"simulator-scene-1",name:"Simulator Test Scene",app:null,app_idx:"simulator-app-1",created_at:new Date().toISOString(),display_time:5e3,language:"en",order:0,playlist_idx:"simulator-playlist-1",updated_at:new Date().toISOString(),translations:{},meta:{title:"Test App",backgroundColor:"#2c3e50",textColor:"#ecf0f1"},mediaAssets:[]}),h=q({async save(e){console.log("Simulator: Scene updated",e),Object.assign(a,e)}},{scene:a});async function v(){try{const e=await fetch(`${p.value}/manifest.json`);if(e.ok){const i=await e.json();return g.value=i,i}}catch{}return g.value=null,null}function E(e){o.forEach(i=>i.remove()),o.length=0,e.forEach(i=>{const c=document.createElement("link");c.rel="stylesheet",c.href=`${p.value}${i}`,document.head.appendChild(c),o.push(c)})}async function _(){var e,i;try{s.value=!0,C.value=null,console.log(`Loading external app from: ${p.value}`);const c=await v(),L=[];(e=c==null?void 0:c.resources)!=null&&e.player&&L.push(...c.resources.player.filter(b=>b.endsWith(".css"))),(i=c==null?void 0:c.resources)!=null&&i.config&&L.push(...c.resources.config.filter(b=>b.endsWith(".css"))),L.length>0&&(console.log("Loading CSS files:",L),E(L));const S=await j(()=>import("external-app/player"),[]);if(S.default)n.value=M(S.default);else if(typeof S.render=="function")n.value=M(S.render());else throw new Error("External app must export a default component or render function from /player");try{const b=await j(()=>import("external-app/config"),[]);b.default?l.value=M(b.default):typeof b.render=="function"?l.value=M(b.render()):l.value=null}catch{l.value=null}h.load(a),console.log("✅ External app loaded successfully"),s.value=!1}catch(c){console.error("Failed to load external app:",c),C.value=c instanceof Error?c.message:"Failed to load external app",s.value=!1}}function f(){console.log("🔄 Reloading app..."),window.location.reload()}return typeof window<"u"&&(window.addEventListener("juuno:live-reload-connected",()=>{u.value=!0}),window.addEventListener("juuno:live-reload-disconnected",()=>{u.value=!1})),I(_),(e,i)=>(d(),$(V(Ce),{"player-component":n.value,"config-component":l.value,scene:a,"app-url":p.value,"is-loading":s.value,error:C.value,manifest:g.value,"live-reload-active":u.value,onReload:f},null,8,["player-component","config-component","scene","app-url","is-loading","error","manifest","live-reload-active"]))}}),xe=J(_e);xe.mount("#app");
@@ -0,0 +1 @@
1
+ import{defineComponent as U,ref as h,computed as x,createElementBlock as f,openBlock as u,normalizeClass as C,createElementVNode as e,createCommentVNode as E,toDisplayString as b,createTextVNode as M,withDirectives as L,createBlock as S,resolveDynamicComponent as P,vShow as A,shallowRef as R,reactive as B,onMounted as N,unref as T,createApp as D}from"vue";import{provideAppConfigContext as j}from"@juuno-sdk/app-sdk";(function(){const p=document.createElement("link").relList;if(p&&p.supports&&p.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))m(o);new MutationObserver(o=>{for(const n of o)if(n.type==="childList")for(const l of n.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&m(l)}).observe(document,{childList:!0,subtree:!0});function c(o){const n={};return o.integrity&&(n.integrity=o.integrity),o.referrerPolicy&&(n.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?n.credentials="include":o.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function m(o){if(o.ep)return;o.ep=!0;const n=c(o);fetch(o.href,n)}})();const F="modulepreload",I=function(s){return"/"+s},O={},$=function(p,c,m){let o=Promise.resolve();if(c&&c.length>0){let l=function(d){return Promise.all(d.map(g=>Promise.resolve(g).then(y=>({status:"fulfilled",value:y}),y=>({status:"rejected",reason:y}))))};document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),w=(a==null?void 0:a.nonce)||(a==null?void 0:a.getAttribute("nonce"));o=l(c.map(d=>{if(d=I(d),d in O)return;O[d]=!0;const g=d.endsWith(".css"),y=g?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${d}"]${y}`))return;const v=document.createElement("link");if(v.rel=g?"stylesheet":F,g||(v.as="script"),v.crossOrigin="",v.href=d,w&&v.setAttribute("nonce",w),document.head.appendChild(v),g)return new Promise((_,r)=>{v.addEventListener("load",_),v.addEventListener("error",()=>r(new Error(`Unable to preload CSS for ${d}`)))})}))}function n(l){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=l,window.dispatchEvent(a),!a.defaultPrevented)throw l}return o.then(l=>{for(const a of l||[])a.status==="rejected"&&n(a.reason);return p().catch(n)})},V={class:"header"},J={class:"header-left"},q={class:"app-info"},z={class:"app-name"},H={key:0,class:"app-version"},K={class:"header-right"},W={key:0,class:"status-badge live"},X={key:0,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},G={key:1,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Q={key:0,class:"loading-state"},Y={class:"url-info"},Z={key:1,class:"error-state"},ee={class:"error-message"},te={class:"error-details"},oe={key:2,class:"main-content"},ne={class:"tabs"},le={key:0,class:"tab-controls"},ae={class:"tab-panels"},se={class:"player-frame"},re={key:1,class:"placeholder"},ie={class:"tab-panel config-panel"},ce={key:1,class:"placeholder"},ue={class:"tab-panel meta-panel"},de={class:"meta-header"},pe={class:"meta-code"},ve={name:"SimulatorLayout"},fe=U({...ve,props:{playerComponent:{},configComponent:{},scene:{},appUrl:{},isLoading:{type:Boolean},error:{},manifest:{},liveReloadActive:{type:Boolean}},emits:["reload"],setup(s,{emit:p}){const c=s,m=p,o=h("player"),n=h("landscape"),l=h(!1),a=h(!1);h(!0);const w=x(()=>{var i;return((i=c.manifest)==null?void 0:i.name)||"External App"}),d=x(()=>{var i;return((i=c.manifest)==null?void 0:i.version)||""}),g=x(()=>n.value==="landscape"?"aspect-landscape":"aspect-portrait"),y=x(()=>{var i;try{return JSON.stringify(((i=c.scene)==null?void 0:i.meta)||{},null,2)}catch{return"{}"}});function v(){l.value=!l.value}function _(){a.value=!a.value}function r(){m("reload")}return(i,t)=>(u(),f("div",{class:C(["simulator-layout",{"dark-mode":a.value}])},[e("header",V,[e("div",J,[t[7]||(t[7]=e("h1",null,"Juuno Simulator",-1)),e("div",q,[e("span",z,b(w.value),1),d.value?(u(),f("span",H,"v"+b(d.value),1)):E("",!0)])]),e("div",K,[s.liveReloadActive?(u(),f("div",W,[...t[8]||(t[8]=[e("span",{class:"status-dot"},null,-1),M(" Live ",-1)])])):E("",!0),e("button",{class:"icon-btn",title:"Reload app",onClick:r},[...t[9]||(t[9]=[e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[e("path",{d:"M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"})],-1)])]),e("button",{class:"icon-btn",title:"Toggle dark mode",onClick:_},[a.value?(u(),f("svg",G,[...t[11]||(t[11]=[e("path",{d:"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"},null,-1)])])):(u(),f("svg",X,[...t[10]||(t[10]=[e("circle",{cx:"12",cy:"12",r:"5"},null,-1),e("path",{d:"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"},null,-1)])]))])])]),s.isLoading?(u(),f("div",Q,[t[12]||(t[12]=e("div",{class:"spinner"},null,-1)),t[13]||(t[13]=e("p",null,"Loading app...",-1)),e("p",Y,b(s.appUrl),1)])):s.error?(u(),f("div",Z,[t[15]||(t[15]=e("div",{class:"error-icon"},"!",-1)),t[16]||(t[16]=e("h2",null,"Failed to Load App",-1)),e("p",ee,b(s.error),1),e("div",te,[e("p",null,[t[14]||(t[14]=e("strong",null,"App URL:",-1)),M(" "+b(s.appUrl),1)]),e("button",{class:"retry-btn",onClick:r},"Try Again")])])):(u(),f("div",oe,[e("div",ne,[e("button",{class:C(["tab",{active:o.value==="player"}]),onClick:t[0]||(t[0]=k=>o.value="player")}," Player ",2),e("button",{class:C(["tab",{active:o.value==="config"}]),onClick:t[1]||(t[1]=k=>o.value="config")}," Config ",2),e("button",{class:C(["tab",{active:o.value==="meta"}]),onClick:t[2]||(t[2]=k=>o.value="meta")}," Scene Meta ",2),o.value==="player"?(u(),f("div",le,[e("button",{class:C(["control-btn",{active:n.value==="landscape"}]),title:"Landscape (16:9)",onClick:t[3]||(t[3]=k=>n.value="landscape")},[...t[17]||(t[17]=[e("svg",{width:"18",height:"12",viewBox:"0 0 18 12",fill:"currentColor"},[e("rect",{x:"0.5",y:"0.5",width:"17",height:"11",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),e("button",{class:C(["control-btn",{active:n.value==="portrait"}]),title:"Portrait (9:16)",onClick:t[4]||(t[4]=k=>n.value="portrait")},[...t[18]||(t[18]=[e("svg",{width:"12",height:"18",viewBox:"0 0 12 18",fill:"currentColor"},[e("rect",{x:"0.5",y:"0.5",width:"11",height:"17",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),e("button",{class:C(["control-btn",{active:l.value}]),title:"Fullscreen",onClick:v},[...t[19]||(t[19]=[e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[e("path",{d:"M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3"})],-1)])],2)])):E("",!0)]),e("div",ae,[L(e("div",{class:C(["tab-panel player-panel",{fullscreen:l.value}])},[e("div",{class:C(["player-container",g.value])},[e("div",se,[s.playerComponent?(u(),S(P(s.playerComponent),{key:0,scene:s.scene,playing:!0,"is-visible":!0,"duration-ms":5e3},null,8,["scene"])):(u(),f("div",re,"No player component"))])],2),l.value?(u(),f("button",{key:0,class:"exit-fullscreen",onClick:t[5]||(t[5]=k=>l.value=!1)}," Exit Fullscreen ")):E("",!0)],2),[[A,o.value==="player"]]),L(e("div",ie,[s.configComponent?(u(),S(P(s.configComponent),{key:0})):(u(),f("div",ce,"No config component"))],512),[[A,o.value==="config"]]),L(e("div",ue,[e("div",de,[t[20]||(t[20]=e("h3",null,"scene.meta",-1)),e("button",{class:"copy-btn",onClick:t[6]||(t[6]=k=>i.navigator.clipboard.writeText(y.value))}," Copy ")]),e("pre",pe,b(y.value),1)],512),[[A,o.value==="meta"]])])]))],2))}}),me=(s,p)=>{const c=s.__vccOpts||s;for(const[m,o]of p)c[m]=o;return c},ge=me(fe,[["__scopeId","data-v-e190f6cf"]]),ye={name:"JuunoCLI"},he=U({...ye,setup(s){const p=h(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),c=h(window.__JUUNO_LIVE_RELOAD__||!1),m=h(null),o=R(null),n=R(null),l=h(!0),a=h(null),w=B({idx:"simulator-scene-1",name:"Simulator Test Scene",app:null,app_idx:"simulator-app-1",created_at:new Date().toISOString(),display_time:5e3,language:"en",order:0,playlist_idx:"simulator-playlist-1",updated_at:new Date().toISOString(),translations:{},meta:{title:"Test App",backgroundColor:"#2c3e50",textColor:"#ecf0f1"},mediaAssets:[]}),g=j({async save(r){console.log("Simulator: Scene updated",r),Object.assign(w,r)}},{scene:w});async function y(){try{const r=await fetch(`${p.value}/manifest.json`);r.ok&&(m.value=await r.json())}catch{m.value=null}}async function v(){try{l.value=!0,a.value=null,console.log(`Loading external app from: ${p.value}`),y();const r=await $(()=>import("external-app/player"),[]);if(r.default)o.value=r.default;else if(typeof r.render=="function")o.value=r.render();else throw new Error("External app must export a default component or render function from /player");try{const i=await $(()=>import("external-app/config"),[]);i.default?n.value=i.default:typeof i.render=="function"?n.value=i.render():n.value=null}catch(i){console.warn("Config component not found or failed to load:",i),n.value=null}g.load(w),console.log("✅ External app loaded successfully"),l.value=!1}catch(r){console.error("Failed to load external app:",r),a.value=r instanceof Error?r.message:"Failed to load external app",l.value=!1}}function _(){console.log("🔄 Reloading app..."),window.location.reload()}return typeof window<"u"&&(window.addEventListener("juuno:live-reload-connected",()=>{c.value=!0}),window.addEventListener("juuno:live-reload-disconnected",()=>{c.value=!1})),N(v),(r,i)=>(u(),S(T(ge),{"player-component":o.value,"config-component":n.value,scene:w,"app-url":p.value,"is-loading":l.value,error:a.value,manifest:m.value,"live-reload-active":c.value,onReload:_},null,8,["player-component","config-component","scene","app-url","is-loading","error","manifest","live-reload-active"]))}}),we=D(he);we.mount("#app");
@@ -0,0 +1 @@
1
+ import{defineComponent as V,ref as w,computed as S,onMounted as j,onUnmounted as D,createElementBlock as d,openBlock as r,normalizeClass as m,createElementVNode as e,createCommentVNode as A,toDisplayString as _,createTextVNode as O,withDirectives as $,createBlock as R,resolveDynamicComponent as U,vShow as B,shallowRef as T,reactive as I,unref as J,markRaw as P,createApp as q}from"vue";import{provideAppConfigContext as z}from"@juuno-sdk/app-sdk";(function(){const y=document.createElement("link").relList;if(y&&y.supports&&y.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))b(o);new MutationObserver(o=>{for(const l of o)if(l.type==="childList")for(const n of l.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&b(n)}).observe(document,{childList:!0,subtree:!0});function h(o){const l={};return o.integrity&&(l.integrity=o.integrity),o.referrerPolicy&&(l.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?l.credentials="include":o.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function b(o){if(o.ep)return;o.ep=!0;const l=h(o);fetch(o.href,l)}})();const H="modulepreload",W=function(a){return"/"+a},N={},F=function(y,h,b){let o=Promise.resolve();if(h&&h.length>0){let n=function(s){return Promise.all(s.map(p=>Promise.resolve(p).then(x=>({status:"fulfilled",value:x}),x=>({status:"rejected",reason:x}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),f=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));o=n(h.map(s=>{if(s=W(s),s in N)return;N[s]=!0;const p=s.endsWith(".css"),x=p?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${s}"]${x}`))return;const k=document.createElement("link");if(k.rel=p?"stylesheet":H,p||(k.as="script"),k.crossOrigin="",k.href=s,f&&k.setAttribute("nonce",f),document.head.appendChild(k),p)return new Promise((E,L)=>{k.addEventListener("load",E),k.addEventListener("error",()=>L(new Error(`Unable to preload CSS for ${s}`)))})}))}function l(n){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=n,window.dispatchEvent(i),!i.defaultPrevented)throw n}return o.then(n=>{for(const i of n||[])i.status==="rejected"&&l(i.reason);return y().catch(l)})},K={class:"header"},X={class:"header-left"},G={class:"app-info"},Q={class:"app-name"},Y={key:0,class:"app-version"},Z={class:"header-right"},ee={key:0,class:"status-badge live"},te={key:0,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},oe={key:1,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},ne={key:0,class:"loading-state"},le={class:"url-info"},se={key:1,class:"error-state"},ie={class:"error-message"},ae={class:"error-details"},re={key:2,class:"main-content"},ue={class:"tabs"},ce={key:0,class:"tab-controls"},de={class:"tab-panels"},ve={class:"player-frame"},pe={key:1,class:"placeholder"},fe={class:"player-controls"},me={class:"controls-group"},ye=["title"],he={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},ge={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},we=["title"],ke={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Ce={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},be=["title"],xe={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},_e={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Ee={class:"controls-info"},Le={class:"tab-panel config-panel"},Me={key:1,class:"placeholder"},Se={class:"tab-panel meta-panel"},Ae={class:"meta-code"},Pe={name:"SimulatorLayout"},$e=V({...Pe,props:{playerComponent:{},configComponent:{},scene:{},appUrl:{},isLoading:{type:Boolean},error:{},manifest:{},liveReloadActive:{type:Boolean}},emits:["reload"],setup(a,{emit:y}){const h=a,b=y,o=w("player"),l=w("landscape"),n=w(!1),i=w(!1),f=w(!0),s=w(!0),p=w(!1),x=w(3e4),k=S(()=>{var v;return((v=h.manifest)==null?void 0:v.name)||"External App"}),E=S(()=>{var v;return((v=h.manifest)==null?void 0:v.version)||""}),L=S(()=>l.value==="landscape"?"aspect-landscape":"aspect-portrait"),M=S(()=>{var v;try{return JSON.stringify(((v=h.scene)==null?void 0:v.meta)||{},null,2)}catch{return"{}"}});function g(){b("reload")}async function C(){try{await navigator.clipboard.writeText(M.value)}catch{console.warn("Failed to copy to clipboard")}}function u(v){v.key==="Escape"&&n.value&&(n.value=!1)}return j(()=>{window.addEventListener("keydown",u)}),D(()=>{window.removeEventListener("keydown",u)}),(v,t)=>(r(),d("div",{class:m(["simulator-layout",{"dark-mode":i.value}])},[e("header",K,[e("div",X,[t[11]||(t[11]=e("h1",null,"Juuno Simulator",-1)),e("div",G,[e("span",Q,_(k.value),1),E.value?(r(),d("span",Y,"v"+_(E.value),1)):A("",!0)])]),e("div",Z,[a.liveReloadActive?(r(),d("div",ee,[...t[12]||(t[12]=[e("span",{class:"status-dot"},null,-1),O(" Live ",-1)])])):A("",!0),e("button",{class:"icon-btn",title:"Reload app",onClick:g},[...t[13]||(t[13]=[e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[e("path",{d:"M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"})],-1)])]),e("button",{class:"icon-btn",title:"Toggle dark mode",onClick:t[0]||(t[0]=c=>i.value=!i.value)},[i.value?(r(),d("svg",oe,[...t[15]||(t[15]=[e("path",{d:"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"},null,-1)])])):(r(),d("svg",te,[...t[14]||(t[14]=[e("circle",{cx:"12",cy:"12",r:"5"},null,-1),e("path",{d:"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"},null,-1)])]))])])]),a.isLoading?(r(),d("div",ne,[t[16]||(t[16]=e("div",{class:"spinner"},null,-1)),t[17]||(t[17]=e("p",null,"Loading app...",-1)),e("p",le,_(a.appUrl),1)])):a.error?(r(),d("div",se,[t[19]||(t[19]=e("div",{class:"error-icon"},"!",-1)),t[20]||(t[20]=e("h2",null,"Failed to Load App",-1)),e("p",ie,_(a.error),1),e("div",ae,[e("p",null,[t[18]||(t[18]=e("strong",null,"App URL:",-1)),O(" "+_(a.appUrl),1)]),e("button",{class:"retry-btn",onClick:g},"Try Again")])])):(r(),d("div",re,[e("div",ue,[e("button",{class:m(["tab",{active:o.value==="player"}]),onClick:t[1]||(t[1]=c=>o.value="player")}," Player ",2),e("button",{class:m(["tab",{active:o.value==="config"}]),onClick:t[2]||(t[2]=c=>o.value="config")}," Config ",2),e("button",{class:m(["tab",{active:o.value==="meta"}]),onClick:t[3]||(t[3]=c=>o.value="meta")}," Scene Meta ",2),o.value==="player"?(r(),d("div",ce,[e("button",{class:m(["control-btn",{active:l.value==="landscape"}]),title:"Landscape (16:9)",onClick:t[4]||(t[4]=c=>l.value="landscape")},[...t[21]||(t[21]=[e("svg",{width:"18",height:"12",viewBox:"0 0 18 12",fill:"currentColor"},[e("rect",{x:"0.5",y:"0.5",width:"17",height:"11",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),e("button",{class:m(["control-btn",{active:l.value==="portrait"}]),title:"Portrait (9:16)",onClick:t[5]||(t[5]=c=>l.value="portrait")},[...t[22]||(t[22]=[e("svg",{width:"12",height:"18",viewBox:"0 0 12 18",fill:"currentColor"},[e("rect",{x:"0.5",y:"0.5",width:"11",height:"17",rx:"1",fill:"none",stroke:"currentColor"})],-1)])],2),e("button",{class:m(["control-btn",{active:n.value}]),title:"Fullscreen (Esc to exit)",onClick:t[6]||(t[6]=c=>n.value=!n.value)},[...t[23]||(t[23]=[e("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[e("path",{d:"M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3"})],-1)])],2)])):A("",!0)]),e("div",de,[$(e("div",{class:m(["tab-panel player-panel",{fullscreen:n.value}])},[e("div",{class:m(["player-container",L.value])},[e("div",ve,[a.playerComponent?(r(),R(U(a.playerComponent),{key:a.scene.idx,scene:a.scene,playing:f.value,"is-visible":s.value,"duration-ms":x.value,muted:p.value},null,8,["scene","playing","is-visible","duration-ms","muted"])):(r(),d("div",pe,"No player component"))])],2),e("div",fe,[e("div",me,[e("button",{class:m(["player-btn",{active:f.value}]),title:f.value?"Pause":"Play",onClick:t[7]||(t[7]=c=>f.value=!f.value)},[f.value?(r(),d("svg",he,[...t[24]||(t[24]=[e("rect",{x:"6",y:"4",width:"4",height:"16"},null,-1),e("rect",{x:"14",y:"4",width:"4",height:"16"},null,-1)])])):(r(),d("svg",ge,[...t[25]||(t[25]=[e("polygon",{points:"5,3 19,12 5,21"},null,-1)])]))],10,ye),e("button",{class:m(["player-btn",{active:!s.value}]),title:s.value?"Hide (simulate off-screen)":"Show",onClick:t[8]||(t[8]=c=>s.value=!s.value)},[s.value?(r(),d("svg",ke,[...t[26]||(t[26]=[e("path",{d:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"},null,-1),e("circle",{cx:"12",cy:"12",r:"3"},null,-1)])])):(r(),d("svg",Ce,[...t[27]||(t[27]=[e("path",{d:"M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24"},null,-1),e("line",{x1:"1",y1:"1",x2:"23",y2:"23"},null,-1)])]))],10,we),e("button",{class:m(["player-btn",{active:p.value}]),title:p.value?"Unmute":"Mute",onClick:t[9]||(t[9]=c=>p.value=!p.value)},[p.value?(r(),d("svg",_e,[...t[29]||(t[29]=[e("polygon",{points:"11,5 6,9 2,9 2,15 6,15 11,19"},null,-1),e("line",{x1:"23",y1:"9",x2:"17",y2:"15"},null,-1),e("line",{x1:"17",y1:"9",x2:"23",y2:"15"},null,-1)])])):(r(),d("svg",xe,[...t[28]||(t[28]=[e("polygon",{points:"11,5 6,9 2,9 2,15 6,15 11,19"},null,-1),e("path",{d:"M19.07 4.93a10 10 0 010 14.14M15.54 8.46a5 5 0 010 7.07"},null,-1)])]))],10,be)]),e("div",Ee,[e("span",{class:m(["state-badge",{inactive:!f.value}])},_(f.value?"Playing":"Paused"),3),e("span",{class:m(["state-badge",{inactive:!s.value}])},_(s.value?"Visible":"Hidden"),3),e("span",{class:m(["state-badge",{inactive:!p.value}])},_(p.value?"Muted":"Sound On"),3)])]),n.value?(r(),d("button",{key:0,class:"exit-fullscreen",onClick:t[10]||(t[10]=c=>n.value=!1)}," Exit Fullscreen ")):A("",!0)],2),[[B,o.value==="player"]]),$(e("div",Le,[a.configComponent?(r(),R(U(a.configComponent),{key:"config-"+a.scene.idx})):(r(),d("div",Me,"No config component"))],512),[[B,o.value==="config"]]),$(e("div",Se,[e("div",{class:"meta-header"},[t[30]||(t[30]=e("h3",null,"scene.meta",-1)),e("button",{class:"copy-btn",onClick:C},"Copy")]),e("pre",Ae,_(M.value),1)],512),[[B,o.value==="meta"]])])]))],2))}}),Be=(a,y)=>{const h=a.__vccOpts||a;for(const[b,o]of y)h[b]=o;return h},Re=Be($e,[["__scopeId","data-v-d3c29943"]]),Oe={name:"JuunoCLI"},Ue=V({...Oe,setup(a){const y=w(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),h=w(!1),b=w(null),o=[],l=T(null),n=T(null),i=w(!0),f=w(null),s=I({idx:"simulator-scene-1",name:"Simulator Test Scene",app:null,app_idx:"simulator-app-1",created_at:new Date().toISOString(),display_time:5e3,language:"en",order:0,playlist_idx:"simulator-playlist-1",updated_at:new Date().toISOString(),translations:{},meta:{title:"Test App",backgroundColor:"#2c3e50",textColor:"#ecf0f1"},mediaAssets:[]}),x=z({async save(g){console.log("Simulator: Scene updated",g),Object.assign(s,g)}},{scene:s});async function k(){try{const g=await fetch(`${y.value}/manifest.json`);if(g.ok){const C=await g.json();return b.value=C,C}}catch{}return b.value=null,null}function E(g){o.forEach(C=>C.remove()),o.length=0,g.forEach(C=>{const u=document.createElement("link");u.rel="stylesheet",u.href=`${y.value}${C}`,document.head.appendChild(u),o.push(u)})}async function L(){var g,C;try{i.value=!0,f.value=null,console.log(`Loading external app from: ${y.value}`);const u=await k(),v=[];(g=u==null?void 0:u.resources)!=null&&g.player&&v.push(...u.resources.player.filter(c=>c.endsWith(".css"))),(C=u==null?void 0:u.resources)!=null&&C.config&&v.push(...u.resources.config.filter(c=>c.endsWith(".css"))),v.length>0&&(console.log("Loading CSS files:",v),E(v));const t=await F(()=>import("external-app/player"),[]);if(t.default)l.value=P(t.default);else if(typeof t.render=="function")l.value=P(t.render());else throw new Error("External app must export a default component or render function from /player");try{const c=await F(()=>import("external-app/config"),[]);c.default?n.value=P(c.default):typeof c.render=="function"?n.value=P(c.render()):n.value=null}catch{n.value=null}x.load(s),console.log("✅ External app loaded successfully"),i.value=!1}catch(u){console.error("Failed to load external app:",u),f.value=u instanceof Error?u.message:"Failed to load external app",i.value=!1}}function M(){console.log("🔄 Reloading app..."),window.location.reload()}return j(L),(g,C)=>(r(),R(J(Re),{"player-component":l.value,"config-component":n.value,scene:s,"app-url":y.value,"is-loading":i.value,error:f.value,manifest:b.value,"live-reload-active":h.value,onReload:M},null,8,["player-component","config-component","scene","app-url","is-loading","error","manifest","live-reload-active"]))}}),Te=q(Ue);Te.mount("#app");
@@ -0,0 +1 @@
1
+ [data-v-d3c29943]{box-sizing:border-box}.simulator-layout[data-v-d3c29943]{min-height:100vh;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background-color:#f8f9fa;color:#1a1a2e;transition:background-color .2s,color .2s}.simulator-layout.dark-mode[data-v-d3c29943]{background-color:#1a1a2e;color:#e8e8e8}.header[data-v-d3c29943]{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1.5rem;background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff}.header-left[data-v-d3c29943]{display:flex;align-items:center;gap:1.5rem}.header h1[data-v-d3c29943]{margin:0;font-size:1.125rem;font-weight:600;letter-spacing:-.02em}.app-info[data-v-d3c29943]{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#ffffff26;border-radius:4px;font-size:.8125rem}.app-name[data-v-d3c29943]{font-weight:500}.app-version[data-v-d3c29943]{opacity:.8;font-size:.75rem}.header-right[data-v-d3c29943]{display:flex;align-items:center;gap:.75rem}.status-badge[data-v-d3c29943]{display:flex;align-items:center;gap:.375rem;padding:.25rem .625rem;border-radius:999px;font-size:.75rem;font-weight:500}.status-badge.live[data-v-d3c29943]{background:#22c55e33;color:#86efac}.status-dot[data-v-d3c29943]{width:6px;height:6px;border-radius:50%;background:#22c55e;animation:pulse-d3c29943 2s infinite}@keyframes pulse-d3c29943{0%,to{opacity:1}50%{opacity:.5}}.icon-btn[data-v-d3c29943]{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;border-radius:6px;background:#ffffff1a;color:#fff;cursor:pointer;transition:background .15s}.icon-btn[data-v-d3c29943]:hover{background:#fff3}.loading-state[data-v-d3c29943],.error-state[data-v-d3c29943]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center}.spinner[data-v-d3c29943]{width:40px;height:40px;border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;animation:spin-d3c29943 .8s linear infinite}.dark-mode .spinner[data-v-d3c29943]{border-color:#374151;border-top-color:#8b5cf6}@keyframes spin-d3c29943{to{transform:rotate(360deg)}}.url-info[data-v-d3c29943]{margin-top:.75rem;font-size:.8125rem;color:#6b7280;font-family:SF Mono,Monaco,monospace}.error-icon[data-v-d3c29943]{width:48px;height:48px;display:flex;align-items:center;justify-content:center;background:#fef2f2;color:#dc2626;border-radius:50%;font-size:1.5rem;font-weight:700;margin-bottom:1rem}.dark-mode .error-icon[data-v-d3c29943]{background:#450a0a}.error-message[data-v-d3c29943]{color:#dc2626;font-weight:500;margin:.5rem 0}.error-details[data-v-d3c29943]{margin-top:1.5rem;padding:1rem;background:#fff;border-radius:8px;font-size:.875rem}.dark-mode .error-details[data-v-d3c29943]{background:#1f2937}.retry-btn[data-v-d3c29943]{margin-top:1rem;padding:.5rem 1rem;background:#6366f1;color:#fff;border:none;border-radius:6px;font-weight:500;cursor:pointer;transition:background .15s}.retry-btn[data-v-d3c29943]:hover{background:#4f46e5}.main-content[data-v-d3c29943]{flex:1;display:flex;flex-direction:column;padding:1rem;gap:1rem}.tabs[data-v-d3c29943]{display:flex;align-items:center;gap:.25rem;padding:.25rem;background:#fff;border-radius:8px;box-shadow:0 1px 2px #0000000d}.dark-mode .tabs[data-v-d3c29943]{background:#1f2937}.tab[data-v-d3c29943]{padding:.5rem 1rem;border:none;border-radius:6px;background:transparent;color:#6b7280;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .15s}.tab[data-v-d3c29943]:hover{color:#1f2937}.dark-mode .tab[data-v-d3c29943]:hover{color:#e5e7eb}.tab.active[data-v-d3c29943]{background:#6366f1;color:#fff}.tab-controls[data-v-d3c29943]{margin-left:auto;display:flex;gap:.25rem}.control-btn[data-v-d3c29943]{display:flex;align-items:center;justify-content:center;width:36px;height:32px;border:none;border-radius:6px;background:transparent;color:#9ca3af;cursor:pointer;transition:all .15s}.control-btn[data-v-d3c29943]:hover{background:#f3f4f6;color:#4b5563}.dark-mode .control-btn[data-v-d3c29943]:hover{background:#374151;color:#d1d5db}.control-btn.active[data-v-d3c29943]{background:#ede9fe;color:#6366f1}.dark-mode .control-btn.active[data-v-d3c29943]{background:#4c1d95;color:#c4b5fd}.tab-panels[data-v-d3c29943]{flex:1;display:flex;flex-direction:column}.tab-panel[data-v-d3c29943]{flex:1;background:#fff;border-radius:8px;box-shadow:0 1px 3px #0000000d;overflow:auto}.dark-mode .tab-panel[data-v-d3c29943]{background:#1f2937}.player-panel[data-v-d3c29943]{display:flex;flex-direction:column;background:#111827}.player-panel.fullscreen[data-v-d3c29943]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;border-radius:0;padding:0}.player-container[data-v-d3c29943]{flex:1;display:flex;transition:all .3s ease}.player-container.aspect-landscape[data-v-d3c29943]{aspect-ratio:16 / 9}.player-container.aspect-portrait[data-v-d3c29943]{aspect-ratio:9 / 16}.fullscreen .player-container[data-v-d3c29943]{width:100%;height:100%}.fullscreen .player-container.aspect-portrait[data-v-d3c29943]{width:auto;height:100%}.player-frame[data-v-d3c29943]{flex:1;position:relative;background:#000;overflow:hidden}.player-frame[data-v-d3c29943]>*{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.exit-fullscreen[data-v-d3c29943]{position:fixed;top:1rem;right:1rem;padding:.5rem 1rem;background:#000000bf;color:#fff;border:none;border-radius:6px;font-size:.875rem;cursor:pointer;z-index:1001}.exit-fullscreen[data-v-d3c29943]:hover{background:#000000e6}.player-controls[data-v-d3c29943]{display:flex;align-items:center;justify-content:center;gap:1.5rem;padding:.75rem 1rem;background:#ffffff1a}.fullscreen .player-controls[data-v-d3c29943]{position:fixed;bottom:1rem;left:50%;transform:translate(-50%);background:#000000bf;z-index:1001}.controls-group[data-v-d3c29943]{display:flex;gap:.5rem}.player-btn[data-v-d3c29943]{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border:none;border-radius:8px;background:#ffffff1a;color:#e5e7eb;cursor:pointer;transition:all .15s}.player-btn[data-v-d3c29943]:hover{background:#fff3;color:#fff}.player-btn.active[data-v-d3c29943]{background:#6366f1;color:#fff}.controls-info[data-v-d3c29943]{display:flex;gap:.5rem}.state-badge[data-v-d3c29943]{padding:.25rem .5rem;border-radius:4px;font-size:.75rem;font-weight:500;background:#22c55e33;color:#86efac}.state-badge.inactive[data-v-d3c29943]{background:#ef444433;color:#fca5a5}.config-panel[data-v-d3c29943]{padding:1.5rem;overflow-y:auto}.meta-panel[data-v-d3c29943]{padding:1rem}.meta-header[data-v-d3c29943]{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.meta-header h3[data-v-d3c29943]{margin:0;font-size:.875rem;font-weight:600;color:#6b7280}.copy-btn[data-v-d3c29943]{padding:.25rem .5rem;background:#f3f4f6;border:none;border-radius:4px;font-size:.75rem;color:#4b5563;cursor:pointer}.dark-mode .copy-btn[data-v-d3c29943]{background:#374151;color:#d1d5db}.copy-btn[data-v-d3c29943]:hover{background:#e5e7eb}.dark-mode .copy-btn[data-v-d3c29943]:hover{background:#4b5563}.meta-code[data-v-d3c29943]{margin:0;padding:1rem;background:#f9fafb;border-radius:6px;font-family:SF Mono,Monaco,Courier New,monospace;font-size:.8125rem;line-height:1.6;overflow-x:auto;white-space:pre-wrap;word-break:break-word}.dark-mode .meta-code[data-v-d3c29943]{background:#111827;color:#e5e7eb}.placeholder[data-v-d3c29943]{display:flex;align-items:center;justify-content:center;height:100%;min-height:200px;color:#9ca3af;font-style:italic}@media (max-width: 640px){.header[data-v-d3c29943]{padding:.5rem 1rem}.header h1[data-v-d3c29943]{font-size:1rem}.app-info[data-v-d3c29943]{display:none}.tabs[data-v-d3c29943]{flex-wrap:wrap}.tab-controls[data-v-d3c29943]{width:100%;justify-content:center;margin-top:.5rem;margin-left:0}}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/deploy/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAa7E;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6ED;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyFrE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/deploy/index.ts"],"names":[],"mappings":"AASA,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAoE7E;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuGD;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA+GrE"}
@@ -1,21 +1,78 @@
1
- import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
2
- import { join } from 'path';
1
+ import { readFileSync, existsSync, readdirSync, statSync, realpathSync, } from 'fs';
2
+ import { join, relative } from 'path';
3
3
  import JSZip from 'jszip';
4
4
  import { getAuthToken, getApiUrl } from '../auth/index.js';
5
+ /**
6
+ * Fetches the currently deployed version of an app.
7
+ * Returns null if the app doesn't exist yet.
8
+ */
9
+ async function fetchDeployedVersion(token, appId) {
10
+ const apiUrl = getApiUrl();
11
+ const infoUrl = `${apiUrl}/api/v1/developer/apps/${appId}`;
12
+ try {
13
+ const response = await fetch(infoUrl, {
14
+ method: 'GET',
15
+ headers: {
16
+ Authorization: `Bearer ${token}`,
17
+ Accept: 'application/json',
18
+ },
19
+ });
20
+ // App doesn't exist yet - that's fine for first deploy.
21
+ if (response.status === 404) {
22
+ return null;
23
+ }
24
+ if (!response.ok) {
25
+ // Non-404 errors - log but don't block deploy.
26
+ return null;
27
+ }
28
+ const data = (await response.json());
29
+ if (!data.success || !data.data) {
30
+ return null;
31
+ }
32
+ return data.data.version ?? null;
33
+ }
34
+ catch {
35
+ // Network errors - don't block deploy.
36
+ return null;
37
+ }
38
+ }
5
39
  /**
6
40
  * Creates a zip bundle from the build directory.
41
+ * Recursively includes all files while protecting against path traversal.
7
42
  */
8
43
  async function createZipBundle(buildDir) {
9
44
  const zip = new JSZip();
10
- const files = readdirSync(buildDir);
11
- for (const file of files) {
12
- const filePath = join(buildDir, file);
13
- const stats = statSync(filePath);
14
- if (stats.isFile()) {
15
- const content = readFileSync(filePath);
16
- zip.file(file, content);
45
+ const resolvedBuildDir = realpathSync(buildDir);
46
+ function addFilesRecursively(dir, zipPath) {
47
+ const entries = readdirSync(dir, { withFileTypes: true });
48
+ for (const entry of entries) {
49
+ const fullPath = join(dir, entry.name);
50
+ const entryZipPath = zipPath ? `${zipPath}/${entry.name}` : entry.name;
51
+ // Resolve symlinks and verify the file is within buildDir.
52
+ let resolvedPath;
53
+ try {
54
+ resolvedPath = realpathSync(fullPath);
55
+ }
56
+ catch {
57
+ // Skip files that can't be resolved (broken symlinks, etc.).
58
+ console.warn(` ⚠ Skipping unresolvable path: ${entry.name}`);
59
+ continue;
60
+ }
61
+ // Protect against path traversal attacks.
62
+ if (!resolvedPath.startsWith(resolvedBuildDir)) {
63
+ console.warn(` ⚠ Skipping path outside build directory: ${entry.name}`);
64
+ continue;
65
+ }
66
+ if (entry.isDirectory()) {
67
+ addFilesRecursively(fullPath, entryZipPath);
68
+ }
69
+ else if (entry.isFile() || entry.isSymbolicLink()) {
70
+ const content = readFileSync(resolvedPath);
71
+ zip.file(entryZipPath, content);
72
+ }
17
73
  }
18
74
  }
75
+ addFilesRecursively(resolvedBuildDir, '');
19
76
  return await zip.generateAsync({ type: 'nodebuffer' });
20
77
  }
21
78
  /**
@@ -91,6 +148,24 @@ export async function deployApp(options) {
91
148
  console.error(`❌ ${error instanceof Error ? error.message : String(error)}`);
92
149
  process.exit(1);
93
150
  }
151
+ // Check if this version is already deployed.
152
+ console.log('🔍 Checking deployed version...');
153
+ const deployedVersion = await fetchDeployedVersion(token, manifest.id);
154
+ if (deployedVersion !== null && deployedVersion === manifest.version) {
155
+ console.error('');
156
+ console.error(`❌ Version ${manifest.version} is already deployed for '${manifest.name}'`);
157
+ console.error('');
158
+ console.error(' Versions are immutable. Bump the version in your manifest and try again.');
159
+ console.error('');
160
+ process.exit(1);
161
+ }
162
+ if (deployedVersion !== null) {
163
+ console.log(` Current: ${deployedVersion} → New: ${manifest.version}`);
164
+ }
165
+ else {
166
+ console.log(' First deploy for this app');
167
+ }
168
+ console.log('');
94
169
  console.log('📁 Creating bundle...');
95
170
  let zipBuffer;
96
171
  try {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Unified dev command that orchestrates the complete development workflow.
3
+ *
4
+ * When run without --app, this command:
5
+ * 1. Auto-detects the app in the current directory
6
+ * 2. Starts vite dev server (with HMR via @juuno/vite-plugin-app-dev)
7
+ * 3. Starts the simulator pointing to the dev server
8
+ */
9
+ /**
10
+ * Options for the unified dev command.
11
+ */
12
+ export interface DevOptions {
13
+ /** Port for the vite dev server (default: 3000). */
14
+ servePort?: string;
15
+ /** Port for the simulator (default: 5004). */
16
+ simulatorPort?: string;
17
+ /** Path to the simulator dist directory. */
18
+ simulatorDistPath: string;
19
+ }
20
+ /**
21
+ * Run the unified dev command.
22
+ */
23
+ export declare function runDev(options: DevOptions): Promise<void>;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/dev/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA2CH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAgDD;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA2I/D"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Unified dev command that orchestrates the complete development workflow.
3
+ *
4
+ * When run without --app, this command:
5
+ * 1. Auto-detects the app in the current directory
6
+ * 2. Starts vite dev server (with HMR via @juuno/vite-plugin-app-dev)
7
+ * 3. Starts the simulator pointing to the dev server
8
+ */
9
+ import { spawn } from 'child_process';
10
+ import { existsSync, readFileSync } from 'fs';
11
+ import { createConnection } from 'net';
12
+ import { join } from 'path';
13
+ import { launchSimulatorWithOptions } from '../simulator/index.js';
14
+ import { findFileUpward } from '../utils/fs.js';
15
+ /**
16
+ * Check if a port is already in use.
17
+ */
18
+ async function isPortInUse(port) {
19
+ return new Promise((resolve) => {
20
+ const socket = createConnection({ port, host: 'localhost' });
21
+ socket.once('connect', () => {
22
+ socket.destroy();
23
+ resolve(true);
24
+ });
25
+ socket.once('error', () => {
26
+ socket.destroy();
27
+ resolve(false);
28
+ });
29
+ });
30
+ }
31
+ /**
32
+ * Wait for a port to become available (server started).
33
+ */
34
+ async function waitForPort(port, timeoutMs = 30000) {
35
+ const startTime = Date.now();
36
+ while (Date.now() - startTime < timeoutMs) {
37
+ if (await isPortInUse(port)) {
38
+ return true;
39
+ }
40
+ await new Promise((resolve) => setTimeout(resolve, 200));
41
+ }
42
+ return false;
43
+ }
44
+ /**
45
+ * Detect if the current directory contains a Juuno external app.
46
+ */
47
+ function detectApp(cwd) {
48
+ const hasViteConfig = existsSync(join(cwd, 'vite.config.ts')) ||
49
+ existsSync(join(cwd, 'vite.config.js'));
50
+ const hasPackageJson = existsSync(join(cwd, 'package.json'));
51
+ let hasDevScript = false;
52
+ let packageManager = 'npm';
53
+ if (hasPackageJson) {
54
+ try {
55
+ const packageJson = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'));
56
+ hasDevScript = !!packageJson.scripts?.dev;
57
+ }
58
+ catch {
59
+ // Ignore parse errors.
60
+ }
61
+ }
62
+ // Detect package manager from lockfiles (check current dir and parent dirs for monorepo support).
63
+ if (findFileUpward(cwd, 'pnpm-lock.yaml')) {
64
+ packageManager = 'pnpm';
65
+ }
66
+ else if (findFileUpward(cwd, 'yarn.lock')) {
67
+ packageManager = 'yarn';
68
+ }
69
+ else if (findFileUpward(cwd, 'package-lock.json')) {
70
+ packageManager = 'npm';
71
+ }
72
+ return {
73
+ hasViteConfig,
74
+ hasPackageJson,
75
+ hasDevScript,
76
+ packageManager,
77
+ };
78
+ }
79
+ /**
80
+ * Run the unified dev command.
81
+ */
82
+ export async function runDev(options) {
83
+ const cwd = process.cwd();
84
+ const servePort = options.servePort || '3000';
85
+ const simulatorPort = options.simulatorPort || '5004';
86
+ console.log('\n🔍 Detecting app structure...\n');
87
+ const detection = detectApp(cwd);
88
+ if (!detection.hasPackageJson) {
89
+ console.error('Error: No package.json found in current directory.');
90
+ console.error('Please run this command from your app directory.');
91
+ process.exit(1);
92
+ }
93
+ if (!detection.hasViteConfig) {
94
+ console.error('Error: No vite.config.ts or vite.config.js found.');
95
+ console.error('Please ensure your app has a Vite config with @juuno/vite-plugin-app-dev.');
96
+ process.exit(1);
97
+ }
98
+ const pm = detection.packageManager;
99
+ console.log(` Package manager: ${pm}`);
100
+ console.log(` Vite config: found`);
101
+ console.log(` Dev server: http://localhost:${servePort}`);
102
+ console.log('');
103
+ // Track child processes for cleanup.
104
+ const processes = [];
105
+ // Cleanup function.
106
+ function cleanup() {
107
+ console.log('\n\n👋 Stopping all processes...');
108
+ processes.forEach((p) => {
109
+ try {
110
+ p.kill('SIGTERM');
111
+ }
112
+ catch {
113
+ // Ignore errors.
114
+ }
115
+ });
116
+ }
117
+ // Handle termination.
118
+ process.on('SIGINT', () => {
119
+ cleanup();
120
+ process.exit(0);
121
+ });
122
+ process.on('SIGTERM', () => {
123
+ cleanup();
124
+ process.exit(0);
125
+ });
126
+ // Check if port is already in use before starting.
127
+ if (await isPortInUse(parseInt(servePort))) {
128
+ console.error(`Error: Port ${servePort} is already in use.`);
129
+ console.error('Try a different port with --serve-port.\n');
130
+ process.exit(1);
131
+ }
132
+ // Step 1: Start Vite dev server.
133
+ console.log('🚀 Starting Vite dev server...\n');
134
+ // Build the command and args based on package manager.
135
+ let devCommand;
136
+ let devArgs;
137
+ if (pm === 'pnpm') {
138
+ devCommand = 'pnpm';
139
+ devArgs = ['exec', 'vite', '--port', servePort, '--strictPort'];
140
+ }
141
+ else if (pm === 'yarn') {
142
+ devCommand = 'yarn';
143
+ devArgs = ['vite', '--port', servePort, '--strictPort'];
144
+ }
145
+ else {
146
+ devCommand = 'npx';
147
+ devArgs = ['vite', '--port', servePort, '--strictPort'];
148
+ }
149
+ const devProcess = spawn(devCommand, devArgs, {
150
+ cwd,
151
+ stdio: ['ignore', 'pipe', 'pipe'],
152
+ shell: false,
153
+ });
154
+ processes.push(devProcess);
155
+ // Forward dev server output with prefix.
156
+ devProcess.stdout?.on('data', (data) => {
157
+ const lines = data.toString().trim().split('\n');
158
+ lines.forEach((line) => {
159
+ if (line.trim()) {
160
+ console.log(`[vite] ${line}`);
161
+ }
162
+ });
163
+ });
164
+ devProcess.stderr?.on('data', (data) => {
165
+ const lines = data.toString().trim().split('\n');
166
+ lines.forEach((line) => {
167
+ if (line.trim()) {
168
+ console.log(`[vite] ${line}`);
169
+ }
170
+ });
171
+ });
172
+ devProcess.on('error', (err) => {
173
+ console.error(`[vite] Error: ${err.message}`);
174
+ });
175
+ // Wait for Vite dev server to be ready.
176
+ console.log('⏳ Waiting for Vite dev server...\n');
177
+ const serverReady = await waitForPort(parseInt(servePort));
178
+ if (!serverReady) {
179
+ console.error('Error: Vite dev server did not start within 30 seconds.');
180
+ console.error('Please check your vite config for errors.');
181
+ cleanup();
182
+ process.exit(1);
183
+ }
184
+ // Give Vite a moment to fully initialize after port is open.
185
+ await new Promise((resolve) => setTimeout(resolve, 500));
186
+ const appUrl = `http://localhost:${servePort}`;
187
+ console.log(`✅ Vite dev server ready at ${appUrl}\n`);
188
+ // Step 2: Launch the simulator.
189
+ console.log('🎮 Starting simulator...\n');
190
+ console.log(`🌍 Simulator: http://localhost:${simulatorPort}\n`);
191
+ // Launch the simulator (this is a blocking call that handles its own lifecycle).
192
+ await launchSimulatorWithOptions({
193
+ appUrl,
194
+ port: simulatorPort,
195
+ distPath: options.simulatorDistPath,
196
+ });
197
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Monorepo detection and Vite dev server launcher for internal app development.
3
+ *
4
+ * When running in the Juuno monorepo, this uses Vite's dev server instead of
5
+ * the built simulator, allowing local @juuno/* packages to be resolved from
6
+ * the workspace.
7
+ */
8
+ import { type ChildProcess } from 'child_process';
9
+ /**
10
+ * Detect if we're running in the Juuno monorepo.
11
+ */
12
+ export declare function detectMonorepo(cwd: string): {
13
+ isMonorepo: boolean;
14
+ workspaceRoot: string | null;
15
+ cliPath: string | null;
16
+ };
17
+ /**
18
+ * Options for launching the simulator in monorepo mode.
19
+ */
20
+ export interface MonorepoSimulatorOptions {
21
+ /** URL of the external app to load. */
22
+ appUrl: string;
23
+ /** Port for the simulator service (default: 5004). */
24
+ port: string;
25
+ /** Path to the monorepo workspace root. */
26
+ workspaceRoot: string;
27
+ /** Directory to watch for live reload. */
28
+ watchDir?: string;
29
+ }
30
+ /**
31
+ * Launch the simulator service (monorepo mode).
32
+ *
33
+ * This uses the internal simulator service which has full monorepo support
34
+ * for @juuno/* packages, GraphQL, Sass, etc.
35
+ */
36
+ export declare function launchMonorepoSimulator(options: MonorepoSimulatorOptions): Promise<ChildProcess>;
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/monorepo/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAKzD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAyBA;AA2BD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,YAAY,CAAC,CAgDvB"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Monorepo detection and Vite dev server launcher for internal app development.
3
+ *
4
+ * When running in the Juuno monorepo, this uses Vite's dev server instead of
5
+ * the built simulator, allowing local @juuno/* packages to be resolved from
6
+ * the workspace.
7
+ */
8
+ import { spawn } from 'child_process';
9
+ import { existsSync } from 'fs';
10
+ import { join, resolve } from 'path';
11
+ import { findFileUpward } from '../utils/fs.js';
12
+ /**
13
+ * Detect if we're running in the Juuno monorepo.
14
+ */
15
+ export function detectMonorepo(cwd) {
16
+ // Look for pnpm-workspace.yaml which indicates monorepo root.
17
+ const workspaceFile = findFileUpward(cwd, 'pnpm-workspace.yaml');
18
+ if (!workspaceFile) {
19
+ return { isMonorepo: false, workspaceRoot: null, cliPath: null };
20
+ }
21
+ const workspaceRoot = resolve(workspaceFile, '..');
22
+ // Check if the CLI package exists in the expected location.
23
+ const cliPath = join(workspaceRoot, 'external', 'cli');
24
+ if (!existsSync(join(cliPath, 'package.json'))) {
25
+ return { isMonorepo: false, workspaceRoot: null, cliPath: null };
26
+ }
27
+ // Check for key monorepo packages to confirm it's the Juuno monorepo.
28
+ const hasCore = existsSync(join(workspaceRoot, 'packages', 'core'));
29
+ const hasCommon = existsSync(join(workspaceRoot, 'packages', 'common'));
30
+ if (!hasCore || !hasCommon) {
31
+ return { isMonorepo: false, workspaceRoot: null, cliPath: null };
32
+ }
33
+ return { isMonorepo: true, workspaceRoot, cliPath };
34
+ }
35
+ /**
36
+ * Fetch the app manifest to get actual resource paths.
37
+ */
38
+ async function fetchManifest(appUrl) {
39
+ try {
40
+ const response = await fetch(`${appUrl}/manifest.json`);
41
+ if (!response.ok) {
42
+ return null;
43
+ }
44
+ return await response.json();
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ /**
51
+ * Launch the simulator service (monorepo mode).
52
+ *
53
+ * This uses the internal simulator service which has full monorepo support
54
+ * for @juuno/* packages, GraphQL, Sass, etc.
55
+ */
56
+ export async function launchMonorepoSimulator(options) {
57
+ const { appUrl, port, workspaceRoot, watchDir } = options;
58
+ // Fetch manifest to get actual resource paths.
59
+ console.log('📦 Fetching app manifest...');
60
+ const manifest = await fetchManifest(appUrl);
61
+ const playerPath = manifest?.resources?.player?.[0] || '/player/index.js';
62
+ const configPath = manifest?.resources?.config?.[0] || '/config/index.js';
63
+ console.log(`
64
+ 🚀 Juuno App Simulator (Monorepo Mode)
65
+
66
+ External App: ${appUrl}
67
+ Player: ${appUrl}${playerPath}
68
+ Config: ${appUrl}${configPath}
69
+ Simulator: http://localhost:${port}
70
+ Watching: ${watchDir || 'disabled'}
71
+ Mode: Internal simulator service (full @juuno/* support)
72
+
73
+ Press Ctrl+C to stop
74
+ `);
75
+ // Set environment variables for the simulator service.
76
+ const env = {
77
+ ...process.env,
78
+ VITE_EXTERNAL_APP_URL: appUrl,
79
+ VITE_EXTERNAL_PLAYER_PATH: playerPath,
80
+ VITE_EXTERNAL_CONFIG_PATH: configPath,
81
+ VITE_WATCH_DIR: watchDir || '',
82
+ };
83
+ // Run the simulator service from the workspace root.
84
+ const simulatorProcess = spawn('pnpm', ['--filter', '@juuno/simulator', 'run', 'dev', '--port', port], {
85
+ cwd: workspaceRoot,
86
+ stdio: 'inherit',
87
+ shell: false,
88
+ env,
89
+ });
90
+ simulatorProcess.on('error', (err) => {
91
+ console.error(`[simulator] Error: ${err.message}`);
92
+ });
93
+ return simulatorProcess;
94
+ }
@@ -1,13 +1,24 @@
1
1
  /**
2
- * Shared simulator logic for launching the app simulator.
2
+ * Simulator logic for launching the app simulator.
3
3
  * Used by both cli.js (production) and cli-dev.js (development).
4
4
  */
5
5
  /**
6
- * Launch the app simulator.
7
- *
8
- * @param appUrl - URL of the external app to load
9
- * @param port - Port to run the simulator on
10
- * @param distPath - Path to the built dist directory
6
+ * Options for launching the simulator.
11
7
  */
12
- export declare function launchSimulator(appUrl: string, port: string, distPath: string): void;
8
+ export interface SimulatorOptions {
9
+ /** URL of the external app to load. */
10
+ appUrl: string;
11
+ /** Port to run the simulator on. */
12
+ port: string;
13
+ /** Path to the built simulator dist directory. */
14
+ distPath: string;
15
+ }
16
+ /**
17
+ * Launch the app simulator with optional live reload.
18
+ */
19
+ export declare function launchSimulator(appUrl: string, port: string, distPath: string): Promise<void>;
20
+ /**
21
+ * Launch the app simulator with full options object.
22
+ */
23
+ export declare function launchSimulatorWithOptions(options: SimulatorOptions): Promise<void>;
13
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/simulator/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoCH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,QA8JjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/simulator/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmCH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,iBAGjB;AA2BD;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,iBAyKzE"}