@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.
package/README.md CHANGED
@@ -19,16 +19,45 @@ npm install --save-dev @juuno-sdk/cli
19
19
 
20
20
  ## Commands
21
21
 
22
- ### Testing: App Simulator
22
+ ### Development: Unified Dev Command (Recommended)
23
23
 
24
- Test your app locally before deployment.
24
+ The easiest way to develop your app - just run one command from your app directory:
25
+
26
+ ```bash
27
+ cd my-juuno-app
28
+ npx juuno-cli dev
29
+ ```
30
+
31
+ This automatically:
32
+
33
+ 1. Detects your app structure (Vite config, package.json)
34
+ 2. Starts the Vite dev server with HMR (Hot Module Replacement)
35
+ 3. Launches the simulator pointing to the dev server
36
+
37
+ **Output:**
38
+
39
+ ```text
40
+ 🔍 Detecting app structure...
41
+
42
+ Package manager: pnpm
43
+ Vite config: found
44
+ Dev server: http://localhost:3000
45
+
46
+ 🚀 Starting Vite dev server...
47
+ ⏳ Waiting for Vite dev server...
48
+ ✅ Vite dev server ready at http://localhost:3000
49
+
50
+ 🎮 Starting simulator...
51
+ 🌍 Simulator: http://localhost:5004
52
+ ```
53
+
54
+ ### Manual Mode: Point to External URL
55
+
56
+ If you prefer to manage the build and server yourself:
25
57
 
26
58
  ```bash
27
59
  # Point to your dev server
28
60
  npx juuno-cli dev --app http://localhost:3000
29
-
30
- # Or point to a deployed URL
31
- npx juuno-cli dev --app https://cdn.example.com/my-app/
32
61
  ```
33
62
 
34
63
  ### Authentication
@@ -83,9 +112,8 @@ Add to your `package.json`:
83
112
  ```json
84
113
  {
85
114
  "scripts": {
86
- "dev": "vite",
115
+ "dev": "juuno-cli dev",
87
116
  "build": "vite build",
88
- "test:player": "juuno-cli dev --app http://localhost:3000",
89
117
  "deploy": "juuno-cli deploy"
90
118
  }
91
119
  }
@@ -97,12 +125,9 @@ Then run:
97
125
  # One-time setup: Login to Juuno
98
126
  npx juuno-cli login
99
127
 
100
- # Terminal 1: Your app dev server
128
+ # Start development (one command does everything!)
101
129
  npm run dev
102
130
 
103
- # Terminal 2: Simulator
104
- npm run test:player
105
-
106
131
  # Deploy to production
107
132
  npm run build
108
133
  npm run deploy
@@ -110,10 +135,10 @@ npm run deploy
110
135
 
111
136
  ## How It Works
112
137
 
113
- 1. **Your App**: Runs on your dev server (e.g., `localhost:3000`)
138
+ 1. **Your App**: Served by Vite dev server (e.g., `localhost:3000`) with HMR
114
139
  2. **Simulator**: Loads at `localhost:5004`
115
140
  3. **Import Maps**: Simulator loads your app via ES module import
116
- 4. **Live Updates**: Changes in your app reflect immediately
141
+ 4. **HMR**: Vite's Hot Module Replacement provides instant updates without full page reloads
117
142
 
118
143
  ## Requirements
119
144
 
@@ -121,15 +146,26 @@ Your app must:
121
146
 
122
147
  - Export ES modules
123
148
  - Provide player and config components
124
- - Build with externalized dependencies:
149
+ - Use `@juuno/vite-plugin-app-dev` for development:
150
+
125
151
  ```js
126
152
  // vite.config.ts
153
+ import { defineConfig } from 'vite';
154
+ import vue from '@vitejs/plugin-vue';
155
+ import { juunoAppDev } from '@juuno/vite-plugin-app-dev';
156
+
127
157
  export default {
158
+ plugins: [
159
+ vue(),
160
+ juunoAppDev(), // Enables dev server entry point routing
161
+ ],
128
162
  build: {
129
163
  lib: {
130
- entry: './src/index.ts',
164
+ entry: {
165
+ player: './src/player/index.ts',
166
+ config: './src/config/index.ts',
167
+ },
131
168
  formats: ['es'],
132
- fileName: 'index',
133
169
  },
134
170
  rollupOptions: {
135
171
  external: ['vue', '@juuno-sdk/app-sdk'],
@@ -140,51 +176,54 @@ Your app must:
140
176
 
141
177
  ## Development Workflow
142
178
 
143
- The simulator works with built app bundles to match the production architecture.
179
+ ### Recommended: Unified Dev Command
144
180
 
145
- **Terminal 1: Auto-rebuild your app**
181
+ The simplest workflow - one command handles everything:
146
182
 
147
183
  ```bash
148
184
  cd my-juuno-app
149
- npm run build:watch
150
- # Watches for changes and rebuilds automatically
185
+ npx juuno-cli dev
151
186
  ```
152
187
 
153
- **Terminal 2: Serve your app bundle**
188
+ **Development Flow:**
154
189
 
155
- ```bash
156
- cd my-juuno-app
157
- npx serve dist -l 3000 --cors
158
- # Serves the built bundle at localhost:3000
159
- ```
190
+ 1. Edit your app's source code
191
+ 2. Vite's HMR instantly updates the browser (no full page reload!)
192
+ 3. See changes reflected in the simulator immediately
160
193
 
161
- **Terminal 3: Start the simulator**
194
+ ### Alternative: Manual Mode
162
195
 
163
- ```bash
164
- npx juuno-cli dev --app http://localhost:3000/index.js
165
- # → localhost:5004
166
- ```
196
+ If you prefer more control, you can point the simulator to your own dev server:
167
197
 
168
- **Development Flow:**
198
+ ```bash
199
+ # Start your own dev server
200
+ cd my-juuno-app
201
+ npm run dev # or: npx vite
169
202
 
170
- 1. Edit your app's source code
171
- 2. Auto-rebuild happens in background (100-500ms)
172
- 3. Manually refresh browser at http://localhost:5004
173
- 4. See changes reflected in the simulator
203
+ # In another terminal, start the simulator pointing to your server
204
+ npx juuno-cli dev --app http://localhost:3000
205
+ ```
174
206
 
175
- **Note:** This workflow matches production closely (import maps + static bundles) but doesn't support HMR.
207
+ **Note:** For the best experience, use `@juuno/vite-plugin-app-dev` in your Vite config to enable proper entry point routing during development.
176
208
 
177
209
  ## CLI Options
178
210
 
179
- ### Simulator (dev command)
211
+ ### Dev Command
180
212
 
181
213
  ```bash
182
214
  juuno-cli dev [options]
183
215
 
216
+ # Unified mode (recommended) - run from your app directory:
217
+ juuno-cli dev
218
+
219
+ # Manual mode - point to external URL:
220
+ juuno-cli dev --app <url>
221
+
184
222
  Options:
185
- --app <url> URL of your external app (required)
186
- --port <number> Port for simulator (default: 5004)
187
- --help Show help
223
+ --app <url> URL of external app (omit for unified mode)
224
+ --port <number> Port for simulator (default: 5004)
225
+ --serve-port <number> Port for Vite dev server in unified mode (default: 3000)
226
+ --help Show help
188
227
  ```
189
228
 
190
229
  ### Login Command
package/bin/cli.js CHANGED
@@ -1,5 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /**
4
+ * Juuno CLI - Official CLI for Juuno external app development.
5
+ *
6
+ * Usage: npx juuno-cli <command> [options]
7
+ *
8
+ * For development, run `pnpm build` first to compile TypeScript.
9
+ */
10
+
3
11
  import { fileURLToPath } from 'url';
4
12
  import { dirname, join } from 'path';
5
13
  import { runCommand } from './cli-router.js';
@@ -7,13 +15,10 @@ import { runCommand } from './cli-router.js';
7
15
  const __filename = fileURLToPath(import.meta.url);
8
16
  const __dirname = dirname(__filename);
9
17
 
10
- // Parse command line arguments.
11
18
  const args = process.argv.slice(2);
12
-
13
- // Check if first argument is a command.
14
19
  const command = args[0];
15
20
 
16
- // Handle help command.
21
+ // --- Help Command ---
17
22
  if (args.includes('--help') || args.includes('-h') || command === 'help') {
18
23
  console.log(`
19
24
  Juuno CLI - Official CLI for Juuno external app development
@@ -25,7 +30,7 @@ Commands:
25
30
  login Authenticate with Juuno
26
31
  logout Remove stored credentials
27
32
  whoami Show authentication status
28
- dev Launch the app simulator (default)
33
+ dev Launch the app simulator
29
34
  deploy Deploy your app to Juuno
30
35
  list List your deployed apps
31
36
  info <app-id> Show detailed information about an app
@@ -42,9 +47,18 @@ Authentication Commands:
42
47
  whoami
43
48
  Show current authentication status.
44
49
 
50
+ Dev Command (Unified Workflow):
51
+ juuno-cli dev
52
+ Auto-detects app, starts Vite dev server with HMR, and launches simulator.
53
+ Run from your app directory - no arguments needed!
54
+
55
+ juuno-cli dev --app <url>
56
+ Manual mode: point to an external app URL.
57
+
45
58
  Simulator Options:
46
- --app <url> URL of your external app (required)
47
- --port <number> Port for simulator (default: 5004)
59
+ --app <url> URL of your external app (omit for auto mode)
60
+ --port <number> Port for simulator (default: 5004)
61
+ --serve-port <number> Port for Vite dev server in auto mode (default: 3000)
48
62
 
49
63
  Deploy Options:
50
64
  --build-dir <dir> Build directory to deploy (default: ./dist)
@@ -64,7 +78,11 @@ Examples:
64
78
  # Authenticate (one-time setup)
65
79
  juuno-cli login
66
80
 
67
- # Start simulator
81
+ # Start unified dev environment (recommended)
82
+ cd my-juuno-app
83
+ juuno-cli dev
84
+
85
+ # Manual mode: point to external app URL
68
86
  juuno-cli dev --app http://localhost:3000
69
87
 
70
88
  # Deploy app (uses saved credentials)
@@ -86,7 +104,7 @@ Examples:
86
104
  process.exit(0);
87
105
  }
88
106
 
89
- // Handle commands via shared router (uses built JS imports for production).
107
+ // --- Auth/Deploy/List/Info Commands ---
90
108
  if (
91
109
  command === 'login' ||
92
110
  command === 'logout' ||
@@ -99,34 +117,42 @@ if (
99
117
  process.exit(exitCode);
100
118
  }
101
119
 
102
- // Handle simulator command (default behavior).
103
- // Remove the command from args if it was 'dev'.
104
- let simulatorArgs = args;
120
+ // --- Dev/Simulator Command ---
121
+ let devArgs = args;
105
122
  if (command === 'dev') {
106
- simulatorArgs = args.slice(1);
123
+ devArgs = args.slice(1);
107
124
  }
108
125
 
109
- const appUrlIndex = simulatorArgs.indexOf('--app');
110
- const portIndex = simulatorArgs.indexOf('--port');
111
-
112
- if (appUrlIndex === -1) {
113
- console.error('Error: --app parameter is required for simulator');
114
- console.error('Usage: juuno-cli dev --app <url>');
115
- console.error('Run with --help for more information');
116
- process.exit(1);
126
+ const appUrlIndex = devArgs.indexOf('--app');
127
+ const portIndex = devArgs.indexOf('--port');
128
+ const servePortIndex = devArgs.indexOf('--serve-port');
129
+
130
+ const simulatorDistPath = join(__dirname, '..', 'dist');
131
+
132
+ if (appUrlIndex !== -1) {
133
+ // Manual mode: user provided --app URL.
134
+ const appUrl = devArgs[appUrlIndex + 1];
135
+ const port = portIndex !== -1 ? devArgs[portIndex + 1] : '5004';
136
+
137
+ if (!appUrl) {
138
+ console.error('Error: --app parameter requires a URL value');
139
+ process.exit(1);
140
+ }
141
+
142
+ const { launchSimulator } = await import(
143
+ '../dist/cli/src/simulator/index.js'
144
+ );
145
+ launchSimulator(appUrl, port, simulatorDistPath);
146
+ } else {
147
+ // Unified dev mode: auto-detect app and start everything.
148
+ const simulatorPort = portIndex !== -1 ? devArgs[portIndex + 1] : '5004';
149
+ const servePort =
150
+ servePortIndex !== -1 ? devArgs[servePortIndex + 1] : '3000';
151
+
152
+ const { runDev } = await import('../dist/cli/src/dev/index.js');
153
+ await runDev({
154
+ simulatorPort,
155
+ servePort,
156
+ simulatorDistPath,
157
+ });
117
158
  }
118
-
119
- const appUrl = simulatorArgs[appUrlIndex + 1];
120
- const port = portIndex !== -1 ? simulatorArgs[portIndex + 1] : '5004';
121
-
122
- if (!appUrl) {
123
- console.error('Error: --app parameter requires a URL value');
124
- process.exit(1);
125
- }
126
-
127
- // Path to the built dist directory.
128
- const distPath = join(__dirname, '..', 'dist');
129
-
130
- // Launch simulator (imports from built JS).
131
- const { launchSimulator } = await import('../dist/cli/src/simulator/index.js');
132
- launchSimulator(appUrl, port, distPath);
@@ -0,0 +1 @@
1
+ import{defineComponent as V,ref as w,computed as M,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 R,createBlock as $,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"},Se={key:1,class:"placeholder"},Me={class:"tab-panel meta-panel"},Ae={class:"meta-code"},Pe={name:"SimulatorLayout"},Re=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=M(()=>{var v;return((v=h.manifest)==null?void 0:v.name)||"External App"}),E=M(()=>{var v;return((v=h.manifest)==null?void 0:v.version)||""}),L=M(()=>l.value==="landscape"?"aspect-landscape":"aspect-portrait"),S=M(()=>{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(S.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,[R(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(),$(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"]]),R(e("div",Le,[a.configComponent?(r(),$(U(a.configComponent),{key:"config-"+a.scene.idx})):(r(),d("div",Se,"No config component"))],512),[[B,o.value==="config"]]),R(e("div",Me,[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,_(S.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},$e=Be(Re,[["__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=new URL(C,y.value).toString(),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 S(){console.log("🔄 Reloading app..."),window.location.reload()}return j(L),(g,C)=>(r(),$(J($e),{"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:S},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
+ import{defineComponent as A,ref as g,shallowRef as y,reactive as P,onMounted as k,createElementBlock as f,openBlock as d,createElementVNode as t,toDisplayString as _,createTextVNode as h,createBlock as E,resolveDynamicComponent as x,createApp as O}from"vue";import{provideAppConfigContext as I}from"@juuno-sdk/app-sdk";(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))u(o);new MutationObserver(o=>{for(const r of o)if(r.type==="childList")for(const l of r.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&u(l)}).observe(document,{childList:!0,subtree:!0});function i(o){const r={};return o.integrity&&(r.integrity=o.integrity),o.referrerPolicy&&(r.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?r.credentials="include":o.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function u(o){if(o.ep)return;o.ep=!0;const r=i(o);fetch(o.href,r)}})();const b="modulepreload",R=function(p){return"/"+p},C={},L=function(a,i,u){let o=Promise.resolve();if(i&&i.length>0){let l=function(n){return Promise.all(n.map(e=>Promise.resolve(e).then(m=>({status:"fulfilled",value:m}),m=>({status:"rejected",reason:m}))))};document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),v=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));o=l(i.map(n=>{if(n=R(n),n in C)return;C[n]=!0;const e=n.endsWith(".css"),m=e?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${n}"]${m}`))return;const c=document.createElement("link");if(c.rel=e?"stylesheet":b,e||(c.as="script"),c.crossOrigin="",c.href=n,v&&c.setAttribute("nonce",v),document.head.appendChild(c),e)return new Promise((w,S)=>{c.addEventListener("load",w),c.addEventListener("error",()=>S(new Error(`Unable to preload CSS for ${n}`)))})}))}function r(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"&&r(s.reason);return a().catch(r)})},U={class:"juuno-cli"},N={class:"header"},T={class:"header-content"},j={class:"header-info"},D={class:"app-url"},V={key:0,class:"loading-state"},q={class:"url-info"},B={key:1,class:"error-state"},F={class:"error-message"},J={class:"error-details"},$={key:2,class:"container"},M={class:"player-section"},K={class:"player-preview"},W={key:1,class:"placeholder"},X={class:"config-section"},z={class:"config-panel"},G={key:1,class:"placeholder"},H={name:"JuunoCLI"},Q=A({...H,setup(p){const a=g(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),i=y(null),u=y(null),o=g(!0),r=g(null),l=P({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:[]}),v=I({async save(n){console.log("Simulator: Scene updated",n),Object.assign(l,n)}},{scene:l});return k(async()=>{try{o.value=!0,r.value=null,console.log(`Loading external app from: ${a.value}`);const n=await L(()=>import("external-app/player"),[]);if(n.default)i.value=n.default;else if(typeof n.render=="function")i.value=n.render();else throw new Error("External app must export a default component or render function from /player");try{const e=await L(()=>import("external-app/config"),[]);e.default?u.value=e.default:typeof e.render=="function"?u.value=e.render():u.value=null}catch(e){console.warn("Config component not found or failed to load:",e),u.value=null}v.load(l),console.log("✅ External app loaded successfully"),o.value=!1}catch(n){console.error("Failed to load external app:",n),r.value=n instanceof Error?n.message:"Failed to load external app",o.value=!1}}),(n,e)=>(d(),f("div",U,[t("header",N,[t("div",T,[e[0]||(e[0]=t("h1",null,"Juuno CLI",-1)),t("div",j,[t("span",D,_(a.value),1)])])]),o.value?(d(),f("div",V,[e[1]||(e[1]=t("div",{class:"spinner"},null,-1)),e[2]||(e[2]=t("p",null,"Loading external app...",-1)),t("p",q,_(a.value),1)])):r.value?(d(),f("div",B,[e[6]||(e[6]=t("div",{class:"error-icon"},"⚠️",-1)),e[7]||(e[7]=t("h2",null,"Failed to Load App",-1)),t("p",F,_(r.value),1),t("div",J,[t("p",null,[e[3]||(e[3]=t("strong",null,"App URL:",-1)),h(" "+_(a.value),1)]),e[4]||(e[4]=t("p",null,[t("strong",null,"Common Issues:")],-1)),e[5]||(e[5]=t("ul",null,[t("li",null,"App dev server not running"),t("li",null,"CORS not enabled on app dev server"),t("li",null," App doesn't export required components from /player and /config "),t("li",null,"Incorrect URL provided"),t("li",null,"Missing exports field in package.json")],-1))])])):(d(),f("div",$,[t("div",M,[e[8]||(e[8]=t("h2",null,"Player View",-1)),t("div",K,[i.value?(d(),E(x(i.value),{key:0,scene:l},null,8,["scene"])):(d(),f("div",W,"No player component found"))])]),t("div",X,[e[9]||(e[9]=t("h2",null,"Configuration UI",-1)),t("div",z,[u.value?(d(),E(x(u.value),{key:0})):(d(),f("div",G,"No config component found"))])])])),e[10]||(e[10]=t("footer",{class:"footer"},[t("p",null,[t("strong",null,"Juuno CLI"),h(" - Test your external app in a production-like environment ")])],-1))]))}}),Y=(p,a)=>{const i=p.__vccOpts||p;for(const[u,o]of a)i[u]=o;return i},Z=Y(Q,[["__scopeId","data-v-b2010f22"]]),ee=O(Z);ee.mount("#app");
@@ -0,0 +1 @@
1
+ import{defineComponent as V,ref as w,computed as S,onMounted as D,onUnmounted as F,createElementBlock as d,openBlock as r,normalizeClass as y,createElementVNode as e,createCommentVNode as A,toDisplayString as _,createTextVNode as O,withDirectives as $,createBlock as B,resolveDynamicComponent as U,vShow as R,shallowRef as N,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 h=document.createElement("link").relList;if(h&&h.supports&&h.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 p(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=p(o);fetch(o.href,l)}})();const H="modulepreload",W=function(a){return"/"+a},T={},j=function(h,p,b){let o=Promise.resolve();if(p&&p.length>0){let n=function(s){return Promise.all(s.map(f=>Promise.resolve(f).then(x=>({status:"fulfilled",value:x}),x=>({status:"rejected",reason:x}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),m=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));o=n(p.map(s=>{if(s=W(s),s in T)return;T[s]=!0;const f=s.endsWith(".css"),x=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${s}"]${x}`))return;const k=document.createElement("link");if(k.rel=f?"stylesheet":H,f||(k.as="script"),k.crossOrigin="",k.href=s,m&&k.setAttribute("nonce",m),document.head.appendChild(k),f)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 h().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:h}){const p=a,b=h,o=w("player"),l=w("landscape"),n=w(!1),i=w(!1),m=w(!0),s=w(!0),f=w(!1),x=w(3e4),k=S(()=>{var v;return((v=p.manifest)==null?void 0:v.name)||"External App"}),E=S(()=>{var v;return((v=p.manifest)==null?void 0:v.version)||""}),L=S(()=>l.value==="landscape"?"aspect-landscape":"aspect-portrait"),M=S(()=>{var v;try{return JSON.stringify(((v=p.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 D(()=>{window.addEventListener("keydown",u)}),F(()=>{window.removeEventListener("keydown",u)}),(v,t)=>(r(),d("div",{class:y(["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:y(["tab",{active:o.value==="player"}]),onClick:t[1]||(t[1]=c=>o.value="player")}," Player ",2),e("button",{class:y(["tab",{active:o.value==="config"}]),onClick:t[2]||(t[2]=c=>o.value="config")}," Config ",2),e("button",{class:y(["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:y(["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:y(["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:y(["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:y(["tab-panel player-panel",{fullscreen:n.value}])},[e("div",{class:y(["player-container",L.value])},[e("div",ve,[a.playerComponent?(r(),B(U(a.playerComponent),{key:a.scene.idx,scene:a.scene,playing:m.value,"is-visible":s.value,"duration-ms":x.value,muted:f.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:y(["player-btn",{active:m.value}]),title:m.value?"Pause":"Play",onClick:t[7]||(t[7]=c=>m.value=!m.value)},[m.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:y(["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:y(["player-btn",{active:f.value}]),title:f.value?"Unmute":"Mute",onClick:t[9]||(t[9]=c=>f.value=!f.value)},[f.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:y(["state-badge",{inactive:!m.value}])},_(m.value?"Playing":"Paused"),3),e("span",{class:y(["state-badge",{inactive:!s.value}])},_(s.value?"Visible":"Hidden"),3),e("span",{class:y(["state-badge",{inactive:!f.value}])},_(f.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),[[R,o.value==="player"]]),$(e("div",Le,[a.configComponent?(r(),B(U(a.configComponent),{key:"config-"+a.scene.idx})):(r(),d("div",Me,"No config component"))],512),[[R,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),[[R,o.value==="meta"]])])]))],2))}}),Re=(a,h)=>{const p=a.__vccOpts||a;for(const[b,o]of h)p[b]=o;return p},Be=Re($e,[["__scopeId","data-v-d3c29943"]]),Oe={name:"JuunoCLI"},Ue=V({...Oe,setup(a){const h=w(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),p=w(window.__JUUNO_LIVE_RELOAD__||!1),b=w(null),o=[],l=N(null),n=N(null),i=w(!0),m=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(`${h.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=`${h.value}${C}`,document.head.appendChild(u),o.push(u)})}async function L(){var g,C;try{i.value=!0,m.value=null,console.log(`Loading external app from: ${h.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 j(()=>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 j(()=>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),m.value=u instanceof Error?u.message:"Failed to load external app",i.value=!1}}function M(){console.log("🔄 Reloading app..."),window.location.reload()}return typeof window<"u"&&(window.addEventListener("juuno:live-reload-connected",()=>{p.value=!0}),window.addEventListener("juuno:live-reload-disconnected",()=>{p.value=!1})),D(L),(g,C)=>(r(),B(J(Be),{"player-component":l.value,"config-component":n.value,scene:s,"app-url":h.value,"is-loading":i.value,error:m.value,manifest:b.value,"live-reload-active":p.value,onReload:M},null,8,["player-component","config-component","scene","app-url","is-loading","error","manifest","live-reload-active"]))}}),Ne=q(Ue);Ne.mount("#app");
@@ -0,0 +1 @@
1
+ [data-v-b2010f22]{box-sizing:border-box}.juuno-cli[data-v-b2010f22]{min-height:100vh;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;background-color:#f5f5f5}.header[data-v-b2010f22]{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;padding:1.5rem 2rem;box-shadow:0 2px 4px #0000001a}.header-content[data-v-b2010f22]{max-width:1600px;margin:0 auto}.header h1[data-v-b2010f22]{margin:0 0 .5rem;font-size:1.75rem;font-weight:600}.header-info[data-v-b2010f22]{display:flex;justify-content:space-between;align-items:center;gap:1rem}.app-url[data-v-b2010f22]{font-size:.875rem;opacity:.9;font-family:Courier New,monospace}.loading-state[data-v-b2010f22],.error-state[data-v-b2010f22]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center}.spinner[data-v-b2010f22]{width:50px;height:50px;border:4px solid #f3f3f3;border-top:4px solid #667eea;border-radius:50%;animation:spin-b2010f22 1s linear infinite}@keyframes spin-b2010f22{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.url-info[data-v-b2010f22]{margin-top:1rem;font-size:.875rem;color:#666;font-family:Courier New,monospace}.error-icon[data-v-b2010f22]{font-size:4rem;margin-bottom:1rem}.error-message[data-v-b2010f22]{color:#d32f2f;font-weight:500;margin:1rem 0}.error-details[data-v-b2010f22]{background:#fff;border-radius:8px;padding:1.5rem;margin-top:2rem;text-align:left;max-width:600px}.error-details ul[data-v-b2010f22]{margin:.5rem 0 0 1.5rem;padding:0}.error-details li[data-v-b2010f22]{margin:.25rem 0}.container[data-v-b2010f22]{flex:1;display:grid;grid-template-columns:1fr 1fr;gap:2rem;padding:2rem;max-width:1600px;margin:0 auto;width:100%}.player-section[data-v-b2010f22],.config-section[data-v-b2010f22]{background:#fff;border-radius:8px;padding:1.5rem;box-shadow:0 1px 3px #0000001a}.player-section h2[data-v-b2010f22],.config-section h2[data-v-b2010f22]{margin:0 0 1rem;font-size:1.25rem;font-weight:600;color:#333}.player-preview[data-v-b2010f22]{aspect-ratio:16 / 9;background:#000;border-radius:4px;overflow:hidden;box-shadow:0 4px 6px #0000001a}.config-panel[data-v-b2010f22]{background:#fafafa;border-radius:4px;padding:1rem;min-height:400px}.placeholder[data-v-b2010f22]{display:flex;align-items:center;justify-content:center;height:100%;color:#999;font-style:italic}.footer[data-v-b2010f22]{background:#fff;padding:1.5rem;text-align:center;border-top:1px solid #e0e0e0;color:#666;font-size:.875rem}.footer p[data-v-b2010f22]{margin:0}@media (max-width: 1200px){.container[data-v-b2010f22]{grid-template-columns:1fr}}
@@ -0,0 +1 @@
1
+ import{defineComponent as D,ref as w,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(C=>Promise.resolve(C).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),b=(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 C=a.endsWith(".css"),h=C?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${a}"]${h}`))return;const v=document.createElement("link");if(v.rel=C?"stylesheet":W,C||(v.as="script"),v.crossOrigin="",v.href=a,b&&v.setAttribute("nonce",b),document.head.appendChild(v),C)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=w("player"),n=w("landscape"),l=w(!1),s=w(!1);w(!0);const b=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)||""}),C=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(b.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",C.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))}}),Ce=(r,p)=>{const u=r.__vccOpts||r;for(const[g,o]of p)u[g]=o;return u},ke=Ce(we,[["__scopeId","data-v-2641604d"]]),be={name:"JuunoCLI"},_e=D({...be,setup(r){const p=w(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),u=w(window.__JUUNO_LIVE_RELOAD__||!1),g=w(null),o=[],n=N(null),l=N(null),s=w(!0),b=w(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,b.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(y=>y.endsWith(".css"))),(i=c==null?void 0:c.resources)!=null&&i.config&&L.push(...c.resources.config.filter(y=>y.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 y=await j(()=>import("external-app/config"),[]);y.default?l.value=M(y.default):typeof y.render=="function"?l.value=M(y.render()):l.value=null}catch(y){console.warn("Config component not found or failed to load:",y),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),b.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(ke),{"player-component":n.value,"config-component":l.value,scene:a,"app-url":p.value,"is-loading":s.value,error:b.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
+ [data-v-2641604d]{box-sizing:border-box}.simulator-layout[data-v-2641604d]{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-2641604d]{background-color:#1a1a2e;color:#e8e8e8}.header[data-v-2641604d]{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-2641604d]{display:flex;align-items:center;gap:1.5rem}.header h1[data-v-2641604d]{margin:0;font-size:1.125rem;font-weight:600;letter-spacing:-.02em}.app-info[data-v-2641604d]{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#ffffff26;border-radius:4px;font-size:.8125rem}.app-name[data-v-2641604d]{font-weight:500}.app-version[data-v-2641604d]{opacity:.8;font-size:.75rem}.header-right[data-v-2641604d]{display:flex;align-items:center;gap:.75rem}.status-badge[data-v-2641604d]{display:flex;align-items:center;gap:.375rem;padding:.25rem .625rem;border-radius:999px;font-size:.75rem;font-weight:500}.status-badge.live[data-v-2641604d]{background:#22c55e33;color:#86efac}.status-dot[data-v-2641604d]{width:6px;height:6px;border-radius:50%;background:#22c55e;animation:pulse-2641604d 2s infinite}@keyframes pulse-2641604d{0%,to{opacity:1}50%{opacity:.5}}.icon-btn[data-v-2641604d]{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-2641604d]:hover{background:#fff3}.loading-state[data-v-2641604d],.error-state[data-v-2641604d]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center}.spinner[data-v-2641604d]{width:40px;height:40px;border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;animation:spin-2641604d .8s linear infinite}.dark-mode .spinner[data-v-2641604d]{border-color:#374151;border-top-color:#8b5cf6}@keyframes spin-2641604d{to{transform:rotate(360deg)}}.url-info[data-v-2641604d]{margin-top:.75rem;font-size:.8125rem;color:#6b7280;font-family:SF Mono,Monaco,monospace}.error-icon[data-v-2641604d]{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-2641604d]{background:#450a0a}.error-message[data-v-2641604d]{color:#dc2626;font-weight:500;margin:.5rem 0}.error-details[data-v-2641604d]{margin-top:1.5rem;padding:1rem;background:#fff;border-radius:8px;font-size:.875rem}.dark-mode .error-details[data-v-2641604d]{background:#1f2937}.retry-btn[data-v-2641604d]{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-2641604d]:hover{background:#4f46e5}.main-content[data-v-2641604d]{flex:1;display:flex;flex-direction:column;padding:1rem;gap:1rem}.tabs[data-v-2641604d]{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-2641604d]{background:#1f2937}.tab[data-v-2641604d]{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-2641604d]:hover{color:#1f2937}.dark-mode .tab[data-v-2641604d]:hover{color:#e5e7eb}.tab.active[data-v-2641604d]{background:#6366f1;color:#fff}.tab-controls[data-v-2641604d]{margin-left:auto;display:flex;gap:.25rem}.control-btn[data-v-2641604d]{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-2641604d]:hover{background:#f3f4f6;color:#4b5563}.dark-mode .control-btn[data-v-2641604d]:hover{background:#374151;color:#d1d5db}.control-btn.active[data-v-2641604d]{background:#ede9fe;color:#6366f1}.dark-mode .control-btn.active[data-v-2641604d]{background:#4c1d95;color:#c4b5fd}.tab-panels[data-v-2641604d]{flex:1;display:flex;flex-direction:column}.tab-panel[data-v-2641604d]{flex:1;background:#fff;border-radius:8px;box-shadow:0 1px 3px #0000000d;overflow:auto}.dark-mode .tab-panel[data-v-2641604d]{background:#1f2937}.player-panel[data-v-2641604d]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;background:#111827}.player-panel.fullscreen[data-v-2641604d]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;border-radius:0;padding:0}.player-container[data-v-2641604d]{width:100%;max-width:960px;transition:all .3s ease}.player-container.aspect-landscape[data-v-2641604d]{aspect-ratio:16 / 9}.player-container.aspect-portrait[data-v-2641604d]{aspect-ratio:9 / 16;max-width:400px}.fullscreen .player-container[data-v-2641604d]{max-width:none;width:100%;height:100%}.fullscreen .player-container.aspect-portrait[data-v-2641604d]{max-width:none;width:auto;height:100%}.player-frame[data-v-2641604d]{position:relative;width:100%;height:100%;background:#000;border-radius:4px;overflow:hidden}.player-frame[data-v-2641604d]>*{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.fullscreen .player-frame[data-v-2641604d]{border-radius:0}.exit-fullscreen[data-v-2641604d]{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-2641604d]:hover{background:#000000e6}.config-panel[data-v-2641604d]{padding:1.5rem;overflow-y:auto}.meta-panel[data-v-2641604d]{padding:1rem}.meta-header[data-v-2641604d]{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.meta-header h3[data-v-2641604d]{margin:0;font-size:.875rem;font-weight:600;color:#6b7280}.copy-btn[data-v-2641604d]{padding:.25rem .5rem;background:#f3f4f6;border:none;border-radius:4px;font-size:.75rem;color:#4b5563;cursor:pointer}.dark-mode .copy-btn[data-v-2641604d]{background:#374151;color:#d1d5db}.copy-btn[data-v-2641604d]:hover{background:#e5e7eb}.dark-mode .copy-btn[data-v-2641604d]:hover{background:#4b5563}.meta-code[data-v-2641604d]{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-2641604d]{background:#111827;color:#e5e7eb}.placeholder[data-v-2641604d]{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-2641604d]{padding:.5rem 1rem}.header h1[data-v-2641604d]{font-size:1rem}.app-info[data-v-2641604d]{display:none}.tabs[data-v-2641604d]{flex-wrap:wrap}.tab-controls[data-v-2641604d]{width:100%;justify-content:center;margin-top:.5rem;margin-left:0}}
@@ -0,0 +1 @@
1
+ import{defineComponent as D,ref as y,computed as S,createElementBlock as d,openBlock as r,normalizeClass as g,createElementVNode as e,createCommentVNode as A,toDisplayString as _,createTextVNode as O,withDirectives as R,createBlock as B,resolveDynamicComponent as U,vShow as $,shallowRef as N,reactive as V,onMounted as F,unref as I,markRaw as P,createApp as J}from"vue";import{provideAppConfigContext as q}from"@juuno-sdk/app-sdk";(function(){const h=document.createElement("link").relList;if(h&&h.supports&&h.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))b(o);new MutationObserver(o=>{for(const n of o)if(n.type==="childList")for(const s of n.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&b(s)}).observe(document,{childList:!0,subtree:!0});function v(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 b(o){if(o.ep)return;o.ep=!0;const n=v(o);fetch(o.href,n)}})();const z="modulepreload",H=function(a){return"/"+a},T={},j=function(h,v,b){let o=Promise.resolve();if(v&&v.length>0){let s=function(l){return Promise.all(l.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]"),m=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));o=s(v.map(l=>{if(l=H(l),l in T)return;T[l]=!0;const p=l.endsWith(".css"),x=p?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${x}`))return;const k=document.createElement("link");if(k.rel=p?"stylesheet":z,p||(k.as="script"),k.crossOrigin="",k.href=l,m&&k.setAttribute("nonce",m),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 ${l}`)))})}))}function n(s){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s}return o.then(s=>{for(const i of s||[])i.status==="rejected"&&n(i.reason);return h().catch(n)})},W={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"},ie={class:"error-details"},ae={key:2,class:"main-content"},re={class:"tabs"},ue={key:0,class:"tab-controls"},ce={class:"tab-panels"},de={class:"player-frame"},ve={key:1,class:"placeholder"},pe={class:"player-controls"},fe={class:"controls-group"},me=["title"],ge={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},he={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},ye=["title"],we={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},ke={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Ce=["title"],be={key:0,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},xe={key:1,width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},_e={class:"controls-info"},Ee={class:"tab-panel config-panel"},Le={key:1,class:"placeholder"},Me={class:"tab-panel meta-panel"},Se={class:"meta-header"},Ae={class:"meta-code"},Pe={name:"SimulatorLayout"},Re=D({...Pe,props:{playerComponent:{},configComponent:{},scene:{},appUrl:{},isLoading:{type:Boolean},error:{},manifest:{},liveReloadActive:{type:Boolean}},emits:["reload"],setup(a,{emit:h}){const v=a,b=h,o=y("player"),n=y("landscape"),s=y(!1),i=y(!1);y(!0);const m=y(!0),l=y(!0),p=y(!1),x=y(3e4),k=S(()=>{var f;return((f=v.manifest)==null?void 0:f.name)||"External App"}),E=S(()=>{var f;return((f=v.manifest)==null?void 0:f.version)||""}),L=S(()=>n.value==="landscape"?"aspect-landscape":"aspect-portrait"),M=S(()=>{var f;try{return JSON.stringify(((f=v.scene)==null?void 0:f.meta)||{},null,2)}catch{return"{}"}});function w(){s.value=!s.value}function C(){i.value=!i.value}function u(){b("reload")}return(f,t)=>(r(),d("div",{class:g(["simulator-layout",{"dark-mode":i.value}])},[e("header",W,[e("div",K,[t[10]||(t[10]=e("h1",null,"Juuno Simulator",-1)),e("div",X,[e("span",G,_(k.value),1),E.value?(r(),d("span",Q,"v"+_(E.value),1)):A("",!0)])]),e("div",Y,[a.liveReloadActive?(r(),d("div",Z,[...t[11]||(t[11]=[e("span",{class:"status-dot"},null,-1),O(" Live ",-1)])])):A("",!0),e("button",{class:"icon-btn",title:"Reload app",onClick:u},[...t[12]||(t[12]=[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:C},[i.value?(r(),d("svg",te,[...t[14]||(t[14]=[e("path",{d:"M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"},null,-1)])])):(r(),d("svg",ee,[...t[13]||(t[13]=[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",oe,[t[15]||(t[15]=e("div",{class:"spinner"},null,-1)),t[16]||(t[16]=e("p",null,"Loading app...",-1)),e("p",ne,_(a.appUrl),1)])):a.error?(r(),d("div",le,[t[18]||(t[18]=e("div",{class:"error-icon"},"!",-1)),t[19]||(t[19]=e("h2",null,"Failed to Load App",-1)),e("p",se,_(a.error),1),e("div",ie,[e("p",null,[t[17]||(t[17]=e("strong",null,"App URL:",-1)),O(" "+_(a.appUrl),1)]),e("button",{class:"retry-btn",onClick:u},"Try Again")])])):(r(),d("div",ae,[e("div",re,[e("button",{class:g(["tab",{active:o.value==="player"}]),onClick:t[0]||(t[0]=c=>o.value="player")}," Player ",2),e("button",{class:g(["tab",{active:o.value==="config"}]),onClick:t[1]||(t[1]=c=>o.value="config")}," Config ",2),e("button",{class:g(["tab",{active:o.value==="meta"}]),onClick:t[2]||(t[2]=c=>o.value="meta")}," Scene Meta ",2),o.value==="player"?(r(),d("div",ue,[e("button",{class:g(["control-btn",{active:n.value==="landscape"}]),title:"Landscape (16:9)",onClick:t[3]||(t[3]=c=>n.value="landscape")},[...t[20]||(t[20]=[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:g(["control-btn",{active:n.value==="portrait"}]),title:"Portrait (9:16)",onClick:t[4]||(t[4]=c=>n.value="portrait")},[...t[21]||(t[21]=[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:g(["control-btn",{active:s.value}]),title:"Fullscreen",onClick:w},[...t[22]||(t[22]=[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",ce,[R(e("div",{class:g(["tab-panel player-panel",{fullscreen:s.value}])},[e("div",{class:g(["player-container",L.value])},[e("div",de,[a.playerComponent?(r(),B(U(a.playerComponent),{key:a.scene.idx,scene:a.scene,playing:m.value,"is-visible":l.value,"duration-ms":x.value,muted:p.value},null,8,["scene","playing","is-visible","duration-ms","muted"])):(r(),d("div",ve,"No player component"))])],2),e("div",pe,[e("div",fe,[e("button",{class:g(["player-btn",{active:m.value}]),title:m.value?"Pause":"Play",onClick:t[5]||(t[5]=c=>m.value=!m.value)},[m.value?(r(),d("svg",ge,[...t[23]||(t[23]=[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",he,[...t[24]||(t[24]=[e("polygon",{points:"5,3 19,12 5,21"},null,-1)])]))],10,me),e("button",{class:g(["player-btn",{active:!l.value}]),title:l.value?"Hide (simulate off-screen)":"Show",onClick:t[6]||(t[6]=c=>l.value=!l.value)},[l.value?(r(),d("svg",we,[...t[25]||(t[25]=[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",ke,[...t[26]||(t[26]=[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,ye),e("button",{class:g(["player-btn",{active:p.value}]),title:p.value?"Unmute":"Mute",onClick:t[7]||(t[7]=c=>p.value=!p.value)},[p.value?(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("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",be,[...t[27]||(t[27]=[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,Ce)]),e("div",_e,[e("span",{class:g(["state-badge",{inactive:!m.value}])},_(m.value?"Playing":"Paused"),3),e("span",{class:g(["state-badge",{inactive:!l.value}])},_(l.value?"Visible":"Hidden"),3),e("span",{class:g(["state-badge",{inactive:!p.value}])},_(p.value?"Muted":"Sound On"),3)])]),s.value?(r(),d("button",{key:0,class:"exit-fullscreen",onClick:t[8]||(t[8]=c=>s.value=!1)}," Exit Fullscreen ")):A("",!0)],2),[[$,o.value==="player"]]),R(e("div",Ee,[a.configComponent?(r(),B(U(a.configComponent),{key:"config-"+a.scene.idx})):(r(),d("div",Le,"No config component"))],512),[[$,o.value==="config"]]),R(e("div",Me,[e("div",Se,[t[29]||(t[29]=e("h3",null,"scene.meta",-1)),e("button",{class:"copy-btn",onClick:t[9]||(t[9]=c=>f.navigator.clipboard.writeText(M.value))}," Copy ")]),e("pre",Ae,_(M.value),1)],512),[[$,o.value==="meta"]])])]))],2))}}),$e=(a,h)=>{const v=a.__vccOpts||a;for(const[b,o]of h)v[b]=o;return v},Be=$e(Re,[["__scopeId","data-v-2ce117d9"]]),Oe={name:"JuunoCLI"},Ue=D({...Oe,setup(a){const h=y(window.__JUUNO_EXTERNAL_APP_URL__||"http://localhost:5002/index.js"),v=y(window.__JUUNO_LIVE_RELOAD__||!1),b=y(null),o=[],n=N(null),s=N(null),i=y(!0),m=y(null),l=V({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=q({async save(w){console.log("Simulator: Scene updated",w),Object.assign(l,w)}},{scene:l});async function k(){try{const w=await fetch(`${h.value}/manifest.json`);if(w.ok){const C=await w.json();return b.value=C,C}}catch{}return b.value=null,null}function E(w){o.forEach(C=>C.remove()),o.length=0,w.forEach(C=>{const u=document.createElement("link");u.rel="stylesheet",u.href=`${h.value}${C}`,document.head.appendChild(u),o.push(u)})}async function L(){var w,C;try{i.value=!0,m.value=null,console.log(`Loading external app from: ${h.value}`);const u=await k(),f=[];(w=u==null?void 0:u.resources)!=null&&w.player&&f.push(...u.resources.player.filter(c=>c.endsWith(".css"))),(C=u==null?void 0:u.resources)!=null&&C.config&&f.push(...u.resources.config.filter(c=>c.endsWith(".css"))),f.length>0&&(console.log("Loading CSS files:",f),E(f));const t=await j(()=>import("external-app/player"),[]);if(t.default)n.value=P(t.default);else if(typeof t.render=="function")n.value=P(t.render());else throw new Error("External app must export a default component or render function from /player");try{const c=await j(()=>import("external-app/config"),[]);c.default?s.value=P(c.default):typeof c.render=="function"?s.value=P(c.render()):s.value=null}catch{s.value=null}x.load(l),console.log("✅ External app loaded successfully"),i.value=!1}catch(u){console.error("Failed to load external app:",u),m.value=u instanceof Error?u.message:"Failed to load external app",i.value=!1}}function M(){console.log("🔄 Reloading app..."),window.location.reload()}return typeof window<"u"&&(window.addEventListener("juuno:live-reload-connected",()=>{v.value=!0}),window.addEventListener("juuno:live-reload-disconnected",()=>{v.value=!1})),F(L),(w,C)=>(r(),B(I(Be),{"player-component":n.value,"config-component":s.value,scene:l,"app-url":h.value,"is-loading":i.value,error:m.value,manifest:b.value,"live-reload-active":v.value,onReload:M},null,8,["player-component","config-component","scene","app-url","is-loading","error","manifest","live-reload-active"]))}}),Ne=J(Ue);Ne.mount("#app");
@@ -0,0 +1 @@
1
+ import{defineComponent as B,ref as h,computed as x,createElementBlock as f,openBlock as u,normalizeClass as k,createElementVNode as e,createCommentVNode as E,toDisplayString as b,createTextVNode as R,withDirectives as A,createBlock as M,resolveDynamicComponent as P,vShow as S,shallowRef as O,reactive as N,onMounted as T,unref as D,markRaw as L,createApp as j}from"vue";import{provideAppConfigContext as F}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 I="modulepreload",V=function(s){return"/"+s},$={},U=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=V(d),d in $)return;$[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":I,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)})},J={class:"header"},q={class:"header-left"},z={class:"app-info"},H={class:"app-name"},K={key:0,class:"app-version"},W={class:"header-right"},X={key:0,class:"status-badge live"},G={key:0,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Q={key:1,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},Y={key:0,class:"loading-state"},Z={class:"url-info"},ee={key:1,class:"error-state"},te={class:"error-message"},oe={class:"error-details"},ne={key:2,class:"main-content"},le={class:"tabs"},ae={key:0,class:"tab-controls"},se={class:"tab-panels"},re={class:"player-frame"},ie={key:1,class:"placeholder"},ce={class:"tab-panel config-panel"},ue={key:1,class:"placeholder"},de={class:"tab-panel meta-panel"},pe={class:"meta-header"},ve={class:"meta-code"},fe={name:"SimulatorLayout"},me=B({...fe,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:k(["simulator-layout",{"dark-mode":a.value}])},[e("header",J,[e("div",q,[t[7]||(t[7]=e("h1",null,"Juuno Simulator",-1)),e("div",z,[e("span",H,b(w.value),1),d.value?(u(),f("span",K,"v"+b(d.value),1)):E("",!0)])]),e("div",W,[s.liveReloadActive?(u(),f("div",X,[...t[8]||(t[8]=[e("span",{class:"status-dot"},null,-1),R(" 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",Q,[...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",G,[...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",Y,[t[12]||(t[12]=e("div",{class:"spinner"},null,-1)),t[13]||(t[13]=e("p",null,"Loading app...",-1)),e("p",Z,b(s.appUrl),1)])):s.error?(u(),f("div",ee,[t[15]||(t[15]=e("div",{class:"error-icon"},"!",-1)),t[16]||(t[16]=e("h2",null,"Failed to Load App",-1)),e("p",te,b(s.error),1),e("div",oe,[e("p",null,[t[14]||(t[14]=e("strong",null,"App URL:",-1)),R(" "+b(s.appUrl),1)]),e("button",{class:"retry-btn",onClick:r},"Try Again")])])):(u(),f("div",ne,[e("div",le,[e("button",{class:k(["tab",{active:o.value==="player"}]),onClick:t[0]||(t[0]=C=>o.value="player")}," Player ",2),e("button",{class:k(["tab",{active:o.value==="config"}]),onClick:t[1]||(t[1]=C=>o.value="config")}," Config ",2),e("button",{class:k(["tab",{active:o.value==="meta"}]),onClick:t[2]||(t[2]=C=>o.value="meta")}," Scene Meta ",2),o.value==="player"?(u(),f("div",ae,[e("button",{class:k(["control-btn",{active:n.value==="landscape"}]),title:"Landscape (16:9)",onClick:t[3]||(t[3]=C=>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:k(["control-btn",{active:n.value==="portrait"}]),title:"Portrait (9:16)",onClick:t[4]||(t[4]=C=>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:k(["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",se,[A(e("div",{class:k(["tab-panel player-panel",{fullscreen:l.value}])},[e("div",{class:k(["player-container",g.value])},[e("div",re,[s.playerComponent?(u(),M(P(s.playerComponent),{key:0,scene:s.scene,playing:!0,"is-visible":!0,"duration-ms":5e3},null,8,["scene"])):(u(),f("div",ie,"No player component"))])],2),l.value?(u(),f("button",{key:0,class:"exit-fullscreen",onClick:t[5]||(t[5]=C=>l.value=!1)}," Exit Fullscreen ")):E("",!0)],2),[[S,o.value==="player"]]),A(e("div",ce,[s.configComponent?(u(),M(P(s.configComponent),{key:0})):(u(),f("div",ue,"No config component"))],512),[[S,o.value==="config"]]),A(e("div",de,[e("div",pe,[t[20]||(t[20]=e("h3",null,"scene.meta",-1)),e("button",{class:"copy-btn",onClick:t[6]||(t[6]=C=>i.navigator.clipboard.writeText(y.value))}," Copy ")]),e("pre",ve,b(y.value),1)],512),[[S,o.value==="meta"]])])]))],2))}}),ge=(s,p)=>{const c=s.__vccOpts||s;for(const[m,o]of p)c[m]=o;return c},ye=ge(me,[["__scopeId","data-v-e190f6cf"]]),he={name:"JuunoCLI"},we=B({...he,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=O(null),n=O(null),l=h(!0),a=h(null),w=N({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=F({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 U(()=>import("external-app/player"),[]);if(r.default)o.value=L(r.default);else if(typeof r.render=="function")o.value=L(r.render());else throw new Error("External app must export a default component or render function from /player");try{const i=await U(()=>import("external-app/config"),[]);i.default?n.value=L(i.default):typeof i.render=="function"?n.value=L(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})),T(v),(r,i)=>(u(),M(D(ye),{"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"]))}}),ke=j(we);ke.mount("#app");
@@ -0,0 +1 @@
1
+ [data-v-2ce117d9]{box-sizing:border-box}.simulator-layout[data-v-2ce117d9]{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-2ce117d9]{background-color:#1a1a2e;color:#e8e8e8}.header[data-v-2ce117d9]{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-2ce117d9]{display:flex;align-items:center;gap:1.5rem}.header h1[data-v-2ce117d9]{margin:0;font-size:1.125rem;font-weight:600;letter-spacing:-.02em}.app-info[data-v-2ce117d9]{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#ffffff26;border-radius:4px;font-size:.8125rem}.app-name[data-v-2ce117d9]{font-weight:500}.app-version[data-v-2ce117d9]{opacity:.8;font-size:.75rem}.header-right[data-v-2ce117d9]{display:flex;align-items:center;gap:.75rem}.status-badge[data-v-2ce117d9]{display:flex;align-items:center;gap:.375rem;padding:.25rem .625rem;border-radius:999px;font-size:.75rem;font-weight:500}.status-badge.live[data-v-2ce117d9]{background:#22c55e33;color:#86efac}.status-dot[data-v-2ce117d9]{width:6px;height:6px;border-radius:50%;background:#22c55e;animation:pulse-2ce117d9 2s infinite}@keyframes pulse-2ce117d9{0%,to{opacity:1}50%{opacity:.5}}.icon-btn[data-v-2ce117d9]{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-2ce117d9]:hover{background:#fff3}.loading-state[data-v-2ce117d9],.error-state[data-v-2ce117d9]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center}.spinner[data-v-2ce117d9]{width:40px;height:40px;border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;animation:spin-2ce117d9 .8s linear infinite}.dark-mode .spinner[data-v-2ce117d9]{border-color:#374151;border-top-color:#8b5cf6}@keyframes spin-2ce117d9{to{transform:rotate(360deg)}}.url-info[data-v-2ce117d9]{margin-top:.75rem;font-size:.8125rem;color:#6b7280;font-family:SF Mono,Monaco,monospace}.error-icon[data-v-2ce117d9]{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-2ce117d9]{background:#450a0a}.error-message[data-v-2ce117d9]{color:#dc2626;font-weight:500;margin:.5rem 0}.error-details[data-v-2ce117d9]{margin-top:1.5rem;padding:1rem;background:#fff;border-radius:8px;font-size:.875rem}.dark-mode .error-details[data-v-2ce117d9]{background:#1f2937}.retry-btn[data-v-2ce117d9]{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-2ce117d9]:hover{background:#4f46e5}.main-content[data-v-2ce117d9]{flex:1;display:flex;flex-direction:column;padding:1rem;gap:1rem}.tabs[data-v-2ce117d9]{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-2ce117d9]{background:#1f2937}.tab[data-v-2ce117d9]{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-2ce117d9]:hover{color:#1f2937}.dark-mode .tab[data-v-2ce117d9]:hover{color:#e5e7eb}.tab.active[data-v-2ce117d9]{background:#6366f1;color:#fff}.tab-controls[data-v-2ce117d9]{margin-left:auto;display:flex;gap:.25rem}.control-btn[data-v-2ce117d9]{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-2ce117d9]:hover{background:#f3f4f6;color:#4b5563}.dark-mode .control-btn[data-v-2ce117d9]:hover{background:#374151;color:#d1d5db}.control-btn.active[data-v-2ce117d9]{background:#ede9fe;color:#6366f1}.dark-mode .control-btn.active[data-v-2ce117d9]{background:#4c1d95;color:#c4b5fd}.tab-panels[data-v-2ce117d9]{flex:1;display:flex;flex-direction:column}.tab-panel[data-v-2ce117d9]{flex:1;background:#fff;border-radius:8px;box-shadow:0 1px 3px #0000000d;overflow:auto}.dark-mode .tab-panel[data-v-2ce117d9]{background:#1f2937}.player-panel[data-v-2ce117d9]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;background:#111827}.player-panel.fullscreen[data-v-2ce117d9]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;border-radius:0;padding:0}.player-container[data-v-2ce117d9]{width:100%;max-width:960px;transition:all .3s ease}.player-container.aspect-landscape[data-v-2ce117d9]{aspect-ratio:16 / 9}.player-container.aspect-portrait[data-v-2ce117d9]{aspect-ratio:9 / 16;max-width:400px}.fullscreen .player-container[data-v-2ce117d9]{max-width:none;width:100%;height:100%}.fullscreen .player-container.aspect-portrait[data-v-2ce117d9]{max-width:none;width:auto;height:100%}.player-frame[data-v-2ce117d9]{position:relative;width:100%;height:100%;background:#000;border-radius:4px;overflow:hidden}.player-frame[data-v-2ce117d9]>*{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.fullscreen .player-frame[data-v-2ce117d9]{border-radius:0}.exit-fullscreen[data-v-2ce117d9]{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-2ce117d9]:hover{background:#000000e6}.player-controls[data-v-2ce117d9]{display:flex;align-items:center;justify-content:center;gap:1.5rem;margin-top:1rem;padding:.75rem 1rem;background:#ffffff1a;border-radius:8px}.fullscreen .player-controls[data-v-2ce117d9]{position:fixed;bottom:1rem;left:50%;transform:translate(-50%);background:#000000bf;z-index:1001}.controls-group[data-v-2ce117d9]{display:flex;gap:.5rem}.player-btn[data-v-2ce117d9]{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-2ce117d9]:hover{background:#fff3;color:#fff}.player-btn.active[data-v-2ce117d9]{background:#6366f1;color:#fff}.controls-info[data-v-2ce117d9]{display:flex;gap:.5rem}.state-badge[data-v-2ce117d9]{padding:.25rem .5rem;border-radius:4px;font-size:.75rem;font-weight:500;background:#22c55e33;color:#86efac}.state-badge.inactive[data-v-2ce117d9]{background:#ef444433;color:#fca5a5}.config-panel[data-v-2ce117d9]{padding:1.5rem;overflow-y:auto}.meta-panel[data-v-2ce117d9]{padding:1rem}.meta-header[data-v-2ce117d9]{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.meta-header h3[data-v-2ce117d9]{margin:0;font-size:.875rem;font-weight:600;color:#6b7280}.copy-btn[data-v-2ce117d9]{padding:.25rem .5rem;background:#f3f4f6;border:none;border-radius:4px;font-size:.75rem;color:#4b5563;cursor:pointer}.dark-mode .copy-btn[data-v-2ce117d9]{background:#374151;color:#d1d5db}.copy-btn[data-v-2ce117d9]:hover{background:#e5e7eb}.dark-mode .copy-btn[data-v-2ce117d9]:hover{background:#4b5563}.meta-code[data-v-2ce117d9]{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-2ce117d9]{background:#111827;color:#e5e7eb}.placeholder[data-v-2ce117d9]{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-2ce117d9]{padding:.5rem 1rem}.header h1[data-v-2ce117d9]{font-size:1rem}.app-info[data-v-2ce117d9]{display:none}.tabs[data-v-2ce117d9]{flex-wrap:wrap}.tab-controls[data-v-2ce117d9]{width:100%;justify-content:center;margin-top:.5rem;margin-left:0}}
@@ -0,0 +1 @@
1
+ [data-v-e190f6cf]{box-sizing:border-box}.simulator-layout[data-v-e190f6cf]{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-e190f6cf]{background-color:#1a1a2e;color:#e8e8e8}.header[data-v-e190f6cf]{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-e190f6cf]{display:flex;align-items:center;gap:1.5rem}.header h1[data-v-e190f6cf]{margin:0;font-size:1.125rem;font-weight:600;letter-spacing:-.02em}.app-info[data-v-e190f6cf]{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;background:#ffffff26;border-radius:4px;font-size:.8125rem}.app-name[data-v-e190f6cf]{font-weight:500}.app-version[data-v-e190f6cf]{opacity:.8;font-size:.75rem}.header-right[data-v-e190f6cf]{display:flex;align-items:center;gap:.75rem}.status-badge[data-v-e190f6cf]{display:flex;align-items:center;gap:.375rem;padding:.25rem .625rem;border-radius:999px;font-size:.75rem;font-weight:500}.status-badge.live[data-v-e190f6cf]{background:#22c55e33;color:#86efac}.status-dot[data-v-e190f6cf]{width:6px;height:6px;border-radius:50%;background:#22c55e;animation:pulse-e190f6cf 2s infinite}@keyframes pulse-e190f6cf{0%,to{opacity:1}50%{opacity:.5}}.icon-btn[data-v-e190f6cf]{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-e190f6cf]:hover{background:#fff3}.loading-state[data-v-e190f6cf],.error-state[data-v-e190f6cf]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center}.spinner[data-v-e190f6cf]{width:40px;height:40px;border:3px solid #e5e7eb;border-top-color:#6366f1;border-radius:50%;animation:spin-e190f6cf .8s linear infinite}.dark-mode .spinner[data-v-e190f6cf]{border-color:#374151;border-top-color:#8b5cf6}@keyframes spin-e190f6cf{to{transform:rotate(360deg)}}.url-info[data-v-e190f6cf]{margin-top:.75rem;font-size:.8125rem;color:#6b7280;font-family:SF Mono,Monaco,monospace}.error-icon[data-v-e190f6cf]{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-e190f6cf]{background:#450a0a}.error-message[data-v-e190f6cf]{color:#dc2626;font-weight:500;margin:.5rem 0}.error-details[data-v-e190f6cf]{margin-top:1.5rem;padding:1rem;background:#fff;border-radius:8px;font-size:.875rem}.dark-mode .error-details[data-v-e190f6cf]{background:#1f2937}.retry-btn[data-v-e190f6cf]{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-e190f6cf]:hover{background:#4f46e5}.main-content[data-v-e190f6cf]{flex:1;display:flex;flex-direction:column;padding:1rem;gap:1rem}.tabs[data-v-e190f6cf]{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-e190f6cf]{background:#1f2937}.tab[data-v-e190f6cf]{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-e190f6cf]:hover{color:#1f2937}.dark-mode .tab[data-v-e190f6cf]:hover{color:#e5e7eb}.tab.active[data-v-e190f6cf]{background:#6366f1;color:#fff}.tab-controls[data-v-e190f6cf]{margin-left:auto;display:flex;gap:.25rem}.control-btn[data-v-e190f6cf]{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-e190f6cf]:hover{background:#f3f4f6;color:#4b5563}.dark-mode .control-btn[data-v-e190f6cf]:hover{background:#374151;color:#d1d5db}.control-btn.active[data-v-e190f6cf]{background:#ede9fe;color:#6366f1}.dark-mode .control-btn.active[data-v-e190f6cf]{background:#4c1d95;color:#c4b5fd}.tab-panels[data-v-e190f6cf]{flex:1;display:flex;flex-direction:column}.tab-panel[data-v-e190f6cf]{flex:1;background:#fff;border-radius:8px;box-shadow:0 1px 3px #0000000d;overflow:auto}.dark-mode .tab-panel[data-v-e190f6cf]{background:#1f2937}.player-panel[data-v-e190f6cf]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;background:#111827}.player-panel.fullscreen[data-v-e190f6cf]{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;border-radius:0;padding:0}.player-container[data-v-e190f6cf]{width:100%;max-width:960px;transition:all .3s ease}.player-container.aspect-landscape[data-v-e190f6cf]{aspect-ratio:16 / 9}.player-container.aspect-portrait[data-v-e190f6cf]{aspect-ratio:9 / 16;max-width:400px}.fullscreen .player-container[data-v-e190f6cf]{max-width:none;width:100%;height:100%}.fullscreen .player-container.aspect-portrait[data-v-e190f6cf]{max-width:none;width:auto;height:100%}.player-frame[data-v-e190f6cf]{width:100%;height:100%;background:#000;border-radius:4px;overflow:hidden}.fullscreen .player-frame[data-v-e190f6cf]{border-radius:0}.exit-fullscreen[data-v-e190f6cf]{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-e190f6cf]:hover{background:#000000e6}.config-panel[data-v-e190f6cf]{padding:1.5rem;overflow-y:auto}.meta-panel[data-v-e190f6cf]{padding:1rem}.meta-header[data-v-e190f6cf]{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.meta-header h3[data-v-e190f6cf]{margin:0;font-size:.875rem;font-weight:600;color:#6b7280}.copy-btn[data-v-e190f6cf]{padding:.25rem .5rem;background:#f3f4f6;border:none;border-radius:4px;font-size:.75rem;color:#4b5563;cursor:pointer}.dark-mode .copy-btn[data-v-e190f6cf]{background:#374151;color:#d1d5db}.copy-btn[data-v-e190f6cf]:hover{background:#e5e7eb}.dark-mode .copy-btn[data-v-e190f6cf]:hover{background:#4b5563}.meta-code[data-v-e190f6cf]{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-e190f6cf]{background:#111827;color:#e5e7eb}.placeholder[data-v-e190f6cf]{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-e190f6cf]{padding:.5rem 1rem}.header h1[data-v-e190f6cf]{font-size:1rem}.app-info[data-v-e190f6cf]{display:none}.tabs[data-v-e190f6cf]{flex-wrap:wrap}.tab-controls[data-v-e190f6cf]{width:100%;justify-content:center;margin-top:.5rem;margin-left:0}}