@playruo/embed 1.1.2 → 1.1.4

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
@@ -36,30 +36,60 @@ export const Demo = () => (
36
36
  ## Headless mode (with renderState)
37
37
 
38
38
  ```tsx
39
+ "use client";
39
40
  import { PlayruoEmbed } from "@playruo/embed";
40
-
41
- <PlayruoEmbed
42
- // operation is optional, defaults to "iframe"
43
- storeUrl="https://store.steampowered.com/app/2751000/..."
44
- showIframe
45
- renderState={({ status, message }) => {
46
- if (status === "loading") return <div>Loading demo…</div>;
47
- if (status === "error") return <div>Error: {message}</div>;
48
- if (status === "available")
49
- return (
50
- <div className="flex gap-3">
51
- <button>Try Now</button>
52
- </div>
53
- );
54
- // unavailable
55
- return (
56
- <div className="flex gap-3">
57
- <span>{message ?? "Unavailable"}</span>
58
- <button>Buy Now</button>
59
- </div>
60
- );
61
- }}
62
- />
41
+ import { useState } from "react";
42
+
43
+ export const Demo = () => {
44
+ const [showIframe, setShowIframe] = useState(false);
45
+
46
+ return (
47
+ <PlayruoEmbed
48
+ operation="iframe"
49
+ storeUrl="https://store.steampowered.com/app/2751000/..."
50
+ showIframe={showIframe}
51
+ renderState={({ status, message, actions }) => {
52
+ if (status === "loading") return <div>Loading demo…</div>;
53
+ if (status === "error") return <div>Error: {message}</div>;
54
+
55
+ if (status === "available") {
56
+ return (
57
+ <div className="flex gap-3">
58
+ <button
59
+ onClick={() => {
60
+ setShowIframe(true);
61
+ actions.tryNow();
62
+ }}
63
+ disabled={!actions.canTryNow}
64
+ >
65
+ Try Now
66
+ </button>
67
+ <button
68
+ onClick={actions.buyNow}
69
+ disabled={!actions.canBuyNow}
70
+ >
71
+ Buy Now
72
+ </button>
73
+ </div>
74
+ );
75
+ }
76
+
77
+ // unavailable
78
+ return (
79
+ <div className="flex gap-3">
80
+ <span>{message ?? "Unavailable"}</span>
81
+ <button
82
+ onClick={actions.buyNow}
83
+ disabled={!actions.canBuyNow}
84
+ >
85
+ Buy Now
86
+ </button>
87
+ </div>
88
+ );
89
+ }}
90
+ />
91
+ );
92
+ };
63
93
  ```
64
94
 
65
95
  ### Props
@@ -76,9 +106,10 @@ import { PlayruoEmbed } from "@playruo/embed";
76
106
 
77
107
  #### Render API
78
108
 
79
- - **renderState({ status, message })**: single render entry point
109
+ - **renderState({ status, message, actions })**: single render entry point
80
110
  - `status`: "loading" | "available" | "unavailable" | "error"
81
111
  - `message?`: optional status message (e.g. availability reason)
112
+ - `actions`: object with `tryNow()`, `buyNow()`, `canTryNow`, `canBuyNow`
82
113
 
83
114
 
84
115
  ### Events
@@ -99,51 +130,173 @@ export interface PlayruoEmbedState {
99
130
  }
100
131
  ```
101
132
 
102
- ## Commands (postMessage)
133
+ ## Actions API
134
+
135
+ The `renderState` callback receives an `actions` object with:
103
136
 
104
- The component exposes two imperative actions via the render API:
137
+ - **`actions.tryNow()`** → sends `try_now` command to the iframe
138
+ - **`actions.buyNow()`** → sends `buy_now` command to the iframe
139
+ - **`actions.canTryNow`** → boolean indicating if try now is available
140
+ - **`actions.canBuyNow`** → boolean indicating if buy now is available
105
141
 
106
- - `tryNow()` → posts `"try_now"` to the iframe
107
- - `buyNow()` → posts `"buy_now"` to the iframe
142
+ ```tsx
143
+ renderState={({ actions }) => (
144
+ <button onClick={actions.tryNow} disabled={!actions.canTryNow}>
145
+ Try Now
146
+ </button>
147
+ )}
148
+ ```
108
149
 
109
150
  ## Incoming messages (from iframe)
110
151
 
111
152
  The component listens to Playruo postMessage events and triggers the corresponding callbacks:
112
153
 
113
- - `"playruo:ready"` (also accepts legacy `"playruo_ready"`) → `onReady()`
114
- - `"playruo:state"` (also accepts legacy `"playruo_state_update"`) → `onStateChange(state)`
115
- - `"playruo:demoCompleted"` (also accepts legacy `"playruo_demo_completed"`) → `onDemoCompleted(data)`
116
- - `"playruo:purchaseRequest"` (also accepts legacy `"playruo_purchase_request"`) → `onPurchaseRequest({ storeUrl })`
117
- - `"playruo:error"` (also accepts legacy `"playruo_error"`) → `onError(message)`
154
+ - `"playruo_ready"` → `onReady()`
155
+ - `"playruo_state_update"` → `onStateChange(state)`
156
+ - `"playruo_demo_completed"` → `onDemoCompleted(data)`
157
+ - `"playruo_purchase_request"` → `onPurchaseRequest({ storeUrl })`
158
+ - `"playruo_error"` → `onError(message)`
118
159
 
119
- ## URL builder
160
+ ## URL builder (Advanced)
120
161
 
121
- You can build the iframe URL manually if needed:
162
+ For advanced use cases, you can build the iframe URL manually:
122
163
 
123
164
  ```ts
124
165
  import { buildEmbedUrl } from "@playruo/embed";
125
166
 
126
167
  const url = buildEmbedUrl({
127
- operation: "iframe", // default
168
+ baseUrl: "https://playruo.example.com",
169
+ operation: "iframe", // optional, default: "iframe"
128
170
  storeUrl: "https://store.steampowered.com/app/2751000/...", // optional
129
171
  });
130
- ``;
131
-
132
- The URL points to `"/embed"` and accepts the query params:
172
+ ```
133
173
 
134
- - `operation` (string, default: `"iframe"`)
135
- - `storeUrl` (string, optional)
174
+ The URL points to `{baseUrl}/embed` with query params:
175
+ - `operation` (optional)
176
+ - `storeUrl` (optional)
136
177
 
137
178
  ## Iframe behavior
138
179
 
139
180
  - The iframe is always present in the DOM. Set `showIframe` to control visibility.
140
181
  - The iframe has `allow="fullscreen *; autoplay *; gamepad *"`.
141
182
 
142
- ## Types
183
+ ## TypeScript Types
184
+
185
+ ### Component Types
186
+
187
+ ```typescript
188
+ import type {
189
+ PlayruoEmbedProps,
190
+ PlayruoEmbedState,
191
+ PlayruoEmbedActions,
192
+ PlayruoEmbedRenderState,
193
+ DemoCompletedPayload,
194
+ } from "@playruo/embed";
195
+ ```
196
+
197
+ ### PostMessage Communication Types
198
+
199
+ For advanced use cases where you need to handle communication with the embed iframe directly:
200
+
201
+ ```typescript
202
+ import type {
203
+ PlayruoEmbedMessage,
204
+ PlayruoEmbedCommand,
205
+ PlayruoReadyMessage,
206
+ PlayruoStateUpdateMessage,
207
+ PlayruoPurchaseRequestMessage,
208
+ PlayruoDemoCompletedMessage,
209
+ PlayruoErrorMessage,
210
+ TryNowCommand,
211
+ BuyNowCommand,
212
+ GetStateCommand,
213
+ } from "@playruo/embed";
214
+ import { isPlayruoEmbedMessage, isPlayruoEmbedCommand } from "@playruo/embed";
215
+ ```
216
+
217
+ #### Example: Custom Message Handler
218
+
219
+ ```typescript
220
+ "use client";
221
+ import { useEffect } from "react";
222
+ import type { PlayruoEmbedMessage } from "@playruo/embed";
223
+ import { isPlayruoEmbedMessage } from "@playruo/embed";
224
+
225
+ export const MyComponent = () => {
226
+ useEffect(() => {
227
+ const handleMessage = (event: MessageEvent) => {
228
+ if (isPlayruoEmbedMessage(event.data)) {
229
+ const message = event.data as PlayruoEmbedMessage;
230
+
231
+ switch (message.type) {
232
+ case 'playruo_ready':
233
+ console.log("Embed is ready!");
234
+ break;
235
+ case 'playruo_state_update':
236
+ console.log("State updated:", message.data);
237
+ break;
238
+ case 'playruo_purchase_request':
239
+ window.open(message.data.storeUrl, "_blank");
240
+ break;
241
+ case 'playruo_demo_completed':
242
+ console.log("Demo completed:", message.data.reason);
243
+ break;
244
+ case 'playruo_error':
245
+ console.error("Embed error:", message.data);
246
+ break;
247
+ }
248
+ }
249
+ };
250
+
251
+ window.addEventListener('message', handleMessage);
252
+ return () => window.removeEventListener('message', handleMessage);
253
+ }, []);
254
+
255
+ // ...
256
+ };
257
+ ```
258
+
259
+ #### Example: Sending Commands
260
+
261
+ ```typescript
262
+ import type { PlayruoEmbedCommand } from "@playruo/embed";
143
263
 
144
- Named exports:
264
+ const sendCommandToEmbed = (command: PlayruoEmbedCommand) => {
265
+ const iframe = document.querySelector('iframe');
266
+ iframe?.contentWindow?.postMessage(command, '*');
267
+ };
268
+
269
+ // Send commands
270
+ sendCommandToEmbed({ type: 'try_now' });
271
+ sendCommandToEmbed({ type: 'buy_now' });
272
+ sendCommandToEmbed({ type: 'get_state' });
273
+ ```
145
274
 
146
- - `PlayruoEmbed`
147
- - `buildEmbedUrl`
148
- - `PlayruoEmbedState`
149
- - `PlayruoEmbedMessage<T>`
275
+ ### Exported Types
276
+
277
+ **Component:**
278
+ - `PlayruoEmbed` - Main React component
279
+ - `PlayruoEmbedProps` - Component props
280
+ - `PlayruoEmbedActions` - Actions API
281
+ - `PlayruoEmbedRenderState` - Render state API
282
+ - `PlayruoEmbedState` - Embed state
283
+ - `DemoCompletedPayload` - Demo completion data
284
+
285
+ **Messages (Embed → Host):**
286
+ - `PlayruoEmbedMessage` - Union of all message types
287
+ - `PlayruoReadyMessage`
288
+ - `PlayruoStateUpdateMessage`
289
+ - `PlayruoPurchaseRequestMessage`
290
+ - `PlayruoDemoCompletedMessage`
291
+ - `PlayruoErrorMessage`
292
+
293
+ **Commands (Host → Embed):**
294
+ - `PlayruoEmbedCommand` - Union of all command types
295
+ - `TryNowCommand`
296
+ - `BuyNowCommand`
297
+ - `GetStateCommand`
298
+
299
+ **Utilities:**
300
+ - `buildEmbedUrl` - URL builder function
301
+ - `isPlayruoEmbedMessage` - Type guard for messages
302
+ - `isPlayruoEmbedCommand` - Type guard for commands
package/dist/index.cjs CHANGED
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  PlayruoEmbed: () => PlayruoEmbed,
24
- buildEmbedUrl: () => buildEmbedUrl
24
+ buildEmbedUrl: () => buildEmbedUrl,
25
+ isPlayruoEmbedCommand: () => isPlayruoEmbedCommand,
26
+ isPlayruoEmbedMessage: () => isPlayruoEmbedMessage
25
27
  });
26
28
  module.exports = __toCommonJS(index_exports);
27
29
 
@@ -49,35 +51,66 @@ var PlayruoEmbed = ({
49
51
  onDemoCompleted,
50
52
  onPurchaseRequest,
51
53
  onError,
52
- renderState
54
+ renderState,
55
+ iframeRef: externalIframeRef
53
56
  }) => {
54
- const iframeRef = (0, import_react.useRef)(null);
57
+ const internalIframeRef = (0, import_react.useRef)(null);
58
+ const iframeRef = externalIframeRef || internalIframeRef;
55
59
  const [isReady, setIsReady] = (0, import_react.useState)(false);
56
60
  const [state, setState] = (0, import_react.useState)(null);
57
61
  const [errorMessage, setErrorMessage] = (0, import_react.useState)(null);
58
62
  const src = (0, import_react.useMemo)(() => buildEmbedUrl({ baseUrl, operation, storeUrl }), [baseUrl, operation, storeUrl]);
63
+ const requestFullscreenWithKeyboardLock = async () => {
64
+ const iframe = iframeRef.current;
65
+ if (!iframe?.requestFullscreen) {
66
+ console.log("[PlayruoEmbed] Fullscreen not supported on this browser");
67
+ return false;
68
+ }
69
+ try {
70
+ const fullscreenPromise = iframe.requestFullscreen();
71
+ if (navigator.keyboard) {
72
+ navigator.keyboard.lock(["Escape"]).then(() => {
73
+ console.log("[PlayruoEmbed] Keyboard locked successfully");
74
+ }).catch((err) => {
75
+ console.warn("[PlayruoEmbed] Keyboard lock failed:", err);
76
+ });
77
+ }
78
+ void fullscreenPromise.then(() => {
79
+ console.log("[PlayruoEmbed] Fullscreen activated successfully");
80
+ }).catch((err) => {
81
+ console.warn("[PlayruoEmbed] Fullscreen failed:", err);
82
+ });
83
+ return true;
84
+ } catch (error) {
85
+ console.error("[PlayruoEmbed] Error requesting fullscreen:", error);
86
+ return false;
87
+ }
88
+ };
59
89
  const sendCommand = (type) => {
60
90
  if (!iframeRef.current?.contentWindow) {
61
91
  console.warn(`PlayruoEmbed: Cannot send ${type} command, iframe not ready`);
62
92
  return;
63
93
  }
94
+ if (type === "try_now") {
95
+ requestFullscreenWithKeyboardLock();
96
+ }
64
97
  iframeRef.current.contentWindow.postMessage({ type }, "*");
65
98
  };
66
99
  (0, import_react.useEffect)(() => {
67
100
  const handler = (event) => {
68
101
  const msg = event?.data ?? {};
69
- if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo")) return;
70
- if (msg.type === "playruo:ready" || msg.type === "playruo_ready") {
102
+ if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo_")) return;
103
+ if (msg.type === "playruo_ready") {
71
104
  setIsReady(true);
72
105
  onReady?.();
73
- } else if (msg.type === "playruo:state" || msg.type === "playruo_state_update") {
106
+ } else if (msg.type === "playruo_state_update") {
74
107
  setState(msg.data);
75
108
  onStateChange?.(msg.data);
76
- } else if (msg.type === "playruo:demoCompleted" || msg.type === "playruo_demo_completed") {
109
+ } else if (msg.type === "playruo_demo_completed") {
77
110
  onDemoCompleted?.(msg.data);
78
- } else if (msg.type === "playruo:purchaseRequest" || msg.type === "playruo_purchase_request") {
111
+ } else if (msg.type === "playruo_purchase_request") {
79
112
  onPurchaseRequest?.(msg.data);
80
- } else if (msg.type === "playruo:error" || msg.type === "playruo_error") {
113
+ } else if (msg.type === "playruo_error") {
81
114
  const message = msg.data?.message ?? "Unknown error";
82
115
  setErrorMessage(message);
83
116
  onError?.(message);
@@ -130,8 +163,24 @@ var PlayruoEmbed = ({
130
163
  )
131
164
  ] });
132
165
  };
166
+
167
+ // src/post-message-types.ts
168
+ var isPlayruoEmbedMessage = (data) => {
169
+ if (!data || typeof data !== "object") return false;
170
+ const msg = data;
171
+ if (typeof msg.type !== "string") return false;
172
+ return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error";
173
+ };
174
+ var isPlayruoEmbedCommand = (data) => {
175
+ if (!data || typeof data !== "object") return false;
176
+ const cmd = data;
177
+ if (typeof cmd.type !== "string") return false;
178
+ return cmd.type === "try_now" || cmd.type === "buy_now" || cmd.type === "get_state";
179
+ };
133
180
  // Annotate the CommonJS export names for ESM import in node:
134
181
  0 && (module.exports = {
135
182
  PlayruoEmbed,
136
- buildEmbedUrl
183
+ buildEmbedUrl,
184
+ isPlayruoEmbedCommand,
185
+ isPlayruoEmbedMessage
137
186
  });
package/dist/index.d.cts CHANGED
@@ -11,15 +11,20 @@ interface PlayruoEmbedState {
11
11
  status?: string;
12
12
  };
13
13
  }
14
- interface PlayruoEmbedMessage<T = unknown> {
15
- type: `playruo:${string}`;
16
- data?: T;
17
- }
18
14
  interface DemoCompletedPayload {
19
15
  reason?: string;
20
16
  storeUrl?: string;
21
17
  }
22
18
 
19
+ interface Keyboard {
20
+ lock: (keys: string[]) => Promise<void>;
21
+ unlock: () => void;
22
+ }
23
+ declare global {
24
+ interface Navigator {
25
+ keyboard?: Keyboard;
26
+ }
27
+ }
23
28
  interface PlayruoEmbedActions {
24
29
  /** Start the demo session */
25
30
  tryNow: () => void;
@@ -53,9 +58,56 @@ interface PlayruoEmbedProps {
53
58
  * Provides status, message, and available actions based on current state.
54
59
  */
55
60
  renderState?: (render: PlayruoEmbedRenderState) => ReactNode;
61
+ /**
62
+ * Ref to access the iframe element for fullscreen requests
63
+ */
64
+ iframeRef?: React.RefObject<HTMLIFrameElement>;
56
65
  }
57
66
  declare const PlayruoEmbed: FC<PlayruoEmbedProps>;
58
67
 
68
+ interface PlayruoReadyMessage {
69
+ type: "playruo_ready";
70
+ }
71
+ interface PlayruoStateUpdateMessage {
72
+ type: "playruo_state_update";
73
+ data: PlayruoEmbedState;
74
+ }
75
+ interface PlayruoPurchaseRequestMessage {
76
+ type: "playruo_purchase_request";
77
+ data: {
78
+ storeUrl: string;
79
+ };
80
+ }
81
+ interface PlayruoDemoCompletedMessage {
82
+ type: "playruo_demo_completed";
83
+ data: DemoCompletedPayload;
84
+ }
85
+ interface PlayruoErrorMessage {
86
+ type: "playruo_error";
87
+ data: {
88
+ message: string;
89
+ } | string;
90
+ }
91
+ type PlayruoEmbedMessage = PlayruoReadyMessage | PlayruoStateUpdateMessage | PlayruoPurchaseRequestMessage | PlayruoDemoCompletedMessage | PlayruoErrorMessage;
92
+ interface TryNowCommand {
93
+ type: "try_now";
94
+ }
95
+ interface BuyNowCommand {
96
+ type: "buy_now";
97
+ }
98
+ interface GetStateCommand {
99
+ type: "get_state";
100
+ }
101
+ type PlayruoEmbedCommand = TryNowCommand | BuyNowCommand | GetStateCommand;
102
+ /**
103
+ * Type guard to check if a message event is a Playruo embed message
104
+ */
105
+ declare const isPlayruoEmbedMessage: (data: unknown) => data is PlayruoEmbedMessage;
106
+ /**
107
+ * Type guard to check if a command is a valid Playruo embed command
108
+ */
109
+ declare const isPlayruoEmbedCommand: (data: unknown) => data is PlayruoEmbedCommand;
110
+
59
111
  interface BuildUrlParams {
60
112
  baseUrl: string;
61
113
  operation?: string;
@@ -63,4 +115,4 @@ interface BuildUrlParams {
63
115
  }
64
116
  declare const buildEmbedUrl: ({ baseUrl, operation, storeUrl }: BuildUrlParams) => string;
65
117
 
66
- export { type DemoCompletedPayload, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, buildEmbedUrl };
118
+ export { type BuyNowCommand, type DemoCompletedPayload, type GetStateCommand, type PlayruoDemoCompletedMessage, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedCommand, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, type PlayruoErrorMessage, type PlayruoPurchaseRequestMessage, type PlayruoReadyMessage, type PlayruoStateUpdateMessage, type TryNowCommand, buildEmbedUrl, isPlayruoEmbedCommand, isPlayruoEmbedMessage };
package/dist/index.d.ts CHANGED
@@ -11,15 +11,20 @@ interface PlayruoEmbedState {
11
11
  status?: string;
12
12
  };
13
13
  }
14
- interface PlayruoEmbedMessage<T = unknown> {
15
- type: `playruo:${string}`;
16
- data?: T;
17
- }
18
14
  interface DemoCompletedPayload {
19
15
  reason?: string;
20
16
  storeUrl?: string;
21
17
  }
22
18
 
19
+ interface Keyboard {
20
+ lock: (keys: string[]) => Promise<void>;
21
+ unlock: () => void;
22
+ }
23
+ declare global {
24
+ interface Navigator {
25
+ keyboard?: Keyboard;
26
+ }
27
+ }
23
28
  interface PlayruoEmbedActions {
24
29
  /** Start the demo session */
25
30
  tryNow: () => void;
@@ -53,9 +58,56 @@ interface PlayruoEmbedProps {
53
58
  * Provides status, message, and available actions based on current state.
54
59
  */
55
60
  renderState?: (render: PlayruoEmbedRenderState) => ReactNode;
61
+ /**
62
+ * Ref to access the iframe element for fullscreen requests
63
+ */
64
+ iframeRef?: React.RefObject<HTMLIFrameElement>;
56
65
  }
57
66
  declare const PlayruoEmbed: FC<PlayruoEmbedProps>;
58
67
 
68
+ interface PlayruoReadyMessage {
69
+ type: "playruo_ready";
70
+ }
71
+ interface PlayruoStateUpdateMessage {
72
+ type: "playruo_state_update";
73
+ data: PlayruoEmbedState;
74
+ }
75
+ interface PlayruoPurchaseRequestMessage {
76
+ type: "playruo_purchase_request";
77
+ data: {
78
+ storeUrl: string;
79
+ };
80
+ }
81
+ interface PlayruoDemoCompletedMessage {
82
+ type: "playruo_demo_completed";
83
+ data: DemoCompletedPayload;
84
+ }
85
+ interface PlayruoErrorMessage {
86
+ type: "playruo_error";
87
+ data: {
88
+ message: string;
89
+ } | string;
90
+ }
91
+ type PlayruoEmbedMessage = PlayruoReadyMessage | PlayruoStateUpdateMessage | PlayruoPurchaseRequestMessage | PlayruoDemoCompletedMessage | PlayruoErrorMessage;
92
+ interface TryNowCommand {
93
+ type: "try_now";
94
+ }
95
+ interface BuyNowCommand {
96
+ type: "buy_now";
97
+ }
98
+ interface GetStateCommand {
99
+ type: "get_state";
100
+ }
101
+ type PlayruoEmbedCommand = TryNowCommand | BuyNowCommand | GetStateCommand;
102
+ /**
103
+ * Type guard to check if a message event is a Playruo embed message
104
+ */
105
+ declare const isPlayruoEmbedMessage: (data: unknown) => data is PlayruoEmbedMessage;
106
+ /**
107
+ * Type guard to check if a command is a valid Playruo embed command
108
+ */
109
+ declare const isPlayruoEmbedCommand: (data: unknown) => data is PlayruoEmbedCommand;
110
+
59
111
  interface BuildUrlParams {
60
112
  baseUrl: string;
61
113
  operation?: string;
@@ -63,4 +115,4 @@ interface BuildUrlParams {
63
115
  }
64
116
  declare const buildEmbedUrl: ({ baseUrl, operation, storeUrl }: BuildUrlParams) => string;
65
117
 
66
- export { type DemoCompletedPayload, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, buildEmbedUrl };
118
+ export { type BuyNowCommand, type DemoCompletedPayload, type GetStateCommand, type PlayruoDemoCompletedMessage, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedCommand, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, type PlayruoErrorMessage, type PlayruoPurchaseRequestMessage, type PlayruoReadyMessage, type PlayruoStateUpdateMessage, type TryNowCommand, buildEmbedUrl, isPlayruoEmbedCommand, isPlayruoEmbedMessage };
package/dist/index.js CHANGED
@@ -22,35 +22,66 @@ var PlayruoEmbed = ({
22
22
  onDemoCompleted,
23
23
  onPurchaseRequest,
24
24
  onError,
25
- renderState
25
+ renderState,
26
+ iframeRef: externalIframeRef
26
27
  }) => {
27
- const iframeRef = useRef(null);
28
+ const internalIframeRef = useRef(null);
29
+ const iframeRef = externalIframeRef || internalIframeRef;
28
30
  const [isReady, setIsReady] = useState(false);
29
31
  const [state, setState] = useState(null);
30
32
  const [errorMessage, setErrorMessage] = useState(null);
31
33
  const src = useMemo(() => buildEmbedUrl({ baseUrl, operation, storeUrl }), [baseUrl, operation, storeUrl]);
34
+ const requestFullscreenWithKeyboardLock = async () => {
35
+ const iframe = iframeRef.current;
36
+ if (!iframe?.requestFullscreen) {
37
+ console.log("[PlayruoEmbed] Fullscreen not supported on this browser");
38
+ return false;
39
+ }
40
+ try {
41
+ const fullscreenPromise = iframe.requestFullscreen();
42
+ if (navigator.keyboard) {
43
+ navigator.keyboard.lock(["Escape"]).then(() => {
44
+ console.log("[PlayruoEmbed] Keyboard locked successfully");
45
+ }).catch((err) => {
46
+ console.warn("[PlayruoEmbed] Keyboard lock failed:", err);
47
+ });
48
+ }
49
+ void fullscreenPromise.then(() => {
50
+ console.log("[PlayruoEmbed] Fullscreen activated successfully");
51
+ }).catch((err) => {
52
+ console.warn("[PlayruoEmbed] Fullscreen failed:", err);
53
+ });
54
+ return true;
55
+ } catch (error) {
56
+ console.error("[PlayruoEmbed] Error requesting fullscreen:", error);
57
+ return false;
58
+ }
59
+ };
32
60
  const sendCommand = (type) => {
33
61
  if (!iframeRef.current?.contentWindow) {
34
62
  console.warn(`PlayruoEmbed: Cannot send ${type} command, iframe not ready`);
35
63
  return;
36
64
  }
65
+ if (type === "try_now") {
66
+ requestFullscreenWithKeyboardLock();
67
+ }
37
68
  iframeRef.current.contentWindow.postMessage({ type }, "*");
38
69
  };
39
70
  useEffect(() => {
40
71
  const handler = (event) => {
41
72
  const msg = event?.data ?? {};
42
- if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo")) return;
43
- if (msg.type === "playruo:ready" || msg.type === "playruo_ready") {
73
+ if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo_")) return;
74
+ if (msg.type === "playruo_ready") {
44
75
  setIsReady(true);
45
76
  onReady?.();
46
- } else if (msg.type === "playruo:state" || msg.type === "playruo_state_update") {
77
+ } else if (msg.type === "playruo_state_update") {
47
78
  setState(msg.data);
48
79
  onStateChange?.(msg.data);
49
- } else if (msg.type === "playruo:demoCompleted" || msg.type === "playruo_demo_completed") {
80
+ } else if (msg.type === "playruo_demo_completed") {
50
81
  onDemoCompleted?.(msg.data);
51
- } else if (msg.type === "playruo:purchaseRequest" || msg.type === "playruo_purchase_request") {
82
+ } else if (msg.type === "playruo_purchase_request") {
52
83
  onPurchaseRequest?.(msg.data);
53
- } else if (msg.type === "playruo:error" || msg.type === "playruo_error") {
84
+ } else if (msg.type === "playruo_error") {
54
85
  const message = msg.data?.message ?? "Unknown error";
55
86
  setErrorMessage(message);
56
87
  onError?.(message);
@@ -103,7 +134,23 @@ var PlayruoEmbed = ({
103
134
  )
104
135
  ] });
105
136
  };
137
+
138
+ // src/post-message-types.ts
139
+ var isPlayruoEmbedMessage = (data) => {
140
+ if (!data || typeof data !== "object") return false;
141
+ const msg = data;
142
+ if (typeof msg.type !== "string") return false;
143
+ return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error";
144
+ };
145
+ var isPlayruoEmbedCommand = (data) => {
146
+ if (!data || typeof data !== "object") return false;
147
+ const cmd = data;
148
+ if (typeof cmd.type !== "string") return false;
149
+ return cmd.type === "try_now" || cmd.type === "buy_now" || cmd.type === "get_state";
150
+ };
106
151
  export {
107
152
  PlayruoEmbed,
108
- buildEmbedUrl
153
+ buildEmbedUrl,
154
+ isPlayruoEmbedCommand,
155
+ isPlayruoEmbedMessage
109
156
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playruo/embed",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "React SDK to embed Playruo with postMessage API.",
5
5
  "license": "MIT",
6
6
  "type": "module",