@appstrata/dev 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +164 -0
  2. package/dist/completion-notification.d.ts +22 -0
  3. package/dist/completion-notification.d.ts.map +1 -0
  4. package/dist/completion-notification.js +184 -0
  5. package/dist/completion-notification.js.map +1 -0
  6. package/dist/dev-overlay.css +450 -0
  7. package/dist/dev-overlay.d.ts +56 -0
  8. package/dist/dev-overlay.d.ts.map +1 -0
  9. package/dist/dev-overlay.js +371 -0
  10. package/dist/dev-overlay.js.map +1 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +22 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/intercepting-transport.d.ts +11 -0
  16. package/dist/intercepting-transport.d.ts.map +1 -0
  17. package/dist/intercepting-transport.js +31 -0
  18. package/dist/intercepting-transport.js.map +1 -0
  19. package/dist/load-config.d.ts +42 -0
  20. package/dist/load-config.d.ts.map +1 -0
  21. package/dist/load-config.js +87 -0
  22. package/dist/load-config.js.map +1 -0
  23. package/dist/logging-hmr.d.ts +20 -0
  24. package/dist/logging-hmr.d.ts.map +1 -0
  25. package/dist/logging-hmr.js +28 -0
  26. package/dist/logging-hmr.js.map +1 -0
  27. package/dist/mock-init.d.ts +9 -0
  28. package/dist/mock-init.d.ts.map +1 -0
  29. package/dist/mock-init.js +171 -0
  30. package/dist/mock-init.js.map +1 -0
  31. package/dist/mock-player.d.ts +117 -0
  32. package/dist/mock-player.d.ts.map +1 -0
  33. package/dist/mock-player.js +132 -0
  34. package/dist/mock-player.js.map +1 -0
  35. package/dist/mock-services.d.ts +32 -0
  36. package/dist/mock-services.d.ts.map +1 -0
  37. package/dist/mock-services.js +141 -0
  38. package/dist/mock-services.js.map +1 -0
  39. package/dist/player/index.html +22 -0
  40. package/dist/player/main.d.ts +10 -0
  41. package/dist/player/main.d.ts.map +1 -0
  42. package/dist/player/main.js +352 -0
  43. package/dist/player/main.js.map +1 -0
  44. package/dist/player/styles.css +80 -0
  45. package/dist/plugin.d.ts +103 -0
  46. package/dist/plugin.d.ts.map +1 -0
  47. package/dist/plugin.js +292 -0
  48. package/dist/plugin.js.map +1 -0
  49. package/dist/types.d.ts +376 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +75 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +50 -0
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Mock implementations of capability services for development.
3
+ */
4
+ /**
5
+ * Create an in-memory mock storage service.
6
+ *
7
+ * Data is stored in a Map and persists only for the current page session.
8
+ * Refreshing the page clears all storage.
9
+ */
10
+ export function createMockStorage() {
11
+ const storage = new Map();
12
+ return {
13
+ async get(key) {
14
+ return storage.get(key);
15
+ },
16
+ async set(key, value) {
17
+ storage.set(key, value);
18
+ },
19
+ async remove(key) {
20
+ storage.delete(key);
21
+ },
22
+ async list() {
23
+ return Array.from(storage.keys());
24
+ },
25
+ async clear() {
26
+ storage.clear();
27
+ },
28
+ };
29
+ }
30
+ /**
31
+ * Create a passthrough mock proxy service.
32
+ *
33
+ * Uses native fetch directly. In dev mode, Vite's dev server can be configured
34
+ * to proxy API requests if CORS is an issue.
35
+ */
36
+ export function createMockProxy() {
37
+ return {
38
+ async fetch(request) {
39
+ try {
40
+ const response = await globalThis.fetch(request.url, {
41
+ method: request.method || "GET",
42
+ headers: request.headers,
43
+ body: request.body,
44
+ });
45
+ const headers = {};
46
+ response.headers.forEach((value, key) => {
47
+ headers[key] = value;
48
+ });
49
+ // Read response body
50
+ const body = await response.text();
51
+ return {
52
+ ok: response.ok,
53
+ status: response.status,
54
+ statusText: response.statusText,
55
+ headers,
56
+ json: async () => JSON.parse(body),
57
+ text: async () => body,
58
+ cached: false,
59
+ stale: false,
60
+ };
61
+ }
62
+ catch (error) {
63
+ console.error("[Dev Player] proxy.fetch failed:", error);
64
+ const errorMessage = error instanceof Error ? error.message : "Network Error";
65
+ return {
66
+ ok: false,
67
+ status: 0,
68
+ statusText: errorMessage,
69
+ headers: {},
70
+ json: async () => Promise.reject(error),
71
+ text: async () => Promise.reject(error),
72
+ cached: false,
73
+ stale: false,
74
+ };
75
+ }
76
+ },
77
+ };
78
+ }
79
+ /**
80
+ * Create a mock media cache service.
81
+ *
82
+ * In dev mode, media files are not actually cached locally.
83
+ * This mock returns the original URLs as "local paths".
84
+ */
85
+ export function createMockMediaCache() {
86
+ const cache = new Map();
87
+ return {
88
+ async download(url) {
89
+ // Create a mock MediaFile that just uses the original URL
90
+ const file = {
91
+ md5: btoa(url).slice(0, 32), // Fake MD5
92
+ localPath: url, // Use original URL in dev mode
93
+ fileName: url.split("/").pop() || "file",
94
+ size: 0,
95
+ status: "cached",
96
+ };
97
+ cache.set(url, file);
98
+ return file;
99
+ },
100
+ async get(url) {
101
+ return cache.get(url) || null;
102
+ },
103
+ async list() {
104
+ return Array.from(cache.values());
105
+ },
106
+ async delete(fileName) {
107
+ // Find and delete by fileName
108
+ for (const [url, file] of cache.entries()) {
109
+ if (file.fileName === fileName) {
110
+ cache.delete(url);
111
+ break;
112
+ }
113
+ }
114
+ },
115
+ async clear() {
116
+ cache.clear();
117
+ },
118
+ async getStorageInfo() {
119
+ return {
120
+ usedBytes: 0,
121
+ freeBytes: Number.MAX_SAFE_INTEGER,
122
+ };
123
+ },
124
+ };
125
+ }
126
+ /**
127
+ * Create a mock static API service.
128
+ *
129
+ * In dev mode, static optimization is disabled (no screenshots taken).
130
+ */
131
+ export function createMockStatic() {
132
+ return {
133
+ markStatic() {
134
+ },
135
+ markSemiStatic(_refreshIntervalSeconds) {
136
+ },
137
+ disableStatic() {
138
+ },
139
+ };
140
+ }
141
+ //# sourceMappingURL=mock-services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-services.js","sourceRoot":"","sources":["../src/mock-services.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AppStrata Dev Player</title>
7
+ <link rel="stylesheet" href="./styles.css">
8
+ </head>
9
+ <body>
10
+ <div class="player-container">
11
+ <div class="player-screen">
12
+ <div class="loading">
13
+ <div class="spinner"></div>
14
+ <p>Loading app...</p>
15
+ </div>
16
+ <iframe id="app-frame"></iframe>
17
+ </div>
18
+ </div>
19
+ <script type="module" src="./main.ts"></script>
20
+ </body>
21
+ </html>
22
+
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Player Page Entry Point.
3
+ *
4
+ * This is the main script for the iframe-based dev player.
5
+ * It loads the user's app in an iframe and provides either:
6
+ * - A local mock player environment (default)
7
+ * - A relay to a remote HTTP player (when relay config is provided)
8
+ */
9
+ import "../dev-overlay.css";
10
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/player/main.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkBH,OAAO,oBAAoB,CAAC"}
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Player Page Entry Point.
3
+ *
4
+ * This is the main script for the iframe-based dev player.
5
+ * It loads the user's app in an iframe and provides either:
6
+ * - A local mock player environment (default)
7
+ * - A relay to a remote HTTP player (when relay config is provided)
8
+ */
9
+ /// <reference types="vite/client" />
10
+ import { createMockPlayer, buildAppContext, buildCapabilityServices, resolveCapabilities, } from "../mock-player.js";
11
+ import { showCompletionNotification } from "../completion-notification.js";
12
+ import { createDevOverlay } from "../dev-overlay.js";
13
+ import { createInterceptingTransport } from "../intercepting-transport.js";
14
+ import { createPostMessageTransport } from "@appstrata/protocol";
15
+ import { configureLogging, createLogger } from "@appstrata/core";
16
+ import { buildLoggingConfig, scaleToFit, setElementResolution } from "@appstrata/utils";
17
+ import { CONFIG_CHANGE_EVENT } from "../types.js";
18
+ import "../dev-overlay.css";
19
+ configureLogging(buildLoggingConfig({
20
+ preset: { level: "debug" },
21
+ }));
22
+ const logger = createLogger("StandaloneDevPlayer");
23
+ logger.info("Initializing iframe dev player...");
24
+ // ═══════════════════════════════════════════════════════════════════════════
25
+ // Bootstrap
26
+ // ═══════════════════════════════════════════════════════════════════════════
27
+ const params = new URLSearchParams(window.location.search);
28
+ const appUrl = params.get("appUrl");
29
+ if (!appUrl) {
30
+ document.body.innerHTML = `
31
+ <div style="color: white; padding: 40px; text-align: center; font-family: system-ui;">
32
+ <h1>⚠️ Missing App URL</h1>
33
+ <p>Please provide the app URL as a query parameter:</p>
34
+ <code style="background: #16213e; padding: 10px; border-radius: 4px; display: inline-block; margin-top: 10px;">
35
+ ?appUrl=http://localhost:3000
36
+ </code>
37
+ </div>
38
+ `;
39
+ throw new Error("App URL is required. Use ?appUrl=http://localhost:3000");
40
+ }
41
+ const iframe = document.getElementById("app-frame");
42
+ const loading = document.querySelector(".loading");
43
+ const playerScreen = document.querySelector(".player-screen");
44
+ const playerContainer = document.querySelector(".player-container");
45
+ // ═══════════════════════════════════════════════════════════════════════════
46
+ // Configuration
47
+ // ═══════════════════════════════════════════════════════════════════════════
48
+ const response = await fetch("/__appstrata__/context");
49
+ let { context: contextConfig, capabilities: capabilityConfig, lifecycle, relay, app, dev: devConfig } = (await response.json());
50
+ const overlay = createDevOverlay({
51
+ container: playerContainer,
52
+ measureElement: iframe,
53
+ context: { ...contextConfig, autoZoom: app?.autoZoom },
54
+ injectStyles: false,
55
+ ...(relay && { relay: { playerUrl: relay.playerUrl } }),
56
+ dropLogMessages: !(devConfig?.messages?.showLogMessages ?? false),
57
+ });
58
+ // ═══════════════════════════════════════════════════════════════════════════
59
+ // Auto-zoom
60
+ // ═══════════════════════════════════════════════════════════════════════════
61
+ let zoomApply = null;
62
+ let zoomReset = null;
63
+ window.addEventListener("resize", () => {
64
+ zoomApply?.();
65
+ overlay.flashResizeToast();
66
+ });
67
+ function applyAutoZoom(mode, targetWidth, targetHeight) {
68
+ zoomReset?.();
69
+ zoomApply = null;
70
+ zoomReset = null;
71
+ if (!mode || mode === 'none')
72
+ return;
73
+ const parent = playerScreen.parentElement;
74
+ let apply;
75
+ let reset;
76
+ switch (mode) {
77
+ case 'contain':
78
+ case 'cover':
79
+ apply = () => scaleToFit(playerScreen, parent.clientWidth, parent.clientHeight, targetWidth, targetHeight, mode);
80
+ reset = () => scaleToFit(playerScreen);
81
+ break;
82
+ case 'auto-width': {
83
+ apply = () => {
84
+ const portrait = window.innerWidth < window.innerHeight;
85
+ const designPortrait = targetWidth < targetHeight;
86
+ const [tw, th] = (portrait !== designPortrait)
87
+ ? [targetHeight, targetWidth]
88
+ : [targetWidth, targetHeight];
89
+ setElementResolution(playerScreen, tw, th, 'width');
90
+ };
91
+ reset = () => setElementResolution(playerScreen);
92
+ break;
93
+ }
94
+ default:
95
+ apply = () => setElementResolution(playerScreen, targetWidth, targetHeight, mode);
96
+ reset = () => setElementResolution(playerScreen);
97
+ break;
98
+ }
99
+ zoomApply = apply;
100
+ zoomReset = reset;
101
+ apply();
102
+ }
103
+ applyAutoZoom(app?.autoZoom, contextConfig.viewportWidth || 1920, contextConfig.viewportHeight || 1080);
104
+ /**
105
+ * Serialise `dev.logging` into `?logLevels=` and `?logSinks=` URL params.
106
+ */
107
+ function buildLogParams(logging) {
108
+ if (!logging?.level && !logging?.sinks?.length) {
109
+ return { levelsParam: null, sinksParam: null };
110
+ }
111
+ let levelsParam = null;
112
+ if (logging.level) {
113
+ const parts = [logging.level];
114
+ if (logging.namespaces) {
115
+ for (const [ns, lvl] of Object.entries(logging.namespaces)) {
116
+ parts.push(`${ns}:${lvl}`);
117
+ }
118
+ }
119
+ levelsParam = parts.join(",");
120
+ }
121
+ const sinksParam = logging.sinks?.length ? logging.sinks.join(",") : null;
122
+ return { levelsParam, sinksParam };
123
+ }
124
+ function setIframeSrc({ levelsParam, sinksParam }) {
125
+ const src = new URL(appUrl);
126
+ if (levelsParam)
127
+ src.searchParams.set("logLevels", levelsParam);
128
+ if (sinksParam)
129
+ src.searchParams.set("logSinks", sinksParam);
130
+ iframe.src = src.href;
131
+ }
132
+ // ═══════════════════════════════════════════════════════════════════════════
133
+ // Session management
134
+ // ═══════════════════════════════════════════════════════════════════════════
135
+ let currentHost = null;
136
+ let currentTransport = null;
137
+ let currentRelay = null;
138
+ let droppedCountInterval = null;
139
+ /**
140
+ * Create (or recreate) a local mock player session.
141
+ *
142
+ * Normally called before setIframeSrc() so the transport listener is
143
+ * in place when the app sends HELLO. Also called by the load handler
144
+ * on unexpected reloads (manual refresh) to replace the stale session.
145
+ *
146
+ * Only applicable to non-relay mode.
147
+ */
148
+ function createLocalSession() {
149
+ currentHost?.destroy();
150
+ currentTransport?.close();
151
+ logger.info("Initializing local mock player...");
152
+ const rawTransport = createPostMessageTransport({
153
+ targetWindow: iframe.contentWindow,
154
+ targetOrigin: new URL(appUrl).origin,
155
+ });
156
+ const transport = currentTransport = createInterceptingTransport(rawTransport, (msg, dir) => {
157
+ overlay.logMessage(dir, msg);
158
+ });
159
+ const host = currentHost = createMockPlayer({
160
+ capabilities: capabilityConfig,
161
+ transport,
162
+ notifyComplete: () => {
163
+ logger.info("App called notifyComplete()");
164
+ const hideDelay = lifecycle?.hideDelay ?? 100;
165
+ const stopDelay = lifecycle?.stopDelay ?? 50;
166
+ setTimeout(() => {
167
+ logger.debug("fireHide");
168
+ host.fireHide();
169
+ }, hideDelay);
170
+ setTimeout(() => {
171
+ logger.debug("fireStop");
172
+ host.fireStop();
173
+ }, hideDelay + stopDelay);
174
+ },
175
+ notifyEstimatedEnd: (expectedFinishTime) => {
176
+ const secsUntil = Math.round((expectedFinishTime - Date.now()) / 1000);
177
+ logger.info(`App called notifyEstimatedEnd() — finishes in ~${secsUntil}s (${new Date(expectedFinishTime).toISOString()})`);
178
+ },
179
+ notifyNoContent: (options) => {
180
+ const parts = ["App called notifyNoContent()"];
181
+ if (options?.reason)
182
+ parts.push(`reason="${options.reason}"`);
183
+ if (options?.retryNotBefore) {
184
+ const secsUntil = Math.round((options.retryNotBefore - Date.now()) / 1000);
185
+ parts.push(`retry in ~${secsUntil}s`);
186
+ }
187
+ logger.info(parts.join(" — "));
188
+ const hideDelay = lifecycle?.hideDelay ?? 100;
189
+ const stopDelay = lifecycle?.stopDelay ?? 50;
190
+ setTimeout(() => {
191
+ logger.debug("fireHide");
192
+ host.fireHide();
193
+ }, hideDelay);
194
+ setTimeout(() => {
195
+ logger.debug("fireStop");
196
+ host.fireStop();
197
+ }, hideDelay + stopDelay);
198
+ },
199
+ log: (level, message, data) => {
200
+ console[level](`[Standalone Dev Player Log Service] ${message}`, data !== undefined ? data : "");
201
+ },
202
+ });
203
+ host.player.onStop(() => {
204
+ overlay.setCompleted();
205
+ showCompletionNotification();
206
+ });
207
+ logger.debug("AppHost created");
208
+ }
209
+ function fireLifecycle() {
210
+ const capabilities = resolveCapabilities(capabilityConfig);
211
+ const appContext = buildAppContext(contextConfig, capabilities);
212
+ const initDelay = lifecycle?.initDelay ?? 0;
213
+ const showDelay = lifecycle?.showDelay ?? 50;
214
+ const startDelay = lifecycle?.startDelay ?? 50;
215
+ setTimeout(() => { logger.debug("fireInit"); currentHost.fireInit(appContext); }, initDelay);
216
+ setTimeout(() => { logger.debug("fireShow"); currentHost.fireShow(); }, initDelay + showDelay);
217
+ setTimeout(() => { logger.debug("fireStart"); currentHost.fireStart(); }, initDelay + showDelay + startDelay);
218
+ logger.info("Mock player ready");
219
+ logger.debug("Context", appContext);
220
+ logger.debug("Capabilities", capabilities);
221
+ }
222
+ async function setupRelaySession() {
223
+ logger.info(`Starting relay to ${relay.playerUrl} (${relay.transport})`);
224
+ const { createRelayTransport, createHttpSseTransport, createHttpPollingTransport, } = await import("@appstrata/protocol");
225
+ const sessionId = "relay-" + Date.now() + "-" + Math.random().toString(36).substr(2, 9);
226
+ const targetOrigin = new URL(appUrl).origin;
227
+ const appTransport = createPostMessageTransport({
228
+ targetWindow: iframe.contentWindow,
229
+ targetOrigin,
230
+ });
231
+ const httpTransportFactory = relay.transport === "polling"
232
+ ? createHttpPollingTransport
233
+ : createHttpSseTransport;
234
+ const hostTransport = httpTransportFactory({
235
+ sendUrl: `${relay.playerUrl}/api/send`,
236
+ receiveUrl: `${relay.playerUrl}/api/receive`,
237
+ sessionId,
238
+ timeout: 30000,
239
+ });
240
+ // Clean up previous relay — also removes the old postMessage listener
241
+ // so it doesn't intercept messages from the new iframe load.
242
+ if (droppedCountInterval !== null)
243
+ clearInterval(droppedCountInterval);
244
+ currentRelay?.close();
245
+ const relayInstance = createRelayTransport({
246
+ appTransport,
247
+ hostTransport,
248
+ onMessage: (msg, direction) => {
249
+ const dir = direction;
250
+ overlay.logMessage(dir, msg);
251
+ if (msg.type === "EVENT" && msg.name === "stop") {
252
+ overlay.setCompleted();
253
+ showCompletionNotification();
254
+ }
255
+ return msg;
256
+ },
257
+ });
258
+ currentRelay = relayInstance;
259
+ // Expose relay globally for debugging
260
+ window.__APPSTRATA_RELAY__ = relayInstance;
261
+ // Periodically sync the relay's dropped count into the overlay
262
+ droppedCountInterval = setInterval(() => overlay.setDroppedCount(relayInstance.getStats().dropped), 1000);
263
+ logger.info(`Relay active (session: ${sessionId})`);
264
+ }
265
+ // ═══════════════════════════════════════════════════════════════════════════
266
+ // Initial load
267
+ // ═══════════════════════════════════════════════════════════════════════════
268
+ // Create the session before loading the iframe so the transport listener
269
+ // is registered on window before the app sends HELLO. This is not strictly
270
+ // necessary, since the protocol handles any order of initialization, but it's
271
+ // nice to see the HELLO in action.
272
+ // True when createLocalSession() is called before setIframeSrc(),
273
+ // which is the normal case (initial load and HMR). After the app
274
+ // is loaded the flag is set to false.
275
+ // Only applicable to non-relay mode.
276
+ let sessionRefreshedBeforeAppLoad = false;
277
+ if (!relay) {
278
+ createLocalSession();
279
+ sessionRefreshedBeforeAppLoad = true;
280
+ }
281
+ const initialLogParams = buildLogParams(devConfig?.logging);
282
+ setIframeSrc(initialLogParams);
283
+ logger.info(`Loading app from ${iframe.src}`);
284
+ // ═══════════════════════════════════════════════════════════════════════════
285
+ // HMR
286
+ // ═══════════════════════════════════════════════════════════════════════════
287
+ if (import.meta.hot) {
288
+ let currentLogKey = JSON.stringify(initialLogParams);
289
+ import.meta.hot.on(CONFIG_CHANGE_EVENT, (data) => {
290
+ logger.info("Config changed via HMR, updating player UI");
291
+ contextConfig = data.context;
292
+ overlay.updateContext({ ...data.context, autoZoom: data.app?.autoZoom });
293
+ overlay.setDropLogMessages(!(data.dev?.messages?.showLogMessages ?? false));
294
+ applyAutoZoom(data.app?.autoZoom, data.context.viewportWidth || 1920, data.context.viewportHeight || 1080);
295
+ const newLogParams = buildLogParams(data.dev?.logging);
296
+ const newLogKey = JSON.stringify(newLogParams);
297
+ const loggingChanged = newLogKey !== currentLogKey;
298
+ if (loggingChanged) {
299
+ currentLogKey = newLogKey;
300
+ // Fresh session BEFORE reload so the listener catches HELLO.
301
+ if (!relay) {
302
+ createLocalSession();
303
+ sessionRefreshedBeforeAppLoad = true;
304
+ }
305
+ setIframeSrc(newLogParams);
306
+ return;
307
+ }
308
+ if (!relay && currentHost) {
309
+ // Hot-swap context/capabilities in-place (no iframe reload needed).
310
+ const newCapabilities = resolveCapabilities(data.capabilities);
311
+ const newServices = buildCapabilityServices(newCapabilities);
312
+ currentHost.updateServices(newServices);
313
+ const newContext = buildAppContext(data.context, newCapabilities);
314
+ currentHost.updateContext(newContext);
315
+ }
316
+ });
317
+ }
318
+ // ═══════════════════════════════════════════════════════════════════════════
319
+ // Iframe load handler
320
+ // ═══════════════════════════════════════════════════════════════════════════
321
+ iframe.addEventListener("load", async () => {
322
+ logger.info("App loaded...");
323
+ loading.classList.add("hidden");
324
+ requestAnimationFrame(() => overlay.updateResolution());
325
+ if (relay) {
326
+ await setupRelaySession();
327
+ }
328
+ else {
329
+ // On unexpected reloads (manual refresh) replace the stale session.
330
+ if (!sessionRefreshedBeforeAppLoad) {
331
+ createLocalSession();
332
+ }
333
+ sessionRefreshedBeforeAppLoad = false;
334
+ fireLifecycle();
335
+ }
336
+ });
337
+ // ═══════════════════════════════════════════════════════════════════════════
338
+ // Iframe error handler
339
+ // ═══════════════════════════════════════════════════════════════════════════
340
+ iframe.addEventListener("error", (e) => {
341
+ logger.error("Failed to load app", e);
342
+ loading.innerHTML = `
343
+ <div style="color: #ff6b6b;">
344
+ <p>❌ Failed to load app</p>
345
+ <p style="font-size: 14px; margin-top: 10px;">Check that your dev server is running at:</p>
346
+ <code style="background: #16213e; padding: 8px; border-radius: 4px; display: inline-block; margin-top: 8px;">
347
+ ${appUrl}
348
+ </code>
349
+ </div>
350
+ `;
351
+ });
352
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/player/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ html, body {
8
+ margin: 0;
9
+ height: 100%;
10
+ }
11
+
12
+ body {
13
+ background: #1a1a2e;
14
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
15
+ overflow: hidden;
16
+ }
17
+
18
+ .player-container {
19
+ position: relative;
20
+ background: #0f0f1e;
21
+ overflow: hidden;
22
+ width: 100%;
23
+ height: 100%;
24
+ }
25
+
26
+ .player-screen {
27
+ position: relative;
28
+ background: black;
29
+ width: 100%;
30
+ height: 100%;
31
+ overflow: hidden;
32
+ }
33
+
34
+ iframe {
35
+ width: 100%;
36
+ height: 100%;
37
+ border: none;
38
+ display: block;
39
+ background: black;
40
+ }
41
+
42
+ .loading {
43
+ position: absolute;
44
+ top: 50%;
45
+ left: 50%;
46
+ transform: translate(-50%, -50%);
47
+ color: white;
48
+ font-size: 16px;
49
+ display: flex;
50
+ flex-direction: column;
51
+ align-items: center;
52
+ gap: 20px;
53
+ z-index: 10;
54
+ }
55
+
56
+ .spinner {
57
+ width: 40px;
58
+ height: 40px;
59
+ border: 3px solid rgba(255, 255, 255, 0.1);
60
+ border-top-color: #00d4ff;
61
+ border-radius: 50%;
62
+ animation: spin 1s linear infinite;
63
+ }
64
+
65
+ @keyframes spin {
66
+ to {
67
+ transform: rotate(360deg);
68
+ }
69
+ }
70
+
71
+ .loading p {
72
+ margin: 0;
73
+ opacity: 0.8;
74
+ }
75
+
76
+ /* Hide loading when iframe is ready */
77
+ .loading.hidden {
78
+ display: none;
79
+ }
80
+