@playruo/embed 1.7.0 → 1.8.1
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 +188 -49
- package/dist/index.d.cts +21 -6
- package/dist/index.d.ts +21 -6
- package/dist/index.js +188 -49
- package/package.json +15 -6
package/dist/index.cjs
CHANGED
|
@@ -211,6 +211,14 @@ var PlayruoEmbed = ({
|
|
|
211
211
|
}, []);
|
|
212
212
|
const isCompatible = !isMobile && isValidChromium;
|
|
213
213
|
const isReady = isCompatible ? isIframeLoaded : true;
|
|
214
|
+
(0, import_react.useEffect)(() => {
|
|
215
|
+
const iframe = iframeRef.current;
|
|
216
|
+
if (!iframe || iframe.getAttribute("src") === src) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
iframe.setAttribute("credentialless", "");
|
|
220
|
+
iframe.src = src;
|
|
221
|
+
}, [iframeRef, isCompatible, src]);
|
|
214
222
|
const currentState = (0, import_react.useMemo)(() => {
|
|
215
223
|
if (iframeState) {
|
|
216
224
|
return iframeState;
|
|
@@ -283,7 +291,7 @@ var PlayruoEmbed = ({
|
|
|
283
291
|
embedOrigin
|
|
284
292
|
);
|
|
285
293
|
} catch {
|
|
286
|
-
console.error("[PlayruoEmbed] Failed to send
|
|
294
|
+
console.error("[PlayruoEmbed] Failed to send iframe action");
|
|
287
295
|
}
|
|
288
296
|
},
|
|
289
297
|
[embedOrigin, iframeRef]
|
|
@@ -806,7 +814,9 @@ var PlayruoEmbed = ({
|
|
|
806
814
|
tryNow: handleTryNow,
|
|
807
815
|
buyNow: handleBuyNow,
|
|
808
816
|
canTryNow,
|
|
809
|
-
canBuyNow: Boolean(isReady),
|
|
817
|
+
canBuyNow: Boolean(isReady && storeUrl),
|
|
818
|
+
mute: () => postPasswordAction({ action: "mute" }),
|
|
819
|
+
unmute: () => postPasswordAction({ action: "unmute" }),
|
|
810
820
|
submitPassword: (password2) => {
|
|
811
821
|
if (!password2) {
|
|
812
822
|
return;
|
|
@@ -872,10 +882,8 @@ var PlayruoEmbed = ({
|
|
|
872
882
|
"iframe",
|
|
873
883
|
{
|
|
874
884
|
ref: iframeRef,
|
|
875
|
-
src,
|
|
876
885
|
allow: PLAYRUO_IFRAME_ALLOW,
|
|
877
886
|
className: "w-full h-[70vh] rounded-lg border",
|
|
878
|
-
...{ credentialless: "true" },
|
|
879
887
|
sandbox: "allow-same-origin allow-scripts allow-popups allow-forms allow-presentation allow-top-navigation allow-modals allow-pointer-lock",
|
|
880
888
|
title: "Playruo",
|
|
881
889
|
referrerPolicy: "strict-origin-when-cross-origin",
|
|
@@ -887,8 +895,10 @@ var PlayruoEmbed = ({
|
|
|
887
895
|
height: "100%",
|
|
888
896
|
border: 0
|
|
889
897
|
},
|
|
890
|
-
onLoad: () => {
|
|
891
|
-
|
|
898
|
+
onLoad: (event) => {
|
|
899
|
+
if (event.currentTarget.getAttribute("src")) {
|
|
900
|
+
setIsIframeLoaded(true);
|
|
901
|
+
}
|
|
892
902
|
},
|
|
893
903
|
onError: () => {
|
|
894
904
|
console.error("[PlayruoEmbed] Iframe failed to load");
|
|
@@ -929,6 +939,7 @@ var createInitialDiagnostics = () => ({
|
|
|
929
939
|
});
|
|
930
940
|
var EmbedTester = (props = {}) => {
|
|
931
941
|
const [operationDraft, setOperationDraft] = (0, import_react2.useState)(props.operation ?? "iframe");
|
|
942
|
+
const [operationKeyDraft, setOperationKeyDraft] = (0, import_react2.useState)(props.operationKey ?? "");
|
|
932
943
|
const [storeUrlDraft, setStoreUrlDraft] = (0, import_react2.useState)(
|
|
933
944
|
props.storeUrl ?? "https://store.steampowered.com/app/123456"
|
|
934
945
|
);
|
|
@@ -936,6 +947,7 @@ var EmbedTester = (props = {}) => {
|
|
|
936
947
|
props.enableWindowedLaunch ?? false
|
|
937
948
|
);
|
|
938
949
|
const [appliedOperation, setAppliedOperation] = (0, import_react2.useState)(props.operation ?? "iframe");
|
|
950
|
+
const [appliedOperationKey, setAppliedOperationKey] = (0, import_react2.useState)(props.operationKey ?? "");
|
|
939
951
|
const [appliedStoreUrl, setAppliedStoreUrl] = (0, import_react2.useState)(
|
|
940
952
|
props.storeUrl ?? "https://store.steampowered.com/app/123456"
|
|
941
953
|
);
|
|
@@ -945,6 +957,7 @@ var EmbedTester = (props = {}) => {
|
|
|
945
957
|
const [key, setKey] = (0, import_react2.useState)(0);
|
|
946
958
|
const [hasUserClicked, setHasUserClicked] = (0, import_react2.useState)(false);
|
|
947
959
|
const [showIframe, setShowIframe] = (0, import_react2.useState)(false);
|
|
960
|
+
const [overlayPassword, setOverlayPassword] = (0, import_react2.useState)("");
|
|
948
961
|
const [embedState, setEmbedState] = (0, import_react2.useState)(createInitialEmbedState);
|
|
949
962
|
const [eventLog, setEventLog] = (0, import_react2.useState)([]);
|
|
950
963
|
const [diagnostics, setDiagnostics] = (0, import_react2.useState)(createInitialDiagnostics);
|
|
@@ -955,10 +968,11 @@ var EmbedTester = (props = {}) => {
|
|
|
955
968
|
const previousStateRef = (0, import_react2.useRef)("");
|
|
956
969
|
const iframeRef = (0, import_react2.useRef)(null);
|
|
957
970
|
const isOperationPending = operationDraft !== appliedOperation;
|
|
971
|
+
const isOperationKeyPending = operationKeyDraft !== appliedOperationKey;
|
|
958
972
|
const isStoreUrlPending = storeUrlDraft !== appliedStoreUrl;
|
|
959
973
|
const isWindowedLaunchPending = enableWindowedLaunchDraft !== appliedEnableWindowedLaunch;
|
|
960
|
-
const hasPendingConfigChanges = isOperationPending || isStoreUrlPending || isWindowedLaunchPending;
|
|
961
|
-
const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
|
|
974
|
+
const hasPendingConfigChanges = isOperationPending || isOperationKeyPending || isStoreUrlPending || isWindowedLaunchPending;
|
|
975
|
+
const pendingChangeCount = Number(isOperationPending) + Number(isOperationKeyPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
|
|
962
976
|
const addEvent = (0, import_react2.useCallback)(
|
|
963
977
|
(message) => {
|
|
964
978
|
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
@@ -1043,7 +1057,7 @@ var EmbedTester = (props = {}) => {
|
|
|
1043
1057
|
}
|
|
1044
1058
|
}, [addEvent, enableConsoleLog]);
|
|
1045
1059
|
const handleEvent = (0, import_react2.useCallback)((data) => {
|
|
1046
|
-
const eventName = typeof data.
|
|
1060
|
+
const eventName = typeof data.name === "string" ? data.name : typeof data.event === "string" ? data.event : "unknown";
|
|
1047
1061
|
addEvent(`Event received: ${eventName}`);
|
|
1048
1062
|
if (enableConsoleLog) {
|
|
1049
1063
|
console.log("Embed event:", data);
|
|
@@ -1059,25 +1073,40 @@ var EmbedTester = (props = {}) => {
|
|
|
1059
1073
|
setKey((prev) => prev + 1);
|
|
1060
1074
|
setHasUserClicked(false);
|
|
1061
1075
|
setShowIframe(false);
|
|
1076
|
+
setOverlayPassword("");
|
|
1062
1077
|
hasReadyBeenCalled.current = false;
|
|
1063
1078
|
previousStateRef.current = "";
|
|
1064
1079
|
addEvent(message);
|
|
1065
1080
|
}, [addEvent]);
|
|
1066
1081
|
const handleApply = (0, import_react2.useCallback)(() => {
|
|
1067
1082
|
setAppliedOperation(operationDraft);
|
|
1083
|
+
setAppliedOperationKey(operationKeyDraft);
|
|
1068
1084
|
setAppliedStoreUrl(storeUrlDraft);
|
|
1069
1085
|
setAppliedEnableWindowedLaunch(enableWindowedLaunchDraft);
|
|
1070
1086
|
resetEmbedSession("Applied updated embed settings");
|
|
1071
|
-
}, [
|
|
1087
|
+
}, [
|
|
1088
|
+
enableWindowedLaunchDraft,
|
|
1089
|
+
operationDraft,
|
|
1090
|
+
operationKeyDraft,
|
|
1091
|
+
resetEmbedSession,
|
|
1092
|
+
storeUrlDraft
|
|
1093
|
+
]);
|
|
1072
1094
|
const handleRestartSession = (0, import_react2.useCallback)(() => {
|
|
1073
1095
|
resetEmbedSession("Embed restarted with applied settings");
|
|
1074
1096
|
}, [resetEmbedSession]);
|
|
1075
1097
|
const handleResetDrafts = (0, import_react2.useCallback)(() => {
|
|
1076
1098
|
setOperationDraft(appliedOperation);
|
|
1099
|
+
setOperationKeyDraft(appliedOperationKey);
|
|
1077
1100
|
setStoreUrlDraft(appliedStoreUrl);
|
|
1078
1101
|
setEnableWindowedLaunchDraft(appliedEnableWindowedLaunch);
|
|
1079
1102
|
addEvent("Reverted draft changes to the applied configuration");
|
|
1080
|
-
}, [
|
|
1103
|
+
}, [
|
|
1104
|
+
addEvent,
|
|
1105
|
+
appliedEnableWindowedLaunch,
|
|
1106
|
+
appliedOperation,
|
|
1107
|
+
appliedOperationKey,
|
|
1108
|
+
appliedStoreUrl
|
|
1109
|
+
]);
|
|
1081
1110
|
const panelStyle = {
|
|
1082
1111
|
position: "relative",
|
|
1083
1112
|
border: "1px solid rgba(148, 163, 184, 0.18)",
|
|
@@ -1609,6 +1638,46 @@ var EmbedTester = (props = {}) => {
|
|
|
1609
1638
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: appliedOperation })
|
|
1610
1639
|
] })
|
|
1611
1640
|
] }),
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: "operationKey", style: labelStyle, children: [
|
|
1643
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Op key" }),
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: buildIndicatorStyle(isOperationKeyPending), children: [
|
|
1645
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: buildIndicatorDotStyle(isOperationKeyPending) }),
|
|
1646
|
+
isOperationKeyPending ? "Pending" : "Live"
|
|
1647
|
+
] })
|
|
1648
|
+
] }),
|
|
1649
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: buildInputShellStyle(isOperationKeyPending), children: [
|
|
1650
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1651
|
+
"input",
|
|
1652
|
+
{
|
|
1653
|
+
id: "operationKey",
|
|
1654
|
+
style: inputStyle,
|
|
1655
|
+
onChange: (e) => setOperationKeyDraft(e.target.value),
|
|
1656
|
+
placeholder: "e.g. embed-password",
|
|
1657
|
+
value: operationKeyDraft
|
|
1658
|
+
}
|
|
1659
|
+
),
|
|
1660
|
+
isOperationKeyPending && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1661
|
+
"span",
|
|
1662
|
+
{
|
|
1663
|
+
"aria-hidden": "true",
|
|
1664
|
+
style: {
|
|
1665
|
+
fontSize: "16px",
|
|
1666
|
+
color: "#fbbf24",
|
|
1667
|
+
fontWeight: 700
|
|
1668
|
+
},
|
|
1669
|
+
children: "!"
|
|
1670
|
+
}
|
|
1671
|
+
)
|
|
1672
|
+
] }),
|
|
1673
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: helperTextStyle, children: [
|
|
1674
|
+
"Optional ",
|
|
1675
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: "?op=" }),
|
|
1676
|
+
" when the path is the campaign. Applied:",
|
|
1677
|
+
" ",
|
|
1678
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: codeStyle, children: appliedOperationKey || "(none)" })
|
|
1679
|
+
] })
|
|
1680
|
+
] }),
|
|
1612
1681
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1613
1682
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: "storeUrl", style: labelStyle, children: [
|
|
1614
1683
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Store URL" }),
|
|
@@ -2049,7 +2118,8 @@ var EmbedTester = (props = {}) => {
|
|
|
2049
2118
|
onStateChange: handleStateChange,
|
|
2050
2119
|
enableWindowedLaunch: appliedEnableWindowedLaunch,
|
|
2051
2120
|
operation: appliedOperation,
|
|
2052
|
-
|
|
2121
|
+
operationKey: appliedOperationKey || void 0,
|
|
2122
|
+
renderState: ({ actions, message, status, password }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: !hasUserClicked && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2053
2123
|
"div",
|
|
2054
2124
|
{
|
|
2055
2125
|
style: {
|
|
@@ -2104,48 +2174,117 @@ var EmbedTester = (props = {}) => {
|
|
|
2104
2174
|
fontSize: "28px",
|
|
2105
2175
|
letterSpacing: "-0.03em"
|
|
2106
2176
|
},
|
|
2107
|
-
children: embedState.hasPlayedOnce ? "Resume the session" : "Start the demo"
|
|
2177
|
+
children: password?.required && !password.unlocked ? "Password required" : embedState.hasPlayedOnce ? "Resume the session" : "Start the demo"
|
|
2108
2178
|
}
|
|
2109
2179
|
),
|
|
2110
2180
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { style: { margin: 0, color: "#94a3b8", lineHeight: 1.7 }, children: [
|
|
2111
|
-
"Status:
|
|
2181
|
+
"Status:",
|
|
2182
|
+
" ",
|
|
2112
2183
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#f8fafc", fontWeight: 700 }, children: status })
|
|
2113
2184
|
] }),
|
|
2114
|
-
message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: "10px 0 0 0", color: "#cbd5e1", lineHeight: 1.7 }, children: message }),
|
|
2115
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
{
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2185
|
+
message && !(password?.required && !password.unlocked) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: "10px 0 0 0", color: "#cbd5e1", lineHeight: 1.7 }, children: message }),
|
|
2186
|
+
password?.required && !password.unlocked ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2187
|
+
"form",
|
|
2188
|
+
{
|
|
2189
|
+
style: {
|
|
2190
|
+
display: "flex",
|
|
2191
|
+
flexWrap: "wrap",
|
|
2192
|
+
gap: "12px",
|
|
2193
|
+
marginTop: "24px",
|
|
2194
|
+
alignItems: "center"
|
|
2195
|
+
},
|
|
2196
|
+
onSubmit: (event) => {
|
|
2197
|
+
event.preventDefault();
|
|
2198
|
+
if (!overlayPassword) {
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
actions.submitPassword(overlayPassword);
|
|
2202
|
+
setOverlayPassword("");
|
|
2203
|
+
},
|
|
2204
|
+
children: [
|
|
2205
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2206
|
+
"input",
|
|
2207
|
+
{
|
|
2208
|
+
"aria-label": "Operation password",
|
|
2209
|
+
autoComplete: "off",
|
|
2210
|
+
onChange: (event) => setOverlayPassword(event.target.value),
|
|
2211
|
+
placeholder: "Operation password",
|
|
2212
|
+
style: {
|
|
2213
|
+
flex: "1 1 180px",
|
|
2214
|
+
minWidth: "140px",
|
|
2215
|
+
padding: "12px 14px",
|
|
2216
|
+
borderRadius: "12px",
|
|
2217
|
+
border: "1px solid rgba(148, 163, 184, 0.28)",
|
|
2218
|
+
backgroundColor: "rgba(15, 23, 42, 0.9)",
|
|
2219
|
+
color: "#f8fafc"
|
|
2220
|
+
},
|
|
2221
|
+
type: "password",
|
|
2222
|
+
value: overlayPassword
|
|
2223
|
+
}
|
|
2224
|
+
),
|
|
2225
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2226
|
+
"button",
|
|
2227
|
+
{
|
|
2228
|
+
type: "submit",
|
|
2229
|
+
style: buildButtonStyle("primary", !overlayPassword),
|
|
2230
|
+
disabled: !overlayPassword,
|
|
2231
|
+
children: "Unlock"
|
|
2232
|
+
}
|
|
2233
|
+
),
|
|
2234
|
+
password.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2235
|
+
"p",
|
|
2236
|
+
{
|
|
2237
|
+
style: {
|
|
2238
|
+
flex: "1 1 100%",
|
|
2239
|
+
margin: 0,
|
|
2240
|
+
color: "#fca5a5",
|
|
2241
|
+
fontSize: "13px"
|
|
2242
|
+
},
|
|
2243
|
+
children: password.error
|
|
2244
|
+
}
|
|
2245
|
+
) : null
|
|
2246
|
+
]
|
|
2247
|
+
}
|
|
2248
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2249
|
+
"div",
|
|
2250
|
+
{
|
|
2251
|
+
style: { display: "flex", flexWrap: "wrap", gap: "12px", marginTop: "24px" },
|
|
2252
|
+
children: [
|
|
2253
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2254
|
+
"button",
|
|
2255
|
+
{
|
|
2256
|
+
type: "button",
|
|
2257
|
+
style: buildButtonStyle("primary", !actions.canTryNow),
|
|
2258
|
+
disabled: !actions.canTryNow,
|
|
2259
|
+
onClick: () => {
|
|
2260
|
+
setHasUserClicked(true);
|
|
2261
|
+
setShowIframe(true);
|
|
2262
|
+
addEvent(
|
|
2263
|
+
embedState.hasPlayedOnce ? "User clicked - Resuming game" : "User clicked - Starting game with tryNow"
|
|
2264
|
+
);
|
|
2265
|
+
actions.tryNow();
|
|
2266
|
+
},
|
|
2267
|
+
children: [
|
|
2268
|
+
"\u{1F3AE} ",
|
|
2269
|
+
embedState.hasPlayedOnce ? "Resume game" : "Try demo now"
|
|
2270
|
+
]
|
|
2271
|
+
}
|
|
2272
|
+
),
|
|
2273
|
+
actions.canBuyNow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2274
|
+
"button",
|
|
2275
|
+
{
|
|
2276
|
+
type: "button",
|
|
2277
|
+
style: buildButtonStyle("secondary"),
|
|
2278
|
+
onClick: () => {
|
|
2279
|
+
addEvent("Buy Now clicked");
|
|
2280
|
+
actions.buyNow();
|
|
2281
|
+
},
|
|
2282
|
+
children: "Buy Now"
|
|
2283
|
+
}
|
|
2284
|
+
)
|
|
2285
|
+
]
|
|
2286
|
+
}
|
|
2287
|
+
)
|
|
2149
2288
|
]
|
|
2150
2289
|
}
|
|
2151
2290
|
)
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { FC, ReactNode } from 'react';
|
|
2
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
1
|
+
import React$1, { FC, ReactNode } from 'react';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* iframe `allow` feature policy delegated to the Playruo player.
|
|
@@ -28,7 +27,13 @@ interface DemoCompletedPayload {
|
|
|
28
27
|
type PlayruoEventPayload = Record<string, unknown> & {
|
|
29
28
|
action?: string;
|
|
30
29
|
category?: string;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated The webapp puts the event identifier on `name`. Nothing it
|
|
32
|
+
* posts carries `event`, so a host switching on it never matches. Kept so
|
|
33
|
+
* older hosts still compile.
|
|
34
|
+
*/
|
|
31
35
|
event?: string;
|
|
36
|
+
/** Event identifier, snake_case (`auth_ok`, `streaming`, `vmnok`). */
|
|
32
37
|
name?: string;
|
|
33
38
|
};
|
|
34
39
|
|
|
@@ -162,8 +167,16 @@ interface PlayruoEmbedActions {
|
|
|
162
167
|
buyNow: () => void;
|
|
163
168
|
/** Whether try now is currently available */
|
|
164
169
|
canTryNow: boolean;
|
|
165
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Whether buy now can call `onPurchaseRequest`. True only when the SDK is
|
|
172
|
+
* ready and a `storeUrl` prop is set. Compatible with mobile/unavailable
|
|
173
|
+
* hosts that still want a store CTA.
|
|
174
|
+
*/
|
|
166
175
|
canBuyNow: boolean;
|
|
176
|
+
/** Mute active stream audio. No acknowledgement is returned. */
|
|
177
|
+
mute: () => void;
|
|
178
|
+
/** Resume and unmute stream audio. No acknowledgement is returned. */
|
|
179
|
+
unmute: () => void;
|
|
167
180
|
/** Send the password collected by the host to a protected operation. */
|
|
168
181
|
submitPassword: (password: string) => void;
|
|
169
182
|
/**
|
|
@@ -263,12 +276,14 @@ interface EmbedTesterProps {
|
|
|
263
276
|
baseUrl?: string;
|
|
264
277
|
enableConsoleLog?: boolean;
|
|
265
278
|
operation?: string;
|
|
279
|
+
/** Optional ?op= key when the campaign path is not the operation itself. */
|
|
280
|
+
operationKey?: string;
|
|
266
281
|
storeUrl?: string;
|
|
267
282
|
/** Start the experience inside the iframe without requesting parent fullscreen. */
|
|
268
283
|
enableWindowedLaunch?: boolean;
|
|
269
284
|
className?: string;
|
|
270
285
|
}
|
|
271
|
-
declare const EmbedTester: (props?: EmbedTesterProps) =>
|
|
286
|
+
declare const EmbedTester: (props?: EmbedTesterProps) => React$1.JSX.Element;
|
|
272
287
|
|
|
273
288
|
interface BuildUrlParams {
|
|
274
289
|
operation: string;
|
|
@@ -291,8 +306,8 @@ declare const buildEmbedUrl: ({ operation, operationKey, storeUrl, }: BuildUrlPa
|
|
|
291
306
|
declare const PLAYRUO_UUID_STORAGE_KEY = "playruo_uuid";
|
|
292
307
|
/**
|
|
293
308
|
* Visitor identity the host answers on `playruo_uuid_request`.
|
|
294
|
-
* Same rule as PlayruoEmbed `clientFingerprint`: a non-empty value is
|
|
295
|
-
* and is not written to localStorage. Otherwise the SDK-managed UUID is used.
|
|
309
|
+
* Same rule as PlayruoEmbed `clientFingerprint`: a non-empty trimmed value is
|
|
310
|
+
* used and is not written to localStorage. Otherwise the SDK-managed UUID is used.
|
|
296
311
|
*/
|
|
297
312
|
declare const resolveVisitorUuid: (clientFingerprint?: string) => string;
|
|
298
313
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { FC, ReactNode } from 'react';
|
|
2
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
1
|
+
import React$1, { FC, ReactNode } from 'react';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* iframe `allow` feature policy delegated to the Playruo player.
|
|
@@ -28,7 +27,13 @@ interface DemoCompletedPayload {
|
|
|
28
27
|
type PlayruoEventPayload = Record<string, unknown> & {
|
|
29
28
|
action?: string;
|
|
30
29
|
category?: string;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated The webapp puts the event identifier on `name`. Nothing it
|
|
32
|
+
* posts carries `event`, so a host switching on it never matches. Kept so
|
|
33
|
+
* older hosts still compile.
|
|
34
|
+
*/
|
|
31
35
|
event?: string;
|
|
36
|
+
/** Event identifier, snake_case (`auth_ok`, `streaming`, `vmnok`). */
|
|
32
37
|
name?: string;
|
|
33
38
|
};
|
|
34
39
|
|
|
@@ -162,8 +167,16 @@ interface PlayruoEmbedActions {
|
|
|
162
167
|
buyNow: () => void;
|
|
163
168
|
/** Whether try now is currently available */
|
|
164
169
|
canTryNow: boolean;
|
|
165
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Whether buy now can call `onPurchaseRequest`. True only when the SDK is
|
|
172
|
+
* ready and a `storeUrl` prop is set. Compatible with mobile/unavailable
|
|
173
|
+
* hosts that still want a store CTA.
|
|
174
|
+
*/
|
|
166
175
|
canBuyNow: boolean;
|
|
176
|
+
/** Mute active stream audio. No acknowledgement is returned. */
|
|
177
|
+
mute: () => void;
|
|
178
|
+
/** Resume and unmute stream audio. No acknowledgement is returned. */
|
|
179
|
+
unmute: () => void;
|
|
167
180
|
/** Send the password collected by the host to a protected operation. */
|
|
168
181
|
submitPassword: (password: string) => void;
|
|
169
182
|
/**
|
|
@@ -263,12 +276,14 @@ interface EmbedTesterProps {
|
|
|
263
276
|
baseUrl?: string;
|
|
264
277
|
enableConsoleLog?: boolean;
|
|
265
278
|
operation?: string;
|
|
279
|
+
/** Optional ?op= key when the campaign path is not the operation itself. */
|
|
280
|
+
operationKey?: string;
|
|
266
281
|
storeUrl?: string;
|
|
267
282
|
/** Start the experience inside the iframe without requesting parent fullscreen. */
|
|
268
283
|
enableWindowedLaunch?: boolean;
|
|
269
284
|
className?: string;
|
|
270
285
|
}
|
|
271
|
-
declare const EmbedTester: (props?: EmbedTesterProps) =>
|
|
286
|
+
declare const EmbedTester: (props?: EmbedTesterProps) => React$1.JSX.Element;
|
|
272
287
|
|
|
273
288
|
interface BuildUrlParams {
|
|
274
289
|
operation: string;
|
|
@@ -291,8 +306,8 @@ declare const buildEmbedUrl: ({ operation, operationKey, storeUrl, }: BuildUrlPa
|
|
|
291
306
|
declare const PLAYRUO_UUID_STORAGE_KEY = "playruo_uuid";
|
|
292
307
|
/**
|
|
293
308
|
* Visitor identity the host answers on `playruo_uuid_request`.
|
|
294
|
-
* Same rule as PlayruoEmbed `clientFingerprint`: a non-empty value is
|
|
295
|
-
* and is not written to localStorage. Otherwise the SDK-managed UUID is used.
|
|
309
|
+
* Same rule as PlayruoEmbed `clientFingerprint`: a non-empty trimmed value is
|
|
310
|
+
* used and is not written to localStorage. Otherwise the SDK-managed UUID is used.
|
|
296
311
|
*/
|
|
297
312
|
declare const resolveVisitorUuid: (clientFingerprint?: string) => string;
|
|
298
313
|
|
package/dist/index.js
CHANGED
|
@@ -167,6 +167,14 @@ var PlayruoEmbed = ({
|
|
|
167
167
|
}, []);
|
|
168
168
|
const isCompatible = !isMobile && isValidChromium;
|
|
169
169
|
const isReady = isCompatible ? isIframeLoaded : true;
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
const iframe = iframeRef.current;
|
|
172
|
+
if (!iframe || iframe.getAttribute("src") === src) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
iframe.setAttribute("credentialless", "");
|
|
176
|
+
iframe.src = src;
|
|
177
|
+
}, [iframeRef, isCompatible, src]);
|
|
170
178
|
const currentState = useMemo(() => {
|
|
171
179
|
if (iframeState) {
|
|
172
180
|
return iframeState;
|
|
@@ -239,7 +247,7 @@ var PlayruoEmbed = ({
|
|
|
239
247
|
embedOrigin
|
|
240
248
|
);
|
|
241
249
|
} catch {
|
|
242
|
-
console.error("[PlayruoEmbed] Failed to send
|
|
250
|
+
console.error("[PlayruoEmbed] Failed to send iframe action");
|
|
243
251
|
}
|
|
244
252
|
},
|
|
245
253
|
[embedOrigin, iframeRef]
|
|
@@ -762,7 +770,9 @@ var PlayruoEmbed = ({
|
|
|
762
770
|
tryNow: handleTryNow,
|
|
763
771
|
buyNow: handleBuyNow,
|
|
764
772
|
canTryNow,
|
|
765
|
-
canBuyNow: Boolean(isReady),
|
|
773
|
+
canBuyNow: Boolean(isReady && storeUrl),
|
|
774
|
+
mute: () => postPasswordAction({ action: "mute" }),
|
|
775
|
+
unmute: () => postPasswordAction({ action: "unmute" }),
|
|
766
776
|
submitPassword: (password2) => {
|
|
767
777
|
if (!password2) {
|
|
768
778
|
return;
|
|
@@ -828,10 +838,8 @@ var PlayruoEmbed = ({
|
|
|
828
838
|
"iframe",
|
|
829
839
|
{
|
|
830
840
|
ref: iframeRef,
|
|
831
|
-
src,
|
|
832
841
|
allow: PLAYRUO_IFRAME_ALLOW,
|
|
833
842
|
className: "w-full h-[70vh] rounded-lg border",
|
|
834
|
-
...{ credentialless: "true" },
|
|
835
843
|
sandbox: "allow-same-origin allow-scripts allow-popups allow-forms allow-presentation allow-top-navigation allow-modals allow-pointer-lock",
|
|
836
844
|
title: "Playruo",
|
|
837
845
|
referrerPolicy: "strict-origin-when-cross-origin",
|
|
@@ -843,8 +851,10 @@ var PlayruoEmbed = ({
|
|
|
843
851
|
height: "100%",
|
|
844
852
|
border: 0
|
|
845
853
|
},
|
|
846
|
-
onLoad: () => {
|
|
847
|
-
|
|
854
|
+
onLoad: (event) => {
|
|
855
|
+
if (event.currentTarget.getAttribute("src")) {
|
|
856
|
+
setIsIframeLoaded(true);
|
|
857
|
+
}
|
|
848
858
|
},
|
|
849
859
|
onError: () => {
|
|
850
860
|
console.error("[PlayruoEmbed] Iframe failed to load");
|
|
@@ -885,6 +895,7 @@ var createInitialDiagnostics = () => ({
|
|
|
885
895
|
});
|
|
886
896
|
var EmbedTester = (props = {}) => {
|
|
887
897
|
const [operationDraft, setOperationDraft] = useState2(props.operation ?? "iframe");
|
|
898
|
+
const [operationKeyDraft, setOperationKeyDraft] = useState2(props.operationKey ?? "");
|
|
888
899
|
const [storeUrlDraft, setStoreUrlDraft] = useState2(
|
|
889
900
|
props.storeUrl ?? "https://store.steampowered.com/app/123456"
|
|
890
901
|
);
|
|
@@ -892,6 +903,7 @@ var EmbedTester = (props = {}) => {
|
|
|
892
903
|
props.enableWindowedLaunch ?? false
|
|
893
904
|
);
|
|
894
905
|
const [appliedOperation, setAppliedOperation] = useState2(props.operation ?? "iframe");
|
|
906
|
+
const [appliedOperationKey, setAppliedOperationKey] = useState2(props.operationKey ?? "");
|
|
895
907
|
const [appliedStoreUrl, setAppliedStoreUrl] = useState2(
|
|
896
908
|
props.storeUrl ?? "https://store.steampowered.com/app/123456"
|
|
897
909
|
);
|
|
@@ -901,6 +913,7 @@ var EmbedTester = (props = {}) => {
|
|
|
901
913
|
const [key, setKey] = useState2(0);
|
|
902
914
|
const [hasUserClicked, setHasUserClicked] = useState2(false);
|
|
903
915
|
const [showIframe, setShowIframe] = useState2(false);
|
|
916
|
+
const [overlayPassword, setOverlayPassword] = useState2("");
|
|
904
917
|
const [embedState, setEmbedState] = useState2(createInitialEmbedState);
|
|
905
918
|
const [eventLog, setEventLog] = useState2([]);
|
|
906
919
|
const [diagnostics, setDiagnostics] = useState2(createInitialDiagnostics);
|
|
@@ -911,10 +924,11 @@ var EmbedTester = (props = {}) => {
|
|
|
911
924
|
const previousStateRef = useRef2("");
|
|
912
925
|
const iframeRef = useRef2(null);
|
|
913
926
|
const isOperationPending = operationDraft !== appliedOperation;
|
|
927
|
+
const isOperationKeyPending = operationKeyDraft !== appliedOperationKey;
|
|
914
928
|
const isStoreUrlPending = storeUrlDraft !== appliedStoreUrl;
|
|
915
929
|
const isWindowedLaunchPending = enableWindowedLaunchDraft !== appliedEnableWindowedLaunch;
|
|
916
|
-
const hasPendingConfigChanges = isOperationPending || isStoreUrlPending || isWindowedLaunchPending;
|
|
917
|
-
const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
|
|
930
|
+
const hasPendingConfigChanges = isOperationPending || isOperationKeyPending || isStoreUrlPending || isWindowedLaunchPending;
|
|
931
|
+
const pendingChangeCount = Number(isOperationPending) + Number(isOperationKeyPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
|
|
918
932
|
const addEvent = useCallback2(
|
|
919
933
|
(message) => {
|
|
920
934
|
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
@@ -999,7 +1013,7 @@ var EmbedTester = (props = {}) => {
|
|
|
999
1013
|
}
|
|
1000
1014
|
}, [addEvent, enableConsoleLog]);
|
|
1001
1015
|
const handleEvent = useCallback2((data) => {
|
|
1002
|
-
const eventName = typeof data.
|
|
1016
|
+
const eventName = typeof data.name === "string" ? data.name : typeof data.event === "string" ? data.event : "unknown";
|
|
1003
1017
|
addEvent(`Event received: ${eventName}`);
|
|
1004
1018
|
if (enableConsoleLog) {
|
|
1005
1019
|
console.log("Embed event:", data);
|
|
@@ -1015,25 +1029,40 @@ var EmbedTester = (props = {}) => {
|
|
|
1015
1029
|
setKey((prev) => prev + 1);
|
|
1016
1030
|
setHasUserClicked(false);
|
|
1017
1031
|
setShowIframe(false);
|
|
1032
|
+
setOverlayPassword("");
|
|
1018
1033
|
hasReadyBeenCalled.current = false;
|
|
1019
1034
|
previousStateRef.current = "";
|
|
1020
1035
|
addEvent(message);
|
|
1021
1036
|
}, [addEvent]);
|
|
1022
1037
|
const handleApply = useCallback2(() => {
|
|
1023
1038
|
setAppliedOperation(operationDraft);
|
|
1039
|
+
setAppliedOperationKey(operationKeyDraft);
|
|
1024
1040
|
setAppliedStoreUrl(storeUrlDraft);
|
|
1025
1041
|
setAppliedEnableWindowedLaunch(enableWindowedLaunchDraft);
|
|
1026
1042
|
resetEmbedSession("Applied updated embed settings");
|
|
1027
|
-
}, [
|
|
1043
|
+
}, [
|
|
1044
|
+
enableWindowedLaunchDraft,
|
|
1045
|
+
operationDraft,
|
|
1046
|
+
operationKeyDraft,
|
|
1047
|
+
resetEmbedSession,
|
|
1048
|
+
storeUrlDraft
|
|
1049
|
+
]);
|
|
1028
1050
|
const handleRestartSession = useCallback2(() => {
|
|
1029
1051
|
resetEmbedSession("Embed restarted with applied settings");
|
|
1030
1052
|
}, [resetEmbedSession]);
|
|
1031
1053
|
const handleResetDrafts = useCallback2(() => {
|
|
1032
1054
|
setOperationDraft(appliedOperation);
|
|
1055
|
+
setOperationKeyDraft(appliedOperationKey);
|
|
1033
1056
|
setStoreUrlDraft(appliedStoreUrl);
|
|
1034
1057
|
setEnableWindowedLaunchDraft(appliedEnableWindowedLaunch);
|
|
1035
1058
|
addEvent("Reverted draft changes to the applied configuration");
|
|
1036
|
-
}, [
|
|
1059
|
+
}, [
|
|
1060
|
+
addEvent,
|
|
1061
|
+
appliedEnableWindowedLaunch,
|
|
1062
|
+
appliedOperation,
|
|
1063
|
+
appliedOperationKey,
|
|
1064
|
+
appliedStoreUrl
|
|
1065
|
+
]);
|
|
1037
1066
|
const panelStyle = {
|
|
1038
1067
|
position: "relative",
|
|
1039
1068
|
border: "1px solid rgba(148, 163, 184, 0.18)",
|
|
@@ -1565,6 +1594,46 @@ var EmbedTester = (props = {}) => {
|
|
|
1565
1594
|
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: appliedOperation })
|
|
1566
1595
|
] })
|
|
1567
1596
|
] }),
|
|
1597
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1598
|
+
/* @__PURE__ */ jsxs2("label", { htmlFor: "operationKey", style: labelStyle, children: [
|
|
1599
|
+
/* @__PURE__ */ jsx2("span", { children: "Op key" }),
|
|
1600
|
+
/* @__PURE__ */ jsxs2("span", { style: buildIndicatorStyle(isOperationKeyPending), children: [
|
|
1601
|
+
/* @__PURE__ */ jsx2("span", { style: buildIndicatorDotStyle(isOperationKeyPending) }),
|
|
1602
|
+
isOperationKeyPending ? "Pending" : "Live"
|
|
1603
|
+
] })
|
|
1604
|
+
] }),
|
|
1605
|
+
/* @__PURE__ */ jsxs2("div", { style: buildInputShellStyle(isOperationKeyPending), children: [
|
|
1606
|
+
/* @__PURE__ */ jsx2(
|
|
1607
|
+
"input",
|
|
1608
|
+
{
|
|
1609
|
+
id: "operationKey",
|
|
1610
|
+
style: inputStyle,
|
|
1611
|
+
onChange: (e) => setOperationKeyDraft(e.target.value),
|
|
1612
|
+
placeholder: "e.g. embed-password",
|
|
1613
|
+
value: operationKeyDraft
|
|
1614
|
+
}
|
|
1615
|
+
),
|
|
1616
|
+
isOperationKeyPending && /* @__PURE__ */ jsx2(
|
|
1617
|
+
"span",
|
|
1618
|
+
{
|
|
1619
|
+
"aria-hidden": "true",
|
|
1620
|
+
style: {
|
|
1621
|
+
fontSize: "16px",
|
|
1622
|
+
color: "#fbbf24",
|
|
1623
|
+
fontWeight: 700
|
|
1624
|
+
},
|
|
1625
|
+
children: "!"
|
|
1626
|
+
}
|
|
1627
|
+
)
|
|
1628
|
+
] }),
|
|
1629
|
+
/* @__PURE__ */ jsxs2("p", { style: helperTextStyle, children: [
|
|
1630
|
+
"Optional ",
|
|
1631
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: "?op=" }),
|
|
1632
|
+
" when the path is the campaign. Applied:",
|
|
1633
|
+
" ",
|
|
1634
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: appliedOperationKey || "(none)" })
|
|
1635
|
+
] })
|
|
1636
|
+
] }),
|
|
1568
1637
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1569
1638
|
/* @__PURE__ */ jsxs2("label", { htmlFor: "storeUrl", style: labelStyle, children: [
|
|
1570
1639
|
/* @__PURE__ */ jsx2("span", { children: "Store URL" }),
|
|
@@ -2005,7 +2074,8 @@ var EmbedTester = (props = {}) => {
|
|
|
2005
2074
|
onStateChange: handleStateChange,
|
|
2006
2075
|
enableWindowedLaunch: appliedEnableWindowedLaunch,
|
|
2007
2076
|
operation: appliedOperation,
|
|
2008
|
-
|
|
2077
|
+
operationKey: appliedOperationKey || void 0,
|
|
2078
|
+
renderState: ({ actions, message, status, password }) => /* @__PURE__ */ jsx2(Fragment, { children: !hasUserClicked && /* @__PURE__ */ jsx2(
|
|
2009
2079
|
"div",
|
|
2010
2080
|
{
|
|
2011
2081
|
style: {
|
|
@@ -2060,48 +2130,117 @@ var EmbedTester = (props = {}) => {
|
|
|
2060
2130
|
fontSize: "28px",
|
|
2061
2131
|
letterSpacing: "-0.03em"
|
|
2062
2132
|
},
|
|
2063
|
-
children: embedState.hasPlayedOnce ? "Resume the session" : "Start the demo"
|
|
2133
|
+
children: password?.required && !password.unlocked ? "Password required" : embedState.hasPlayedOnce ? "Resume the session" : "Start the demo"
|
|
2064
2134
|
}
|
|
2065
2135
|
),
|
|
2066
2136
|
/* @__PURE__ */ jsxs2("p", { style: { margin: 0, color: "#94a3b8", lineHeight: 1.7 }, children: [
|
|
2067
|
-
"Status:
|
|
2137
|
+
"Status:",
|
|
2138
|
+
" ",
|
|
2068
2139
|
/* @__PURE__ */ jsx2("span", { style: { color: "#f8fafc", fontWeight: 700 }, children: status })
|
|
2069
2140
|
] }),
|
|
2070
|
-
message && /* @__PURE__ */ jsx2("p", { style: { margin: "10px 0 0 0", color: "#cbd5e1", lineHeight: 1.7 }, children: message }),
|
|
2071
|
-
/* @__PURE__ */ jsxs2(
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
{
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2141
|
+
message && !(password?.required && !password.unlocked) && /* @__PURE__ */ jsx2("p", { style: { margin: "10px 0 0 0", color: "#cbd5e1", lineHeight: 1.7 }, children: message }),
|
|
2142
|
+
password?.required && !password.unlocked ? /* @__PURE__ */ jsxs2(
|
|
2143
|
+
"form",
|
|
2144
|
+
{
|
|
2145
|
+
style: {
|
|
2146
|
+
display: "flex",
|
|
2147
|
+
flexWrap: "wrap",
|
|
2148
|
+
gap: "12px",
|
|
2149
|
+
marginTop: "24px",
|
|
2150
|
+
alignItems: "center"
|
|
2151
|
+
},
|
|
2152
|
+
onSubmit: (event) => {
|
|
2153
|
+
event.preventDefault();
|
|
2154
|
+
if (!overlayPassword) {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
actions.submitPassword(overlayPassword);
|
|
2158
|
+
setOverlayPassword("");
|
|
2159
|
+
},
|
|
2160
|
+
children: [
|
|
2161
|
+
/* @__PURE__ */ jsx2(
|
|
2162
|
+
"input",
|
|
2163
|
+
{
|
|
2164
|
+
"aria-label": "Operation password",
|
|
2165
|
+
autoComplete: "off",
|
|
2166
|
+
onChange: (event) => setOverlayPassword(event.target.value),
|
|
2167
|
+
placeholder: "Operation password",
|
|
2168
|
+
style: {
|
|
2169
|
+
flex: "1 1 180px",
|
|
2170
|
+
minWidth: "140px",
|
|
2171
|
+
padding: "12px 14px",
|
|
2172
|
+
borderRadius: "12px",
|
|
2173
|
+
border: "1px solid rgba(148, 163, 184, 0.28)",
|
|
2174
|
+
backgroundColor: "rgba(15, 23, 42, 0.9)",
|
|
2175
|
+
color: "#f8fafc"
|
|
2176
|
+
},
|
|
2177
|
+
type: "password",
|
|
2178
|
+
value: overlayPassword
|
|
2179
|
+
}
|
|
2180
|
+
),
|
|
2181
|
+
/* @__PURE__ */ jsx2(
|
|
2182
|
+
"button",
|
|
2183
|
+
{
|
|
2184
|
+
type: "submit",
|
|
2185
|
+
style: buildButtonStyle("primary", !overlayPassword),
|
|
2186
|
+
disabled: !overlayPassword,
|
|
2187
|
+
children: "Unlock"
|
|
2188
|
+
}
|
|
2189
|
+
),
|
|
2190
|
+
password.error ? /* @__PURE__ */ jsx2(
|
|
2191
|
+
"p",
|
|
2192
|
+
{
|
|
2193
|
+
style: {
|
|
2194
|
+
flex: "1 1 100%",
|
|
2195
|
+
margin: 0,
|
|
2196
|
+
color: "#fca5a5",
|
|
2197
|
+
fontSize: "13px"
|
|
2198
|
+
},
|
|
2199
|
+
children: password.error
|
|
2200
|
+
}
|
|
2201
|
+
) : null
|
|
2202
|
+
]
|
|
2203
|
+
}
|
|
2204
|
+
) : /* @__PURE__ */ jsxs2(
|
|
2205
|
+
"div",
|
|
2206
|
+
{
|
|
2207
|
+
style: { display: "flex", flexWrap: "wrap", gap: "12px", marginTop: "24px" },
|
|
2208
|
+
children: [
|
|
2209
|
+
/* @__PURE__ */ jsxs2(
|
|
2210
|
+
"button",
|
|
2211
|
+
{
|
|
2212
|
+
type: "button",
|
|
2213
|
+
style: buildButtonStyle("primary", !actions.canTryNow),
|
|
2214
|
+
disabled: !actions.canTryNow,
|
|
2215
|
+
onClick: () => {
|
|
2216
|
+
setHasUserClicked(true);
|
|
2217
|
+
setShowIframe(true);
|
|
2218
|
+
addEvent(
|
|
2219
|
+
embedState.hasPlayedOnce ? "User clicked - Resuming game" : "User clicked - Starting game with tryNow"
|
|
2220
|
+
);
|
|
2221
|
+
actions.tryNow();
|
|
2222
|
+
},
|
|
2223
|
+
children: [
|
|
2224
|
+
"\u{1F3AE} ",
|
|
2225
|
+
embedState.hasPlayedOnce ? "Resume game" : "Try demo now"
|
|
2226
|
+
]
|
|
2227
|
+
}
|
|
2228
|
+
),
|
|
2229
|
+
actions.canBuyNow && /* @__PURE__ */ jsx2(
|
|
2230
|
+
"button",
|
|
2231
|
+
{
|
|
2232
|
+
type: "button",
|
|
2233
|
+
style: buildButtonStyle("secondary"),
|
|
2234
|
+
onClick: () => {
|
|
2235
|
+
addEvent("Buy Now clicked");
|
|
2236
|
+
actions.buyNow();
|
|
2237
|
+
},
|
|
2238
|
+
children: "Buy Now"
|
|
2239
|
+
}
|
|
2240
|
+
)
|
|
2241
|
+
]
|
|
2242
|
+
}
|
|
2243
|
+
)
|
|
2105
2244
|
]
|
|
2106
2245
|
}
|
|
2107
2246
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playruo/embed",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "React SDK to embed Playruo with postMessage API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,15 +35,22 @@
|
|
|
35
35
|
"@types/node": "^26.2.0",
|
|
36
36
|
"@types/react": "^19",
|
|
37
37
|
"@types/react-dom": "^19",
|
|
38
|
-
"@vitest/browser": "^
|
|
39
|
-
"@vitest/
|
|
38
|
+
"@vitest/browser": "^5.0.2",
|
|
39
|
+
"@vitest/browser-playwright": "^5.0.2",
|
|
40
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
40
41
|
"jsdom": "^26.1.0",
|
|
41
|
-
"playwright": "
|
|
42
|
+
"playwright": "1.62.1",
|
|
42
43
|
"react": "^19.1.0",
|
|
43
44
|
"react-dom": "^19.1.0",
|
|
44
45
|
"tsup": "^8.0.0",
|
|
45
46
|
"typescript": "^5.4.0",
|
|
46
|
-
"vitest": "^
|
|
47
|
+
"vitest": "^5.0.2"
|
|
48
|
+
},
|
|
49
|
+
"overrides": {
|
|
50
|
+
"esbuild": "^0.28.1",
|
|
51
|
+
"picomatch": "^4.0.4",
|
|
52
|
+
"rollup": "^4.59.0",
|
|
53
|
+
"source-map-js": "^1.2.2"
|
|
47
54
|
},
|
|
48
55
|
"scripts": {
|
|
49
56
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
@@ -52,9 +59,11 @@
|
|
|
52
59
|
"typecheck": "tsc -p tsconfig.test.json",
|
|
53
60
|
"check:exports": "attw --pack .",
|
|
54
61
|
"test": "npm run build && vitest run --coverage",
|
|
62
|
+
"test:smoke": "vitest run tests/smoke",
|
|
55
63
|
"test:browser": "vitest run --config vitest.browser.config.ts",
|
|
56
64
|
"test:watch": "npm run build && vitest",
|
|
57
65
|
"docs:notion:dry-run": "node scripts/sync-notion-docs.mjs",
|
|
58
|
-
"docs:notion:publish": "node scripts/sync-notion-docs.mjs --publish"
|
|
66
|
+
"docs:notion:publish": "node scripts/sync-notion-docs.mjs --publish",
|
|
67
|
+
"docs:catalog:check": "node scripts/check-embed-event-catalog.mjs && node scripts/check-embed-error-catalog.mjs"
|
|
59
68
|
}
|
|
60
69
|
}
|