@playruo/embed 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  React SDK to embed Playruo via iframe + postMessage.
4
4
 
5
+ ## Windowed launch
6
+
7
+ `PlayruoEmbed` requests browser fullscreen on the iframe when the user starts a
8
+ demo. This remains the default behavior.
9
+
10
+ Set `enableWindowedLaunch` to `true` to skip the parent fullscreen request and
11
+ start the experience inside the current iframe bounds:
12
+
13
+ ```tsx
14
+ <PlayruoEmbed
15
+ operation="iframe"
16
+ enableWindowedLaunch
17
+ />
18
+ ```
19
+
5
20
  ## Event forwarding
6
21
 
7
22
  When the embedded Playruo iframe sends `{ type: "playruo_event", data }`, the
package/dist/index.cjs CHANGED
@@ -77,6 +77,7 @@ var PlayruoEmbed = ({
77
77
  onEvent,
78
78
  onPurchaseRequest,
79
79
  onError,
80
+ enableWindowedLaunch = false,
80
81
  forwardEventsToParent = true,
81
82
  renderState,
82
83
  iframeRef: externalIframeRef
@@ -337,17 +338,24 @@ var PlayruoEmbed = ({
337
338
  hasNotifiedCompletionRef.current = false;
338
339
  setShowGameIframe(true);
339
340
  try {
340
- try {
341
- await requestImmersiveMode(iframe);
342
- } catch (fsError) {
343
- console.warn("[PlayruoEmbed] Fullscreen denied, continuing without:", fsError);
341
+ let isParentFullscreen = false;
342
+ if (enableWindowedLaunch) {
343
+ releaseEscapeKeyLock();
344
+ console.log("[PlayruoEmbed] Windowed launch enabled, skipping fullscreen request");
345
+ } else {
346
+ try {
347
+ await requestImmersiveMode(iframe);
348
+ isParentFullscreen = true;
349
+ } catch (fsError) {
350
+ console.warn("[PlayruoEmbed] Fullscreen denied, continuing without:", fsError);
351
+ }
344
352
  }
345
353
  iframe.contentWindow?.postMessage({
346
354
  type: "playruo",
347
355
  action: "tryNowClick",
348
356
  fromEmbed: true,
349
- isFullscreen: true,
350
- parentWillHandleFullscreen: true,
357
+ isFullscreen: isParentFullscreen,
358
+ parentWillHandleFullscreen: isParentFullscreen,
351
359
  isReplay: hasPlayedOnce
352
360
  }, "*");
353
361
  console.log("[PlayruoEmbed] Sent tryNowClick to game", hasPlayedOnce ? "(replay)" : "(first time)");
@@ -362,7 +370,15 @@ var PlayruoEmbed = ({
362
370
  setErrorMessage("Failed to start game");
363
371
  onError?.("Failed to start game");
364
372
  }
365
- }, [isCompatible, hasPlayedOnce, onError, iframeRef, requestImmersiveMode]);
373
+ }, [
374
+ enableWindowedLaunch,
375
+ hasPlayedOnce,
376
+ iframeRef,
377
+ isCompatible,
378
+ onError,
379
+ releaseEscapeKeyLock,
380
+ requestImmersiveMode
381
+ ]);
366
382
  const handleBuyNow = (0, import_react.useCallback)(() => {
367
383
  if (!storeUrl) {
368
384
  console.warn("[PlayruoEmbed] No store URL provided");
@@ -782,10 +798,16 @@ var EmbedTester = (props = {}) => {
782
798
  const [storeUrlDraft, setStoreUrlDraft] = (0, import_react2.useState)(
783
799
  props.storeUrl ?? "https://store.steampowered.com/app/123456"
784
800
  );
801
+ const [enableWindowedLaunchDraft, setEnableWindowedLaunchDraft] = (0, import_react2.useState)(
802
+ props.enableWindowedLaunch ?? false
803
+ );
785
804
  const [appliedOperation, setAppliedOperation] = (0, import_react2.useState)(props.operation ?? "iframe");
786
805
  const [appliedStoreUrl, setAppliedStoreUrl] = (0, import_react2.useState)(
787
806
  props.storeUrl ?? "https://store.steampowered.com/app/123456"
788
807
  );
808
+ const [appliedEnableWindowedLaunch, setAppliedEnableWindowedLaunch] = (0, import_react2.useState)(
809
+ props.enableWindowedLaunch ?? false
810
+ );
789
811
  const [key, setKey] = (0, import_react2.useState)(0);
790
812
  const [hasUserClicked, setHasUserClicked] = (0, import_react2.useState)(false);
791
813
  const [showIframe, setShowIframe] = (0, import_react2.useState)(false);
@@ -800,8 +822,9 @@ var EmbedTester = (props = {}) => {
800
822
  const iframeRef = (0, import_react2.useRef)(null);
801
823
  const isOperationPending = operationDraft !== appliedOperation;
802
824
  const isStoreUrlPending = storeUrlDraft !== appliedStoreUrl;
803
- const hasPendingConfigChanges = isOperationPending || isStoreUrlPending;
804
- const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending);
825
+ const isWindowedLaunchPending = enableWindowedLaunchDraft !== appliedEnableWindowedLaunch;
826
+ const hasPendingConfigChanges = isOperationPending || isStoreUrlPending || isWindowedLaunchPending;
827
+ const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
805
828
  const addEvent = (0, import_react2.useCallback)(
806
829
  (message) => {
807
830
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
@@ -912,16 +935,24 @@ var EmbedTester = (props = {}) => {
912
935
  }
913
936
  setAppliedOperation(operationDraft);
914
937
  setAppliedStoreUrl(storeUrlDraft);
938
+ setAppliedEnableWindowedLaunch(enableWindowedLaunchDraft);
915
939
  resetEmbedSession("Applied updated embed settings");
916
- }, [hasPendingConfigChanges, operationDraft, resetEmbedSession, storeUrlDraft]);
940
+ }, [
941
+ enableWindowedLaunchDraft,
942
+ hasPendingConfigChanges,
943
+ operationDraft,
944
+ resetEmbedSession,
945
+ storeUrlDraft
946
+ ]);
917
947
  const handleRestartSession = (0, import_react2.useCallback)(() => {
918
948
  resetEmbedSession("Embed restarted with applied settings");
919
949
  }, [resetEmbedSession]);
920
950
  const handleResetDrafts = (0, import_react2.useCallback)(() => {
921
951
  setOperationDraft(appliedOperation);
922
952
  setStoreUrlDraft(appliedStoreUrl);
953
+ setEnableWindowedLaunchDraft(appliedEnableWindowedLaunch);
923
954
  addEvent("Reverted draft changes to the applied configuration");
924
- }, [addEvent, appliedOperation, appliedStoreUrl]);
955
+ }, [addEvent, appliedEnableWindowedLaunch, appliedOperation, appliedStoreUrl]);
925
956
  const panelStyle = {
926
957
  position: "relative",
927
958
  border: "1px solid rgba(148, 163, 184, 0.18)",
@@ -1489,6 +1520,50 @@ var EmbedTester = (props = {}) => {
1489
1520
  "Applied value: ",
1490
1521
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: appliedStoreUrl })
1491
1522
  ] })
1523
+ ] }),
1524
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: labelStyle, children: [
1526
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Launch mode" }),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: buildIndicatorStyle(isWindowedLaunchPending), children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(isWindowedLaunchPending) }),
1529
+ isWindowedLaunchPending ? "Pending" : "Live"
1530
+ ] })
1531
+ ] }),
1532
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1533
+ "label",
1534
+ {
1535
+ htmlFor: "enableWindowedLaunch",
1536
+ style: {
1537
+ ...buildInputShellStyle(isWindowedLaunchPending),
1538
+ justifyContent: "space-between",
1539
+ minHeight: "48px",
1540
+ cursor: "pointer"
1541
+ },
1542
+ children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#f8fafc", fontSize: "15px", fontWeight: 600 }, children: "Windowed launch" }),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1545
+ "input",
1546
+ {
1547
+ id: "enableWindowedLaunch",
1548
+ type: "checkbox",
1549
+ checked: enableWindowedLaunchDraft,
1550
+ onChange: (event) => setEnableWindowedLaunchDraft(event.target.checked),
1551
+ style: {
1552
+ width: "18px",
1553
+ height: "18px",
1554
+ accentColor: "#38bdf8",
1555
+ cursor: "pointer"
1556
+ }
1557
+ }
1558
+ )
1559
+ ]
1560
+ }
1561
+ ),
1562
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: helperTextStyle, children: [
1563
+ "Applied value:",
1564
+ " ",
1565
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: appliedEnableWindowedLaunch ? "windowed" : "fullscreen request" })
1566
+ ] })
1492
1567
  ] })
1493
1568
  ] }),
1494
1569
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
@@ -1768,6 +1843,10 @@ var EmbedTester = (props = {}) => {
1768
1843
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
1769
1844
  "Operation: ",
1770
1845
  appliedOperation
1846
+ ] }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
1848
+ "Launch: ",
1849
+ appliedEnableWindowedLaunch ? "windowed" : "fullscreen request"
1771
1850
  ] })
1772
1851
  ] })
1773
1852
  ]
@@ -1843,6 +1922,7 @@ var EmbedTester = (props = {}) => {
1843
1922
  onPurchaseRequest: handlePurchaseRequest,
1844
1923
  onReady: handleReady,
1845
1924
  onStateChange: handleStateChange,
1925
+ enableWindowedLaunch: appliedEnableWindowedLaunch,
1846
1926
  operation: appliedOperation,
1847
1927
  renderState: ({ actions, message, status }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: !hasUserClicked && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1848
1928
  "div",
package/dist/index.d.cts CHANGED
@@ -55,6 +55,11 @@ interface PlayruoEmbedProps {
55
55
  storeUrl: string;
56
56
  }) => void;
57
57
  onError?: (error: string) => void;
58
+ /**
59
+ * Skip the parent fullscreen request and start the experience inside the
60
+ * current iframe bounds. Defaults to false to preserve the current behavior.
61
+ */
62
+ enableWindowedLaunch?: boolean;
58
63
  /**
59
64
  * Forward playruo_event messages to the parent window when the package host
60
65
  * itself is embedded in another iframe. Direct host pages already receive the
@@ -79,6 +84,8 @@ interface EmbedTesterProps {
79
84
  enableConsoleLog?: boolean;
80
85
  operation?: string;
81
86
  storeUrl?: string;
87
+ /** Start the experience inside the iframe without requesting parent fullscreen. */
88
+ enableWindowedLaunch?: boolean;
82
89
  className?: string;
83
90
  }
84
91
  declare const EmbedTester: (props?: EmbedTesterProps) => react_jsx_runtime.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -55,6 +55,11 @@ interface PlayruoEmbedProps {
55
55
  storeUrl: string;
56
56
  }) => void;
57
57
  onError?: (error: string) => void;
58
+ /**
59
+ * Skip the parent fullscreen request and start the experience inside the
60
+ * current iframe bounds. Defaults to false to preserve the current behavior.
61
+ */
62
+ enableWindowedLaunch?: boolean;
58
63
  /**
59
64
  * Forward playruo_event messages to the parent window when the package host
60
65
  * itself is embedded in another iframe. Direct host pages already receive the
@@ -79,6 +84,8 @@ interface EmbedTesterProps {
79
84
  enableConsoleLog?: boolean;
80
85
  operation?: string;
81
86
  storeUrl?: string;
87
+ /** Start the experience inside the iframe without requesting parent fullscreen. */
88
+ enableWindowedLaunch?: boolean;
82
89
  className?: string;
83
90
  }
84
91
  declare const EmbedTester: (props?: EmbedTesterProps) => react_jsx_runtime.JSX.Element;
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ var PlayruoEmbed = ({
37
37
  onEvent,
38
38
  onPurchaseRequest,
39
39
  onError,
40
+ enableWindowedLaunch = false,
40
41
  forwardEventsToParent = true,
41
42
  renderState,
42
43
  iframeRef: externalIframeRef
@@ -297,17 +298,24 @@ var PlayruoEmbed = ({
297
298
  hasNotifiedCompletionRef.current = false;
298
299
  setShowGameIframe(true);
299
300
  try {
300
- try {
301
- await requestImmersiveMode(iframe);
302
- } catch (fsError) {
303
- console.warn("[PlayruoEmbed] Fullscreen denied, continuing without:", fsError);
301
+ let isParentFullscreen = false;
302
+ if (enableWindowedLaunch) {
303
+ releaseEscapeKeyLock();
304
+ console.log("[PlayruoEmbed] Windowed launch enabled, skipping fullscreen request");
305
+ } else {
306
+ try {
307
+ await requestImmersiveMode(iframe);
308
+ isParentFullscreen = true;
309
+ } catch (fsError) {
310
+ console.warn("[PlayruoEmbed] Fullscreen denied, continuing without:", fsError);
311
+ }
304
312
  }
305
313
  iframe.contentWindow?.postMessage({
306
314
  type: "playruo",
307
315
  action: "tryNowClick",
308
316
  fromEmbed: true,
309
- isFullscreen: true,
310
- parentWillHandleFullscreen: true,
317
+ isFullscreen: isParentFullscreen,
318
+ parentWillHandleFullscreen: isParentFullscreen,
311
319
  isReplay: hasPlayedOnce
312
320
  }, "*");
313
321
  console.log("[PlayruoEmbed] Sent tryNowClick to game", hasPlayedOnce ? "(replay)" : "(first time)");
@@ -322,7 +330,15 @@ var PlayruoEmbed = ({
322
330
  setErrorMessage("Failed to start game");
323
331
  onError?.("Failed to start game");
324
332
  }
325
- }, [isCompatible, hasPlayedOnce, onError, iframeRef, requestImmersiveMode]);
333
+ }, [
334
+ enableWindowedLaunch,
335
+ hasPlayedOnce,
336
+ iframeRef,
337
+ isCompatible,
338
+ onError,
339
+ releaseEscapeKeyLock,
340
+ requestImmersiveMode
341
+ ]);
326
342
  const handleBuyNow = useCallback(() => {
327
343
  if (!storeUrl) {
328
344
  console.warn("[PlayruoEmbed] No store URL provided");
@@ -742,10 +758,16 @@ var EmbedTester = (props = {}) => {
742
758
  const [storeUrlDraft, setStoreUrlDraft] = useState2(
743
759
  props.storeUrl ?? "https://store.steampowered.com/app/123456"
744
760
  );
761
+ const [enableWindowedLaunchDraft, setEnableWindowedLaunchDraft] = useState2(
762
+ props.enableWindowedLaunch ?? false
763
+ );
745
764
  const [appliedOperation, setAppliedOperation] = useState2(props.operation ?? "iframe");
746
765
  const [appliedStoreUrl, setAppliedStoreUrl] = useState2(
747
766
  props.storeUrl ?? "https://store.steampowered.com/app/123456"
748
767
  );
768
+ const [appliedEnableWindowedLaunch, setAppliedEnableWindowedLaunch] = useState2(
769
+ props.enableWindowedLaunch ?? false
770
+ );
749
771
  const [key, setKey] = useState2(0);
750
772
  const [hasUserClicked, setHasUserClicked] = useState2(false);
751
773
  const [showIframe, setShowIframe] = useState2(false);
@@ -760,8 +782,9 @@ var EmbedTester = (props = {}) => {
760
782
  const iframeRef = useRef2(null);
761
783
  const isOperationPending = operationDraft !== appliedOperation;
762
784
  const isStoreUrlPending = storeUrlDraft !== appliedStoreUrl;
763
- const hasPendingConfigChanges = isOperationPending || isStoreUrlPending;
764
- const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending);
785
+ const isWindowedLaunchPending = enableWindowedLaunchDraft !== appliedEnableWindowedLaunch;
786
+ const hasPendingConfigChanges = isOperationPending || isStoreUrlPending || isWindowedLaunchPending;
787
+ const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
765
788
  const addEvent = useCallback2(
766
789
  (message) => {
767
790
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
@@ -872,16 +895,24 @@ var EmbedTester = (props = {}) => {
872
895
  }
873
896
  setAppliedOperation(operationDraft);
874
897
  setAppliedStoreUrl(storeUrlDraft);
898
+ setAppliedEnableWindowedLaunch(enableWindowedLaunchDraft);
875
899
  resetEmbedSession("Applied updated embed settings");
876
- }, [hasPendingConfigChanges, operationDraft, resetEmbedSession, storeUrlDraft]);
900
+ }, [
901
+ enableWindowedLaunchDraft,
902
+ hasPendingConfigChanges,
903
+ operationDraft,
904
+ resetEmbedSession,
905
+ storeUrlDraft
906
+ ]);
877
907
  const handleRestartSession = useCallback2(() => {
878
908
  resetEmbedSession("Embed restarted with applied settings");
879
909
  }, [resetEmbedSession]);
880
910
  const handleResetDrafts = useCallback2(() => {
881
911
  setOperationDraft(appliedOperation);
882
912
  setStoreUrlDraft(appliedStoreUrl);
913
+ setEnableWindowedLaunchDraft(appliedEnableWindowedLaunch);
883
914
  addEvent("Reverted draft changes to the applied configuration");
884
- }, [addEvent, appliedOperation, appliedStoreUrl]);
915
+ }, [addEvent, appliedEnableWindowedLaunch, appliedOperation, appliedStoreUrl]);
885
916
  const panelStyle = {
886
917
  position: "relative",
887
918
  border: "1px solid rgba(148, 163, 184, 0.18)",
@@ -1449,6 +1480,50 @@ var EmbedTester = (props = {}) => {
1449
1480
  "Applied value: ",
1450
1481
  /* @__PURE__ */ jsx2("code", { style: codeStyle, children: appliedStoreUrl })
1451
1482
  ] })
1483
+ ] }),
1484
+ /* @__PURE__ */ jsxs2("div", { children: [
1485
+ /* @__PURE__ */ jsxs2("div", { style: labelStyle, children: [
1486
+ /* @__PURE__ */ jsx2("span", { children: "Launch mode" }),
1487
+ /* @__PURE__ */ jsxs2("span", { style: buildIndicatorStyle(isWindowedLaunchPending), children: [
1488
+ /* @__PURE__ */ jsx2("span", { style: buildIndicatorDotStyle(isWindowedLaunchPending) }),
1489
+ isWindowedLaunchPending ? "Pending" : "Live"
1490
+ ] })
1491
+ ] }),
1492
+ /* @__PURE__ */ jsxs2(
1493
+ "label",
1494
+ {
1495
+ htmlFor: "enableWindowedLaunch",
1496
+ style: {
1497
+ ...buildInputShellStyle(isWindowedLaunchPending),
1498
+ justifyContent: "space-between",
1499
+ minHeight: "48px",
1500
+ cursor: "pointer"
1501
+ },
1502
+ children: [
1503
+ /* @__PURE__ */ jsx2("span", { style: { color: "#f8fafc", fontSize: "15px", fontWeight: 600 }, children: "Windowed launch" }),
1504
+ /* @__PURE__ */ jsx2(
1505
+ "input",
1506
+ {
1507
+ id: "enableWindowedLaunch",
1508
+ type: "checkbox",
1509
+ checked: enableWindowedLaunchDraft,
1510
+ onChange: (event) => setEnableWindowedLaunchDraft(event.target.checked),
1511
+ style: {
1512
+ width: "18px",
1513
+ height: "18px",
1514
+ accentColor: "#38bdf8",
1515
+ cursor: "pointer"
1516
+ }
1517
+ }
1518
+ )
1519
+ ]
1520
+ }
1521
+ ),
1522
+ /* @__PURE__ */ jsxs2("p", { style: helperTextStyle, children: [
1523
+ "Applied value:",
1524
+ " ",
1525
+ /* @__PURE__ */ jsx2("code", { style: codeStyle, children: appliedEnableWindowedLaunch ? "windowed" : "fullscreen request" })
1526
+ ] })
1452
1527
  ] })
1453
1528
  ] }),
1454
1529
  /* @__PURE__ */ jsxs2(
@@ -1728,6 +1803,10 @@ var EmbedTester = (props = {}) => {
1728
1803
  /* @__PURE__ */ jsxs2("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
1729
1804
  "Operation: ",
1730
1805
  appliedOperation
1806
+ ] }),
1807
+ /* @__PURE__ */ jsxs2("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
1808
+ "Launch: ",
1809
+ appliedEnableWindowedLaunch ? "windowed" : "fullscreen request"
1731
1810
  ] })
1732
1811
  ] })
1733
1812
  ]
@@ -1803,6 +1882,7 @@ var EmbedTester = (props = {}) => {
1803
1882
  onPurchaseRequest: handlePurchaseRequest,
1804
1883
  onReady: handleReady,
1805
1884
  onStateChange: handleStateChange,
1885
+ enableWindowedLaunch: appliedEnableWindowedLaunch,
1806
1886
  operation: appliedOperation,
1807
1887
  renderState: ({ actions, message, status }) => /* @__PURE__ */ jsx2(Fragment, { children: !hasUserClicked && /* @__PURE__ */ jsx2(
1808
1888
  "div",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playruo/embed",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "React SDK to embed Playruo with postMessage API.",
5
5
  "license": "MIT",
6
6
  "type": "module",