@playruo/embed 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -74,15 +74,18 @@ var PlayruoEmbed = ({
74
74
  onReady,
75
75
  onStateChange,
76
76
  onDemoCompleted,
77
+ onEvent,
77
78
  onPurchaseRequest,
78
79
  onError,
80
+ forwardEventsToParent = true,
79
81
  renderState,
80
82
  iframeRef: externalIframeRef
81
83
  }) => {
82
84
  const internalIframeRef = (0, import_react.useRef)(null);
83
85
  const iframeRef = externalIframeRef || internalIframeRef;
84
86
  const wasEmbedFullscreenRef = (0, import_react.useRef)(false);
85
- const wasEmbedPointerLockedRef = (0, import_react.useRef)(false);
87
+ const hasNotifiedCompletionRef = (0, import_react.useRef)(false);
88
+ const suppressNextFullscreenExitCleanupRef = (0, import_react.useRef)(false);
86
89
  const [isIframeLoaded, setIsIframeLoaded] = (0, import_react.useState)(false);
87
90
  const [hasPlayedOnce, setHasPlayedOnce] = (0, import_react.useState)(false);
88
91
  const [showGameIframe, setShowGameIframe] = (0, import_react.useState)(false);
@@ -152,6 +155,149 @@ var PlayruoEmbed = ({
152
155
  console.warn("[PlayruoEmbed] Failed to release Escape key lock:", error);
153
156
  }
154
157
  }, [getKeyboardApi]);
158
+ const getFullscreenElement = (0, import_react.useCallback)(() => {
159
+ const doc = document;
160
+ return doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement || null;
161
+ }, []);
162
+ const notifyDemoCompletedOnce = (0, import_react.useCallback)(
163
+ (payload) => {
164
+ if (hasNotifiedCompletionRef.current) {
165
+ return;
166
+ }
167
+ hasNotifiedCompletionRef.current = true;
168
+ onDemoCompleted?.(
169
+ payload ?? {
170
+ reason: "Demo session ended",
171
+ storeUrl
172
+ }
173
+ );
174
+ },
175
+ [onDemoCompleted, storeUrl]
176
+ );
177
+ const postExitFullscreenToIframe = (0, import_react.useCallback)(() => {
178
+ const iframe = iframeRef.current;
179
+ if (!iframe) {
180
+ return;
181
+ }
182
+ try {
183
+ iframe.contentWindow?.postMessage({
184
+ type: "playruo",
185
+ action: "exitFullscreen",
186
+ fromEmbed: true,
187
+ isFullscreen: false,
188
+ parentWillHandleFullscreen: true
189
+ }, "*");
190
+ } catch {
191
+ console.error("[PlayruoEmbed] Failed to exit fullscreen");
192
+ }
193
+ }, [iframeRef]);
194
+ const postExitFullscreenAckToIframe = (0, import_react.useCallback)(() => {
195
+ const iframe = iframeRef.current;
196
+ if (!iframe) {
197
+ return;
198
+ }
199
+ try {
200
+ iframe.contentWindow?.postMessage({ type: "ACK_EXIT_FULLSCREEN" }, "*");
201
+ } catch {
202
+ console.error("[PlayruoEmbed] Failed to acknowledge fullscreen exit");
203
+ }
204
+ }, [iframeRef]);
205
+ const exitEmbedFullscreen = (0, import_react.useCallback)(
206
+ async ({
207
+ reason = "cleanup",
208
+ suppressFullscreenExitCleanup = true
209
+ } = {}) => {
210
+ const fullscreenElement = getFullscreenElement();
211
+ const doc = document;
212
+ const exitFullscreen = doc.exitFullscreen?.bind(doc) ?? doc.webkitExitFullscreen?.bind(doc) ?? doc.mozCancelFullScreen?.bind(doc) ?? doc.msExitFullscreen?.bind(doc);
213
+ const shouldAttemptExit = Boolean(fullscreenElement || wasEmbedFullscreenRef.current);
214
+ if (!shouldAttemptExit || !exitFullscreen) {
215
+ console.log("[PlayruoEmbed] Parent fullscreen exit skipped", {
216
+ hasFullscreenElement: Boolean(fullscreenElement),
217
+ hasExitFullscreen: Boolean(exitFullscreen),
218
+ reason,
219
+ wasEmbedFullscreen: wasEmbedFullscreenRef.current
220
+ });
221
+ return false;
222
+ }
223
+ console.log("[PlayruoEmbed] Parent fullscreen exit requested", {
224
+ fullscreenElement: fullscreenElement?.tagName,
225
+ reason,
226
+ wasEmbedFullscreen: wasEmbedFullscreenRef.current
227
+ });
228
+ const shouldSuppressFullscreenExitCleanup = Boolean(
229
+ suppressFullscreenExitCleanup && fullscreenElement
230
+ );
231
+ if (shouldSuppressFullscreenExitCleanup) {
232
+ suppressNextFullscreenExitCleanupRef.current = true;
233
+ wasEmbedFullscreenRef.current = false;
234
+ }
235
+ try {
236
+ await exitFullscreen();
237
+ if (shouldSuppressFullscreenExitCleanup) {
238
+ window.setTimeout(() => {
239
+ suppressNextFullscreenExitCleanupRef.current = false;
240
+ }, 1e3);
241
+ }
242
+ return true;
243
+ } catch (error) {
244
+ if (shouldSuppressFullscreenExitCleanup) {
245
+ suppressNextFullscreenExitCleanupRef.current = false;
246
+ wasEmbedFullscreenRef.current = Boolean(getFullscreenElement());
247
+ }
248
+ console.warn("[PlayruoEmbed] Failed to leave fullscreen:", error);
249
+ return false;
250
+ }
251
+ },
252
+ [getFullscreenElement]
253
+ );
254
+ const cleanupEmbedSession = (0, import_react.useCallback)(
255
+ ({
256
+ payload,
257
+ notifyCompletion = true,
258
+ requestFullscreenExit = true,
259
+ notifyIframe = false,
260
+ markPlayed = true
261
+ } = {}) => {
262
+ setShowGameIframe(false);
263
+ if (markPlayed) {
264
+ setHasPlayedOnce(true);
265
+ }
266
+ releaseEscapeKeyLock();
267
+ if (notifyIframe) {
268
+ postExitFullscreenToIframe();
269
+ }
270
+ if (requestFullscreenExit) {
271
+ void exitEmbedFullscreen();
272
+ }
273
+ if (notifyCompletion) {
274
+ notifyDemoCompletedOnce(payload);
275
+ }
276
+ },
277
+ [
278
+ exitEmbedFullscreen,
279
+ notifyDemoCompletedOnce,
280
+ postExitFullscreenToIframe,
281
+ releaseEscapeKeyLock
282
+ ]
283
+ );
284
+ const markStreamActive = (0, import_react.useCallback)(() => {
285
+ hasNotifiedCompletionRef.current = false;
286
+ setShowGameIframe(true);
287
+ }, []);
288
+ const forwardEventToParent = (0, import_react.useCallback)(
289
+ (message) => {
290
+ if (!forwardEventsToParent || typeof window === "undefined" || window.parent === window) {
291
+ return;
292
+ }
293
+ try {
294
+ window.parent.postMessage(message, "*");
295
+ } catch (error) {
296
+ console.warn("[PlayruoEmbed] Failed to forward event to parent:", error);
297
+ }
298
+ },
299
+ [forwardEventsToParent]
300
+ );
155
301
  const lockEscapeKey = (0, import_react.useCallback)(async () => {
156
302
  const keyboard = getKeyboardApi();
157
303
  if (!keyboard?.lock) {
@@ -164,55 +310,8 @@ var PlayruoEmbed = ({
164
310
  console.warn("[PlayruoEmbed] Failed to lock Escape key:", error);
165
311
  }
166
312
  }, [getKeyboardApi]);
167
- const getPointerLockElement = (0, import_react.useCallback)(() => {
168
- const doc = document;
169
- return doc.pointerLockElement || doc.mozPointerLockElement || doc.webkitPointerLockElement || null;
170
- }, []);
171
- const releasePointerLock = (0, import_react.useCallback)(() => {
172
- const doc = document;
173
- const exitPointerLock = doc.exitPointerLock?.bind(doc) || doc.mozExitPointerLock?.bind(doc) || doc.webkitExitPointerLock?.bind(doc);
174
- try {
175
- exitPointerLock?.();
176
- wasEmbedPointerLockedRef.current = false;
177
- } catch (error) {
178
- console.warn("[PlayruoEmbed] Failed to release pointer lock:", error);
179
- }
180
- }, []);
181
313
  const requestImmersiveMode = (0, import_react.useCallback)(
182
314
  async (iframe) => {
183
- const pointerTarget = iframe;
184
- if (pointerTarget.requestPointerLock) {
185
- try {
186
- const pointerLockResult = pointerTarget.requestPointerLock({ unadjustedMovement: true });
187
- if (pointerLockResult && typeof pointerLockResult.then === "function") {
188
- void pointerLockResult.then(() => {
189
- wasEmbedPointerLockedRef.current = true;
190
- console.log("[PlayruoEmbed] Pointer locked on iframe");
191
- }).catch(async (error) => {
192
- if (error instanceof DOMException && error.name === "NotSupportedError") {
193
- try {
194
- const fallbackResult = pointerTarget.requestPointerLock?.();
195
- if (fallbackResult && typeof fallbackResult.then === "function") {
196
- await fallbackResult;
197
- }
198
- wasEmbedPointerLockedRef.current = true;
199
- console.log("[PlayruoEmbed] Pointer locked on iframe without raw input");
200
- return;
201
- } catch (fallbackError) {
202
- console.warn("[PlayruoEmbed] Failed to lock pointer without raw input:", fallbackError);
203
- return;
204
- }
205
- }
206
- console.warn("[PlayruoEmbed] Failed to lock pointer:", error);
207
- });
208
- } else {
209
- wasEmbedPointerLockedRef.current = true;
210
- console.log("[PlayruoEmbed] Pointer lock requested on iframe");
211
- }
212
- } catch (error) {
213
- console.warn("[PlayruoEmbed] Failed to request pointer lock:", error);
214
- }
215
- }
216
315
  const fullscreenPromise = iframe.requestFullscreen();
217
316
  try {
218
317
  await fullscreenPromise;
@@ -220,11 +319,10 @@ var PlayruoEmbed = ({
220
319
  console.log("[PlayruoEmbed] Fullscreen granted");
221
320
  } catch (error) {
222
321
  releaseEscapeKeyLock();
223
- releasePointerLock();
224
322
  throw error;
225
323
  }
226
324
  },
227
- [releaseEscapeKeyLock, releasePointerLock]
325
+ [releaseEscapeKeyLock]
228
326
  );
229
327
  const handleTryNow = (0, import_react.useCallback)(async () => {
230
328
  if (!isCompatible) {
@@ -236,6 +334,7 @@ var PlayruoEmbed = ({
236
334
  console.warn("[PlayruoEmbed] Iframe not ready");
237
335
  return;
238
336
  }
337
+ hasNotifiedCompletionRef.current = false;
239
338
  setShowGameIframe(true);
240
339
  try {
241
340
  try {
@@ -259,6 +358,7 @@ var PlayruoEmbed = ({
259
358
  setHasPlayedOnce(true);
260
359
  } catch (error) {
261
360
  console.error("[PlayruoEmbed] Failed to start game:", error);
361
+ setShowGameIframe(false);
262
362
  setErrorMessage("Failed to start game");
263
363
  onError?.("Failed to start game");
264
364
  }
@@ -282,7 +382,8 @@ var PlayruoEmbed = ({
282
382
  }, [isReady, onReady]);
283
383
  (0, import_react.useEffect)(() => {
284
384
  const handleGameMessage = (event) => {
285
- const { type, data } = event.data || {};
385
+ const message = event.data || {};
386
+ const { type, data } = message;
286
387
  const iframe = iframeRef.current;
287
388
  if (!iframe || event.source !== iframe.contentWindow) {
288
389
  return;
@@ -303,8 +404,7 @@ var PlayruoEmbed = ({
303
404
  onPurchaseRequest?.(data);
304
405
  } else if (type === "playruo_demo_completed" && data) {
305
406
  console.log("\u{1F537} [Embed] playruo_demo_completed received:", data);
306
- setHasPlayedOnce(true);
307
- onDemoCompleted?.(data);
407
+ cleanupEmbedSession({ payload: data });
308
408
  } else if (type === "playruo_error") {
309
409
  const errorMessage2 = typeof data === "string" ? data : data?.message || "Unknown error";
310
410
  console.log("\u{1F537} [Embed] playruo_error received:", errorMessage2);
@@ -325,25 +425,65 @@ var PlayruoEmbed = ({
325
425
  onError?.(errorMessage2);
326
426
  } else if (type === "playruo_session_over" && data) {
327
427
  console.log("\u{1F537} [Embed] playruo_session_over received:", data);
328
- setHasPlayedOnce(true);
428
+ void exitEmbedFullscreen({ reason: "playruo_session_over" });
329
429
  setIframeState(data);
330
430
  onStateChange?.(data);
431
+ cleanupEmbedSession({ requestFullscreenExit: false });
331
432
  } else if (type === "playruo_stream_start") {
332
433
  console.log("\u{1F537} [Embed] playruo_stream_start received");
434
+ markStreamActive();
435
+ } else if (type === "playruo_stream_stop") {
436
+ console.log("\u{1F537} [Embed] playruo_stream_stop received");
437
+ void exitEmbedFullscreen({ reason: "playruo_stream_stop" });
438
+ cleanupEmbedSession({
439
+ markPlayed: false,
440
+ notifyCompletion: false,
441
+ requestFullscreenExit: false
442
+ });
443
+ } else if (type === "REQUEST_EXIT_FULLSCREEN") {
444
+ console.log("\u{1F537} [Embed] REQUEST_EXIT_FULLSCREEN received");
445
+ void exitEmbedFullscreen({
446
+ reason: "REQUEST_EXIT_FULLSCREEN",
447
+ suppressFullscreenExitCleanup: false
448
+ }).finally(postExitFullscreenAckToIframe);
449
+ } else if (type === "playruo_event" && data && typeof data === "object") {
450
+ console.log("\u{1F537} [Embed] playruo_event received:", data);
451
+ const eventPayload = data;
452
+ onEvent?.(eventPayload);
453
+ forwardEventToParent({ type: "playruo_event", data: eventPayload });
454
+ } else if (type === "playruo" && message.action === "event" && message.payload && typeof message.payload === "object") {
455
+ console.log("\u{1F537} [Embed] legacy playruo event received:", message.payload);
456
+ const eventPayload = message.payload;
457
+ onEvent?.(eventPayload);
458
+ forwardEventToParent({ type: "playruo_event", data: eventPayload });
333
459
  }
334
460
  if (type === "error" || type === "closeForBuy" || type === "goToStore") {
335
461
  console.log("\u{1F537} [Embed] Legacy message received:", type);
336
- setHasPlayedOnce(true);
337
- const payload = {
338
- reason: "Demo session ended",
339
- storeUrl
340
- };
341
- onDemoCompleted?.(payload);
462
+ cleanupEmbedSession({
463
+ payload: {
464
+ reason: "Demo session ended",
465
+ storeUrl
466
+ }
467
+ });
342
468
  }
343
469
  };
344
470
  window.addEventListener("message", handleGameMessage);
345
471
  return () => window.removeEventListener("message", handleGameMessage);
346
- }, [storeUrl, onDemoCompleted, onReady, onStateChange, onPurchaseRequest, onError, iframeRef]);
472
+ }, [
473
+ cleanupEmbedSession,
474
+ exitEmbedFullscreen,
475
+ forwardEventToParent,
476
+ hasPlayedOnce,
477
+ iframeRef,
478
+ markStreamActive,
479
+ onEvent,
480
+ onError,
481
+ onPurchaseRequest,
482
+ onReady,
483
+ onStateChange,
484
+ postExitFullscreenAckToIframe,
485
+ storeUrl
486
+ ]);
347
487
  const generateUUID = () => {
348
488
  if (self.crypto.randomUUID) return self.crypto.randomUUID();
349
489
  const bytes = new Uint8Array(16);
@@ -390,9 +530,8 @@ var PlayruoEmbed = ({
390
530
  }, [iframeRef]);
391
531
  (0, import_react.useEffect)(() => {
392
532
  const handleFullscreenChange = () => {
393
- const doc = document;
394
533
  const iframe = iframeRef.current;
395
- const fullscreenElement = doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement || null;
534
+ const fullscreenElement = getFullscreenElement();
396
535
  const isEmbedFullscreen = Boolean(iframe && fullscreenElement === iframe);
397
536
  if (isEmbedFullscreen) {
398
537
  wasEmbedFullscreenRef.current = true;
@@ -409,30 +548,15 @@ var PlayruoEmbed = ({
409
548
  return;
410
549
  }
411
550
  releaseEscapeKeyLock();
551
+ if (suppressNextFullscreenExitCleanupRef.current) {
552
+ suppressNextFullscreenExitCleanupRef.current = false;
553
+ return;
554
+ }
412
555
  if (wasEmbedFullscreenRef.current) {
413
556
  wasEmbedFullscreenRef.current = false;
414
- const isEmbedPointerLocked = Boolean(iframe && getPointerLockElement() === iframe);
415
- if (wasEmbedPointerLockedRef.current || isEmbedPointerLocked) {
416
- releasePointerLock();
417
- }
418
- if (iframe) {
419
- try {
420
- iframe.contentWindow?.postMessage({
421
- type: "playruo",
422
- action: "exitFullscreen",
423
- fromEmbed: true,
424
- isFullscreen: false,
425
- parentWillHandleFullscreen: true
426
- }, "*");
427
- } catch {
428
- console.error("[PlayruoEmbed] Failed to exit fullscreen");
429
- }
430
- }
431
- setShowGameIframe(false);
432
- setHasPlayedOnce(true);
433
- onDemoCompleted?.({
434
- reason: "Demo session ended",
435
- storeUrl
557
+ cleanupEmbedSession({
558
+ notifyIframe: true,
559
+ requestFullscreenExit: false
436
560
  });
437
561
  }
438
562
  };
@@ -446,33 +570,18 @@ var PlayruoEmbed = ({
446
570
  document.removeEventListener("mozfullscreenchange", handleFullscreenChange);
447
571
  document.removeEventListener("MSFullscreenChange", handleFullscreenChange);
448
572
  };
449
- }, [storeUrl, onDemoCompleted, iframeRef, getPointerLockElement, lockEscapeKey, releaseEscapeKeyLock, releasePointerLock]);
450
- (0, import_react.useEffect)(() => {
451
- const handlePointerLockChange = () => {
452
- const iframe = iframeRef.current;
453
- wasEmbedPointerLockedRef.current = Boolean(iframe && getPointerLockElement() === iframe);
454
- };
455
- const handlePointerLockError = () => {
456
- wasEmbedPointerLockedRef.current = false;
457
- console.warn("[PlayruoEmbed] Pointer lock request was rejected");
458
- };
459
- document.addEventListener("pointerlockchange", handlePointerLockChange);
460
- document.addEventListener("pointerlockerror", handlePointerLockError);
461
- return () => {
462
- document.removeEventListener("pointerlockchange", handlePointerLockChange);
463
- document.removeEventListener("pointerlockerror", handlePointerLockError);
464
- };
465
- }, [getPointerLockElement, iframeRef]);
573
+ }, [
574
+ cleanupEmbedSession,
575
+ getFullscreenElement,
576
+ iframeRef,
577
+ lockEscapeKey,
578
+ releaseEscapeKeyLock
579
+ ]);
466
580
  (0, import_react.useEffect)(() => {
467
581
  return () => {
468
582
  releaseEscapeKeyLock();
469
- const iframe = iframeRef.current;
470
- const isEmbedPointerLocked = Boolean(iframe && getPointerLockElement() === iframe);
471
- if (wasEmbedPointerLockedRef.current || isEmbedPointerLocked) {
472
- releasePointerLock();
473
- }
474
583
  };
475
- }, [getPointerLockElement, iframeRef, releaseEscapeKeyLock, releasePointerLock]);
584
+ }, [releaseEscapeKeyLock]);
476
585
  (0, import_react.useEffect)(() => {
477
586
  const logCrossOriginStatus = () => {
478
587
  const isIsolated = self.crossOriginIsolated;
@@ -552,6 +661,8 @@ var PlayruoEmbed = ({
552
661
  }
553
662
  }, [isReady]);
554
663
  const isAvailable = Boolean(isReady && isCompatible && hasIframeConfirmed);
664
+ const shouldDisplayGameIframe = showGameIframe;
665
+ const shouldShowIframeMisuseMessage = Boolean(showIframe && !showGameIframe);
555
666
  const renderContent = () => {
556
667
  if (!renderState) return null;
557
668
  const canTryNow = Boolean(
@@ -581,6 +692,26 @@ var PlayruoEmbed = ({
581
692
  };
582
693
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, children: [
583
694
  renderContent(),
695
+ isCompatible && shouldShowIframeMisuseMessage && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
696
+ "div",
697
+ {
698
+ role: "status",
699
+ style: {
700
+ alignItems: "center",
701
+ background: "#000",
702
+ color: "#fff",
703
+ display: "flex",
704
+ fontFamily: "system-ui, sans-serif",
705
+ fontSize: 14,
706
+ height: "100%",
707
+ justifyContent: "center",
708
+ padding: 24,
709
+ textAlign: "center",
710
+ width: "100%"
711
+ },
712
+ children: "This iframe is only meant to be displayed through PlayruoEmbed during an active stream."
713
+ }
714
+ ),
584
715
  isCompatible && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
585
716
  "iframe",
586
717
  {
@@ -593,9 +724,9 @@ var PlayruoEmbed = ({
593
724
  title: "Playruo",
594
725
  referrerPolicy: "strict-origin-when-cross-origin",
595
726
  style: {
596
- // Only mounted when compatible; visible after click (internal) or if parent explicitly asks
597
- opacity: showGameIframe || showIframe ? 1 : 0,
598
- pointerEvents: showGameIframe || showIframe ? "auto" : "none",
727
+ // The iframe stays mounted for readiness checks, visible only during launch/play, and hidden after terminal events.
728
+ opacity: shouldDisplayGameIframe ? 1 : 0,
729
+ pointerEvents: shouldDisplayGameIframe ? "auto" : "none",
599
730
  width: "100%",
600
731
  height: "100%",
601
732
  border: 0
@@ -622,36 +753,61 @@ var PlayruoEmbed = ({
622
753
  // src/react/embed-tester.tsx
623
754
  var import_react2 = __toESM(require("react"), 1);
624
755
  var import_jsx_runtime2 = require("react/jsx-runtime");
756
+ var createInitialEmbedState = () => ({
757
+ buyNow: { available: false, status: "Not ready" },
758
+ hasPlayedOnce: false,
759
+ isReady: false,
760
+ tryNow: { available: false, status: "Not ready" }
761
+ });
762
+ var createInitialDiagnostics = () => ({
763
+ chromiumBrowser: false,
764
+ crossOriginIsolation: false,
765
+ desktopBrowser: false,
766
+ fullscreenSupported: false,
767
+ gamepadSupported: false,
768
+ iframeAllowsFullscreen: false,
769
+ iframeAllowsGamepad: false,
770
+ iframeAllowsPointerLock: false,
771
+ keyboardLockSupported: false,
772
+ localStorageSupported: false,
773
+ pointerLockSupported: false,
774
+ secureContext: false,
775
+ sharedArrayBufferSupported: false,
776
+ topLevelContext: false,
777
+ webAssemblySupported: false,
778
+ webgl2Supported: false
779
+ });
625
780
  var EmbedTester = (props = {}) => {
626
- const [operation, setOperation] = (0, import_react2.useState)(props.operation ?? "iframe");
627
- const [storeUrl, setStoreUrl] = (0, import_react2.useState)(
781
+ const [operationDraft, setOperationDraft] = (0, import_react2.useState)(props.operation ?? "iframe");
782
+ const [storeUrlDraft, setStoreUrlDraft] = (0, import_react2.useState)(
783
+ props.storeUrl ?? "https://store.steampowered.com/app/123456"
784
+ );
785
+ const [appliedOperation, setAppliedOperation] = (0, import_react2.useState)(props.operation ?? "iframe");
786
+ const [appliedStoreUrl, setAppliedStoreUrl] = (0, import_react2.useState)(
628
787
  props.storeUrl ?? "https://store.steampowered.com/app/123456"
629
788
  );
630
789
  const [key, setKey] = (0, import_react2.useState)(0);
631
790
  const [hasUserClicked, setHasUserClicked] = (0, import_react2.useState)(false);
632
791
  const [showIframe, setShowIframe] = (0, import_react2.useState)(false);
633
- const [embedState, setEmbedState] = (0, import_react2.useState)({
634
- buyNow: { available: false, status: "Not ready" },
635
- hasPlayedOnce: false,
636
- isReady: false,
637
- tryNow: { available: false, status: "Not ready" }
638
- });
792
+ const [embedState, setEmbedState] = (0, import_react2.useState)(createInitialEmbedState);
639
793
  const [eventLog, setEventLog] = (0, import_react2.useState)([]);
640
- const [diagnostics, setDiagnostics] = (0, import_react2.useState)({
641
- crossOriginIsolation: false,
642
- gamepadSupported: false,
643
- fullscreenSupported: false,
644
- keyboardLockSupported: false
645
- });
794
+ const [diagnostics, setDiagnostics] = (0, import_react2.useState)(createInitialDiagnostics);
646
795
  const displayHost = props.baseUrl ? new URL(props.baseUrl).host : "playruo.live";
647
796
  const enableConsoleLog = props.enableConsoleLog ?? false;
648
797
  const hasReadyBeenCalled = (0, import_react2.useRef)(false);
798
+ const nextEventIdRef = (0, import_react2.useRef)(0);
649
799
  const previousStateRef = (0, import_react2.useRef)("");
650
800
  const iframeRef = (0, import_react2.useRef)(null);
801
+ const isOperationPending = operationDraft !== appliedOperation;
802
+ const isStoreUrlPending = storeUrlDraft !== appliedStoreUrl;
803
+ const hasPendingConfigChanges = isOperationPending || isStoreUrlPending;
804
+ const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending);
651
805
  const addEvent = (0, import_react2.useCallback)(
652
806
  (message) => {
653
807
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
654
- setEventLog((prev) => [...prev.slice(-10), { message, timestamp }]);
808
+ const id = nextEventIdRef.current;
809
+ nextEventIdRef.current += 1;
810
+ setEventLog((prev) => [...prev.slice(-10), { id, message, timestamp }]);
655
811
  if (enableConsoleLog) {
656
812
  console.log(`[EmbedTester] ${timestamp}: ${message}`);
657
813
  }
@@ -659,18 +815,48 @@ var EmbedTester = (props = {}) => {
659
815
  [enableConsoleLog]
660
816
  );
661
817
  const runDiagnostics = (0, import_react2.useCallback)(() => {
818
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera || "";
819
+ const isMobileDevice = /android|iPad|iPhone|iPod/i.test(userAgent);
820
+ const hasChrome = !!window.chrome;
821
+ const isChromium = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
822
+ const iframeAllow = iframeRef.current?.getAttribute("allow") ?? "";
823
+ const iframeSandbox = iframeRef.current?.getAttribute("sandbox") ?? "";
824
+ const canvas = document.createElement("canvas");
825
+ let localStorageSupported = false;
826
+ try {
827
+ const storageKey = "__playruo_embed_tester__";
828
+ window.localStorage.setItem(storageKey, "1");
829
+ window.localStorage.removeItem(storageKey);
830
+ localStorageSupported = true;
831
+ } catch {
832
+ localStorageSupported = false;
833
+ }
662
834
  const newDiagnostics = {
835
+ chromiumBrowser: hasChrome || isChromium,
663
836
  crossOriginIsolation: !!(window.crossOriginIsolated || window.crossOriginIsolated),
664
- gamepadSupported: "getGamepads" in navigator,
837
+ desktopBrowser: !isMobileDevice,
665
838
  fullscreenSupported: !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled),
666
- keyboardLockSupported: "keyboard" in navigator && "lock" in navigator.keyboard
839
+ gamepadSupported: "getGamepads" in navigator,
840
+ iframeAllowsFullscreen: iframeAllow.includes("fullscreen"),
841
+ iframeAllowsGamepad: iframeAllow.includes("gamepad"),
842
+ iframeAllowsPointerLock: iframeSandbox.includes("allow-pointer-lock"),
843
+ keyboardLockSupported: "keyboard" in navigator && "lock" in navigator.keyboard,
844
+ localStorageSupported,
845
+ pointerLockSupported: "requestPointerLock" in HTMLIFrameElement.prototype || "requestPointerLock" in HTMLElement.prototype || "pointerLockElement" in document,
846
+ secureContext: window.isSecureContext,
847
+ sharedArrayBufferSupported: typeof SharedArrayBuffer !== "undefined",
848
+ topLevelContext: window.self === window.top,
849
+ webAssemblySupported: typeof WebAssembly !== "undefined",
850
+ webgl2Supported: !!canvas.getContext("webgl2")
667
851
  };
668
852
  setDiagnostics(newDiagnostics);
669
- addEvent(`\u{1F50D} Diagnostics updated - COI: ${newDiagnostics.crossOriginIsolation}, Gamepad: ${newDiagnostics.gamepadSupported}`);
670
- }, [addEvent]);
853
+ addEvent(
854
+ `\u{1F50D} Diagnostics updated - Browser: ${newDiagnostics.desktopBrowser && newDiagnostics.chromiumBrowser}, COI: ${newDiagnostics.crossOriginIsolation}, Fullscreen: ${newDiagnostics.fullscreenSupported}`
855
+ );
856
+ }, [addEvent, iframeRef]);
671
857
  import_react2.default.useEffect(() => {
672
858
  runDiagnostics();
673
- }, [runDiagnostics]);
859
+ }, [key, runDiagnostics]);
674
860
  const handleReady = (0, import_react2.useCallback)(() => {
675
861
  if (!hasReadyBeenCalled.current) {
676
862
  hasReadyBeenCalled.current = true;
@@ -699,284 +885,1081 @@ var EmbedTester = (props = {}) => {
699
885
  console.error("Embed error:", error);
700
886
  }
701
887
  }, [addEvent, enableConsoleLog]);
888
+ const handleEvent = (0, import_react2.useCallback)((data) => {
889
+ const eventName = typeof data.event === "string" ? data.event : typeof data.name === "string" ? data.name : "unknown";
890
+ addEvent(`Event received: ${eventName}`);
891
+ if (enableConsoleLog) {
892
+ console.log("Embed event:", data);
893
+ }
894
+ }, [addEvent, enableConsoleLog]);
702
895
  const handlePurchaseRequest = (0, import_react2.useCallback)((data) => {
703
896
  addEvent(`\u{1F4B3} Explicit purchase request from Buy Now button`);
704
897
  addEvent(`Opening store: ${data.storeUrl}`);
705
898
  window.open(data.storeUrl, "_blank");
706
899
  }, [addEvent]);
707
- const handleRestart = (0, import_react2.useCallback)(() => {
900
+ const resetEmbedSession = (0, import_react2.useCallback)((message) => {
901
+ setEmbedState(createInitialEmbedState());
708
902
  setKey((prev) => prev + 1);
709
903
  setHasUserClicked(false);
710
904
  setShowIframe(false);
711
905
  hasReadyBeenCalled.current = false;
712
906
  previousStateRef.current = "";
713
- addEvent("Embed restarted");
907
+ addEvent(message);
714
908
  }, [addEvent]);
715
- const buttonStyle = {
716
- padding: "8px 16px",
717
- margin: "4px",
718
- border: "1px solid #404040",
719
- borderRadius: "4px",
720
- backgroundColor: "#2a2a2a",
721
- color: "#e5e5e5",
722
- cursor: "pointer",
723
- fontSize: "14px",
724
- transition: "all 0.2s ease"
909
+ const handleApply = (0, import_react2.useCallback)(() => {
910
+ if (!hasPendingConfigChanges) {
911
+ return;
912
+ }
913
+ setAppliedOperation(operationDraft);
914
+ setAppliedStoreUrl(storeUrlDraft);
915
+ resetEmbedSession("Applied updated embed settings");
916
+ }, [hasPendingConfigChanges, operationDraft, resetEmbedSession, storeUrlDraft]);
917
+ const handleRestartSession = (0, import_react2.useCallback)(() => {
918
+ resetEmbedSession("Embed restarted with applied settings");
919
+ }, [resetEmbedSession]);
920
+ const handleResetDrafts = (0, import_react2.useCallback)(() => {
921
+ setOperationDraft(appliedOperation);
922
+ setStoreUrlDraft(appliedStoreUrl);
923
+ addEvent("Reverted draft changes to the applied configuration");
924
+ }, [addEvent, appliedOperation, appliedStoreUrl]);
925
+ const panelStyle = {
926
+ position: "relative",
927
+ border: "1px solid rgba(148, 163, 184, 0.18)",
928
+ borderRadius: "24px",
929
+ padding: "20px",
930
+ background: "linear-gradient(180deg, rgba(15, 23, 42, 0.88) 0%, rgba(7, 12, 24, 0.96) 100%)",
931
+ boxShadow: "0 24px 80px rgba(0, 0, 0, 0.35)",
932
+ backdropFilter: "blur(18px)"
933
+ };
934
+ const sectionTitleStyle = {
935
+ margin: 0,
936
+ fontSize: "18px",
937
+ fontWeight: 600,
938
+ color: "#f8fafc"
725
939
  };
940
+ const labelStyle = {
941
+ display: "flex",
942
+ alignItems: "center",
943
+ justifyContent: "space-between",
944
+ gap: "12px",
945
+ marginBottom: "8px",
946
+ fontSize: "13px",
947
+ fontWeight: 600,
948
+ color: "#dbe7ff"
949
+ };
950
+ const buildIndicatorStyle = (pending) => ({
951
+ display: "inline-flex",
952
+ alignItems: "center",
953
+ gap: "8px",
954
+ padding: "4px 10px",
955
+ borderRadius: "999px",
956
+ border: pending ? "1px solid rgba(251, 191, 36, 0.28)" : "1px solid rgba(74, 222, 128, 0.22)",
957
+ backgroundColor: pending ? "rgba(245, 158, 11, 0.12)" : "rgba(34, 197, 94, 0.12)",
958
+ color: pending ? "#fbbf24" : "#86efac",
959
+ fontSize: "11px",
960
+ fontWeight: 700,
961
+ letterSpacing: "0.08em",
962
+ textTransform: "uppercase"
963
+ });
964
+ const buildIndicatorDotStyle = (pending) => ({
965
+ width: "8px",
966
+ height: "8px",
967
+ borderRadius: "999px",
968
+ backgroundColor: pending ? "#f59e0b" : "#22c55e",
969
+ boxShadow: pending ? "0 0 0 4px rgba(245, 158, 11, 0.12)" : "0 0 0 4px rgba(34, 197, 94, 0.12)"
970
+ });
971
+ const buildInputShellStyle = (pending) => ({
972
+ display: "flex",
973
+ alignItems: "center",
974
+ gap: "12px",
975
+ border: pending ? "1px solid rgba(245, 158, 11, 0.35)" : "1px solid rgba(148, 163, 184, 0.18)",
976
+ borderRadius: "16px",
977
+ padding: "0 14px",
978
+ background: pending ? "rgba(35, 23, 8, 0.72)" : "rgba(15, 23, 42, 0.7)",
979
+ boxShadow: pending ? "0 0 0 1px rgba(245, 158, 11, 0.08)" : "none"
980
+ });
726
981
  const inputStyle = {
727
982
  width: "100%",
728
- padding: "8px",
729
- margin: "4px 0",
730
- border: "1px solid #404040",
731
- borderRadius: "4px",
732
- backgroundColor: "#1a1a1a",
733
- color: "#e5e5e5",
734
- fontSize: "14px"
983
+ padding: "14px 0",
984
+ border: "none",
985
+ outline: "none",
986
+ backgroundColor: "transparent",
987
+ color: "#f8fafc",
988
+ fontSize: "15px"
735
989
  };
736
- const cardStyle = {
737
- border: "1px solid #333333",
738
- borderRadius: "8px",
739
- padding: "16px",
740
- margin: "8px 0",
741
- backgroundColor: "#1a1a1a"
990
+ const helperTextStyle = {
991
+ margin: "8px 0 0 0",
992
+ color: "#94a3b8",
993
+ fontSize: "12px",
994
+ lineHeight: 1.5
742
995
  };
743
- const statusStyle = {
996
+ const codeStyle = {
744
997
  display: "inline-block",
745
- padding: "4px 8px",
746
- margin: "2px",
747
- borderRadius: "12px",
998
+ padding: "2px 8px",
999
+ borderRadius: "999px",
1000
+ backgroundColor: "rgba(148, 163, 184, 0.12)",
1001
+ color: "#e2e8f0",
1002
+ fontSize: "12px",
1003
+ fontFamily: '"SF Mono", "Roboto Mono", monospace',
1004
+ wordBreak: "break-all"
1005
+ };
1006
+ const buildButtonStyle = (variant, disabled = false) => {
1007
+ const palette = {
1008
+ ghost: {
1009
+ background: "rgba(15, 23, 42, 0.5)",
1010
+ border: "1px solid rgba(148, 163, 184, 0.18)",
1011
+ color: "#dbe7ff"
1012
+ },
1013
+ primary: {
1014
+ background: "linear-gradient(135deg, #38bdf8 0%, #2563eb 100%)",
1015
+ border: "1px solid rgba(125, 211, 252, 0.32)",
1016
+ color: "#eff6ff"
1017
+ },
1018
+ secondary: {
1019
+ background: "linear-gradient(135deg, rgba(30, 41, 59, 0.92) 0%, rgba(15, 23, 42, 0.98) 100%)",
1020
+ border: "1px solid rgba(148, 163, 184, 0.18)",
1021
+ color: "#e2e8f0"
1022
+ }
1023
+ }[variant];
1024
+ return {
1025
+ display: "inline-flex",
1026
+ alignItems: "center",
1027
+ justifyContent: "center",
1028
+ gap: "10px",
1029
+ minHeight: "44px",
1030
+ padding: "0 18px",
1031
+ borderRadius: "14px",
1032
+ background: palette.background,
1033
+ border: palette.border,
1034
+ color: palette.color,
1035
+ cursor: disabled ? "not-allowed" : "pointer",
1036
+ opacity: disabled ? 0.45 : 1,
1037
+ fontSize: "14px",
1038
+ fontWeight: 700,
1039
+ letterSpacing: "0.01em",
1040
+ transition: "all 0.2s ease",
1041
+ boxShadow: variant === "primary" && !disabled ? "0 16px 32px rgba(37, 99, 235, 0.28)" : "none"
1042
+ };
1043
+ };
1044
+ const buildStatusChipStyle = (active) => ({
1045
+ display: "inline-flex",
1046
+ alignItems: "center",
1047
+ gap: "8px",
1048
+ padding: "8px 12px",
1049
+ borderRadius: "999px",
1050
+ backgroundColor: active ? "rgba(34, 197, 94, 0.12)" : "rgba(248, 113, 113, 0.12)",
1051
+ border: active ? "1px solid rgba(34, 197, 94, 0.22)" : "1px solid rgba(248, 113, 113, 0.22)",
1052
+ color: active ? "#86efac" : "#fca5a5",
748
1053
  fontSize: "12px",
749
- fontWeight: "500"
1054
+ fontWeight: 700
1055
+ });
1056
+ const statusItems = [
1057
+ { label: "Ready", value: Boolean(embedState.isReady) },
1058
+ { label: "Played", value: Boolean(embedState.hasPlayedOnce) },
1059
+ { label: "Try Now", value: Boolean(embedState.tryNow?.available) },
1060
+ { label: "Buy Now", value: Boolean(embedState.buyNow?.available) }
1061
+ ];
1062
+ const diagnosticGroups = [
1063
+ {
1064
+ title: "Compatibility",
1065
+ items: [
1066
+ {
1067
+ detail: "Playruo currently expects a non-mobile browser.",
1068
+ label: "Desktop browser",
1069
+ value: diagnostics.desktopBrowser
1070
+ },
1071
+ {
1072
+ detail: "The embed flow is tuned for Chromium-based browsers.",
1073
+ label: "Chromium browser",
1074
+ value: diagnostics.chromiumBrowser
1075
+ },
1076
+ {
1077
+ detail: "Keyboard lock works best when the page is not itself embedded.",
1078
+ label: "Top-level tab",
1079
+ value: diagnostics.topLevelContext
1080
+ },
1081
+ {
1082
+ detail: "HTTPS or localhost unlocks the best browser APIs.",
1083
+ label: "Secure context",
1084
+ value: diagnostics.secureContext
1085
+ }
1086
+ ]
1087
+ },
1088
+ {
1089
+ title: "Gameplay APIs",
1090
+ items: [
1091
+ {
1092
+ detail: "Needed for the immersive play flow.",
1093
+ label: "Fullscreen API",
1094
+ value: diagnostics.fullscreenSupported
1095
+ },
1096
+ {
1097
+ detail: "Needed for raw mouse capture on the iframe.",
1098
+ label: "Pointer lock",
1099
+ value: diagnostics.pointerLockSupported
1100
+ },
1101
+ {
1102
+ detail: "Used to keep Escape reserved while fullscreen is active.",
1103
+ label: "Keyboard lock",
1104
+ value: diagnostics.keyboardLockSupported
1105
+ },
1106
+ {
1107
+ detail: "Lets the game read connected controllers.",
1108
+ label: "Gamepad API",
1109
+ value: diagnostics.gamepadSupported
1110
+ }
1111
+ ]
1112
+ },
1113
+ {
1114
+ title: "Performance",
1115
+ items: [
1116
+ {
1117
+ detail: "Improves the overall runtime budget for the experience.",
1118
+ label: "Cross-origin isolated",
1119
+ value: diagnostics.crossOriginIsolation
1120
+ },
1121
+ {
1122
+ detail: "Important for heavier threaded workloads.",
1123
+ label: "SharedArrayBuffer",
1124
+ value: diagnostics.sharedArrayBufferSupported
1125
+ },
1126
+ {
1127
+ detail: "Confirms modern rendering features are reachable.",
1128
+ label: "WebGL2",
1129
+ value: diagnostics.webgl2Supported
1130
+ },
1131
+ {
1132
+ detail: "Confirms the runtime can execute compiled game code paths.",
1133
+ label: "WebAssembly",
1134
+ value: diagnostics.webAssemblySupported
1135
+ }
1136
+ ]
1137
+ },
1138
+ {
1139
+ title: "Embed wiring",
1140
+ items: [
1141
+ {
1142
+ detail: "The iframe allow list includes fullscreen access.",
1143
+ label: "Iframe fullscreen allow",
1144
+ value: diagnostics.iframeAllowsFullscreen
1145
+ },
1146
+ {
1147
+ detail: "The iframe allow list includes gamepad access.",
1148
+ label: "Iframe gamepad allow",
1149
+ value: diagnostics.iframeAllowsGamepad
1150
+ },
1151
+ {
1152
+ detail: "The iframe sandbox includes pointer-lock permission.",
1153
+ label: "Iframe pointer-lock allow",
1154
+ value: diagnostics.iframeAllowsPointerLock
1155
+ },
1156
+ {
1157
+ detail: "Needed for UUID persistence and session continuity.",
1158
+ label: "Local storage",
1159
+ value: diagnostics.localStorageSupported
1160
+ }
1161
+ ]
1162
+ }
1163
+ ];
1164
+ const criticalDiagnosticChecks = [
1165
+ { label: "Desktop browser", value: diagnostics.desktopBrowser },
1166
+ { label: "Chromium browser", value: diagnostics.chromiumBrowser },
1167
+ { label: "Fullscreen API", value: diagnostics.fullscreenSupported },
1168
+ { label: "Pointer lock", value: diagnostics.pointerLockSupported }
1169
+ ];
1170
+ const warningDiagnosticChecks = [
1171
+ { label: "Top-level tab", value: diagnostics.topLevelContext },
1172
+ { label: "Secure context", value: diagnostics.secureContext },
1173
+ { label: "Cross-origin isolation", value: diagnostics.crossOriginIsolation },
1174
+ { label: "SharedArrayBuffer", value: diagnostics.sharedArrayBufferSupported },
1175
+ { label: "Keyboard lock", value: diagnostics.keyboardLockSupported },
1176
+ { label: "Gamepad API", value: diagnostics.gamepadSupported },
1177
+ { label: "WebGL2", value: diagnostics.webgl2Supported }
1178
+ ];
1179
+ const blockingDiagnostics = criticalDiagnosticChecks.filter((item) => !item.value);
1180
+ const advisoryDiagnostics = warningDiagnosticChecks.filter((item) => !item.value);
1181
+ const diagnosticsVerdict = blockingDiagnostics.length > 0 ? {
1182
+ label: "Blocked",
1183
+ tone: "#fca5a5",
1184
+ background: "rgba(127, 29, 29, 0.28)",
1185
+ border: "1px solid rgba(248, 113, 113, 0.24)",
1186
+ text: "A core requirement is missing for the intended embed flow."
1187
+ } : advisoryDiagnostics.length > 0 ? {
1188
+ label: "Limited",
1189
+ tone: "#fde68a",
1190
+ background: "rgba(120, 53, 15, 0.24)",
1191
+ border: "1px solid rgba(245, 158, 11, 0.22)",
1192
+ text: "The embed should run, but some features or performance may be reduced."
1193
+ } : {
1194
+ label: "Ready",
1195
+ tone: "#86efac",
1196
+ background: "rgba(20, 83, 45, 0.24)",
1197
+ border: "1px solid rgba(34, 197, 94, 0.22)",
1198
+ text: "All key browser, performance, and iframe capabilities are present."
750
1199
  };
751
- const successStatus = { ...statusStyle, backgroundColor: "#0d5d1f", color: "#4ade80" };
752
- const errorStatus = { ...statusStyle, backgroundColor: "#5d1a1a", color: "#f87171" };
753
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: props.className, style: { minHeight: "100vh", padding: "20px", backgroundColor: "#000000", color: "#e5e5e5" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { maxWidth: "1200px", margin: "0 auto" }, children: [
754
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: { marginBottom: "20px", fontSize: "24px", fontWeight: "bold", color: "#ffffff" }, children: "\u{1F9EA} Playruo Embed Tester" }),
755
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "20px" }, children: [
756
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
757
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardStyle, children: [
758
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { margin: "0 0 16px 0", fontSize: "18px", fontWeight: "600", color: "#ffffff" }, children: "Test Controls" }),
759
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "12px" }, children: [
760
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "operation", style: { display: "block", marginBottom: "4px", fontWeight: "500", color: "#d4d4d4" }, children: "Operation" }),
761
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
762
- "input",
763
- {
764
- style: inputStyle,
765
- onChange: (e) => setOperation(e.target.value),
766
- placeholder: "e.g., iframe",
767
- value: operation
768
- }
769
- )
770
- ] }),
771
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "12px" }, children: [
772
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "storeUrl", style: { display: "block", marginBottom: "4px", fontWeight: "500", color: "#d4d4d4" }, children: "Store URL" }),
773
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
774
- "input",
775
- {
776
- style: inputStyle,
777
- onChange: (e) => setStoreUrl(e.target.value),
778
- placeholder: "https://store.example.com",
779
- value: storeUrl
780
- }
781
- )
782
- ] }),
1200
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1201
+ "div",
1202
+ {
1203
+ className: props.className,
1204
+ style: {
1205
+ position: "relative",
1206
+ minHeight: "100vh",
1207
+ overflow: "hidden",
1208
+ padding: "32px 20px 48px",
1209
+ background: "radial-gradient(circle at top left, rgba(56, 189, 248, 0.18) 0%, rgba(2, 6, 23, 0) 34%), radial-gradient(circle at 85% 10%, rgba(37, 99, 235, 0.18) 0%, rgba(2, 6, 23, 0) 26%), linear-gradient(180deg, #020617 0%, #050816 48%, #020617 100%)",
1210
+ color: "#e5e7eb"
1211
+ },
1212
+ children: [
1213
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1214
+ "div",
1215
+ {
1216
+ style: {
1217
+ position: "absolute",
1218
+ inset: 0,
1219
+ overflow: "hidden",
1220
+ pointerEvents: "none"
1221
+ },
1222
+ children: [
1223
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1224
+ "div",
1225
+ {
1226
+ style: {
1227
+ position: "absolute",
1228
+ top: "-120px",
1229
+ left: "-120px",
1230
+ width: "320px",
1231
+ height: "320px",
1232
+ borderRadius: "999px",
1233
+ background: "rgba(56, 189, 248, 0.12)",
1234
+ filter: "blur(60px)"
1235
+ }
1236
+ }
1237
+ ),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1239
+ "div",
1240
+ {
1241
+ style: {
1242
+ position: "absolute",
1243
+ right: "-80px",
1244
+ top: "120px",
1245
+ width: "280px",
1246
+ height: "280px",
1247
+ borderRadius: "999px",
1248
+ background: "rgba(37, 99, 235, 0.12)",
1249
+ filter: "blur(60px)"
1250
+ }
1251
+ }
1252
+ )
1253
+ ]
1254
+ }
1255
+ ),
1256
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative", maxWidth: "1280px", margin: "0 auto" }, children: [
783
1257
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
784
- "button",
1258
+ "div",
785
1259
  {
786
- type: "button",
787
1260
  style: {
788
- ...buttonStyle,
789
- backgroundColor: "#0066cc",
790
- color: "#ffffff",
791
- border: "none",
792
- fontWeight: "600"
1261
+ ...panelStyle,
1262
+ marginBottom: "24px",
1263
+ padding: "28px",
1264
+ background: "linear-gradient(135deg, rgba(15, 23, 42, 0.92) 0%, rgba(8, 15, 30, 0.98) 55%, rgba(15, 23, 42, 0.92) 100%)"
793
1265
  },
794
- onClick: handleRestart,
795
- onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "#0052a3",
796
- onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "#0066cc",
797
- children: "\u{1F504} Restart Embed"
1266
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1267
+ "div",
1268
+ {
1269
+ style: {
1270
+ display: "flex",
1271
+ flexWrap: "wrap",
1272
+ gap: "20px",
1273
+ justifyContent: "space-between",
1274
+ alignItems: "flex-start"
1275
+ },
1276
+ children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { maxWidth: "720px" }, children: [
1278
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1279
+ "div",
1280
+ {
1281
+ style: {
1282
+ display: "inline-flex",
1283
+ alignItems: "center",
1284
+ gap: "10px",
1285
+ padding: "6px 12px",
1286
+ borderRadius: "999px",
1287
+ backgroundColor: "rgba(56, 189, 248, 0.12)",
1288
+ border: "1px solid rgba(125, 211, 252, 0.18)",
1289
+ color: "#7dd3fc",
1290
+ fontSize: "11px",
1291
+ fontWeight: 700,
1292
+ letterSpacing: "0.12em",
1293
+ textTransform: "uppercase"
1294
+ },
1295
+ children: "\u{1F9EA} Playruo QA Surface"
1296
+ }
1297
+ ),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1299
+ "h1",
1300
+ {
1301
+ style: {
1302
+ margin: "16px 0 12px 0",
1303
+ fontSize: "clamp(32px, 4vw, 46px)",
1304
+ lineHeight: 1.05,
1305
+ letterSpacing: "-0.04em",
1306
+ color: "#f8fafc"
1307
+ },
1308
+ children: "Embed Tester"
1309
+ }
1310
+ ),
1311
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1312
+ "p",
1313
+ {
1314
+ style: {
1315
+ margin: 0,
1316
+ maxWidth: "640px",
1317
+ color: "#94a3b8",
1318
+ fontSize: "16px",
1319
+ lineHeight: 1.7
1320
+ },
1321
+ children: "Edit configuration safely, apply changes only when you decide to, and keep runtime feedback visible without the UI fighting your typing."
1322
+ }
1323
+ )
1324
+ ] }),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1326
+ "div",
1327
+ {
1328
+ style: {
1329
+ display: "grid",
1330
+ gap: "12px",
1331
+ gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
1332
+ flex: "1 1 320px"
1333
+ },
1334
+ children: [
1335
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1336
+ "div",
1337
+ {
1338
+ style: {
1339
+ padding: "16px",
1340
+ borderRadius: "18px",
1341
+ backgroundColor: "rgba(15, 23, 42, 0.72)",
1342
+ border: "1px solid rgba(148, 163, 184, 0.14)"
1343
+ },
1344
+ children: [
1345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: "12px", color: "#94a3b8", marginBottom: "8px" }, children: "Active host" }),
1346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#f8fafc", fontSize: "16px", fontWeight: 700 }, children: displayHost })
1347
+ ]
1348
+ }
1349
+ ),
1350
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1351
+ "div",
1352
+ {
1353
+ style: {
1354
+ padding: "16px",
1355
+ borderRadius: "18px",
1356
+ backgroundColor: hasPendingConfigChanges ? "rgba(35, 23, 8, 0.72)" : "rgba(15, 23, 42, 0.72)",
1357
+ border: hasPendingConfigChanges ? "1px solid rgba(245, 158, 11, 0.24)" : "1px solid rgba(148, 163, 184, 0.14)"
1358
+ },
1359
+ children: [
1360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: "12px", color: "#94a3b8", marginBottom: "8px" }, children: "Config state" }),
1361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1362
+ "div",
1363
+ {
1364
+ style: {
1365
+ display: "flex",
1366
+ alignItems: "center",
1367
+ gap: "10px",
1368
+ color: hasPendingConfigChanges ? "#fbbf24" : "#86efac",
1369
+ fontSize: "16px",
1370
+ fontWeight: 700
1371
+ },
1372
+ children: [
1373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(hasPendingConfigChanges) }),
1374
+ hasPendingConfigChanges ? `${pendingChangeCount} pending change(s)` : "Everything applied"
1375
+ ]
1376
+ }
1377
+ )
1378
+ ]
1379
+ }
1380
+ )
1381
+ ]
1382
+ }
1383
+ )
1384
+ ]
1385
+ }
1386
+ )
798
1387
  }
799
- )
800
- ] }),
801
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardStyle, children: [
802
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { margin: "0 0 16px 0", fontSize: "18px", fontWeight: "600", color: "#ffffff" }, children: "Status" }),
803
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
804
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: embedState.isReady ? successStatus : errorStatus, children: embedState.isReady ? "\u2705 Ready" : "\u274C Not Ready" }),
805
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: embedState.hasPlayedOnce ? successStatus : errorStatus, children: embedState.hasPlayedOnce ? "\u2705 Played" : "\u274C Not Played" }),
806
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: embedState.tryNow?.available ? successStatus : errorStatus, children: embedState.tryNow?.available ? "\u2705 Try Now" : "\u274C Try Now" }),
807
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: embedState.buyNow?.available ? successStatus : errorStatus, children: embedState.buyNow?.available ? "\u2705 Buy Now" : "\u274C Buy Now" })
808
- ] }),
809
- !embedState.tryNow?.available && embedState.tryNow?.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { margin: "8px 0 0 0", fontSize: "12px", color: "#a3a3a3" }, children: [
810
- "Try Now reason: ",
811
- embedState.tryNow.status
812
- ] })
813
- ] }),
814
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardStyle, children: [
815
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { margin: "0 0 16px 0", fontSize: "18px", fontWeight: "600", color: "#ffffff" }, children: "\u{1F50D} Browser Diagnostics" }),
816
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px" }, children: [
817
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: diagnostics.crossOriginIsolation ? successStatus : errorStatus, children: diagnostics.crossOriginIsolation ? "\u2705" : "\u274C" }),
819
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: "12px", color: "#d4d4d4" }, children: "Cross-Origin Isolation" })
820
- ] }),
821
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
822
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: diagnostics.gamepadSupported ? successStatus : errorStatus, children: diagnostics.gamepadSupported ? "\u2705" : "\u274C" }),
823
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: "12px", color: "#d4d4d4" }, children: "Gamepad API" })
824
- ] }),
825
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
826
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: diagnostics.fullscreenSupported ? successStatus : errorStatus, children: diagnostics.fullscreenSupported ? "\u2705" : "\u274C" }),
827
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: "12px", color: "#d4d4d4" }, children: "Fullscreen API" })
828
- ] }),
829
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
830
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: diagnostics.keyboardLockSupported ? successStatus : errorStatus, children: diagnostics.keyboardLockSupported ? "\u2705" : "\u274C" }),
831
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: "12px", color: "#d4d4d4" }, children: "Keyboard Lock" })
832
- ] })
833
- ] })
834
- ] }),
835
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardStyle, children: [
836
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { margin: "0 0 16px 0", fontSize: "18px", fontWeight: "600", color: "#ffffff" }, children: "Event Log" }),
837
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
838
- backgroundColor: "#0a0a0a",
839
- padding: "12px",
840
- borderRadius: "4px",
841
- minHeight: "200px",
842
- height: "min(50vh, 480px)",
843
- overflowY: "auto",
844
- fontFamily: "monospace",
845
- fontSize: "12px",
846
- border: "1px solid #333333"
847
- }, children: eventLog.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: eventLog.map((event) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { marginBottom: "4px" }, children: [
848
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#737373" }, children: [
849
- "[",
850
- event.timestamp,
851
- "]"
852
- ] }),
853
- " ",
854
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#e5e5e5" }, children: event.message })
855
- ] }, event.timestamp)) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#737373" }, children: "No events yet..." }) })
856
- ] })
857
- ] }),
858
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: cardStyle, children: [
859
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "16px" }, children: [
860
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { margin: "0", fontSize: "18px", fontWeight: "600", color: "#ffffff" }, children: "Embed Container" }),
861
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: {
862
- backgroundColor: "#2a2a2a",
863
- color: "#e5e5e5",
864
- padding: "4px 8px",
865
- borderRadius: "12px",
866
- fontSize: "12px",
867
- fontWeight: "500",
868
- border: "1px solid #404040"
869
- }, children: [
870
- "\u{1F9EA} ",
871
- displayHost
872
- ] })
873
- ] }),
874
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: {
875
- position: "relative",
876
- minHeight: "500px",
877
- overflow: "hidden",
878
- borderRadius: "8px",
879
- border: "1px solid #333333",
880
- backgroundColor: "#0a0a0a"
881
- }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
882
- PlayruoEmbed,
883
- {
884
- baseUrl: props.baseUrl,
885
- className: "w-full h-full absolute inset-0",
886
- iframeRef,
887
- onDemoCompleted: handleDemoCompleted,
888
- onError: handleError,
889
- onPurchaseRequest: handlePurchaseRequest,
890
- onReady: handleReady,
891
- onStateChange: handleStateChange,
892
- operation,
893
- renderState: ({ actions, message, status }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: !hasUserClicked && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: {
894
- position: "absolute",
895
- inset: "0",
896
- display: "flex",
897
- flexDirection: "column",
898
- alignItems: "center",
899
- justifyContent: "center",
900
- gap: "16px",
901
- backgroundColor: "rgba(0, 0, 0, 0.85)",
902
- backdropFilter: "blur(8px)",
903
- zIndex: 50,
904
- padding: "20px"
905
- }, children: [
906
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { margin: "0", fontSize: "14px", color: "#d4d4d4" }, children: [
907
- "Status: ",
908
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#ffffff", fontWeight: "600" }, children: status })
1388
+ ),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "24px", alignItems: "flex-start" }, children: [
1390
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: "1 1 380px", minWidth: "320px" }, children: [
1391
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { ...panelStyle, marginBottom: "18px" }, children: [
1392
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1393
+ "div",
1394
+ {
1395
+ style: {
1396
+ display: "flex",
1397
+ flexWrap: "wrap",
1398
+ justifyContent: "space-between",
1399
+ gap: "12px",
1400
+ marginBottom: "18px"
1401
+ },
1402
+ children: [
1403
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1404
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: sectionTitleStyle, children: "Configuration" }),
1405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { ...helperTextStyle, marginTop: "6px" }, children: [
1406
+ "Draft values stay local until you press ",
1407
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { style: { color: "#e2e8f0" }, children: "Apply" }),
1408
+ "."
1409
+ ] })
1410
+ ] }),
1411
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: buildIndicatorStyle(hasPendingConfigChanges), children: [
1412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(hasPendingConfigChanges) }),
1413
+ hasPendingConfigChanges ? "Not applied" : "Applied"
1414
+ ] })
1415
+ ]
1416
+ }
1417
+ ),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "grid", gap: "16px" }, children: [
1419
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1420
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: "operation", style: labelStyle, children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Operation" }),
1422
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: buildIndicatorStyle(isOperationPending), children: [
1423
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(isOperationPending) }),
1424
+ isOperationPending ? "Pending" : "Live"
1425
+ ] })
1426
+ ] }),
1427
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: buildInputShellStyle(isOperationPending), children: [
1428
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1429
+ "input",
1430
+ {
1431
+ id: "operation",
1432
+ style: inputStyle,
1433
+ onChange: (e) => setOperationDraft(e.target.value),
1434
+ placeholder: "e.g. iframe",
1435
+ value: operationDraft
1436
+ }
1437
+ ),
1438
+ isOperationPending && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1439
+ "span",
1440
+ {
1441
+ "aria-hidden": "true",
1442
+ style: {
1443
+ fontSize: "16px",
1444
+ color: "#fbbf24",
1445
+ fontWeight: 700
1446
+ },
1447
+ children: "!"
1448
+ }
1449
+ )
1450
+ ] }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: helperTextStyle, children: [
1452
+ "Applied value: ",
1453
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: appliedOperation })
1454
+ ] })
1455
+ ] }),
1456
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1457
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: "storeUrl", style: labelStyle, children: [
1458
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Store URL" }),
1459
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: buildIndicatorStyle(isStoreUrlPending), children: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(isStoreUrlPending) }),
1461
+ isStoreUrlPending ? "Pending" : "Live"
1462
+ ] })
1463
+ ] }),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: buildInputShellStyle(isStoreUrlPending), children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1466
+ "input",
1467
+ {
1468
+ id: "storeUrl",
1469
+ style: inputStyle,
1470
+ onChange: (e) => setStoreUrlDraft(e.target.value),
1471
+ placeholder: "https://store.example.com",
1472
+ value: storeUrlDraft
1473
+ }
1474
+ ),
1475
+ isStoreUrlPending && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1476
+ "span",
1477
+ {
1478
+ "aria-hidden": "true",
1479
+ style: {
1480
+ fontSize: "16px",
1481
+ color: "#fbbf24",
1482
+ fontWeight: 700
1483
+ },
1484
+ children: "!"
1485
+ }
1486
+ )
1487
+ ] }),
1488
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: helperTextStyle, children: [
1489
+ "Applied value: ",
1490
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: appliedStoreUrl })
1491
+ ] })
1492
+ ] })
1493
+ ] }),
1494
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1495
+ "div",
1496
+ {
1497
+ style: {
1498
+ display: "flex",
1499
+ flexWrap: "wrap",
1500
+ gap: "12px",
1501
+ marginTop: "20px"
1502
+ },
1503
+ children: [
1504
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1505
+ "button",
1506
+ {
1507
+ type: "button",
1508
+ style: buildButtonStyle("primary", !hasPendingConfigChanges),
1509
+ disabled: !hasPendingConfigChanges,
1510
+ onClick: handleApply,
1511
+ children: "Apply"
1512
+ }
1513
+ ),
1514
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1515
+ "button",
1516
+ {
1517
+ type: "button",
1518
+ style: buildButtonStyle("secondary"),
1519
+ onClick: handleRestartSession,
1520
+ children: "Restart Session"
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1524
+ "button",
1525
+ {
1526
+ type: "button",
1527
+ style: buildButtonStyle("ghost", !hasPendingConfigChanges),
1528
+ disabled: !hasPendingConfigChanges,
1529
+ onClick: handleResetDrafts,
1530
+ children: "Reset Drafts"
1531
+ }
1532
+ )
1533
+ ]
1534
+ }
1535
+ )
1536
+ ] }),
1537
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { ...panelStyle, marginBottom: "18px" }, children: [
1538
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: sectionTitleStyle, children: "Runtime status" }),
1539
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "10px", marginTop: "16px" }, children: statusItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: buildStatusChipStyle(item.value), children: [
1540
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: item.value ? "\u25CF" : "\u25CB" }),
1541
+ item.label
1542
+ ] }, item.label)) }),
1543
+ !embedState.tryNow?.available && embedState.tryNow?.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { ...helperTextStyle, marginTop: "14px" }, children: [
1544
+ "Try Now reason: ",
1545
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#cbd5e1" }, children: embedState.tryNow.status })
1546
+ ] })
909
1547
  ] }),
910
- message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: "0", fontSize: "14px", color: "#d4d4d4" }, children: message }),
1548
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { ...panelStyle, marginBottom: "18px" }, children: [
1549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: sectionTitleStyle, children: "Browser diagnostics" }),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1551
+ "div",
1552
+ {
1553
+ style: {
1554
+ marginTop: "16px",
1555
+ padding: "16px 18px",
1556
+ borderRadius: "20px",
1557
+ background: diagnosticsVerdict.background,
1558
+ border: diagnosticsVerdict.border
1559
+ },
1560
+ children: [
1561
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1562
+ "div",
1563
+ {
1564
+ style: {
1565
+ display: "flex",
1566
+ flexWrap: "wrap",
1567
+ justifyContent: "space-between",
1568
+ gap: "12px",
1569
+ alignItems: "center"
1570
+ },
1571
+ children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1574
+ "div",
1575
+ {
1576
+ style: {
1577
+ color: diagnosticsVerdict.tone,
1578
+ fontSize: "12px",
1579
+ fontWeight: 700,
1580
+ letterSpacing: "0.1em",
1581
+ textTransform: "uppercase"
1582
+ },
1583
+ children: "Diagnostics verdict"
1584
+ }
1585
+ ),
1586
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#f8fafc", fontSize: "20px", fontWeight: 700, marginTop: "6px" }, children: diagnosticsVerdict.label })
1587
+ ] }),
1588
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#cbd5e1", fontSize: "13px", maxWidth: "420px", lineHeight: 1.6 }, children: diagnosticsVerdict.text })
1589
+ ]
1590
+ }
1591
+ ),
1592
+ (blockingDiagnostics.length > 0 || advisoryDiagnostics.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1593
+ "div",
1594
+ {
1595
+ style: {
1596
+ display: "grid",
1597
+ gap: "10px",
1598
+ marginTop: "16px"
1599
+ },
1600
+ children: [
1601
+ blockingDiagnostics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { color: "#fca5a5", fontSize: "13px", lineHeight: 1.6 }, children: [
1602
+ "Blocking: ",
1603
+ blockingDiagnostics.map((item) => item.label).join(", ")
1604
+ ] }),
1605
+ advisoryDiagnostics.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { color: "#fde68a", fontSize: "13px", lineHeight: 1.6 }, children: [
1606
+ "Worth checking: ",
1607
+ advisoryDiagnostics.map((item) => item.label).join(", ")
1608
+ ] })
1609
+ ]
1610
+ }
1611
+ )
1612
+ ]
1613
+ }
1614
+ ),
1615
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1616
+ "div",
1617
+ {
1618
+ style: {
1619
+ display: "grid",
1620
+ gap: "14px",
1621
+ marginTop: "18px"
1622
+ },
1623
+ children: diagnosticGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1624
+ "div",
1625
+ {
1626
+ style: {
1627
+ padding: "16px",
1628
+ borderRadius: "20px",
1629
+ backgroundColor: "rgba(15, 23, 42, 0.42)",
1630
+ border: "1px solid rgba(148, 163, 184, 0.12)"
1631
+ },
1632
+ children: [
1633
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1634
+ "div",
1635
+ {
1636
+ style: {
1637
+ color: "#e2e8f0",
1638
+ fontSize: "14px",
1639
+ fontWeight: 700,
1640
+ marginBottom: "14px"
1641
+ },
1642
+ children: group.title
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1646
+ "div",
1647
+ {
1648
+ style: {
1649
+ display: "grid",
1650
+ gridTemplateColumns: "repeat(auto-fit, minmax(190px, 1fr))",
1651
+ gap: "12px"
1652
+ },
1653
+ children: group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1654
+ "div",
1655
+ {
1656
+ style: {
1657
+ padding: "14px 16px",
1658
+ borderRadius: "18px",
1659
+ backgroundColor: item.value ? "rgba(8, 31, 17, 0.72)" : "rgba(42, 12, 12, 0.52)",
1660
+ border: item.value ? "1px solid rgba(34, 197, 94, 0.18)" : "1px solid rgba(248, 113, 113, 0.18)"
1661
+ },
1662
+ children: [
1663
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#94a3b8", fontSize: "12px", marginBottom: "8px" }, children: item.label }),
1664
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1665
+ "div",
1666
+ {
1667
+ style: {
1668
+ display: "flex",
1669
+ alignItems: "center",
1670
+ gap: "8px",
1671
+ color: item.value ? "#86efac" : "#fca5a5",
1672
+ fontWeight: 700
1673
+ },
1674
+ children: [
1675
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: item.value ? "\u2713" : "\u2715" }),
1676
+ item.value ? "Available" : "Missing"
1677
+ ]
1678
+ }
1679
+ ),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#94a3b8", fontSize: "12px", lineHeight: 1.5, marginTop: "10px" }, children: item.detail })
1681
+ ]
1682
+ },
1683
+ item.label
1684
+ ))
1685
+ }
1686
+ )
1687
+ ]
1688
+ },
1689
+ group.title
1690
+ ))
1691
+ }
1692
+ )
1693
+ ] }),
1694
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: panelStyle, children: [
1695
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1696
+ "div",
1697
+ {
1698
+ style: {
1699
+ display: "flex",
1700
+ justifyContent: "space-between",
1701
+ alignItems: "center",
1702
+ gap: "12px",
1703
+ marginBottom: "16px"
1704
+ },
1705
+ children: [
1706
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: sectionTitleStyle, children: "Event log" }),
1707
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { ...codeStyle, padding: "4px 10px" }, children: [
1708
+ eventLog.length,
1709
+ " recent events"
1710
+ ] })
1711
+ ]
1712
+ }
1713
+ ),
1714
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1715
+ "div",
1716
+ {
1717
+ style: {
1718
+ background: "linear-gradient(180deg, rgba(2, 6, 23, 0.88) 0%, rgba(2, 6, 23, 0.64) 100%)",
1719
+ padding: "14px",
1720
+ borderRadius: "18px",
1721
+ minHeight: "220px",
1722
+ height: "min(46vh, 420px)",
1723
+ overflowY: "auto",
1724
+ fontFamily: '"SF Mono", "Roboto Mono", monospace',
1725
+ fontSize: "12px",
1726
+ border: "1px solid rgba(148, 163, 184, 0.12)"
1727
+ },
1728
+ children: eventLog.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "grid", gap: "10px" }, children: eventLog.map((event) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1729
+ "div",
1730
+ {
1731
+ style: {
1732
+ paddingBottom: "10px",
1733
+ borderBottom: "1px solid rgba(148, 163, 184, 0.08)"
1734
+ },
1735
+ children: [
1736
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#64748b", marginBottom: "4px" }, children: event.timestamp }),
1737
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#e2e8f0", lineHeight: 1.6 }, children: event.message })
1738
+ ]
1739
+ },
1740
+ event.id
1741
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { color: "#64748b" }, children: "No events yet. Start a session or apply a config change." })
1742
+ }
1743
+ )
1744
+ ] })
1745
+ ] }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { flex: "1 1 560px", minWidth: "320px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { ...panelStyle, padding: "24px" }, children: [
911
1747
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
912
- "button",
1748
+ "div",
913
1749
  {
914
- type: "button",
915
1750
  style: {
916
- ...buttonStyle,
917
- backgroundColor: "#16a34a",
918
- color: "#ffffff",
919
- border: "none",
920
- padding: "12px 24px",
921
- fontSize: "16px",
922
- fontWeight: "600",
923
- cursor: actions.canTryNow ? "pointer" : "not-allowed",
924
- opacity: actions.canTryNow ? 1 : 0.5
925
- },
926
- disabled: !actions.canTryNow,
927
- onMouseEnter: (e) => {
928
- if (actions.canTryNow) {
929
- e.currentTarget.style.backgroundColor = "#15803d";
930
- }
931
- },
932
- onMouseLeave: (e) => {
933
- if (actions.canTryNow) {
934
- e.currentTarget.style.backgroundColor = "#16a34a";
935
- }
936
- },
937
- onClick: () => {
938
- setHasUserClicked(true);
939
- setShowIframe(true);
940
- addEvent(embedState.hasPlayedOnce ? "User clicked - Resuming game" : "User clicked - Starting game with tryNow");
941
- actions.tryNow();
1751
+ display: "flex",
1752
+ flexWrap: "wrap",
1753
+ justifyContent: "space-between",
1754
+ alignItems: "center",
1755
+ gap: "12px",
1756
+ marginBottom: "16px"
942
1757
  },
943
1758
  children: [
944
- "\u{1F3AE} ",
945
- embedState.hasPlayedOnce ? "Resume Game" : "Try Demo Now"
1759
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1760
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: sectionTitleStyle, children: "Embed preview" }),
1761
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { ...helperTextStyle, marginTop: "6px" }, children: "The live iframe always reflects the applied configuration." })
1762
+ ] }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "10px" }, children: [
1764
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
1765
+ "Host: ",
1766
+ displayHost
1767
+ ] }),
1768
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
1769
+ "Operation: ",
1770
+ appliedOperation
1771
+ ] })
1772
+ ] })
946
1773
  ]
947
1774
  }
948
1775
  ),
949
- actions.canBuyNow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
950
- "button",
1776
+ hasPendingConfigChanges && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1777
+ "div",
951
1778
  {
952
- type: "button",
953
1779
  style: {
954
- ...buttonStyle,
955
- backgroundColor: "#525252",
956
- color: "#ffffff",
957
- border: "none",
958
- padding: "8px 16px",
959
- fontSize: "14px",
960
- fontWeight: "500"
1780
+ display: "flex",
1781
+ alignItems: "center",
1782
+ gap: "12px",
1783
+ padding: "14px 16px",
1784
+ marginBottom: "16px",
1785
+ borderRadius: "18px",
1786
+ backgroundColor: "rgba(35, 23, 8, 0.72)",
1787
+ border: "1px solid rgba(245, 158, 11, 0.22)",
1788
+ color: "#fde68a"
961
1789
  },
962
- onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "#404040",
963
- onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "#525252",
964
- onClick: () => {
965
- addEvent("Buy Now clicked");
966
- actions.buyNow();
1790
+ children: [
1791
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(true) }),
1792
+ "Draft values are ready but not applied to the iframe yet."
1793
+ ]
1794
+ }
1795
+ ),
1796
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1797
+ "div",
1798
+ {
1799
+ style: {
1800
+ position: "relative",
1801
+ minHeight: "640px",
1802
+ overflow: "hidden",
1803
+ borderRadius: "24px",
1804
+ border: "1px solid rgba(148, 163, 184, 0.14)",
1805
+ background: "linear-gradient(180deg, rgba(3, 7, 18, 0.86) 0%, rgba(2, 6, 23, 1) 100%)",
1806
+ boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.04)"
967
1807
  },
968
- children: "Buy Now"
1808
+ children: [
1809
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1810
+ "div",
1811
+ {
1812
+ style: {
1813
+ display: "flex",
1814
+ alignItems: "center",
1815
+ justifyContent: "space-between",
1816
+ gap: "12px",
1817
+ padding: "14px 18px",
1818
+ borderBottom: "1px solid rgba(148, 163, 184, 0.08)",
1819
+ backgroundColor: "rgba(15, 23, 42, 0.52)"
1820
+ },
1821
+ children: [
1822
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1823
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { width: "10px", height: "10px", borderRadius: "999px", backgroundColor: "#f97316" } }),
1824
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { width: "10px", height: "10px", borderRadius: "999px", backgroundColor: "#facc15" } }),
1825
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { width: "10px", height: "10px", borderRadius: "999px", backgroundColor: "#4ade80" } })
1826
+ ] }),
1827
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#94a3b8", fontSize: "12px", fontFamily: '"SF Mono", "Roboto Mono", monospace' }, children: [
1828
+ "/embed?operation=",
1829
+ appliedOperation
1830
+ ] })
1831
+ ]
1832
+ }
1833
+ ),
1834
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1835
+ PlayruoEmbed,
1836
+ {
1837
+ baseUrl: props.baseUrl,
1838
+ className: "w-full h-full absolute inset-0",
1839
+ iframeRef,
1840
+ onDemoCompleted: handleDemoCompleted,
1841
+ onError: handleError,
1842
+ onEvent: handleEvent,
1843
+ onPurchaseRequest: handlePurchaseRequest,
1844
+ onReady: handleReady,
1845
+ onStateChange: handleStateChange,
1846
+ operation: appliedOperation,
1847
+ renderState: ({ actions, message, status }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: !hasUserClicked && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1848
+ "div",
1849
+ {
1850
+ style: {
1851
+ position: "absolute",
1852
+ inset: "53px 0 0 0",
1853
+ display: "flex",
1854
+ alignItems: "center",
1855
+ justifyContent: "center",
1856
+ padding: "24px",
1857
+ background: "linear-gradient(180deg, rgba(2, 6, 23, 0.4) 0%, rgba(2, 6, 23, 0.84) 70%, rgba(2, 6, 23, 0.9) 100%)",
1858
+ backdropFilter: "blur(12px)",
1859
+ zIndex: 50
1860
+ },
1861
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1862
+ "div",
1863
+ {
1864
+ style: {
1865
+ width: "min(100%, 420px)",
1866
+ padding: "28px",
1867
+ borderRadius: "28px",
1868
+ border: "1px solid rgba(148, 163, 184, 0.18)",
1869
+ background: "linear-gradient(180deg, rgba(15, 23, 42, 0.94) 0%, rgba(7, 12, 24, 0.98) 100%)",
1870
+ boxShadow: "0 32px 72px rgba(0, 0, 0, 0.35)"
1871
+ },
1872
+ children: [
1873
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1874
+ "div",
1875
+ {
1876
+ style: {
1877
+ display: "inline-flex",
1878
+ alignItems: "center",
1879
+ gap: "10px",
1880
+ padding: "6px 12px",
1881
+ borderRadius: "999px",
1882
+ backgroundColor: "rgba(56, 189, 248, 0.12)",
1883
+ border: "1px solid rgba(125, 211, 252, 0.18)",
1884
+ color: "#7dd3fc",
1885
+ fontSize: "11px",
1886
+ fontWeight: 700,
1887
+ letterSpacing: "0.08em",
1888
+ textTransform: "uppercase"
1889
+ },
1890
+ children: "Live overlay"
1891
+ }
1892
+ ),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1894
+ "h4",
1895
+ {
1896
+ style: {
1897
+ margin: "18px 0 10px 0",
1898
+ color: "#f8fafc",
1899
+ fontSize: "28px",
1900
+ letterSpacing: "-0.03em"
1901
+ },
1902
+ children: embedState.hasPlayedOnce ? "Resume the session" : "Start the demo"
1903
+ }
1904
+ ),
1905
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { margin: 0, color: "#94a3b8", lineHeight: 1.7 }, children: [
1906
+ "Status: ",
1907
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#f8fafc", fontWeight: 700 }, children: status })
1908
+ ] }),
1909
+ message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: "10px 0 0 0", color: "#cbd5e1", lineHeight: 1.7 }, children: message }),
1910
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexWrap: "wrap", gap: "12px", marginTop: "24px" }, children: [
1911
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1912
+ "button",
1913
+ {
1914
+ type: "button",
1915
+ style: buildButtonStyle("primary", !actions.canTryNow),
1916
+ disabled: !actions.canTryNow,
1917
+ onClick: () => {
1918
+ setHasUserClicked(true);
1919
+ setShowIframe(true);
1920
+ addEvent(
1921
+ embedState.hasPlayedOnce ? "User clicked - Resuming game" : "User clicked - Starting game with tryNow"
1922
+ );
1923
+ actions.tryNow();
1924
+ },
1925
+ children: [
1926
+ "\u{1F3AE} ",
1927
+ embedState.hasPlayedOnce ? "Resume game" : "Try demo now"
1928
+ ]
1929
+ }
1930
+ ),
1931
+ actions.canBuyNow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1932
+ "button",
1933
+ {
1934
+ type: "button",
1935
+ style: buildButtonStyle("secondary"),
1936
+ onClick: () => {
1937
+ addEvent("Buy Now clicked");
1938
+ actions.buyNow();
1939
+ },
1940
+ children: "Buy Now"
1941
+ }
1942
+ )
1943
+ ] })
1944
+ ]
1945
+ }
1946
+ )
1947
+ }
1948
+ ) }),
1949
+ showIframe,
1950
+ storeUrl: appliedStoreUrl
1951
+ },
1952
+ key
1953
+ )
1954
+ ]
969
1955
  }
970
1956
  )
971
- ] }) }),
972
- showIframe,
973
- storeUrl
974
- },
975
- key
976
- ) })
977
- ] }) })
978
- ] })
979
- ] }) });
1957
+ ] }) })
1958
+ ] })
1959
+ ] })
1960
+ ]
1961
+ }
1962
+ );
980
1963
  };
981
1964
 
982
1965
  // src/post-message-types.ts
@@ -984,7 +1967,7 @@ var isPlayruoEmbedMessage = (data) => {
984
1967
  if (!data || typeof data !== "object") return false;
985
1968
  const msg = data;
986
1969
  if (typeof msg.type !== "string") return false;
987
- return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error" || msg.type === "playruo_session_over" || msg.type === "playruo_stream_start" || msg.type === "playruo_stream_stop";
1970
+ return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error" || msg.type === "playruo_session_over" || msg.type === "playruo_stream_start" || msg.type === "playruo_stream_stop" || msg.type === "REQUEST_EXIT_FULLSCREEN" || msg.type === "playruo_event";
988
1971
  };
989
1972
  var isPlayruoInternalMessage = (data) => {
990
1973
  if (!data || typeof data !== "object") return false;