@oasiz/sdk 1.6.0 → 1.6.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/README.md +540 -82
- package/dist/index.cjs +3076 -215
- package/dist/index.d.cts +304 -110
- package/dist/index.d.ts +304 -110
- package/dist/index.js +3064 -204
- package/package.json +14 -4
package/dist/index.cjs
CHANGED
|
@@ -20,114 +20,2444 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
addScore: () => addScore,
|
|
24
|
+
enableAppSimulator: () => enableAppSimulator,
|
|
25
|
+
enableBackButtonTesting: () => enableBackButtonTesting,
|
|
26
|
+
enableLogOverlay: () => enableLogOverlay,
|
|
25
27
|
flushGameState: () => flushGameState,
|
|
26
|
-
getEntitlements: () => getEntitlements,
|
|
27
28
|
getGameId: () => getGameId,
|
|
28
|
-
|
|
29
|
+
getGraphicsPerformance: () => getGraphicsPerformance,
|
|
29
30
|
getPlayerAvatar: () => getPlayerAvatar,
|
|
31
|
+
getPlayerCharacter: () => getPlayerCharacter,
|
|
32
|
+
getPlayerId: () => getPlayerId,
|
|
30
33
|
getPlayerName: () => getPlayerName,
|
|
31
|
-
getProducts: () => getProducts,
|
|
32
|
-
getQuantity: () => getQuantity,
|
|
33
34
|
getRoomCode: () => getRoomCode,
|
|
34
|
-
|
|
35
|
+
getSafeAreaTop: () => getSafeAreaTop,
|
|
36
|
+
getViewportInsets: () => getViewportInsets,
|
|
35
37
|
leaveGame: () => leaveGame,
|
|
36
38
|
loadGameState: () => loadGameState,
|
|
37
39
|
oasiz: () => oasiz,
|
|
38
40
|
onBackButton: () => onBackButton,
|
|
39
|
-
onEntitlementsChanged: () => onEntitlementsChanged,
|
|
40
|
-
onJemBalanceChanged: () => onJemBalanceChanged,
|
|
41
41
|
onLeaveGame: () => onLeaveGame,
|
|
42
42
|
onPause: () => onPause,
|
|
43
43
|
onResume: () => onResume,
|
|
44
|
-
|
|
44
|
+
openInviteModal: () => openInviteModal,
|
|
45
45
|
saveGameState: () => saveGameState,
|
|
46
|
+
setLeaderboardVisible: () => setLeaderboardVisible,
|
|
47
|
+
setScore: () => setScore,
|
|
46
48
|
share: () => share,
|
|
47
49
|
shareRoomCode: () => shareRoomCode,
|
|
48
50
|
submitScore: () => submitScore,
|
|
49
|
-
syncProducts: () => syncProducts,
|
|
50
51
|
triggerHaptic: () => triggerHaptic
|
|
51
52
|
});
|
|
52
53
|
module.exports = __toCommonJS(index_exports);
|
|
53
54
|
|
|
54
|
-
// src/
|
|
55
|
+
// src/navigation.ts
|
|
56
|
+
var BACK_BUTTON_TEST_STATE_KEY = "__oasizBackButtonTest";
|
|
57
|
+
var activeBackListeners = 0;
|
|
58
|
+
var activeBackButtonTestingHandle;
|
|
55
59
|
function isDevelopment() {
|
|
56
60
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
57
61
|
return nodeEnv !== "production";
|
|
58
62
|
}
|
|
59
|
-
function getBridgeWindow() {
|
|
63
|
+
function getBridgeWindow() {
|
|
64
|
+
if (typeof window === "undefined") {
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
return window;
|
|
68
|
+
}
|
|
69
|
+
function warnMissingBridge(methodName) {
|
|
70
|
+
if (isDevelopment()) {
|
|
71
|
+
console.warn(
|
|
72
|
+
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function normalizeNavigationError(error) {
|
|
77
|
+
if (error instanceof Error) {
|
|
78
|
+
return error;
|
|
79
|
+
}
|
|
80
|
+
return new Error(
|
|
81
|
+
typeof error === "string" ? error : "Back button callback failed."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function isRecord(value) {
|
|
85
|
+
return typeof value === "object" && value !== null;
|
|
86
|
+
}
|
|
87
|
+
function dispatchNavigationEvent(eventName) {
|
|
88
|
+
const bridge = getBridgeWindow();
|
|
89
|
+
if (!bridge || typeof bridge.dispatchEvent !== "function") {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
bridge.dispatchEvent(new Event(eventName));
|
|
93
|
+
}
|
|
94
|
+
function addNavigationListener(eventName, callback) {
|
|
95
|
+
if (typeof window === "undefined") {
|
|
96
|
+
if (isDevelopment()) {
|
|
97
|
+
console.warn(
|
|
98
|
+
"[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return () => {
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const handler = () => callback();
|
|
105
|
+
window.addEventListener(eventName, handler);
|
|
106
|
+
return () => window.removeEventListener(eventName, handler);
|
|
107
|
+
}
|
|
108
|
+
function enableBackButtonTesting(options = {}) {
|
|
109
|
+
activeBackButtonTestingHandle?.destroy();
|
|
110
|
+
const bridge = getBridgeWindow();
|
|
111
|
+
if (!bridge) {
|
|
112
|
+
if (isDevelopment()) {
|
|
113
|
+
console.warn(
|
|
114
|
+
"[oasiz/sdk] enableBackButtonTesting requires a browser window."
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
destroy: () => {
|
|
119
|
+
},
|
|
120
|
+
isBackOverrideActive: () => false,
|
|
121
|
+
triggerBack: () => {
|
|
122
|
+
},
|
|
123
|
+
triggerLeave: () => {
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const bridgeWindow = bridge;
|
|
128
|
+
const keyboard = options.keyboard ?? true;
|
|
129
|
+
const browserHistory = options.browserHistory ?? true;
|
|
130
|
+
const log = options.log === true;
|
|
131
|
+
const previousSetBackOverride = bridgeWindow.__oasizSetBackOverride;
|
|
132
|
+
const previousLeaveGame = bridgeWindow.__oasizLeaveGame;
|
|
133
|
+
let destroyed = false;
|
|
134
|
+
let backOverrideActive = false;
|
|
135
|
+
let historyTrapArmed = false;
|
|
136
|
+
function maybeLog(message) {
|
|
137
|
+
if (log) {
|
|
138
|
+
console.info("[oasiz/sdk] " + message);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function canUseHistoryTrap() {
|
|
142
|
+
return browserHistory && typeof bridgeWindow.history?.pushState === "function" && typeof bridgeWindow.history?.replaceState === "function" && typeof bridgeWindow.location?.href === "string";
|
|
143
|
+
}
|
|
144
|
+
function ensureHistoryTrap() {
|
|
145
|
+
if (!backOverrideActive || historyTrapArmed || !canUseHistoryTrap()) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const currentState = isRecord(bridgeWindow.history.state) ? bridgeWindow.history.state : {};
|
|
150
|
+
bridgeWindow.history.replaceState(
|
|
151
|
+
{ ...currentState, [BACK_BUTTON_TEST_STATE_KEY]: "base" },
|
|
152
|
+
"",
|
|
153
|
+
bridgeWindow.location.href
|
|
154
|
+
);
|
|
155
|
+
bridgeWindow.history.pushState(
|
|
156
|
+
{ [BACK_BUTTON_TEST_STATE_KEY]: "trap" },
|
|
157
|
+
"",
|
|
158
|
+
bridgeWindow.location.href
|
|
159
|
+
);
|
|
160
|
+
historyTrapArmed = true;
|
|
161
|
+
maybeLog("Local browser Back testing is armed.");
|
|
162
|
+
} catch (error) {
|
|
163
|
+
historyTrapArmed = false;
|
|
164
|
+
if (log) {
|
|
165
|
+
console.warn("[oasiz/sdk] Failed to arm browser Back testing:", error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function triggerBack() {
|
|
170
|
+
dispatchNavigationEvent("oasiz:back");
|
|
171
|
+
}
|
|
172
|
+
function triggerLeave() {
|
|
173
|
+
dispatchNavigationEvent("oasiz:leave");
|
|
174
|
+
}
|
|
175
|
+
function setBackOverride(active) {
|
|
176
|
+
backOverrideActive = active;
|
|
177
|
+
if (active) {
|
|
178
|
+
ensureHistoryTrap();
|
|
179
|
+
}
|
|
180
|
+
if (typeof previousSetBackOverride === "function") {
|
|
181
|
+
previousSetBackOverride(active);
|
|
182
|
+
}
|
|
183
|
+
maybeLog("Back override " + (active ? "enabled" : "disabled") + ".");
|
|
184
|
+
}
|
|
185
|
+
function stopBackEvent(event) {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
event.stopPropagation();
|
|
188
|
+
event.stopImmediatePropagation?.();
|
|
189
|
+
}
|
|
190
|
+
const handleKeyDown = (event) => {
|
|
191
|
+
if (!backOverrideActive || event.key !== "Escape") {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
stopBackEvent(event);
|
|
195
|
+
triggerBack();
|
|
196
|
+
};
|
|
197
|
+
const handlePopState = (event) => {
|
|
198
|
+
if (!backOverrideActive) {
|
|
199
|
+
historyTrapArmed = false;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
stopBackEvent(event);
|
|
203
|
+
triggerBack();
|
|
204
|
+
historyTrapArmed = false;
|
|
205
|
+
ensureHistoryTrap();
|
|
206
|
+
};
|
|
207
|
+
const testLeaveGame = () => {
|
|
208
|
+
triggerLeave();
|
|
209
|
+
if (typeof previousLeaveGame === "function") {
|
|
210
|
+
previousLeaveGame();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
bridgeWindow.__oasizSetBackOverride = setBackOverride;
|
|
214
|
+
bridgeWindow.__oasizLeaveGame = testLeaveGame;
|
|
215
|
+
if (keyboard) {
|
|
216
|
+
bridgeWindow.addEventListener("keydown", handleKeyDown);
|
|
217
|
+
}
|
|
218
|
+
if (browserHistory) {
|
|
219
|
+
bridgeWindow.addEventListener("popstate", handlePopState);
|
|
220
|
+
}
|
|
221
|
+
if (activeBackListeners > 0) {
|
|
222
|
+
setBackOverride(true);
|
|
223
|
+
}
|
|
224
|
+
maybeLog("Back button testing bridge installed.");
|
|
225
|
+
const handle = {
|
|
226
|
+
destroy: () => {
|
|
227
|
+
if (destroyed) return;
|
|
228
|
+
destroyed = true;
|
|
229
|
+
if (keyboard) {
|
|
230
|
+
bridgeWindow.removeEventListener("keydown", handleKeyDown);
|
|
231
|
+
}
|
|
232
|
+
if (browserHistory) {
|
|
233
|
+
bridgeWindow.removeEventListener("popstate", handlePopState);
|
|
234
|
+
}
|
|
235
|
+
if (bridgeWindow.__oasizSetBackOverride === setBackOverride) {
|
|
236
|
+
bridgeWindow.__oasizSetBackOverride = previousSetBackOverride;
|
|
237
|
+
}
|
|
238
|
+
if (bridgeWindow.__oasizLeaveGame === testLeaveGame) {
|
|
239
|
+
bridgeWindow.__oasizLeaveGame = previousLeaveGame;
|
|
240
|
+
}
|
|
241
|
+
if (activeBackButtonTestingHandle === handle) {
|
|
242
|
+
activeBackButtonTestingHandle = void 0;
|
|
243
|
+
}
|
|
244
|
+
maybeLog("Back button testing bridge removed.");
|
|
245
|
+
},
|
|
246
|
+
isBackOverrideActive: () => backOverrideActive,
|
|
247
|
+
triggerBack,
|
|
248
|
+
triggerLeave
|
|
249
|
+
};
|
|
250
|
+
activeBackButtonTestingHandle = handle;
|
|
251
|
+
return handle;
|
|
252
|
+
}
|
|
253
|
+
function onBackButton(callback) {
|
|
254
|
+
const off = addNavigationListener("oasiz:back", () => {
|
|
255
|
+
try {
|
|
256
|
+
callback();
|
|
257
|
+
} catch (error) {
|
|
258
|
+
leaveGame();
|
|
259
|
+
throw normalizeNavigationError(error);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
const bridge = getBridgeWindow();
|
|
263
|
+
activeBackListeners += 1;
|
|
264
|
+
if (activeBackListeners === 1) {
|
|
265
|
+
if (typeof bridge?.__oasizSetBackOverride === "function") {
|
|
266
|
+
bridge.__oasizSetBackOverride(true);
|
|
267
|
+
} else {
|
|
268
|
+
warnMissingBridge("__oasizSetBackOverride");
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return () => {
|
|
272
|
+
off();
|
|
273
|
+
activeBackListeners = Math.max(0, activeBackListeners - 1);
|
|
274
|
+
if (activeBackListeners === 0) {
|
|
275
|
+
const currentBridge = getBridgeWindow();
|
|
276
|
+
if (typeof currentBridge?.__oasizSetBackOverride === "function") {
|
|
277
|
+
currentBridge.__oasizSetBackOverride(false);
|
|
278
|
+
} else {
|
|
279
|
+
warnMissingBridge("__oasizSetBackOverride");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function onLeaveGame(callback) {
|
|
285
|
+
return addNavigationListener("oasiz:leave", callback);
|
|
286
|
+
}
|
|
287
|
+
function leaveGame() {
|
|
288
|
+
const bridge = getBridgeWindow();
|
|
289
|
+
if (typeof bridge?.__oasizLeaveGame === "function") {
|
|
290
|
+
bridge.__oasizLeaveGame();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
warnMissingBridge("__oasizLeaveGame");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/app-simulator.ts
|
|
297
|
+
var DEVICE_PRESETS = {
|
|
298
|
+
"iphone-11": {
|
|
299
|
+
name: "iPhone 11",
|
|
300
|
+
width: 414,
|
|
301
|
+
height: 896,
|
|
302
|
+
safeArea: { top: 48, right: 0, bottom: 34, left: 0 }
|
|
303
|
+
},
|
|
304
|
+
"iphone-11-pro": {
|
|
305
|
+
name: "iPhone 11 Pro",
|
|
306
|
+
width: 375,
|
|
307
|
+
height: 812,
|
|
308
|
+
safeArea: { top: 44, right: 0, bottom: 34, left: 0 }
|
|
309
|
+
},
|
|
310
|
+
"iphone-11-pro-max": {
|
|
311
|
+
name: "iPhone 11 Pro Max",
|
|
312
|
+
width: 414,
|
|
313
|
+
height: 896,
|
|
314
|
+
safeArea: { top: 44, right: 0, bottom: 34, left: 0 }
|
|
315
|
+
},
|
|
316
|
+
"iphone-12-mini": {
|
|
317
|
+
name: "iPhone 12 mini",
|
|
318
|
+
width: 375,
|
|
319
|
+
height: 812,
|
|
320
|
+
safeArea: { top: 50, right: 0, bottom: 34, left: 0 }
|
|
321
|
+
},
|
|
322
|
+
"iphone-12": {
|
|
323
|
+
name: "iPhone 12",
|
|
324
|
+
width: 390,
|
|
325
|
+
height: 844,
|
|
326
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
327
|
+
},
|
|
328
|
+
"iphone-12-pro": {
|
|
329
|
+
name: "iPhone 12 Pro",
|
|
330
|
+
width: 390,
|
|
331
|
+
height: 844,
|
|
332
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
333
|
+
},
|
|
334
|
+
"iphone-12-pro-max": {
|
|
335
|
+
name: "iPhone 12 Pro Max",
|
|
336
|
+
width: 428,
|
|
337
|
+
height: 926,
|
|
338
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
339
|
+
},
|
|
340
|
+
"iphone-13-mini": {
|
|
341
|
+
name: "iPhone 13 mini",
|
|
342
|
+
width: 375,
|
|
343
|
+
height: 812,
|
|
344
|
+
safeArea: { top: 50, right: 0, bottom: 34, left: 0 }
|
|
345
|
+
},
|
|
346
|
+
"iphone-13": {
|
|
347
|
+
name: "iPhone 13",
|
|
348
|
+
width: 390,
|
|
349
|
+
height: 844,
|
|
350
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
351
|
+
},
|
|
352
|
+
"iphone-13-pro": {
|
|
353
|
+
name: "iPhone 13 Pro",
|
|
354
|
+
width: 390,
|
|
355
|
+
height: 844,
|
|
356
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
357
|
+
},
|
|
358
|
+
"iphone-13-pro-max": {
|
|
359
|
+
name: "iPhone 13 Pro Max",
|
|
360
|
+
width: 428,
|
|
361
|
+
height: 926,
|
|
362
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
363
|
+
},
|
|
364
|
+
"iphone-14": {
|
|
365
|
+
name: "iPhone 14",
|
|
366
|
+
width: 390,
|
|
367
|
+
height: 844,
|
|
368
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
369
|
+
},
|
|
370
|
+
"iphone-14-plus": {
|
|
371
|
+
name: "iPhone 14 Plus",
|
|
372
|
+
width: 428,
|
|
373
|
+
height: 926,
|
|
374
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
375
|
+
},
|
|
376
|
+
"iphone-14-pro": {
|
|
377
|
+
name: "iPhone 14 Pro",
|
|
378
|
+
width: 393,
|
|
379
|
+
height: 852,
|
|
380
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
381
|
+
},
|
|
382
|
+
"iphone-14-pro-max": {
|
|
383
|
+
name: "iPhone 14 Pro Max",
|
|
384
|
+
width: 430,
|
|
385
|
+
height: 932,
|
|
386
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
387
|
+
},
|
|
388
|
+
"iphone-15": {
|
|
389
|
+
name: "iPhone 15",
|
|
390
|
+
width: 393,
|
|
391
|
+
height: 852,
|
|
392
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
393
|
+
},
|
|
394
|
+
"iphone-15-plus": {
|
|
395
|
+
name: "iPhone 15 Plus",
|
|
396
|
+
width: 430,
|
|
397
|
+
height: 932,
|
|
398
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
399
|
+
},
|
|
400
|
+
"iphone-15-pro": {
|
|
401
|
+
name: "iPhone 15 Pro",
|
|
402
|
+
width: 393,
|
|
403
|
+
height: 852,
|
|
404
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
405
|
+
},
|
|
406
|
+
"iphone-15-pro-max": {
|
|
407
|
+
name: "iPhone 15 Pro Max",
|
|
408
|
+
width: 430,
|
|
409
|
+
height: 932,
|
|
410
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
411
|
+
},
|
|
412
|
+
"iphone-16": {
|
|
413
|
+
name: "iPhone 16",
|
|
414
|
+
width: 393,
|
|
415
|
+
height: 852,
|
|
416
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
417
|
+
},
|
|
418
|
+
"iphone-16-plus": {
|
|
419
|
+
name: "iPhone 16 Plus",
|
|
420
|
+
width: 430,
|
|
421
|
+
height: 932,
|
|
422
|
+
safeArea: { top: 59, right: 0, bottom: 34, left: 0 }
|
|
423
|
+
},
|
|
424
|
+
"iphone-16-pro": {
|
|
425
|
+
name: "iPhone 16 Pro",
|
|
426
|
+
width: 402,
|
|
427
|
+
height: 874,
|
|
428
|
+
safeArea: { top: 62, right: 0, bottom: 34, left: 0 }
|
|
429
|
+
},
|
|
430
|
+
"iphone-16-pro-max": {
|
|
431
|
+
name: "iPhone 16 Pro Max",
|
|
432
|
+
width: 440,
|
|
433
|
+
height: 956,
|
|
434
|
+
safeArea: { top: 62, right: 0, bottom: 34, left: 0 }
|
|
435
|
+
},
|
|
436
|
+
"iphone-16e": {
|
|
437
|
+
name: "iPhone 16e",
|
|
438
|
+
width: 390,
|
|
439
|
+
height: 844,
|
|
440
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
441
|
+
},
|
|
442
|
+
"iphone-17": {
|
|
443
|
+
name: "iPhone 17",
|
|
444
|
+
width: 402,
|
|
445
|
+
height: 874,
|
|
446
|
+
safeArea: { top: 62, right: 0, bottom: 34, left: 0 }
|
|
447
|
+
},
|
|
448
|
+
"iphone-17-pro": {
|
|
449
|
+
name: "iPhone 17 Pro",
|
|
450
|
+
width: 402,
|
|
451
|
+
height: 874,
|
|
452
|
+
safeArea: { top: 62, right: 0, bottom: 34, left: 0 }
|
|
453
|
+
},
|
|
454
|
+
"iphone-17-pro-max": {
|
|
455
|
+
name: "iPhone 17 Pro Max",
|
|
456
|
+
width: 440,
|
|
457
|
+
height: 956,
|
|
458
|
+
safeArea: { top: 62, right: 0, bottom: 34, left: 0 }
|
|
459
|
+
},
|
|
460
|
+
"iphone-17e": {
|
|
461
|
+
name: "iPhone 17e",
|
|
462
|
+
width: 390,
|
|
463
|
+
height: 844,
|
|
464
|
+
safeArea: { top: 47, right: 0, bottom: 34, left: 0 }
|
|
465
|
+
},
|
|
466
|
+
"iphone-air": {
|
|
467
|
+
name: "iPhone Air",
|
|
468
|
+
width: 420,
|
|
469
|
+
height: 912,
|
|
470
|
+
safeArea: { top: 62, right: 0, bottom: 34, left: 0 }
|
|
471
|
+
},
|
|
472
|
+
"iphone-se": {
|
|
473
|
+
name: "iPhone SE",
|
|
474
|
+
width: 375,
|
|
475
|
+
height: 667,
|
|
476
|
+
safeArea: { top: 20, right: 0, bottom: 0, left: 0 }
|
|
477
|
+
},
|
|
478
|
+
"pixel-8": {
|
|
479
|
+
name: "Pixel 8",
|
|
480
|
+
width: 412,
|
|
481
|
+
height: 915,
|
|
482
|
+
safeArea: { top: 32, right: 0, bottom: 24, left: 0 }
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
var DEFAULT_COUNTS = {
|
|
486
|
+
comments: 18,
|
|
487
|
+
likes: 128
|
|
488
|
+
};
|
|
489
|
+
var DEFAULT_SCORE = 12400;
|
|
490
|
+
var TOP_BAR_OFFSET = 12;
|
|
491
|
+
var TOP_CHROME_HEIGHT = 44;
|
|
492
|
+
var BODY_BACKGROUND = "#08090d";
|
|
493
|
+
var TEXT_PRIMARY = "#F6F9FB";
|
|
494
|
+
var TEXT_SECONDARY = "rgba(246,249,251,0.72)";
|
|
495
|
+
var TEXT_MUTED = "rgba(246,249,251,0.52)";
|
|
496
|
+
var BORDER = "rgba(255,255,255,0.14)";
|
|
497
|
+
var ACCENT = "#00A1E4";
|
|
498
|
+
var LIKE = "#ef4444";
|
|
499
|
+
var TROPHY = "#FBBF24";
|
|
500
|
+
var MAX_Z_INDEX = 2147483638;
|
|
501
|
+
var EMPTY_INSETS = {
|
|
502
|
+
top: 0,
|
|
503
|
+
right: 0,
|
|
504
|
+
bottom: 0,
|
|
505
|
+
left: 0
|
|
506
|
+
};
|
|
507
|
+
var BRIDGE_KEYS = [
|
|
508
|
+
"__OASIZ_SAFE_AREA_BOTTOM__",
|
|
509
|
+
"__OASIZ_SAFE_AREA_BOTTOM_PERCENT__",
|
|
510
|
+
"__OASIZ_SAFE_AREA_LEFT__",
|
|
511
|
+
"__OASIZ_SAFE_AREA_LEFT_PERCENT__",
|
|
512
|
+
"__OASIZ_SAFE_AREA_RIGHT__",
|
|
513
|
+
"__OASIZ_SAFE_AREA_RIGHT_PERCENT__",
|
|
514
|
+
"__OASIZ_SAFE_AREA_TOP__",
|
|
515
|
+
"__OASIZ_SAFE_AREA_TOP_PERCENT__",
|
|
516
|
+
"__OASIZ_VIEWPORT_INSETS__",
|
|
517
|
+
"__OASIZ_VIEWPORT_INSETS_PERCENT__",
|
|
518
|
+
"__oasizSetLeaderboardVisible",
|
|
519
|
+
"getSafeAreaBottom",
|
|
520
|
+
"getSafeAreaBottomPercent",
|
|
521
|
+
"getSafeAreaLeft",
|
|
522
|
+
"getSafeAreaLeftPercent",
|
|
523
|
+
"getSafeAreaRight",
|
|
524
|
+
"getSafeAreaRightPercent",
|
|
525
|
+
"getSafeAreaTop",
|
|
526
|
+
"getSafeAreaTopPercent",
|
|
527
|
+
"getViewportInsets",
|
|
528
|
+
"getViewportInsetsPercent"
|
|
529
|
+
];
|
|
530
|
+
var NOOP_HANDLE = {
|
|
531
|
+
closeSheet() {
|
|
532
|
+
},
|
|
533
|
+
destroy() {
|
|
534
|
+
},
|
|
535
|
+
getViewportInsets() {
|
|
536
|
+
return { pixels: { ...EMPTY_INSETS }, percent: { ...EMPTY_INSETS } };
|
|
537
|
+
},
|
|
538
|
+
hide() {
|
|
539
|
+
},
|
|
540
|
+
isVisible() {
|
|
541
|
+
return false;
|
|
542
|
+
},
|
|
543
|
+
openComments() {
|
|
544
|
+
},
|
|
545
|
+
openLeaderboard() {
|
|
546
|
+
},
|
|
547
|
+
setCounts() {
|
|
548
|
+
},
|
|
549
|
+
setLeaderboardVisible() {
|
|
550
|
+
},
|
|
551
|
+
setLiked() {
|
|
552
|
+
},
|
|
553
|
+
show() {
|
|
554
|
+
},
|
|
555
|
+
triggerBack() {
|
|
556
|
+
},
|
|
557
|
+
triggerLeave() {
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
function getBrowserWindow() {
|
|
561
|
+
if (typeof window === "undefined") {
|
|
562
|
+
return void 0;
|
|
563
|
+
}
|
|
564
|
+
return window;
|
|
565
|
+
}
|
|
566
|
+
function getDocument() {
|
|
567
|
+
if (typeof document === "undefined") {
|
|
568
|
+
return void 0;
|
|
569
|
+
}
|
|
570
|
+
return document;
|
|
571
|
+
}
|
|
572
|
+
function warn(message) {
|
|
573
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
574
|
+
if (nodeEnv !== "production") {
|
|
575
|
+
console.warn("[oasiz/sdk] " + message);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function normalizeCount(value, fallback) {
|
|
579
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
580
|
+
return fallback;
|
|
581
|
+
}
|
|
582
|
+
return Math.max(0, Math.floor(value));
|
|
583
|
+
}
|
|
584
|
+
function formatCompactCount(value) {
|
|
585
|
+
if (value >= 1e6) {
|
|
586
|
+
return (value / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
|
|
587
|
+
}
|
|
588
|
+
if (value >= 1e3) {
|
|
589
|
+
return (value / 1e3).toFixed(1).replace(/\.0$/, "") + "k";
|
|
590
|
+
}
|
|
591
|
+
return String(value);
|
|
592
|
+
}
|
|
593
|
+
function resolveDevice(device, orientation) {
|
|
594
|
+
const preset = typeof device === "string" ? DEVICE_PRESETS[device] : device ? {
|
|
595
|
+
name: device.name ?? "Custom phone",
|
|
596
|
+
width: device.width,
|
|
597
|
+
height: device.height,
|
|
598
|
+
safeArea: {
|
|
599
|
+
...EMPTY_INSETS,
|
|
600
|
+
...device.safeArea
|
|
601
|
+
}
|
|
602
|
+
} : DEVICE_PRESETS["iphone-17-pro-max"];
|
|
603
|
+
const normalized = {
|
|
604
|
+
name: preset.name,
|
|
605
|
+
width: Math.max(320, Math.floor(preset.width)),
|
|
606
|
+
height: Math.max(480, Math.floor(preset.height)),
|
|
607
|
+
safeArea: {
|
|
608
|
+
...EMPTY_INSETS,
|
|
609
|
+
...preset.safeArea
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
if (orientation === "landscape") {
|
|
613
|
+
return {
|
|
614
|
+
...normalized,
|
|
615
|
+
width: Math.max(normalized.width, normalized.height),
|
|
616
|
+
height: Math.min(normalized.width, normalized.height),
|
|
617
|
+
safeArea: {
|
|
618
|
+
top: 0,
|
|
619
|
+
right: normalized.safeArea.top,
|
|
620
|
+
bottom: 21,
|
|
621
|
+
left: normalized.safeArea.top
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
return normalized;
|
|
626
|
+
}
|
|
627
|
+
function shouldFrame(frame, browserWindow, device) {
|
|
628
|
+
if (frame === true) {
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
if (frame === false) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
return browserWindow.innerWidth > device.width + 80 || browserWindow.innerHeight > device.height + 80;
|
|
635
|
+
}
|
|
636
|
+
function computeRect(browserWindow, device, frameEnabled) {
|
|
637
|
+
const viewportWidth = Math.max(320, browserWindow.innerWidth || device.width);
|
|
638
|
+
const viewportHeight = Math.max(480, browserWindow.innerHeight || device.height);
|
|
639
|
+
if (!frameEnabled) {
|
|
640
|
+
return {
|
|
641
|
+
left: 0,
|
|
642
|
+
top: 0,
|
|
643
|
+
width: viewportWidth,
|
|
644
|
+
height: viewportHeight,
|
|
645
|
+
scale: Math.min(viewportWidth / device.width, viewportHeight / device.height)
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const margin = 24;
|
|
649
|
+
const scale = Math.min(
|
|
650
|
+
(viewportWidth - margin) / device.width,
|
|
651
|
+
(viewportHeight - margin) / device.height,
|
|
652
|
+
1
|
|
653
|
+
);
|
|
654
|
+
const width = Math.round(device.width * scale);
|
|
655
|
+
const height = Math.round(device.height * scale);
|
|
656
|
+
return {
|
|
657
|
+
left: Math.round((viewportWidth - width) / 2),
|
|
658
|
+
top: Math.round((viewportHeight - height) / 2),
|
|
659
|
+
width,
|
|
660
|
+
height,
|
|
661
|
+
scale
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function scaledInset(value, scale) {
|
|
665
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
666
|
+
return 0;
|
|
667
|
+
}
|
|
668
|
+
return Math.max(0, Math.round(value * scale));
|
|
669
|
+
}
|
|
670
|
+
function getSimulatorInsets(state) {
|
|
671
|
+
const safe = state.device.safeArea;
|
|
672
|
+
const top = scaledInset(safe.top, state.rect.scale) + Math.round((TOP_BAR_OFFSET + TOP_CHROME_HEIGHT) * state.rect.scale);
|
|
673
|
+
const pixels = {
|
|
674
|
+
top,
|
|
675
|
+
right: scaledInset(safe.right, state.rect.scale),
|
|
676
|
+
bottom: scaledInset(safe.bottom, state.rect.scale),
|
|
677
|
+
left: scaledInset(safe.left, state.rect.scale)
|
|
678
|
+
};
|
|
679
|
+
const percent = {
|
|
680
|
+
top: state.rect.height > 0 ? pixels.top / state.rect.height * 100 : 0,
|
|
681
|
+
right: state.rect.width > 0 ? pixels.right / state.rect.width * 100 : 0,
|
|
682
|
+
bottom: state.rect.height > 0 ? pixels.bottom / state.rect.height * 100 : 0,
|
|
683
|
+
left: state.rect.width > 0 ? pixels.left / state.rect.width * 100 : 0
|
|
684
|
+
};
|
|
685
|
+
return { pixels, percent };
|
|
686
|
+
}
|
|
687
|
+
function snapshotBridge(browserWindow) {
|
|
688
|
+
const globals = {};
|
|
689
|
+
for (const key of BRIDGE_KEYS) {
|
|
690
|
+
globals[key] = browserWindow[key];
|
|
691
|
+
}
|
|
692
|
+
return { globals };
|
|
693
|
+
}
|
|
694
|
+
function restoreBridge(browserWindow, snapshot) {
|
|
695
|
+
for (const key of BRIDGE_KEYS) {
|
|
696
|
+
const value = snapshot.globals[key];
|
|
697
|
+
if (typeof value === "undefined") {
|
|
698
|
+
delete browserWindow[key];
|
|
699
|
+
} else {
|
|
700
|
+
browserWindow[key] = value;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
function installBridge(state) {
|
|
705
|
+
const browserWindow = getBrowserWindow();
|
|
706
|
+
if (!browserWindow) return;
|
|
707
|
+
const bridge = browserWindow;
|
|
708
|
+
function refreshInsets() {
|
|
709
|
+
const insets = getSimulatorInsets(state);
|
|
710
|
+
bridge.__OASIZ_VIEWPORT_INSETS__ = insets;
|
|
711
|
+
bridge.__OASIZ_VIEWPORT_INSETS_PERCENT__ = insets.percent;
|
|
712
|
+
bridge.__OASIZ_SAFE_AREA_TOP__ = insets.pixels.top;
|
|
713
|
+
bridge.__OASIZ_SAFE_AREA_RIGHT__ = insets.pixels.right;
|
|
714
|
+
bridge.__OASIZ_SAFE_AREA_BOTTOM__ = insets.pixels.bottom;
|
|
715
|
+
bridge.__OASIZ_SAFE_AREA_LEFT__ = insets.pixels.left;
|
|
716
|
+
bridge.__OASIZ_SAFE_AREA_TOP_PERCENT__ = insets.percent.top;
|
|
717
|
+
bridge.__OASIZ_SAFE_AREA_RIGHT_PERCENT__ = insets.percent.right;
|
|
718
|
+
bridge.__OASIZ_SAFE_AREA_BOTTOM_PERCENT__ = insets.percent.bottom;
|
|
719
|
+
bridge.__OASIZ_SAFE_AREA_LEFT_PERCENT__ = insets.percent.left;
|
|
720
|
+
return insets;
|
|
721
|
+
}
|
|
722
|
+
bridge.getViewportInsets = () => refreshInsets();
|
|
723
|
+
bridge.getViewportInsetsPercent = () => refreshInsets().percent;
|
|
724
|
+
bridge.getSafeAreaTop = () => refreshInsets().pixels.top;
|
|
725
|
+
bridge.getSafeAreaRight = () => refreshInsets().pixels.right;
|
|
726
|
+
bridge.getSafeAreaBottom = () => refreshInsets().pixels.bottom;
|
|
727
|
+
bridge.getSafeAreaLeft = () => refreshInsets().pixels.left;
|
|
728
|
+
bridge.getSafeAreaTopPercent = () => refreshInsets().percent.top;
|
|
729
|
+
bridge.getSafeAreaRightPercent = () => refreshInsets().percent.right;
|
|
730
|
+
bridge.getSafeAreaBottomPercent = () => refreshInsets().percent.bottom;
|
|
731
|
+
bridge.getSafeAreaLeftPercent = () => refreshInsets().percent.left;
|
|
732
|
+
bridge.__oasizSetLeaderboardVisible = (visible) => {
|
|
733
|
+
state.leaderboard.visible = visible;
|
|
734
|
+
renderSimulator(state);
|
|
735
|
+
};
|
|
736
|
+
refreshInsets();
|
|
737
|
+
}
|
|
738
|
+
function createStyleElement() {
|
|
739
|
+
const style = document.createElement("style");
|
|
740
|
+
style.setAttribute("data-oasiz-app-simulator", "styles");
|
|
741
|
+
style.textContent = [
|
|
742
|
+
".oasiz-app-sim-button{appearance:none;border:0;margin:0;padding:0;font:inherit;color:inherit;background:transparent;cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation}",
|
|
743
|
+
".oasiz-app-sim-button:active{transform:scale(0.97)}",
|
|
744
|
+
".oasiz-app-sim-glass{background:rgba(18,20,24,0.42);border:1px solid rgba(255,255,255,0.22);box-shadow:0 12px 30px rgba(0,0,0,0.28),inset 0 1px 0 rgba(255,255,255,0.18);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}",
|
|
745
|
+
".oasiz-app-sim-scroll::-webkit-scrollbar{width:0;height:0}"
|
|
746
|
+
].join("\n");
|
|
747
|
+
return style;
|
|
748
|
+
}
|
|
749
|
+
function applyTextStyle(element, size = 12, weight = 600) {
|
|
750
|
+
element.style.font = String(weight) + " " + String(size) + "px/1.2 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif";
|
|
751
|
+
}
|
|
752
|
+
function createDiv(className) {
|
|
753
|
+
const element = document.createElement("div");
|
|
754
|
+
if (className) {
|
|
755
|
+
element.className = className;
|
|
756
|
+
}
|
|
757
|
+
return element;
|
|
758
|
+
}
|
|
759
|
+
function createButton(label, title) {
|
|
760
|
+
const button = document.createElement("button");
|
|
761
|
+
button.type = "button";
|
|
762
|
+
button.className = "oasiz-app-sim-button";
|
|
763
|
+
button.setAttribute("aria-label", title);
|
|
764
|
+
button.title = title;
|
|
765
|
+
button.innerHTML = label;
|
|
766
|
+
return button;
|
|
767
|
+
}
|
|
768
|
+
function svgIcon(name, filled = false) {
|
|
769
|
+
if (name === "back") {
|
|
770
|
+
return '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>';
|
|
771
|
+
}
|
|
772
|
+
if (name === "heart") {
|
|
773
|
+
return filled ? '<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M12 21s-7.4-4.4-9.7-9.1C.7 8.5 2.7 4.8 6.4 4.3c2-.3 3.8.7 5.6 2.8 1.8-2.1 3.6-3.1 5.6-2.8 3.7.5 5.7 4.2 4.1 7.6C19.4 16.6 12 21 12 21Z"/></svg>' : '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.8 4.6c-2-1.8-5.1-1.5-6.9.6L12 7.4l-1.9-2.2C8.3 3.1 5.2 2.8 3.2 4.6.8 6.8.7 10.5 3 12.9L12 21l9-8.1c2.3-2.4 2.2-6.1-.2-8.3Z"/></svg>';
|
|
774
|
+
}
|
|
775
|
+
if (name === "chat") {
|
|
776
|
+
return filled ? '<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M4 5.5A3.5 3.5 0 0 1 7.5 2h9A3.5 3.5 0 0 1 20 5.5v7A3.5 3.5 0 0 1 16.5 16H9l-4.2 3.1A1.1 1.1 0 0 1 3 18.2V5.5Z"/></svg>' : '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5.5A3.5 3.5 0 0 1 7.5 2h9A3.5 3.5 0 0 1 20 5.5v7A3.5 3.5 0 0 1 16.5 16H9l-4.2 3.1A1.1 1.1 0 0 1 3 18.2V5.5Z"/></svg>';
|
|
777
|
+
}
|
|
778
|
+
if (name === "trophy") {
|
|
779
|
+
return '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M7 3h10v3h3a1 1 0 0 1 1 1c0 3.2-1.8 5.5-4.6 6.1A5 5 0 0 1 13 15.9V19h3v2H8v-2h3v-3.1a5 5 0 0 1-3.4-2.8C4.8 12.5 3 10.2 3 7a1 1 0 0 1 1-1h3V3Zm10 5v2.8c1.1-.5 1.8-1.5 2-2.8h-2ZM5 8c.2 1.3.9 2.3 2 2.8V8H5Z"/></svg>';
|
|
780
|
+
}
|
|
781
|
+
if (name === "share") {
|
|
782
|
+
return '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round"><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M5 14v5h14v-5"/></svg>';
|
|
783
|
+
}
|
|
784
|
+
return filled ? '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M6 3h12a1 1 0 0 1 1 1v17l-7-4-7 4V4a1 1 0 0 1 1-1Z"/></svg>' : '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3h12a1 1 0 0 1 1 1v17l-7-4-7 4V4a1 1 0 0 1 1-1Z"/></svg>';
|
|
785
|
+
}
|
|
786
|
+
function setBoxStyle(element, state) {
|
|
787
|
+
const { rect } = state;
|
|
788
|
+
element.style.left = rect.left + "px";
|
|
789
|
+
element.style.top = rect.top + "px";
|
|
790
|
+
element.style.width = rect.width + "px";
|
|
791
|
+
element.style.height = rect.height + "px";
|
|
792
|
+
}
|
|
793
|
+
function createCircleButton(icon, title, onClick) {
|
|
794
|
+
const button = createButton(icon, title);
|
|
795
|
+
button.classList.add("oasiz-app-sim-glass");
|
|
796
|
+
button.style.cssText += [
|
|
797
|
+
"position:absolute",
|
|
798
|
+
"width:44px",
|
|
799
|
+
"height:44px",
|
|
800
|
+
"border-radius:999px",
|
|
801
|
+
"display:flex",
|
|
802
|
+
"align-items:center",
|
|
803
|
+
"justify-content:center",
|
|
804
|
+
"color:" + TEXT_PRIMARY,
|
|
805
|
+
"pointer-events:auto"
|
|
806
|
+
].join(";");
|
|
807
|
+
button.addEventListener("click", (event) => {
|
|
808
|
+
event.preventDefault();
|
|
809
|
+
event.stopPropagation();
|
|
810
|
+
onClick();
|
|
811
|
+
});
|
|
812
|
+
return button;
|
|
813
|
+
}
|
|
814
|
+
function createHubPill(state) {
|
|
815
|
+
const button = createButton("", "Open comments");
|
|
816
|
+
button.classList.add("oasiz-app-sim-glass");
|
|
817
|
+
button.style.cssText += [
|
|
818
|
+
"position:absolute",
|
|
819
|
+
"right:16px",
|
|
820
|
+
"height:44px",
|
|
821
|
+
"min-width:94px",
|
|
822
|
+
"border-radius:999px",
|
|
823
|
+
"display:flex",
|
|
824
|
+
"align-items:center",
|
|
825
|
+
"justify-content:center",
|
|
826
|
+
"gap:10px",
|
|
827
|
+
"padding:0 14px",
|
|
828
|
+
"color:" + TEXT_PRIMARY,
|
|
829
|
+
"pointer-events:auto"
|
|
830
|
+
].join(";");
|
|
831
|
+
const likeColor = state.wasLiked ? LIKE : TEXT_PRIMARY;
|
|
832
|
+
button.innerHTML = '<span style="display:inline-flex;align-items:center;gap:4px;color:' + likeColor + '">' + svgIcon("heart", state.wasLiked) + "<span>" + formatCompactCount(state.counts.likes) + '</span></span><span style="display:inline-flex;align-items:center;gap:4px;color:' + ACCENT + '">' + svgIcon("chat", true) + "<span>" + formatCompactCount(state.counts.comments) + "</span></span>";
|
|
833
|
+
applyTextStyle(button, 12, 700);
|
|
834
|
+
button.addEventListener("click", (event) => {
|
|
835
|
+
event.preventDefault();
|
|
836
|
+
event.stopPropagation();
|
|
837
|
+
state.sheet = "comments";
|
|
838
|
+
renderSimulator(state);
|
|
839
|
+
});
|
|
840
|
+
return button;
|
|
841
|
+
}
|
|
842
|
+
function createLeaderboardPill(state) {
|
|
843
|
+
const button = createButton("", "Open leaderboard");
|
|
844
|
+
button.classList.add("oasiz-app-sim-glass");
|
|
845
|
+
button.style.cssText += [
|
|
846
|
+
"position:absolute",
|
|
847
|
+
"left:50%",
|
|
848
|
+
"height:44px",
|
|
849
|
+
"min-width:118px",
|
|
850
|
+
"border-radius:999px",
|
|
851
|
+
"display:" + (state.leaderboard.visible ? "flex" : "none"),
|
|
852
|
+
"align-items:center",
|
|
853
|
+
"justify-content:center",
|
|
854
|
+
"gap:8px",
|
|
855
|
+
"padding:0 14px",
|
|
856
|
+
"color:" + TEXT_PRIMARY,
|
|
857
|
+
"pointer-events:auto",
|
|
858
|
+
"transform:translateX(-50%)"
|
|
859
|
+
].join(";");
|
|
860
|
+
button.innerHTML = '<span style="color:' + TROPHY + ';display:inline-flex">' + svgIcon("trophy", true) + '</span><span style="min-width:0">' + formatCompactCount(state.leaderboard.score) + "</span>";
|
|
861
|
+
applyTextStyle(button, 14, 800);
|
|
862
|
+
button.addEventListener("click", (event) => {
|
|
863
|
+
event.preventDefault();
|
|
864
|
+
event.stopPropagation();
|
|
865
|
+
state.sheet = "leaderboard";
|
|
866
|
+
renderSimulator(state);
|
|
867
|
+
});
|
|
868
|
+
return button;
|
|
869
|
+
}
|
|
870
|
+
function createToolbarButton(icon, count, color, title, onClick) {
|
|
871
|
+
const button = createButton("", title);
|
|
872
|
+
button.classList.add("oasiz-app-sim-glass");
|
|
873
|
+
button.style.cssText += [
|
|
874
|
+
"height:42px",
|
|
875
|
+
"min-width:42px",
|
|
876
|
+
"border-radius:999px",
|
|
877
|
+
"display:flex",
|
|
878
|
+
"align-items:center",
|
|
879
|
+
"justify-content:center",
|
|
880
|
+
"gap:6px",
|
|
881
|
+
"padding:0 12px",
|
|
882
|
+
"color:" + color,
|
|
883
|
+
"pointer-events:auto"
|
|
884
|
+
].join(";");
|
|
885
|
+
button.innerHTML = icon + (count === null ? "" : '<span style="color:' + color + ';min-width:0">' + formatCompactCount(count) + "</span>");
|
|
886
|
+
applyTextStyle(button, 13, 700);
|
|
887
|
+
button.addEventListener("click", (event) => {
|
|
888
|
+
event.preventDefault();
|
|
889
|
+
event.stopPropagation();
|
|
890
|
+
onClick();
|
|
891
|
+
});
|
|
892
|
+
return button;
|
|
893
|
+
}
|
|
894
|
+
function createSheet(state) {
|
|
895
|
+
if (!state.sheet) {
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
const overlay = createDiv();
|
|
899
|
+
overlay.style.cssText = [
|
|
900
|
+
"position:absolute",
|
|
901
|
+
"left:0",
|
|
902
|
+
"top:0",
|
|
903
|
+
"right:0",
|
|
904
|
+
"bottom:0",
|
|
905
|
+
"display:flex",
|
|
906
|
+
"align-items:center",
|
|
907
|
+
"justify-content:center",
|
|
908
|
+
"padding:" + String(scaledInset(state.device.safeArea.top, state.rect.scale) + 72) + "px 12px " + String(Math.max(18, scaledInset(state.device.safeArea.bottom, state.rect.scale) + 18)) + "px",
|
|
909
|
+
"background:rgba(0,0,0,0.34)",
|
|
910
|
+
"pointer-events:auto"
|
|
911
|
+
].join(";");
|
|
912
|
+
overlay.addEventListener("click", () => {
|
|
913
|
+
state.sheet = null;
|
|
914
|
+
renderSimulator(state);
|
|
915
|
+
});
|
|
916
|
+
const sheet = createDiv();
|
|
917
|
+
sheet.style.cssText = [
|
|
918
|
+
"position:relative",
|
|
919
|
+
"width:min(100%, 408px)",
|
|
920
|
+
"max-height:100%",
|
|
921
|
+
"min-height:min(390px, 64%)",
|
|
922
|
+
"display:flex",
|
|
923
|
+
"flex-direction:column",
|
|
924
|
+
"border-radius:24px",
|
|
925
|
+
"border:1px solid " + BORDER,
|
|
926
|
+
"background:linear-gradient(180deg, rgba(18,20,24,0.97), rgba(9,11,15,0.98))",
|
|
927
|
+
"box-shadow:0 30px 80px rgba(0,0,0,0.52), inset 0 1px 0 rgba(255,255,255,0.10)",
|
|
928
|
+
"overflow:hidden",
|
|
929
|
+
"pointer-events:auto"
|
|
930
|
+
].join(";");
|
|
931
|
+
sheet.addEventListener("click", (event) => {
|
|
932
|
+
event.stopPropagation();
|
|
933
|
+
});
|
|
934
|
+
const handle = createDiv();
|
|
935
|
+
handle.style.cssText = [
|
|
936
|
+
"width:42px",
|
|
937
|
+
"height:5px",
|
|
938
|
+
"border-radius:999px",
|
|
939
|
+
"background:rgba(255,255,255,0.28)",
|
|
940
|
+
"align-self:center",
|
|
941
|
+
"margin:10px 0 2px"
|
|
942
|
+
].join(";");
|
|
943
|
+
const header = createDiv();
|
|
944
|
+
header.style.cssText = [
|
|
945
|
+
"display:flex",
|
|
946
|
+
"align-items:center",
|
|
947
|
+
"justify-content:space-between",
|
|
948
|
+
"gap:8px",
|
|
949
|
+
"min-height:62px",
|
|
950
|
+
"padding:6px 14px 10px",
|
|
951
|
+
"border-bottom:1px solid " + BORDER
|
|
952
|
+
].join(";");
|
|
953
|
+
const left = createDiv();
|
|
954
|
+
left.style.cssText = "display:flex;align-items:center;gap:8px;min-width:112px";
|
|
955
|
+
const title = createDiv();
|
|
956
|
+
title.textContent = state.sheet === "comments" ? String(state.counts.comments) + " comments" : "Leaderboard";
|
|
957
|
+
title.style.cssText = [
|
|
958
|
+
"flex:1",
|
|
959
|
+
"min-width:0",
|
|
960
|
+
"text-align:center",
|
|
961
|
+
"color:" + TEXT_PRIMARY
|
|
962
|
+
].join(";");
|
|
963
|
+
applyTextStyle(title, 14, 700);
|
|
964
|
+
const right = createDiv();
|
|
965
|
+
right.style.cssText = "display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:112px";
|
|
966
|
+
const back = createToolbarButton(svgIcon("back"), null, TEXT_PRIMARY, "Close", () => {
|
|
967
|
+
state.sheet = null;
|
|
968
|
+
renderSimulator(state);
|
|
969
|
+
});
|
|
970
|
+
back.style.width = "36px";
|
|
971
|
+
back.style.height = "36px";
|
|
972
|
+
back.style.padding = "0";
|
|
973
|
+
left.appendChild(back);
|
|
974
|
+
if (state.sheet === "comments") {
|
|
975
|
+
const like = createToolbarButton(
|
|
976
|
+
svgIcon("heart", state.wasLiked),
|
|
977
|
+
state.counts.likes,
|
|
978
|
+
state.wasLiked ? LIKE : TEXT_PRIMARY,
|
|
979
|
+
"Like game",
|
|
980
|
+
() => {
|
|
981
|
+
state.wasLiked = !state.wasLiked;
|
|
982
|
+
state.counts.likes = Math.max(0, state.counts.likes + (state.wasLiked ? 1 : -1));
|
|
983
|
+
renderSimulator(state);
|
|
984
|
+
}
|
|
985
|
+
);
|
|
986
|
+
left.appendChild(like);
|
|
987
|
+
} else {
|
|
988
|
+
const trophy = createToolbarButton(svgIcon("trophy", true), null, TROPHY, "Leaderboard", () => {
|
|
989
|
+
});
|
|
990
|
+
trophy.style.width = "36px";
|
|
991
|
+
trophy.style.height = "36px";
|
|
992
|
+
trophy.style.padding = "0";
|
|
993
|
+
left.appendChild(trophy);
|
|
994
|
+
}
|
|
995
|
+
const share2 = createToolbarButton(svgIcon("share"), null, TEXT_PRIMARY, "Share", () => {
|
|
996
|
+
});
|
|
997
|
+
const save = createToolbarButton(svgIcon("bookmark"), null, TEXT_PRIMARY, "Save", () => {
|
|
998
|
+
});
|
|
999
|
+
right.appendChild(share2);
|
|
1000
|
+
right.appendChild(save);
|
|
1001
|
+
header.appendChild(left);
|
|
1002
|
+
header.appendChild(title);
|
|
1003
|
+
header.appendChild(right);
|
|
1004
|
+
const body = createDiv("oasiz-app-sim-scroll");
|
|
1005
|
+
body.style.cssText = [
|
|
1006
|
+
"display:flex",
|
|
1007
|
+
"flex-direction:column",
|
|
1008
|
+
"gap:10px",
|
|
1009
|
+
"overflow:auto",
|
|
1010
|
+
"padding:14px 16px calc(" + String(Math.max(16, scaledInset(state.device.safeArea.bottom, state.rect.scale))) + "px + 14px)",
|
|
1011
|
+
"min-height:0"
|
|
1012
|
+
].join(";");
|
|
1013
|
+
if (state.sheet === "leaderboard") {
|
|
1014
|
+
appendLeaderboardBody(body);
|
|
1015
|
+
} else {
|
|
1016
|
+
appendCommentsBody(body, state);
|
|
1017
|
+
}
|
|
1018
|
+
sheet.appendChild(handle);
|
|
1019
|
+
sheet.appendChild(header);
|
|
1020
|
+
sheet.appendChild(body);
|
|
1021
|
+
overlay.appendChild(sheet);
|
|
1022
|
+
return overlay;
|
|
1023
|
+
}
|
|
1024
|
+
function appendLeaderboardBody(body) {
|
|
1025
|
+
const tabs = createDiv();
|
|
1026
|
+
tabs.style.cssText = [
|
|
1027
|
+
"display:grid",
|
|
1028
|
+
"grid-template-columns:repeat(3,1fr)",
|
|
1029
|
+
"gap:6px",
|
|
1030
|
+
"padding:4px",
|
|
1031
|
+
"border-radius:14px",
|
|
1032
|
+
"background:rgba(255,255,255,0.07)"
|
|
1033
|
+
].join(";");
|
|
1034
|
+
for (const label of ["Weekly", "Global", "Friends"]) {
|
|
1035
|
+
const tab = createDiv();
|
|
1036
|
+
tab.textContent = label;
|
|
1037
|
+
tab.style.cssText = [
|
|
1038
|
+
"border-radius:10px",
|
|
1039
|
+
"padding:8px 6px",
|
|
1040
|
+
"text-align:center",
|
|
1041
|
+
"color:" + (label === "Weekly" ? TEXT_PRIMARY : TEXT_MUTED),
|
|
1042
|
+
"background:" + (label === "Weekly" ? "rgba(0,161,228,0.28)" : "transparent")
|
|
1043
|
+
].join(";");
|
|
1044
|
+
applyTextStyle(tab, 12, 700);
|
|
1045
|
+
tabs.appendChild(tab);
|
|
1046
|
+
}
|
|
1047
|
+
body.appendChild(tabs);
|
|
1048
|
+
const entries = [
|
|
1049
|
+
["1", "Nova", "24.8k"],
|
|
1050
|
+
["2", "You", "12.4k"],
|
|
1051
|
+
["3", "Mika", "9.7k"],
|
|
1052
|
+
["4", "Ari", "8.1k"]
|
|
1053
|
+
];
|
|
1054
|
+
for (const [rank, name, score] of entries) {
|
|
1055
|
+
const row = createDiv();
|
|
1056
|
+
row.style.cssText = [
|
|
1057
|
+
"display:grid",
|
|
1058
|
+
"grid-template-columns:34px 1fr auto",
|
|
1059
|
+
"align-items:center",
|
|
1060
|
+
"gap:10px",
|
|
1061
|
+
"min-height:58px",
|
|
1062
|
+
"padding:10px 12px",
|
|
1063
|
+
"border-radius:14px",
|
|
1064
|
+
"background:" + (name === "You" ? "rgba(0,161,228,0.13)" : "rgba(255,255,255,0.06)"),
|
|
1065
|
+
"border:1px solid " + (name === "You" ? "rgba(0,161,228,0.28)" : "rgba(255,255,255,0.08)")
|
|
1066
|
+
].join(";");
|
|
1067
|
+
const rankEl = createDiv();
|
|
1068
|
+
rankEl.textContent = rank;
|
|
1069
|
+
rankEl.style.cssText = "color:" + (rank === "1" ? TROPHY : TEXT_MUTED) + ";text-align:center";
|
|
1070
|
+
applyTextStyle(rankEl, 14, 800);
|
|
1071
|
+
const nameEl = createDiv();
|
|
1072
|
+
nameEl.textContent = name;
|
|
1073
|
+
nameEl.style.cssText = "color:" + TEXT_PRIMARY + ";min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
1074
|
+
applyTextStyle(nameEl, 14, 700);
|
|
1075
|
+
const scoreEl = createDiv();
|
|
1076
|
+
scoreEl.textContent = score;
|
|
1077
|
+
scoreEl.style.cssText = "color:" + TEXT_SECONDARY;
|
|
1078
|
+
applyTextStyle(scoreEl, 13, 700);
|
|
1079
|
+
row.appendChild(rankEl);
|
|
1080
|
+
row.appendChild(nameEl);
|
|
1081
|
+
row.appendChild(scoreEl);
|
|
1082
|
+
body.appendChild(row);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
function appendCommentsBody(body, state) {
|
|
1086
|
+
const toolbar = createDiv();
|
|
1087
|
+
toolbar.style.cssText = [
|
|
1088
|
+
"display:flex",
|
|
1089
|
+
"align-items:center",
|
|
1090
|
+
"gap:8px",
|
|
1091
|
+
"height:48px"
|
|
1092
|
+
].join(";");
|
|
1093
|
+
toolbar.appendChild(
|
|
1094
|
+
createToolbarButton(
|
|
1095
|
+
svgIcon("heart", state.wasLiked),
|
|
1096
|
+
state.counts.likes,
|
|
1097
|
+
state.wasLiked ? LIKE : TEXT_PRIMARY,
|
|
1098
|
+
"Like game",
|
|
1099
|
+
() => {
|
|
1100
|
+
state.wasLiked = !state.wasLiked;
|
|
1101
|
+
state.counts.likes = Math.max(0, state.counts.likes + (state.wasLiked ? 1 : -1));
|
|
1102
|
+
renderSimulator(state);
|
|
1103
|
+
}
|
|
1104
|
+
)
|
|
1105
|
+
);
|
|
1106
|
+
toolbar.appendChild(
|
|
1107
|
+
createToolbarButton(svgIcon("chat", true), state.counts.comments, ACCENT, "Comments", () => {
|
|
1108
|
+
})
|
|
1109
|
+
);
|
|
1110
|
+
toolbar.appendChild(createToolbarButton(svgIcon("share"), null, TEXT_PRIMARY, "Share", () => {
|
|
1111
|
+
}));
|
|
1112
|
+
toolbar.appendChild(createToolbarButton(svgIcon("bookmark"), null, TEXT_PRIMARY, "Save", () => {
|
|
1113
|
+
}));
|
|
1114
|
+
body.appendChild(toolbar);
|
|
1115
|
+
const comments = [
|
|
1116
|
+
["You", "Can my score panel clear the top buttons?"],
|
|
1117
|
+
["Nova", "This is a good spot to check pause and menu spacing."],
|
|
1118
|
+
["Mika", "The app chrome stays above the game just like mobile."]
|
|
1119
|
+
];
|
|
1120
|
+
for (const [author, text] of comments) {
|
|
1121
|
+
const row = createDiv();
|
|
1122
|
+
row.style.cssText = [
|
|
1123
|
+
"display:grid",
|
|
1124
|
+
"grid-template-columns:34px 1fr",
|
|
1125
|
+
"gap:10px",
|
|
1126
|
+
"padding:10px 0"
|
|
1127
|
+
].join(";");
|
|
1128
|
+
const avatar = createDiv();
|
|
1129
|
+
avatar.textContent = author.charAt(0);
|
|
1130
|
+
avatar.style.cssText = [
|
|
1131
|
+
"width:34px",
|
|
1132
|
+
"height:34px",
|
|
1133
|
+
"border-radius:999px",
|
|
1134
|
+
"display:flex",
|
|
1135
|
+
"align-items:center",
|
|
1136
|
+
"justify-content:center",
|
|
1137
|
+
"background:rgba(0,161,228,0.28)",
|
|
1138
|
+
"color:" + TEXT_PRIMARY
|
|
1139
|
+
].join(";");
|
|
1140
|
+
applyTextStyle(avatar, 13, 800);
|
|
1141
|
+
const message = createDiv();
|
|
1142
|
+
message.innerHTML = '<div style="color:' + TEXT_PRIMARY + ';font-weight:700;margin-bottom:3px">' + author + '</div><div style="color:' + TEXT_SECONDARY + '">' + text + "</div>";
|
|
1143
|
+
applyTextStyle(message, 13, 500);
|
|
1144
|
+
row.appendChild(avatar);
|
|
1145
|
+
row.appendChild(message);
|
|
1146
|
+
body.appendChild(row);
|
|
1147
|
+
}
|
|
1148
|
+
const composer = createDiv();
|
|
1149
|
+
composer.textContent = "Add comment...";
|
|
1150
|
+
composer.style.cssText = [
|
|
1151
|
+
"height:44px",
|
|
1152
|
+
"border-radius:18px",
|
|
1153
|
+
"display:flex",
|
|
1154
|
+
"align-items:center",
|
|
1155
|
+
"padding:0 14px",
|
|
1156
|
+
"background:rgba(255,255,255,0.08)",
|
|
1157
|
+
"border:1px solid rgba(255,255,255,0.10)",
|
|
1158
|
+
"color:" + TEXT_MUTED
|
|
1159
|
+
].join(";");
|
|
1160
|
+
applyTextStyle(composer, 13, 600);
|
|
1161
|
+
body.appendChild(composer);
|
|
1162
|
+
}
|
|
1163
|
+
function renderSimulator(state) {
|
|
1164
|
+
if (state.wasDestroyed) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
const { root, stage, gameViewport } = state.elements;
|
|
1168
|
+
root.style.display = state.visible ? "block" : "none";
|
|
1169
|
+
setBoxStyle(stage, state);
|
|
1170
|
+
if (gameViewport) {
|
|
1171
|
+
setBoxStyle(gameViewport, state);
|
|
1172
|
+
}
|
|
1173
|
+
stage.replaceChildren();
|
|
1174
|
+
const top = scaledInset(state.device.safeArea.top, state.rect.scale) + Math.round(TOP_BAR_OFFSET * state.rect.scale);
|
|
1175
|
+
const back = createCircleButton(svgIcon("back"), "Back", () => {
|
|
1176
|
+
if (state.backHandle.isBackOverrideActive()) {
|
|
1177
|
+
state.backHandle.triggerBack();
|
|
1178
|
+
} else {
|
|
1179
|
+
state.backHandle.triggerLeave();
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
back.style.left = Math.round(16 * state.rect.scale) + "px";
|
|
1183
|
+
back.style.top = top + "px";
|
|
1184
|
+
const leaderboard = createLeaderboardPill(state);
|
|
1185
|
+
leaderboard.style.top = top + "px";
|
|
1186
|
+
const hub = createHubPill(state);
|
|
1187
|
+
hub.style.top = top + "px";
|
|
1188
|
+
hub.style.right = Math.round(16 * state.rect.scale) + "px";
|
|
1189
|
+
stage.appendChild(back);
|
|
1190
|
+
stage.appendChild(leaderboard);
|
|
1191
|
+
stage.appendChild(hub);
|
|
1192
|
+
const sheet = createSheet(state);
|
|
1193
|
+
if (sheet) {
|
|
1194
|
+
stage.appendChild(sheet);
|
|
1195
|
+
}
|
|
1196
|
+
if (state.options.log) {
|
|
1197
|
+
console.info("[oasiz/sdk] App simulator rendered.", {
|
|
1198
|
+
device: state.device.name,
|
|
1199
|
+
frame: state.frameEnabled,
|
|
1200
|
+
insets: getSimulatorInsets(state)
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
function installFrame(doc, state) {
|
|
1205
|
+
if (!state.frameEnabled || !doc.body) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
const body = doc.body;
|
|
1209
|
+
const html = doc.documentElement;
|
|
1210
|
+
const viewport = state.elements.gameViewport;
|
|
1211
|
+
if (!viewport) {
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
html.style.cssText = [
|
|
1215
|
+
state.previousDocumentElementStyle ?? "",
|
|
1216
|
+
"width:100%",
|
|
1217
|
+
"height:100%",
|
|
1218
|
+
"background:" + BODY_BACKGROUND,
|
|
1219
|
+
"overflow:hidden"
|
|
1220
|
+
].join(";");
|
|
1221
|
+
body.style.cssText = [
|
|
1222
|
+
state.previousBodyStyle ?? "",
|
|
1223
|
+
"width:100%",
|
|
1224
|
+
"height:100%",
|
|
1225
|
+
"margin:0",
|
|
1226
|
+
"background:" + BODY_BACKGROUND,
|
|
1227
|
+
"overflow:hidden"
|
|
1228
|
+
].join(";");
|
|
1229
|
+
const candidates = Array.from(body.children).filter(
|
|
1230
|
+
(node) => shouldMoveIntoGameViewport(node, state.elements)
|
|
1231
|
+
);
|
|
1232
|
+
for (const node of candidates) {
|
|
1233
|
+
state.movedNodes.push(node);
|
|
1234
|
+
viewport.appendChild(node);
|
|
1235
|
+
}
|
|
1236
|
+
body.appendChild(viewport);
|
|
1237
|
+
}
|
|
1238
|
+
function shouldMoveIntoGameViewport(node, elements) {
|
|
1239
|
+
if (node === elements.root || node === elements.style || node === elements.gameViewport) {
|
|
1240
|
+
return false;
|
|
1241
|
+
}
|
|
1242
|
+
const tagName = node.tagName.toUpperCase();
|
|
1243
|
+
if (tagName === "SCRIPT" || tagName === "STYLE" || tagName === "LINK" || tagName === "META" || tagName === "NOSCRIPT") {
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1246
|
+
return node.getAttribute("data-oasiz-app-simulator") !== "true";
|
|
1247
|
+
}
|
|
1248
|
+
function restoreFrame(doc, state) {
|
|
1249
|
+
const body = doc.body;
|
|
1250
|
+
if (!body) {
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
for (const node of state.movedNodes) {
|
|
1254
|
+
body.appendChild(node);
|
|
1255
|
+
}
|
|
1256
|
+
state.movedNodes = [];
|
|
1257
|
+
state.elements.gameViewport?.remove();
|
|
1258
|
+
if (state.previousBodyStyle === null) {
|
|
1259
|
+
body.removeAttribute("style");
|
|
1260
|
+
} else {
|
|
1261
|
+
body.setAttribute("style", state.previousBodyStyle);
|
|
1262
|
+
}
|
|
1263
|
+
if (state.previousDocumentElementStyle === null) {
|
|
1264
|
+
doc.documentElement.removeAttribute("style");
|
|
1265
|
+
} else {
|
|
1266
|
+
doc.documentElement.setAttribute("style", state.previousDocumentElementStyle);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
function createElements(frameEnabled) {
|
|
1270
|
+
const root = document.createElement("div");
|
|
1271
|
+
root.setAttribute("data-oasiz-app-simulator", "true");
|
|
1272
|
+
root.style.cssText = [
|
|
1273
|
+
"position:fixed",
|
|
1274
|
+
"inset:0",
|
|
1275
|
+
"z-index:" + String(MAX_Z_INDEX),
|
|
1276
|
+
"pointer-events:none",
|
|
1277
|
+
"display:block",
|
|
1278
|
+
"font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif"
|
|
1279
|
+
].join(";");
|
|
1280
|
+
const stage = document.createElement("div");
|
|
1281
|
+
stage.setAttribute("data-oasiz-app-simulator", "stage");
|
|
1282
|
+
stage.style.cssText = [
|
|
1283
|
+
"position:absolute",
|
|
1284
|
+
"overflow:hidden",
|
|
1285
|
+
"pointer-events:none"
|
|
1286
|
+
].join(";");
|
|
1287
|
+
root.appendChild(stage);
|
|
1288
|
+
const gameViewport = frameEnabled ? document.createElement("div") : null;
|
|
1289
|
+
if (gameViewport) {
|
|
1290
|
+
gameViewport.setAttribute("data-oasiz-app-simulator", "game-viewport");
|
|
1291
|
+
gameViewport.style.cssText = [
|
|
1292
|
+
"position:fixed",
|
|
1293
|
+
"z-index:0",
|
|
1294
|
+
"overflow:hidden",
|
|
1295
|
+
"background:#000",
|
|
1296
|
+
"border-radius:28px",
|
|
1297
|
+
"outline:1px solid rgba(255,255,255,0.28)",
|
|
1298
|
+
"box-shadow:0 28px 90px rgba(0,0,0,0.48),0 0 0 8px rgba(255,255,255,0.06),0 0 0 10px rgba(0,0,0,0.58)"
|
|
1299
|
+
].join(";");
|
|
1300
|
+
}
|
|
1301
|
+
return {
|
|
1302
|
+
gameViewport,
|
|
1303
|
+
root,
|
|
1304
|
+
stage,
|
|
1305
|
+
style: createStyleElement()
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
function installResizeHandler(browserWindow, state) {
|
|
1309
|
+
const resize = () => {
|
|
1310
|
+
state.rect = computeRect(browserWindow, state.device, state.frameEnabled);
|
|
1311
|
+
installBridge(state);
|
|
1312
|
+
renderSimulator(state);
|
|
1313
|
+
};
|
|
1314
|
+
browserWindow.addEventListener("resize", resize);
|
|
1315
|
+
browserWindow.addEventListener("orientationchange", resize);
|
|
1316
|
+
return () => {
|
|
1317
|
+
browserWindow.removeEventListener("resize", resize);
|
|
1318
|
+
browserWindow.removeEventListener("orientationchange", resize);
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
function enableAppSimulator(options = {}) {
|
|
1322
|
+
if (options.enabled === false) {
|
|
1323
|
+
return NOOP_HANDLE;
|
|
1324
|
+
}
|
|
1325
|
+
const browserWindow = getBrowserWindow();
|
|
1326
|
+
const doc = getDocument();
|
|
1327
|
+
if (!browserWindow || !doc?.body || !doc.documentElement) {
|
|
1328
|
+
warn("enableAppSimulator requires a browser document.");
|
|
1329
|
+
return NOOP_HANDLE;
|
|
1330
|
+
}
|
|
1331
|
+
browserWindow.__oasizAppSimulatorHandle__?.destroy();
|
|
1332
|
+
const orientation = options.orientation ?? "portrait";
|
|
1333
|
+
const device = resolveDevice(options.device, orientation);
|
|
1334
|
+
const frameEnabled = shouldFrame(options.frame ?? true, browserWindow, device);
|
|
1335
|
+
const elements = createElements(frameEnabled);
|
|
1336
|
+
const rect = computeRect(browserWindow, device, frameEnabled);
|
|
1337
|
+
const backHandle = enableBackButtonTesting({
|
|
1338
|
+
browserHistory: options.browserHistoryBack ?? true,
|
|
1339
|
+
keyboard: options.keyboardBack ?? true,
|
|
1340
|
+
log: options.log === true
|
|
1341
|
+
});
|
|
1342
|
+
const state = {
|
|
1343
|
+
backHandle,
|
|
1344
|
+
bridgeSnapshot: snapshotBridge(browserWindow),
|
|
1345
|
+
cleanupResize: () => {
|
|
1346
|
+
},
|
|
1347
|
+
counts: {
|
|
1348
|
+
comments: normalizeCount(options.comments, DEFAULT_COUNTS.comments),
|
|
1349
|
+
likes: normalizeCount(options.likes, DEFAULT_COUNTS.likes)
|
|
1350
|
+
},
|
|
1351
|
+
device,
|
|
1352
|
+
elements,
|
|
1353
|
+
frameEnabled,
|
|
1354
|
+
leaderboard: {
|
|
1355
|
+
score: normalizeCount(options.score, DEFAULT_SCORE),
|
|
1356
|
+
visible: options.leaderboardVisible ?? true
|
|
1357
|
+
},
|
|
1358
|
+
movedNodes: [],
|
|
1359
|
+
options: {
|
|
1360
|
+
browserHistoryBack: options.browserHistoryBack ?? true,
|
|
1361
|
+
keyboardBack: options.keyboardBack ?? true,
|
|
1362
|
+
log: options.log === true,
|
|
1363
|
+
orientation,
|
|
1364
|
+
title: options.title ?? "Oasiz App Preview"
|
|
1365
|
+
},
|
|
1366
|
+
previousBodyStyle: doc.body.getAttribute("style"),
|
|
1367
|
+
previousDocumentElementStyle: doc.documentElement.getAttribute("style"),
|
|
1368
|
+
rect,
|
|
1369
|
+
sheet: null,
|
|
1370
|
+
visible: true,
|
|
1371
|
+
wasDestroyed: false,
|
|
1372
|
+
wasLiked: false
|
|
1373
|
+
};
|
|
1374
|
+
if (doc.head) {
|
|
1375
|
+
doc.head.appendChild(elements.style);
|
|
1376
|
+
} else {
|
|
1377
|
+
doc.body.appendChild(elements.style);
|
|
1378
|
+
}
|
|
1379
|
+
installFrame(doc, state);
|
|
1380
|
+
doc.body.appendChild(elements.root);
|
|
1381
|
+
installBridge(state);
|
|
1382
|
+
state.cleanupResize = installResizeHandler(browserWindow, state);
|
|
1383
|
+
const handle = {
|
|
1384
|
+
closeSheet: () => {
|
|
1385
|
+
state.sheet = null;
|
|
1386
|
+
renderSimulator(state);
|
|
1387
|
+
},
|
|
1388
|
+
destroy: () => {
|
|
1389
|
+
if (state.wasDestroyed) return;
|
|
1390
|
+
state.wasDestroyed = true;
|
|
1391
|
+
state.cleanupResize();
|
|
1392
|
+
state.backHandle.destroy();
|
|
1393
|
+
restoreBridge(browserWindow, state.bridgeSnapshot);
|
|
1394
|
+
delete browserWindow.__oasizAppSimulatorHandle__;
|
|
1395
|
+
elements.root.remove();
|
|
1396
|
+
elements.style.remove();
|
|
1397
|
+
restoreFrame(doc, state);
|
|
1398
|
+
},
|
|
1399
|
+
getViewportInsets: () => getSimulatorInsets(state),
|
|
1400
|
+
hide: () => {
|
|
1401
|
+
state.visible = false;
|
|
1402
|
+
renderSimulator(state);
|
|
1403
|
+
},
|
|
1404
|
+
isVisible: () => state.visible,
|
|
1405
|
+
openComments: () => {
|
|
1406
|
+
state.sheet = "comments";
|
|
1407
|
+
renderSimulator(state);
|
|
1408
|
+
},
|
|
1409
|
+
openLeaderboard: () => {
|
|
1410
|
+
state.sheet = "leaderboard";
|
|
1411
|
+
renderSimulator(state);
|
|
1412
|
+
},
|
|
1413
|
+
setCounts: (counts) => {
|
|
1414
|
+
state.counts.comments = normalizeCount(counts.comments, state.counts.comments);
|
|
1415
|
+
state.counts.likes = normalizeCount(counts.likes, state.counts.likes);
|
|
1416
|
+
renderSimulator(state);
|
|
1417
|
+
},
|
|
1418
|
+
setLeaderboardVisible: (visible) => {
|
|
1419
|
+
state.leaderboard.visible = visible;
|
|
1420
|
+
renderSimulator(state);
|
|
1421
|
+
},
|
|
1422
|
+
setLiked: (liked) => {
|
|
1423
|
+
if (state.wasLiked === liked) {
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
state.wasLiked = liked;
|
|
1427
|
+
state.counts.likes = Math.max(0, state.counts.likes + (liked ? 1 : -1));
|
|
1428
|
+
renderSimulator(state);
|
|
1429
|
+
},
|
|
1430
|
+
show: () => {
|
|
1431
|
+
state.visible = true;
|
|
1432
|
+
renderSimulator(state);
|
|
1433
|
+
},
|
|
1434
|
+
triggerBack: () => state.backHandle.triggerBack(),
|
|
1435
|
+
triggerLeave: () => state.backHandle.triggerLeave()
|
|
1436
|
+
};
|
|
1437
|
+
browserWindow.__oasizAppSimulatorHandle__ = handle;
|
|
1438
|
+
renderSimulator(state);
|
|
1439
|
+
return handle;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// src/character.ts
|
|
1443
|
+
function isDevelopment2() {
|
|
1444
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1445
|
+
return nodeEnv !== "production";
|
|
1446
|
+
}
|
|
1447
|
+
function getBridgeWindow2() {
|
|
1448
|
+
if (typeof window === "undefined") {
|
|
1449
|
+
return void 0;
|
|
1450
|
+
}
|
|
1451
|
+
return window;
|
|
1452
|
+
}
|
|
1453
|
+
function warnMissingBridge2(methodName) {
|
|
1454
|
+
if (isDevelopment2()) {
|
|
1455
|
+
console.warn(
|
|
1456
|
+
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
async function getPlayerCharacter() {
|
|
1461
|
+
const bridge = getBridgeWindow2();
|
|
1462
|
+
if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
|
|
1463
|
+
warnMissingBridge2("getPlayerCharacter");
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1466
|
+
try {
|
|
1467
|
+
const result = await bridge.__oasizGetPlayerCharacter();
|
|
1468
|
+
return result ?? null;
|
|
1469
|
+
} catch (error) {
|
|
1470
|
+
if (isDevelopment2()) {
|
|
1471
|
+
console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
|
|
1472
|
+
}
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/haptics.ts
|
|
1478
|
+
function isDevelopment3() {
|
|
1479
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1480
|
+
return nodeEnv !== "production";
|
|
1481
|
+
}
|
|
1482
|
+
function getBridgeWindow3() {
|
|
1483
|
+
if (typeof window === "undefined") {
|
|
1484
|
+
return void 0;
|
|
1485
|
+
}
|
|
1486
|
+
return window;
|
|
1487
|
+
}
|
|
1488
|
+
function triggerHaptic(type) {
|
|
1489
|
+
const bridge = getBridgeWindow3();
|
|
1490
|
+
if (typeof bridge?.triggerHaptic === "function") {
|
|
1491
|
+
bridge.triggerHaptic(type);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
if (isDevelopment3()) {
|
|
1495
|
+
console.warn(
|
|
1496
|
+
"[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// src/log-overlay.ts
|
|
1502
|
+
var CONSOLE_METHODS = [
|
|
1503
|
+
"debug",
|
|
1504
|
+
"log",
|
|
1505
|
+
"info",
|
|
1506
|
+
"warn",
|
|
1507
|
+
"error"
|
|
1508
|
+
];
|
|
1509
|
+
var DEFAULT_MAX_ENTRIES = 200;
|
|
1510
|
+
var DEFAULT_TITLE = "SDK Logs";
|
|
1511
|
+
var OVERLAY_MARGIN = 12;
|
|
1512
|
+
var DEFAULT_COLLAPSED_WIDTH = 156;
|
|
1513
|
+
var DEFAULT_COLLAPSED_HEIGHT = 52;
|
|
1514
|
+
var DEFAULT_EXPANDED_WIDTH = 565;
|
|
1515
|
+
var DEFAULT_EXPANDED_HEIGHT = 372;
|
|
1516
|
+
var DRAG_THRESHOLD_PX = 6;
|
|
1517
|
+
var MIN_EXPANDED_WIDTH = 160;
|
|
1518
|
+
var MIN_EXPANDED_HEIGHT = 110;
|
|
1519
|
+
var RESIZE_HOTSPOT_PX = 28;
|
|
1520
|
+
var TOP_DRAG_ZONE_PX = 44;
|
|
1521
|
+
var NOOP_HANDLE2 = {
|
|
1522
|
+
clear() {
|
|
1523
|
+
},
|
|
1524
|
+
destroy() {
|
|
1525
|
+
},
|
|
1526
|
+
hide() {
|
|
1527
|
+
},
|
|
1528
|
+
isVisible() {
|
|
1529
|
+
return false;
|
|
1530
|
+
},
|
|
1531
|
+
show() {
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
function getBrowserWindow2() {
|
|
60
1535
|
if (typeof window === "undefined") {
|
|
61
1536
|
return void 0;
|
|
62
1537
|
}
|
|
63
1538
|
return window;
|
|
64
1539
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1540
|
+
function getDocument2() {
|
|
1541
|
+
if (typeof document === "undefined") {
|
|
1542
|
+
return void 0;
|
|
1543
|
+
}
|
|
1544
|
+
return document;
|
|
1545
|
+
}
|
|
1546
|
+
function clampMaxEntries(value) {
|
|
1547
|
+
if (!Number.isFinite(value)) {
|
|
1548
|
+
return DEFAULT_MAX_ENTRIES;
|
|
1549
|
+
}
|
|
1550
|
+
return Math.max(10, Math.floor(value));
|
|
1551
|
+
}
|
|
1552
|
+
function createConsoleSnapshot() {
|
|
1553
|
+
const fallback = console.log.bind(console);
|
|
1554
|
+
return {
|
|
1555
|
+
debug: typeof console.debug === "function" ? console.debug.bind(console) : fallback,
|
|
1556
|
+
log: fallback,
|
|
1557
|
+
info: typeof console.info === "function" ? console.info.bind(console) : fallback,
|
|
1558
|
+
warn: typeof console.warn === "function" ? console.warn.bind(console) : fallback,
|
|
1559
|
+
error: typeof console.error === "function" ? console.error.bind(console) : fallback
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
function formatTimestamp(timestamp) {
|
|
1563
|
+
const date = new Date(timestamp);
|
|
1564
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1565
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1566
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
1567
|
+
const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
|
|
1568
|
+
return "[" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + "]";
|
|
1569
|
+
}
|
|
1570
|
+
function safeStringify(value) {
|
|
1571
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1572
|
+
try {
|
|
1573
|
+
return JSON.stringify(
|
|
1574
|
+
value,
|
|
1575
|
+
(_key, candidate) => {
|
|
1576
|
+
if (typeof candidate === "bigint") {
|
|
1577
|
+
return candidate.toString() + "n";
|
|
1578
|
+
}
|
|
1579
|
+
if (typeof candidate === "object" && candidate !== null) {
|
|
1580
|
+
if (seen.has(candidate)) {
|
|
1581
|
+
return "[Circular]";
|
|
1582
|
+
}
|
|
1583
|
+
seen.add(candidate);
|
|
1584
|
+
}
|
|
1585
|
+
return candidate;
|
|
1586
|
+
},
|
|
1587
|
+
2
|
|
1588
|
+
) ?? String(value);
|
|
1589
|
+
} catch {
|
|
1590
|
+
return String(value);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
function formatArg(value) {
|
|
1594
|
+
if (typeof value === "string") {
|
|
1595
|
+
return value;
|
|
1596
|
+
}
|
|
1597
|
+
if (value instanceof Error) {
|
|
1598
|
+
if (value.stack) {
|
|
1599
|
+
return value.stack;
|
|
1600
|
+
}
|
|
1601
|
+
return value.name + ": " + value.message;
|
|
1602
|
+
}
|
|
1603
|
+
if (typeof value === "undefined") {
|
|
1604
|
+
return "undefined";
|
|
1605
|
+
}
|
|
1606
|
+
if (typeof value === "function") {
|
|
1607
|
+
return "[Function " + (value.name || "anonymous") + "]";
|
|
1608
|
+
}
|
|
1609
|
+
return safeStringify(value);
|
|
1610
|
+
}
|
|
1611
|
+
function formatEntryMessage(args) {
|
|
1612
|
+
const message = args.map(formatArg).join(" ");
|
|
1613
|
+
if (message.length <= 4e3) {
|
|
1614
|
+
return message;
|
|
1615
|
+
}
|
|
1616
|
+
return message.slice(0, 3997) + "...";
|
|
1617
|
+
}
|
|
1618
|
+
function createEntry(level, args, id) {
|
|
1619
|
+
return {
|
|
1620
|
+
id,
|
|
1621
|
+
level,
|
|
1622
|
+
message: formatEntryMessage(args),
|
|
1623
|
+
timestamp: Date.now()
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
function createButton2(label) {
|
|
1627
|
+
const button = document.createElement("button");
|
|
1628
|
+
button.type = "button";
|
|
1629
|
+
button.textContent = label;
|
|
1630
|
+
button.style.cssText = [
|
|
1631
|
+
"appearance:none",
|
|
1632
|
+
"border:1px solid rgba(255,255,255,0.18)",
|
|
1633
|
+
"background:rgba(255,255,255,0.06)",
|
|
1634
|
+
"color:#f8fafc",
|
|
1635
|
+
"border-radius:999px",
|
|
1636
|
+
"padding:6px 10px",
|
|
1637
|
+
"font:600 12px/1.1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
1638
|
+
"cursor:pointer"
|
|
1639
|
+
].join(";");
|
|
1640
|
+
return button;
|
|
1641
|
+
}
|
|
1642
|
+
function getLevelAccent(level) {
|
|
1643
|
+
if (level === "error") {
|
|
1644
|
+
return {
|
|
1645
|
+
lineBackground: "rgba(255, 109, 122, 0.08)"
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
if (level === "warn") {
|
|
1649
|
+
return {
|
|
1650
|
+
lineBackground: "rgba(255, 196, 94, 0.07)"
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
if (level === "info") {
|
|
1654
|
+
return {
|
|
1655
|
+
lineBackground: "rgba(82, 187, 255, 0.07)"
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
if (level === "debug") {
|
|
1659
|
+
return {
|
|
1660
|
+
lineBackground: "rgba(166, 137, 255, 0.07)"
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
return {
|
|
1664
|
+
lineBackground: "rgba(117, 235, 191, 0.06)"
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function getViewportSize() {
|
|
1668
|
+
const browserWindow = getBrowserWindow2();
|
|
1669
|
+
return {
|
|
1670
|
+
width: Math.max(320, browserWindow?.innerWidth ?? 1280),
|
|
1671
|
+
height: Math.max(240, browserWindow?.innerHeight ?? 720)
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
function clampPanelSize(size) {
|
|
1675
|
+
const viewport = getViewportSize();
|
|
1676
|
+
const maxWidth = Math.max(MIN_EXPANDED_WIDTH, viewport.width - OVERLAY_MARGIN * 2);
|
|
1677
|
+
const maxHeight = Math.max(
|
|
1678
|
+
MIN_EXPANDED_HEIGHT,
|
|
1679
|
+
viewport.height - OVERLAY_MARGIN * 2
|
|
1680
|
+
);
|
|
1681
|
+
return {
|
|
1682
|
+
width: Math.min(maxWidth, Math.max(MIN_EXPANDED_WIDTH, size.width)),
|
|
1683
|
+
height: Math.min(maxHeight, Math.max(MIN_EXPANDED_HEIGHT, size.height))
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
function getOverlaySize(state) {
|
|
1687
|
+
if (state.expanded && state.panelSize) {
|
|
1688
|
+
return state.panelSize;
|
|
1689
|
+
}
|
|
1690
|
+
const rect = state.ui?.root && typeof state.ui.root.getBoundingClientRect === "function" ? state.ui.root.getBoundingClientRect() : null;
|
|
1691
|
+
if (rect && Number.isFinite(rect.width) && Number.isFinite(rect.height)) {
|
|
1692
|
+
return {
|
|
1693
|
+
width: Math.max(1, rect.width),
|
|
1694
|
+
height: Math.max(1, rect.height)
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
return state.expanded ? clampPanelSize({
|
|
1698
|
+
width: DEFAULT_EXPANDED_WIDTH,
|
|
1699
|
+
height: DEFAULT_EXPANDED_HEIGHT
|
|
1700
|
+
}) : { width: DEFAULT_COLLAPSED_WIDTH, height: DEFAULT_COLLAPSED_HEIGHT };
|
|
1701
|
+
}
|
|
1702
|
+
function applyPanelSize(state) {
|
|
1703
|
+
if (!state.ui) {
|
|
69
1704
|
return;
|
|
70
1705
|
}
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
1706
|
+
if (!state.expanded) {
|
|
1707
|
+
state.ui.root.style.width = "auto";
|
|
1708
|
+
state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
|
|
1709
|
+
state.ui.panel.style.height = "auto";
|
|
1710
|
+
state.ui.entries.style.maxHeight = "min(36vh, 280px)";
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
if (!state.panelSize) {
|
|
1714
|
+
state.ui.root.style.width = "min(565px, calc(100vw - 24px))";
|
|
1715
|
+
state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
|
|
1716
|
+
state.ui.panel.style.height = "auto";
|
|
1717
|
+
state.ui.entries.style.maxHeight = "min(36vh, 280px)";
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
const nextSize = clampPanelSize(
|
|
1721
|
+
state.panelSize
|
|
1722
|
+
);
|
|
1723
|
+
state.panelSize = nextSize;
|
|
1724
|
+
state.ui.root.style.width = nextSize.width + "px";
|
|
1725
|
+
state.ui.panel.style.width = "100%";
|
|
1726
|
+
state.ui.panel.style.height = nextSize.height + "px";
|
|
1727
|
+
state.ui.entries.style.maxHeight = Math.max(72, nextSize.height - 88) + "px";
|
|
1728
|
+
}
|
|
1729
|
+
function clampPosition(point, state) {
|
|
1730
|
+
const viewport = getViewportSize();
|
|
1731
|
+
const size = getOverlaySize(state);
|
|
1732
|
+
const maxX = Math.max(OVERLAY_MARGIN, viewport.width - size.width - OVERLAY_MARGIN);
|
|
1733
|
+
const maxY = Math.max(
|
|
1734
|
+
OVERLAY_MARGIN,
|
|
1735
|
+
viewport.height - size.height - OVERLAY_MARGIN
|
|
1736
|
+
);
|
|
1737
|
+
return {
|
|
1738
|
+
x: Math.min(maxX, Math.max(OVERLAY_MARGIN, point.x)),
|
|
1739
|
+
y: Math.min(maxY, Math.max(OVERLAY_MARGIN, point.y))
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function applyOverlayPosition(state) {
|
|
1743
|
+
if (!state.ui) {
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
if (!state.position) {
|
|
1747
|
+
const viewport = getViewportSize();
|
|
1748
|
+
const size = getOverlaySize(state);
|
|
1749
|
+
state.position = clampPosition(
|
|
1750
|
+
{
|
|
1751
|
+
x: viewport.width - size.width - OVERLAY_MARGIN,
|
|
1752
|
+
y: viewport.height - size.height - OVERLAY_MARGIN
|
|
1753
|
+
},
|
|
1754
|
+
state
|
|
1755
|
+
);
|
|
1756
|
+
} else {
|
|
1757
|
+
state.position = clampPosition(state.position, state);
|
|
1758
|
+
}
|
|
1759
|
+
state.ui.root.style.left = state.position.x + "px";
|
|
1760
|
+
state.ui.root.style.top = state.position.y + "px";
|
|
1761
|
+
}
|
|
1762
|
+
function getPointFromMouseEvent(event) {
|
|
1763
|
+
return { x: event.clientX, y: event.clientY };
|
|
1764
|
+
}
|
|
1765
|
+
function getPointFromTouchEvent(event) {
|
|
1766
|
+
const touch = event.touches[0] ?? event.changedTouches[0];
|
|
1767
|
+
if (!touch) {
|
|
1768
|
+
return null;
|
|
1769
|
+
}
|
|
1770
|
+
return { x: touch.clientX, y: touch.clientY };
|
|
1771
|
+
}
|
|
1772
|
+
function stopDragging(state) {
|
|
1773
|
+
if (state.isDragging || state.isResizing) {
|
|
1774
|
+
state.suppressToggleClickUntil = Date.now() + 180;
|
|
1775
|
+
}
|
|
1776
|
+
state.isDragging = false;
|
|
1777
|
+
state.dragStartPoint = null;
|
|
1778
|
+
state.lastDragPoint = null;
|
|
1779
|
+
state.removeDragListeners?.();
|
|
1780
|
+
state.removeDragListeners = null;
|
|
1781
|
+
if (state.ui) {
|
|
1782
|
+
state.ui.panel.style.cursor = "default";
|
|
1783
|
+
state.ui.dragZone.style.cursor = "grab";
|
|
1784
|
+
state.ui.toggleButton.style.cursor = "grab";
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
function stopResizing(state) {
|
|
1788
|
+
if (state.isResizing) {
|
|
1789
|
+
state.suppressToggleClickUntil = Date.now() + 180;
|
|
1790
|
+
}
|
|
1791
|
+
state.isResizing = false;
|
|
1792
|
+
state.resizeStartPoint = null;
|
|
1793
|
+
state.resizeStartSize = null;
|
|
1794
|
+
state.removeResizeListeners?.();
|
|
1795
|
+
state.removeResizeListeners = null;
|
|
1796
|
+
}
|
|
1797
|
+
function beginDragTracking(state, startPoint) {
|
|
1798
|
+
const doc = getDocument2();
|
|
1799
|
+
if (!doc) {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
stopResizing(state);
|
|
1803
|
+
stopDragging(state);
|
|
1804
|
+
state.dragMoved = false;
|
|
1805
|
+
state.dragStartPoint = startPoint;
|
|
1806
|
+
state.lastDragPoint = startPoint;
|
|
1807
|
+
const handlePointerMove = (nextPoint) => {
|
|
1808
|
+
if (!state.dragStartPoint || !state.lastDragPoint || !nextPoint) {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
if (!state.isDragging) {
|
|
1812
|
+
const deltaFromStartX = nextPoint.x - state.dragStartPoint.x;
|
|
1813
|
+
const deltaFromStartY = nextPoint.y - state.dragStartPoint.y;
|
|
1814
|
+
const distance = Math.sqrt(
|
|
1815
|
+
deltaFromStartX * deltaFromStartX + deltaFromStartY * deltaFromStartY
|
|
1816
|
+
);
|
|
1817
|
+
if (distance < DRAG_THRESHOLD_PX) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
state.isDragging = true;
|
|
1821
|
+
state.dragMoved = true;
|
|
1822
|
+
if (state.ui) {
|
|
1823
|
+
state.ui.panel.style.cursor = "grabbing";
|
|
1824
|
+
state.ui.dragZone.style.cursor = "grabbing";
|
|
1825
|
+
state.ui.toggleButton.style.cursor = "grabbing";
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
const currentPosition = state.position ?? { x: OVERLAY_MARGIN, y: OVERLAY_MARGIN };
|
|
1829
|
+
state.position = clampPosition(
|
|
1830
|
+
{
|
|
1831
|
+
x: currentPosition.x + (nextPoint.x - state.lastDragPoint.x),
|
|
1832
|
+
y: currentPosition.y + (nextPoint.y - state.lastDragPoint.y)
|
|
1833
|
+
},
|
|
1834
|
+
state
|
|
74
1835
|
);
|
|
1836
|
+
state.lastDragPoint = nextPoint;
|
|
1837
|
+
applyOverlayPosition(state);
|
|
1838
|
+
};
|
|
1839
|
+
const handleMouseMove = (event) => {
|
|
1840
|
+
handlePointerMove(getPointFromMouseEvent(event));
|
|
1841
|
+
};
|
|
1842
|
+
const handleTouchMove = (event) => {
|
|
1843
|
+
handlePointerMove(getPointFromTouchEvent(event));
|
|
1844
|
+
};
|
|
1845
|
+
const handleMouseUp = () => {
|
|
1846
|
+
stopDragging(state);
|
|
1847
|
+
};
|
|
1848
|
+
const handleTouchEnd = () => {
|
|
1849
|
+
stopDragging(state);
|
|
1850
|
+
};
|
|
1851
|
+
doc.addEventListener("mousemove", handleMouseMove);
|
|
1852
|
+
doc.addEventListener("mouseup", handleMouseUp);
|
|
1853
|
+
doc.addEventListener("touchmove", handleTouchMove, { passive: true });
|
|
1854
|
+
doc.addEventListener("touchend", handleTouchEnd);
|
|
1855
|
+
doc.addEventListener("touchcancel", handleTouchEnd);
|
|
1856
|
+
state.removeDragListeners = () => {
|
|
1857
|
+
doc.removeEventListener("mousemove", handleMouseMove);
|
|
1858
|
+
doc.removeEventListener("mouseup", handleMouseUp);
|
|
1859
|
+
doc.removeEventListener("touchmove", handleTouchMove);
|
|
1860
|
+
doc.removeEventListener("touchend", handleTouchEnd);
|
|
1861
|
+
doc.removeEventListener("touchcancel", handleTouchEnd);
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
function startResizing(state, startPoint) {
|
|
1865
|
+
const doc = getDocument2();
|
|
1866
|
+
if (!doc) {
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
stopDragging(state);
|
|
1870
|
+
stopResizing(state);
|
|
1871
|
+
state.isResizing = true;
|
|
1872
|
+
state.resizeStartPoint = startPoint;
|
|
1873
|
+
state.resizeStartSize = state.panelSize ?? clampPanelSize({ width: DEFAULT_EXPANDED_WIDTH, height: DEFAULT_EXPANDED_HEIGHT });
|
|
1874
|
+
const handleResizeMove = (nextPoint) => {
|
|
1875
|
+
if (!state.isResizing || !state.resizeStartPoint || !state.resizeStartSize || !nextPoint) {
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
state.panelSize = clampPanelSize({
|
|
1879
|
+
width: state.resizeStartSize.width + (nextPoint.x - state.resizeStartPoint.x),
|
|
1880
|
+
height: state.resizeStartSize.height + (nextPoint.y - state.resizeStartPoint.y)
|
|
1881
|
+
});
|
|
1882
|
+
applyPanelSize(state);
|
|
1883
|
+
applyOverlayPosition(state);
|
|
1884
|
+
};
|
|
1885
|
+
const handleMouseMove = (event) => {
|
|
1886
|
+
handleResizeMove(getPointFromMouseEvent(event));
|
|
1887
|
+
};
|
|
1888
|
+
const handleTouchMove = (event) => {
|
|
1889
|
+
handleResizeMove(getPointFromTouchEvent(event));
|
|
1890
|
+
};
|
|
1891
|
+
const handleFinish = () => {
|
|
1892
|
+
stopResizing(state);
|
|
1893
|
+
};
|
|
1894
|
+
doc.addEventListener("mousemove", handleMouseMove);
|
|
1895
|
+
doc.addEventListener("mouseup", handleFinish);
|
|
1896
|
+
doc.addEventListener("touchmove", handleTouchMove, { passive: true });
|
|
1897
|
+
doc.addEventListener("touchend", handleFinish);
|
|
1898
|
+
doc.addEventListener("touchcancel", handleFinish);
|
|
1899
|
+
state.removeResizeListeners = () => {
|
|
1900
|
+
doc.removeEventListener("mousemove", handleMouseMove);
|
|
1901
|
+
doc.removeEventListener("mouseup", handleFinish);
|
|
1902
|
+
doc.removeEventListener("touchmove", handleTouchMove);
|
|
1903
|
+
doc.removeEventListener("touchend", handleFinish);
|
|
1904
|
+
doc.removeEventListener("touchcancel", handleFinish);
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
function isInBottomRightResizeZone(element, point) {
|
|
1908
|
+
const rect = element.getBoundingClientRect();
|
|
1909
|
+
return point.x >= rect.right - RESIZE_HOTSPOT_PX && point.x <= rect.right && point.y >= rect.bottom - RESIZE_HOTSPOT_PX && point.y <= rect.bottom;
|
|
1910
|
+
}
|
|
1911
|
+
function canStartDragFromTarget(target) {
|
|
1912
|
+
if (!(target instanceof Element)) {
|
|
1913
|
+
return true;
|
|
1914
|
+
}
|
|
1915
|
+
if (target.closest("button") || target.closest("a") || target.closest("input") || target.closest("textarea") || target.closest("select")) {
|
|
1916
|
+
return false;
|
|
1917
|
+
}
|
|
1918
|
+
return true;
|
|
1919
|
+
}
|
|
1920
|
+
function isInTopDragZone(element, point, zoneHeight) {
|
|
1921
|
+
const rect = element.getBoundingClientRect();
|
|
1922
|
+
return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.top + zoneHeight;
|
|
1923
|
+
}
|
|
1924
|
+
function attachDragStartListeners(element, state, options = {}) {
|
|
1925
|
+
element.addEventListener("mousedown", (event) => {
|
|
1926
|
+
if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
const point = getPointFromMouseEvent(event);
|
|
1930
|
+
if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
beginDragTracking(state, point);
|
|
1934
|
+
});
|
|
1935
|
+
element.addEventListener("touchstart", (event) => {
|
|
1936
|
+
if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
const point = getPointFromTouchEvent(event);
|
|
1940
|
+
if (!point) {
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
beginDragTracking(state, point);
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
function attachCollapsedToggleListeners(element, state) {
|
|
1950
|
+
const startToggleInteraction = (startPoint) => {
|
|
1951
|
+
const doc = getDocument2();
|
|
1952
|
+
if (!doc) {
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
beginDragTracking(state, startPoint);
|
|
1956
|
+
const finishInteraction = () => {
|
|
1957
|
+
releaseListeners();
|
|
1958
|
+
if (state.dragMoved || Date.now() < state.suppressToggleClickUntil) {
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
state.expanded = true;
|
|
1962
|
+
state.unreadCount = 0;
|
|
1963
|
+
renderOverlay(state);
|
|
1964
|
+
};
|
|
1965
|
+
const releaseListeners = () => {
|
|
1966
|
+
doc.removeEventListener("mouseup", finishInteraction);
|
|
1967
|
+
doc.removeEventListener("touchend", finishInteraction);
|
|
1968
|
+
doc.removeEventListener("touchcancel", finishInteraction);
|
|
1969
|
+
};
|
|
1970
|
+
doc.addEventListener("mouseup", finishInteraction);
|
|
1971
|
+
doc.addEventListener("touchend", finishInteraction);
|
|
1972
|
+
doc.addEventListener("touchcancel", finishInteraction);
|
|
1973
|
+
};
|
|
1974
|
+
element.addEventListener("mousedown", (event) => {
|
|
1975
|
+
event.preventDefault();
|
|
1976
|
+
startToggleInteraction(getPointFromMouseEvent(event));
|
|
1977
|
+
});
|
|
1978
|
+
element.addEventListener("touchstart", (event) => {
|
|
1979
|
+
const point = getPointFromTouchEvent(event);
|
|
1980
|
+
if (!point) {
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
event.preventDefault();
|
|
1984
|
+
startToggleInteraction(point);
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
function attachPanelResizeListeners(element, state) {
|
|
1988
|
+
element.addEventListener("mousedown", (event) => {
|
|
1989
|
+
if (!state.expanded || !canStartDragFromTarget(event.target)) {
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
const point = getPointFromMouseEvent(event);
|
|
1993
|
+
if (!isInBottomRightResizeZone(element, point)) {
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1996
|
+
event.preventDefault();
|
|
1997
|
+
event.stopPropagation();
|
|
1998
|
+
startResizing(state, point);
|
|
1999
|
+
});
|
|
2000
|
+
element.addEventListener("touchstart", (event) => {
|
|
2001
|
+
if (!state.expanded || !canStartDragFromTarget(event.target)) {
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
const point = getPointFromTouchEvent(event);
|
|
2005
|
+
if (!point || !isInBottomRightResizeZone(element, point)) {
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
2008
|
+
event.preventDefault();
|
|
2009
|
+
event.stopPropagation();
|
|
2010
|
+
startResizing(state, point);
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
function createOverlayUi(state) {
|
|
2014
|
+
const root = document.createElement("div");
|
|
2015
|
+
root.style.cssText = [
|
|
2016
|
+
"position:fixed",
|
|
2017
|
+
"left:12px",
|
|
2018
|
+
"top:12px",
|
|
2019
|
+
"z-index:2147483647",
|
|
2020
|
+
"display:flex",
|
|
2021
|
+
"flex-direction:column",
|
|
2022
|
+
"align-items:stretch",
|
|
2023
|
+
"gap:8px",
|
|
2024
|
+
"width:min(565px, calc(100vw - 24px))",
|
|
2025
|
+
"pointer-events:none"
|
|
2026
|
+
].join(";");
|
|
2027
|
+
const toggleButton = createButton2("Logs");
|
|
2028
|
+
toggleButton.style.pointerEvents = "auto";
|
|
2029
|
+
toggleButton.style.alignSelf = "flex-end";
|
|
2030
|
+
toggleButton.style.display = "inline-flex";
|
|
2031
|
+
toggleButton.style.alignItems = "center";
|
|
2032
|
+
toggleButton.style.justifyContent = "center";
|
|
2033
|
+
toggleButton.style.minHeight = "40px";
|
|
2034
|
+
toggleButton.style.minWidth = "76px";
|
|
2035
|
+
toggleButton.style.padding = "8px 14px";
|
|
2036
|
+
toggleButton.style.textAlign = "center";
|
|
2037
|
+
toggleButton.style.border = "1px solid rgba(122, 212, 255, 0.22)";
|
|
2038
|
+
toggleButton.style.background = "linear-gradient(180deg, rgba(13,31,54,0.98), rgba(8,19,37,0.98))";
|
|
2039
|
+
toggleButton.style.boxShadow = "0 18px 40px rgba(4,12,24,0.34)";
|
|
2040
|
+
toggleButton.style.cursor = "grab";
|
|
2041
|
+
toggleButton.style.touchAction = "none";
|
|
2042
|
+
const panel = document.createElement("div");
|
|
2043
|
+
panel.style.cssText = [
|
|
2044
|
+
"position:relative",
|
|
2045
|
+
"display:flex",
|
|
2046
|
+
"flex-direction:column",
|
|
2047
|
+
"width:min(565px, calc(100vw - 24px))",
|
|
2048
|
+
"max-height:min(48vh, 372px)",
|
|
2049
|
+
"border-radius:18px",
|
|
2050
|
+
"border:1px solid rgba(116,167,255,0.16)",
|
|
2051
|
+
"background:linear-gradient(180deg, rgba(9,19,37,0.98), rgba(5,12,24,0.98))",
|
|
2052
|
+
"box-shadow:0 28px 64px rgba(2,8,18,0.46)",
|
|
2053
|
+
"backdrop-filter:blur(16px)",
|
|
2054
|
+
"overflow:hidden",
|
|
2055
|
+
"cursor:default",
|
|
2056
|
+
"pointer-events:auto"
|
|
2057
|
+
].join(";");
|
|
2058
|
+
const dragZone = document.createElement("div");
|
|
2059
|
+
dragZone.style.cssText = [
|
|
2060
|
+
"position:absolute",
|
|
2061
|
+
"top:0",
|
|
2062
|
+
"left:0",
|
|
2063
|
+
"right:0",
|
|
2064
|
+
"height:" + String(TOP_DRAG_ZONE_PX) + "px",
|
|
2065
|
+
"z-index:1",
|
|
2066
|
+
"cursor:grab",
|
|
2067
|
+
"background:transparent",
|
|
2068
|
+
"pointer-events:auto"
|
|
2069
|
+
].join(";");
|
|
2070
|
+
const controls = document.createElement("div");
|
|
2071
|
+
controls.style.cssText = [
|
|
2072
|
+
"position:absolute",
|
|
2073
|
+
"top:22px",
|
|
2074
|
+
"right:22px",
|
|
2075
|
+
"z-index:2",
|
|
2076
|
+
"display:flex",
|
|
2077
|
+
"align-items:center",
|
|
2078
|
+
"gap:8px",
|
|
2079
|
+
"pointer-events:auto"
|
|
2080
|
+
].join(";");
|
|
2081
|
+
const clearButton = createButton2("Clear");
|
|
2082
|
+
clearButton.style.background = "rgba(255,255,255,0.1)";
|
|
2083
|
+
clearButton.style.border = "1px solid rgba(255,255,255,0.16)";
|
|
2084
|
+
clearButton.style.color = "#eef6ff";
|
|
2085
|
+
clearButton.style.minHeight = "30px";
|
|
2086
|
+
clearButton.style.padding = "4px 9px";
|
|
2087
|
+
clearButton.style.fontSize = "11px";
|
|
2088
|
+
clearButton.style.backdropFilter = "blur(8px)";
|
|
2089
|
+
const collapseButton = createButton2("Hide");
|
|
2090
|
+
collapseButton.style.background = "rgba(113, 171, 255, 0.12)";
|
|
2091
|
+
collapseButton.style.border = "1px solid rgba(113, 171, 255, 0.2)";
|
|
2092
|
+
collapseButton.style.color = "#d9ebff";
|
|
2093
|
+
collapseButton.style.minHeight = "30px";
|
|
2094
|
+
collapseButton.style.padding = "4px 9px";
|
|
2095
|
+
collapseButton.style.fontSize = "11px";
|
|
2096
|
+
collapseButton.style.backdropFilter = "blur(8px)";
|
|
2097
|
+
const body = document.createElement("div");
|
|
2098
|
+
body.style.cssText = [
|
|
2099
|
+
"position:relative",
|
|
2100
|
+
"display:flex",
|
|
2101
|
+
"flex-direction:column",
|
|
2102
|
+
"padding:12px",
|
|
2103
|
+
"background:linear-gradient(180deg, rgba(4,10,20,0.88), rgba(3,8,18,0.98))",
|
|
2104
|
+
"flex:1 1 auto",
|
|
2105
|
+
"min-height:0"
|
|
2106
|
+
].join(";");
|
|
2107
|
+
const entries = document.createElement("div");
|
|
2108
|
+
entries.style.cssText = [
|
|
2109
|
+
"display:flex",
|
|
2110
|
+
"flex-direction:column",
|
|
2111
|
+
"gap:0",
|
|
2112
|
+
"overflow:auto",
|
|
2113
|
+
"flex:1 1 auto",
|
|
2114
|
+
"min-height:96px",
|
|
2115
|
+
"max-height:min(36vh, 280px)",
|
|
2116
|
+
"padding:0",
|
|
2117
|
+
"border:1px solid rgba(115,153,212,0.14)",
|
|
2118
|
+
"border-radius:12px",
|
|
2119
|
+
"background:rgba(4,10,20,0.82)"
|
|
2120
|
+
].join(";");
|
|
2121
|
+
const emptyState = document.createElement("div");
|
|
2122
|
+
emptyState.style.cssText = [
|
|
2123
|
+
"display:flex",
|
|
2124
|
+
"align-items:center",
|
|
2125
|
+
"justify-content:center",
|
|
2126
|
+
"flex:1 1 auto",
|
|
2127
|
+
"min-height:96px",
|
|
2128
|
+
"color:rgba(204,222,250,0.6)",
|
|
2129
|
+
"font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
2130
|
+
"text-align:center",
|
|
2131
|
+
"padding:18px"
|
|
2132
|
+
].join(";");
|
|
2133
|
+
emptyState.textContent = "Console output will appear here.";
|
|
2134
|
+
collapseButton.addEventListener("click", (event) => {
|
|
2135
|
+
event.stopPropagation();
|
|
2136
|
+
state.expanded = false;
|
|
2137
|
+
renderOverlay(state);
|
|
2138
|
+
});
|
|
2139
|
+
clearButton.addEventListener("click", (event) => {
|
|
2140
|
+
event.stopPropagation();
|
|
2141
|
+
state.entries = [];
|
|
2142
|
+
state.unreadCount = 0;
|
|
2143
|
+
renderOverlay(state);
|
|
2144
|
+
});
|
|
2145
|
+
controls.appendChild(clearButton);
|
|
2146
|
+
controls.appendChild(collapseButton);
|
|
2147
|
+
entries.appendChild(emptyState);
|
|
2148
|
+
body.appendChild(entries);
|
|
2149
|
+
body.appendChild(controls);
|
|
2150
|
+
panel.appendChild(dragZone);
|
|
2151
|
+
panel.appendChild(body);
|
|
2152
|
+
attachDragStartListeners(dragZone, state);
|
|
2153
|
+
attachPanelResizeListeners(panel, state);
|
|
2154
|
+
attachCollapsedToggleListeners(toggleButton, state);
|
|
2155
|
+
root.appendChild(panel);
|
|
2156
|
+
root.appendChild(toggleButton);
|
|
2157
|
+
return {
|
|
2158
|
+
body,
|
|
2159
|
+
clearButton,
|
|
2160
|
+
collapseButton,
|
|
2161
|
+
controls,
|
|
2162
|
+
dragZone,
|
|
2163
|
+
emptyState,
|
|
2164
|
+
entries,
|
|
2165
|
+
panel,
|
|
2166
|
+
root,
|
|
2167
|
+
toggleButton
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
function renderOverlay(state) {
|
|
2171
|
+
if (!state.ui) {
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
state.ui.panel.style.display = state.expanded ? "flex" : "none";
|
|
2175
|
+
state.ui.toggleButton.style.display = state.expanded ? "none" : "inline-flex";
|
|
2176
|
+
state.ui.toggleButton.textContent = "Logs";
|
|
2177
|
+
if (state.entries.length === 0) {
|
|
2178
|
+
state.ui.entries.style.display = "flex";
|
|
2179
|
+
state.ui.emptyState.style.display = "flex";
|
|
2180
|
+
state.ui.entries.replaceChildren(state.ui.emptyState);
|
|
2181
|
+
applyPanelSize(state);
|
|
2182
|
+
applyOverlayPosition(state);
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
state.ui.entries.style.display = "flex";
|
|
2186
|
+
state.ui.emptyState.style.display = "none";
|
|
2187
|
+
const nextChildren = state.entries.map((entry) => {
|
|
2188
|
+
const accent = getLevelAccent(entry.level);
|
|
2189
|
+
const row = document.createElement("div");
|
|
2190
|
+
row.style.cssText = [
|
|
2191
|
+
"display:flex",
|
|
2192
|
+
"align-items:flex-start",
|
|
2193
|
+
"gap:0",
|
|
2194
|
+
"padding:4px 12px",
|
|
2195
|
+
"background:" + accent.lineBackground
|
|
2196
|
+
].join(";");
|
|
2197
|
+
const line = document.createElement("div");
|
|
2198
|
+
line.textContent = formatTimestamp(entry.timestamp) + " " + entry.level.toUpperCase() + " " + entry.message;
|
|
2199
|
+
line.style.cssText = [
|
|
2200
|
+
"color:#ecf4ff",
|
|
2201
|
+
"font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
2202
|
+
"white-space:pre-wrap",
|
|
2203
|
+
"word-break:break-word",
|
|
2204
|
+
"flex:1 1 auto"
|
|
2205
|
+
].join(";");
|
|
2206
|
+
row.appendChild(line);
|
|
2207
|
+
return row;
|
|
2208
|
+
});
|
|
2209
|
+
state.ui.entries.replaceChildren(...nextChildren);
|
|
2210
|
+
state.ui.entries.scrollTop = state.ui.entries.scrollHeight;
|
|
2211
|
+
applyPanelSize(state);
|
|
2212
|
+
applyOverlayPosition(state);
|
|
2213
|
+
}
|
|
2214
|
+
function mountOverlay(state) {
|
|
2215
|
+
const doc = getDocument2();
|
|
2216
|
+
if (!doc?.body || state.ui) {
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
state.ui = createOverlayUi(state);
|
|
2220
|
+
doc.body.appendChild(state.ui.root);
|
|
2221
|
+
applyPanelSize(state);
|
|
2222
|
+
applyOverlayPosition(state);
|
|
2223
|
+
renderOverlay(state);
|
|
2224
|
+
}
|
|
2225
|
+
function enqueueEntry(state, level, args) {
|
|
2226
|
+
state.entries.push(createEntry(level, args, state.nextEntryId));
|
|
2227
|
+
state.nextEntryId += 1;
|
|
2228
|
+
if (state.entries.length > state.maxEntries) {
|
|
2229
|
+
state.entries.splice(0, state.entries.length - state.maxEntries);
|
|
2230
|
+
}
|
|
2231
|
+
if (!state.expanded) {
|
|
2232
|
+
state.unreadCount += 1;
|
|
2233
|
+
}
|
|
2234
|
+
renderOverlay(state);
|
|
2235
|
+
}
|
|
2236
|
+
function restoreConsole(snapshot) {
|
|
2237
|
+
for (const method of CONSOLE_METHODS) {
|
|
2238
|
+
console[method] = snapshot[method];
|
|
75
2239
|
}
|
|
76
2240
|
}
|
|
2241
|
+
function patchConsole(state) {
|
|
2242
|
+
for (const method of CONSOLE_METHODS) {
|
|
2243
|
+
const original = state.originalConsole[method];
|
|
2244
|
+
console[method] = (...args) => {
|
|
2245
|
+
enqueueEntry(state, method, args);
|
|
2246
|
+
original(...args);
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
function cleanupOverlay(browserWindow, state) {
|
|
2251
|
+
restoreConsole(state.originalConsole);
|
|
2252
|
+
stopResizing(state);
|
|
2253
|
+
stopDragging(state);
|
|
2254
|
+
if (state.domReadyHandler) {
|
|
2255
|
+
getDocument2()?.removeEventListener("DOMContentLoaded", state.domReadyHandler);
|
|
2256
|
+
state.domReadyHandler = void 0;
|
|
2257
|
+
}
|
|
2258
|
+
if (state.resizeHandler && typeof browserWindow.removeEventListener === "function") {
|
|
2259
|
+
browserWindow.removeEventListener("resize", state.resizeHandler);
|
|
2260
|
+
state.resizeHandler = void 0;
|
|
2261
|
+
}
|
|
2262
|
+
state.ui?.root.remove();
|
|
2263
|
+
state.ui = null;
|
|
2264
|
+
delete browserWindow.__oasizLogOverlayController__;
|
|
2265
|
+
delete browserWindow.__oasizLogOverlayState__;
|
|
2266
|
+
}
|
|
2267
|
+
function createController(browserWindow, state) {
|
|
2268
|
+
return {
|
|
2269
|
+
retain() {
|
|
2270
|
+
state.refCount += 1;
|
|
2271
|
+
this.ensureMounted();
|
|
2272
|
+
},
|
|
2273
|
+
ensureMounted() {
|
|
2274
|
+
const doc = getDocument2();
|
|
2275
|
+
if (!doc) {
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
if (doc.body) {
|
|
2279
|
+
mountOverlay(state);
|
|
2280
|
+
applyOverlayPosition(state);
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
if (!state.domReadyHandler) {
|
|
2284
|
+
state.domReadyHandler = () => {
|
|
2285
|
+
mountOverlay(state);
|
|
2286
|
+
state.domReadyHandler = void 0;
|
|
2287
|
+
};
|
|
2288
|
+
doc.addEventListener("DOMContentLoaded", state.domReadyHandler, {
|
|
2289
|
+
once: true
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
},
|
|
2293
|
+
clear() {
|
|
2294
|
+
state.entries = [];
|
|
2295
|
+
state.unreadCount = 0;
|
|
2296
|
+
renderOverlay(state);
|
|
2297
|
+
},
|
|
2298
|
+
show() {
|
|
2299
|
+
state.expanded = true;
|
|
2300
|
+
state.unreadCount = 0;
|
|
2301
|
+
renderOverlay(state);
|
|
2302
|
+
},
|
|
2303
|
+
hide() {
|
|
2304
|
+
state.expanded = false;
|
|
2305
|
+
renderOverlay(state);
|
|
2306
|
+
},
|
|
2307
|
+
isVisible() {
|
|
2308
|
+
return state.expanded;
|
|
2309
|
+
},
|
|
2310
|
+
destroy() {
|
|
2311
|
+
state.refCount = Math.max(0, state.refCount - 1);
|
|
2312
|
+
if (state.refCount === 0) {
|
|
2313
|
+
cleanupOverlay(browserWindow, state);
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
function enableLogOverlay(options = {}) {
|
|
2319
|
+
if (options.enabled === false) {
|
|
2320
|
+
return NOOP_HANDLE2;
|
|
2321
|
+
}
|
|
2322
|
+
const browserWindow = getBrowserWindow2();
|
|
2323
|
+
const doc = getDocument2();
|
|
2324
|
+
if (!browserWindow || !doc) {
|
|
2325
|
+
return NOOP_HANDLE2;
|
|
2326
|
+
}
|
|
2327
|
+
const existingController = browserWindow.__oasizLogOverlayController__;
|
|
2328
|
+
const existingState = browserWindow.__oasizLogOverlayState__;
|
|
2329
|
+
if (existingController && existingState) {
|
|
2330
|
+
existingController.retain();
|
|
2331
|
+
if (typeof options.maxEntries === "number") {
|
|
2332
|
+
existingState.maxEntries = clampMaxEntries(options.maxEntries);
|
|
2333
|
+
if (existingState.entries.length > existingState.maxEntries) {
|
|
2334
|
+
existingState.entries.splice(
|
|
2335
|
+
0,
|
|
2336
|
+
existingState.entries.length - existingState.maxEntries
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
if (typeof options.collapsed === "boolean") {
|
|
2341
|
+
existingState.expanded = !options.collapsed;
|
|
2342
|
+
applyPanelSize(existingState);
|
|
2343
|
+
if (existingState.expanded) {
|
|
2344
|
+
existingState.unreadCount = 0;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
if (typeof options.title === "string" && options.title.trim().length > 0) {
|
|
2348
|
+
existingState.title = options.title.trim();
|
|
2349
|
+
}
|
|
2350
|
+
existingController.ensureMounted();
|
|
2351
|
+
renderOverlay(existingState);
|
|
2352
|
+
return existingController;
|
|
2353
|
+
}
|
|
2354
|
+
const state = {
|
|
2355
|
+
dragMoved: false,
|
|
2356
|
+
dragStartPoint: null,
|
|
2357
|
+
entries: [],
|
|
2358
|
+
expanded: options.collapsed !== true,
|
|
2359
|
+
isDragging: false,
|
|
2360
|
+
isResizing: false,
|
|
2361
|
+
lastDragPoint: null,
|
|
2362
|
+
maxEntries: clampMaxEntries(options.maxEntries),
|
|
2363
|
+
nextEntryId: 1,
|
|
2364
|
+
originalConsole: createConsoleSnapshot(),
|
|
2365
|
+
panelSize: null,
|
|
2366
|
+
position: null,
|
|
2367
|
+
refCount: 1,
|
|
2368
|
+
removeDragListeners: null,
|
|
2369
|
+
removeResizeListeners: null,
|
|
2370
|
+
resizeStartPoint: null,
|
|
2371
|
+
resizeStartSize: null,
|
|
2372
|
+
suppressToggleClickUntil: 0,
|
|
2373
|
+
title: typeof options.title === "string" && options.title.trim().length > 0 ? options.title.trim() : DEFAULT_TITLE,
|
|
2374
|
+
resizeHandler: void 0,
|
|
2375
|
+
ui: null,
|
|
2376
|
+
unreadCount: 0
|
|
2377
|
+
};
|
|
2378
|
+
const controller = createController(browserWindow, state);
|
|
2379
|
+
browserWindow.__oasizLogOverlayState__ = state;
|
|
2380
|
+
browserWindow.__oasizLogOverlayController__ = controller;
|
|
2381
|
+
patchConsole(state);
|
|
2382
|
+
if (typeof browserWindow.addEventListener === "function") {
|
|
2383
|
+
state.resizeHandler = () => {
|
|
2384
|
+
applyOverlayPosition(state);
|
|
2385
|
+
};
|
|
2386
|
+
browserWindow.addEventListener("resize", state.resizeHandler);
|
|
2387
|
+
}
|
|
2388
|
+
controller.ensureMounted();
|
|
2389
|
+
return controller;
|
|
2390
|
+
}
|
|
77
2391
|
|
|
78
2392
|
// src/multiplayer.ts
|
|
79
|
-
function
|
|
2393
|
+
function isDevelopment4() {
|
|
80
2394
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
81
2395
|
return nodeEnv !== "production";
|
|
82
2396
|
}
|
|
83
|
-
function
|
|
2397
|
+
function getBridgeWindow4() {
|
|
84
2398
|
if (typeof window === "undefined") {
|
|
85
2399
|
return void 0;
|
|
86
2400
|
}
|
|
87
2401
|
return window;
|
|
88
2402
|
}
|
|
89
|
-
function shareRoomCode(roomCode) {
|
|
90
|
-
const bridge =
|
|
2403
|
+
function shareRoomCode(roomCode, options) {
|
|
2404
|
+
const bridge = getBridgeWindow4();
|
|
91
2405
|
if (typeof bridge?.shareRoomCode === "function") {
|
|
92
|
-
bridge.shareRoomCode(roomCode);
|
|
2406
|
+
bridge.shareRoomCode(roomCode, options);
|
|
93
2407
|
return;
|
|
94
2408
|
}
|
|
95
|
-
if (
|
|
2409
|
+
if (isDevelopment4()) {
|
|
96
2410
|
console.warn(
|
|
97
2411
|
"[oasiz/sdk] shareRoomCode bridge is unavailable. This is expected in local development."
|
|
98
2412
|
);
|
|
99
2413
|
}
|
|
100
2414
|
}
|
|
2415
|
+
function openInviteModal() {
|
|
2416
|
+
const bridge = getBridgeWindow4();
|
|
2417
|
+
if (typeof bridge?.openInviteModal === "function") {
|
|
2418
|
+
bridge.openInviteModal();
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
if (isDevelopment4()) {
|
|
2422
|
+
console.warn(
|
|
2423
|
+
"[oasiz/sdk] openInviteModal bridge is unavailable. This is expected in local development."
|
|
2424
|
+
);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
101
2427
|
function getGameId() {
|
|
102
|
-
const bridge =
|
|
2428
|
+
const bridge = getBridgeWindow4();
|
|
103
2429
|
return bridge?.__GAME_ID__;
|
|
104
2430
|
}
|
|
105
2431
|
function getRoomCode() {
|
|
106
|
-
const bridge =
|
|
2432
|
+
const bridge = getBridgeWindow4();
|
|
107
2433
|
return bridge?.__ROOM_CODE__;
|
|
108
2434
|
}
|
|
2435
|
+
function getPlayerId() {
|
|
2436
|
+
const bridge = getBridgeWindow4();
|
|
2437
|
+
return bridge?.__PLAYER_ID__;
|
|
2438
|
+
}
|
|
109
2439
|
function getPlayerName() {
|
|
110
|
-
const bridge =
|
|
2440
|
+
const bridge = getBridgeWindow4();
|
|
111
2441
|
return bridge?.__PLAYER_NAME__;
|
|
112
2442
|
}
|
|
113
2443
|
function getPlayerAvatar() {
|
|
114
|
-
const bridge =
|
|
2444
|
+
const bridge = getBridgeWindow4();
|
|
115
2445
|
return bridge?.__PLAYER_AVATAR__;
|
|
116
2446
|
}
|
|
117
2447
|
|
|
118
2448
|
// src/score.ts
|
|
119
|
-
function
|
|
2449
|
+
function isDevelopment5() {
|
|
120
2450
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
121
2451
|
return nodeEnv !== "production";
|
|
122
2452
|
}
|
|
123
|
-
function
|
|
124
|
-
if (
|
|
2453
|
+
function warnMissingBridge3(methodName) {
|
|
2454
|
+
if (isDevelopment5()) {
|
|
125
2455
|
console.warn(
|
|
126
2456
|
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
127
2457
|
);
|
|
128
2458
|
}
|
|
129
2459
|
}
|
|
130
|
-
function
|
|
2460
|
+
function getBridgeWindow5() {
|
|
131
2461
|
if (typeof window === "undefined") {
|
|
132
2462
|
return void 0;
|
|
133
2463
|
}
|
|
@@ -135,41 +2465,92 @@ function getBridgeWindow3() {
|
|
|
135
2465
|
}
|
|
136
2466
|
function submitScore(score) {
|
|
137
2467
|
if (!Number.isFinite(score)) {
|
|
138
|
-
if (
|
|
2468
|
+
if (isDevelopment5()) {
|
|
139
2469
|
console.warn("[oasiz/sdk] submitScore expected a finite number:", score);
|
|
140
2470
|
}
|
|
141
2471
|
return;
|
|
142
2472
|
}
|
|
143
|
-
const bridge =
|
|
2473
|
+
const bridge = getBridgeWindow5();
|
|
144
2474
|
const normalizedScore = Math.max(0, Math.floor(score));
|
|
145
2475
|
if (typeof bridge?.submitScore === "function") {
|
|
146
2476
|
bridge.submitScore(normalizedScore);
|
|
147
2477
|
return;
|
|
148
2478
|
}
|
|
149
|
-
|
|
2479
|
+
warnMissingBridge3("submitScore");
|
|
150
2480
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
2481
|
+
|
|
2482
|
+
// src/score-edit.ts
|
|
2483
|
+
function isDevelopment6() {
|
|
2484
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
2485
|
+
return nodeEnv !== "production";
|
|
2486
|
+
}
|
|
2487
|
+
function getBridgeWindow6() {
|
|
2488
|
+
if (typeof window === "undefined") {
|
|
2489
|
+
return void 0;
|
|
2490
|
+
}
|
|
2491
|
+
return window;
|
|
2492
|
+
}
|
|
2493
|
+
function warnMissingBridge4(methodName) {
|
|
2494
|
+
if (isDevelopment6()) {
|
|
2495
|
+
console.warn(
|
|
2496
|
+
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
async function editScore(payload, methodName) {
|
|
2501
|
+
const bridge = getBridgeWindow6();
|
|
2502
|
+
if (typeof bridge?.__oasizEditScore !== "function") {
|
|
2503
|
+
warnMissingBridge4(methodName);
|
|
2504
|
+
return null;
|
|
2505
|
+
}
|
|
2506
|
+
try {
|
|
2507
|
+
const result = await bridge.__oasizEditScore(payload);
|
|
2508
|
+
return result ?? null;
|
|
2509
|
+
} catch (error) {
|
|
2510
|
+
if (isDevelopment6()) {
|
|
2511
|
+
console.error("[oasiz/sdk] " + methodName + " failed:", error);
|
|
2512
|
+
}
|
|
2513
|
+
return null;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
async function addScore(delta) {
|
|
2517
|
+
if (!Number.isInteger(delta)) {
|
|
2518
|
+
if (isDevelopment6()) {
|
|
2519
|
+
console.warn("[oasiz/sdk] addScore expected an integer:", delta);
|
|
2520
|
+
}
|
|
2521
|
+
return null;
|
|
2522
|
+
}
|
|
2523
|
+
if (delta === 0) {
|
|
2524
|
+
return null;
|
|
2525
|
+
}
|
|
2526
|
+
return editScore({ delta }, "addScore");
|
|
2527
|
+
}
|
|
2528
|
+
async function setScore(score) {
|
|
2529
|
+
if (!Number.isInteger(score) || score < 0) {
|
|
2530
|
+
if (isDevelopment6()) {
|
|
2531
|
+
console.warn(
|
|
2532
|
+
"[oasiz/sdk] setScore expected a non-negative integer:",
|
|
2533
|
+
score
|
|
2534
|
+
);
|
|
2535
|
+
}
|
|
2536
|
+
return null;
|
|
156
2537
|
}
|
|
157
|
-
|
|
2538
|
+
return editScore({ score }, "setScore");
|
|
158
2539
|
}
|
|
159
2540
|
|
|
160
2541
|
// src/share.ts
|
|
161
|
-
function
|
|
2542
|
+
function isDevelopment7() {
|
|
162
2543
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
163
2544
|
return nodeEnv !== "production";
|
|
164
2545
|
}
|
|
165
|
-
function
|
|
2546
|
+
function getBridgeWindow7() {
|
|
166
2547
|
if (typeof window === "undefined") {
|
|
167
2548
|
return void 0;
|
|
168
2549
|
}
|
|
169
2550
|
return window;
|
|
170
2551
|
}
|
|
171
|
-
function
|
|
172
|
-
if (
|
|
2552
|
+
function warnMissingBridge5(methodName) {
|
|
2553
|
+
if (isDevelopment7()) {
|
|
173
2554
|
console.warn(
|
|
174
2555
|
"[oasiz/sdk] " + methodName + " share bridge is unavailable. This is expected in local development."
|
|
175
2556
|
);
|
|
@@ -212,20 +2593,20 @@ function validateRequest(options) {
|
|
|
212
2593
|
}
|
|
213
2594
|
async function share(options) {
|
|
214
2595
|
const request = validateRequest(options);
|
|
215
|
-
const bridge =
|
|
2596
|
+
const bridge = getBridgeWindow7();
|
|
216
2597
|
if (typeof bridge?.__oasizShareRequest !== "function") {
|
|
217
|
-
|
|
2598
|
+
warnMissingBridge5("__oasizShareRequest");
|
|
218
2599
|
throw new Error("Share bridge unavailable");
|
|
219
2600
|
}
|
|
220
2601
|
await bridge.__oasizShareRequest(request);
|
|
221
2602
|
}
|
|
222
2603
|
|
|
223
2604
|
// src/state.ts
|
|
224
|
-
function
|
|
2605
|
+
function isDevelopment8() {
|
|
225
2606
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
226
2607
|
return nodeEnv !== "production";
|
|
227
2608
|
}
|
|
228
|
-
function
|
|
2609
|
+
function getBridgeWindow8() {
|
|
229
2610
|
if (typeof window === "undefined") {
|
|
230
2611
|
return void 0;
|
|
231
2612
|
}
|
|
@@ -238,22 +2619,22 @@ function isPlainObject(value) {
|
|
|
238
2619
|
const proto = Object.getPrototypeOf(value);
|
|
239
2620
|
return proto === Object.prototype || proto === null;
|
|
240
2621
|
}
|
|
241
|
-
function
|
|
242
|
-
if (
|
|
2622
|
+
function warnMissingBridge6(methodName) {
|
|
2623
|
+
if (isDevelopment8()) {
|
|
243
2624
|
console.warn(
|
|
244
2625
|
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
245
2626
|
);
|
|
246
2627
|
}
|
|
247
2628
|
}
|
|
248
2629
|
function loadGameState() {
|
|
249
|
-
const bridge =
|
|
2630
|
+
const bridge = getBridgeWindow8();
|
|
250
2631
|
if (typeof bridge?.loadGameState !== "function") {
|
|
251
|
-
|
|
2632
|
+
warnMissingBridge6("loadGameState");
|
|
252
2633
|
return {};
|
|
253
2634
|
}
|
|
254
2635
|
const state = bridge.loadGameState();
|
|
255
2636
|
if (!isPlainObject(state)) {
|
|
256
|
-
if (
|
|
2637
|
+
if (isDevelopment8()) {
|
|
257
2638
|
console.warn(
|
|
258
2639
|
"[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
|
|
259
2640
|
);
|
|
@@ -264,35 +2645,35 @@ function loadGameState() {
|
|
|
264
2645
|
}
|
|
265
2646
|
function saveGameState(state) {
|
|
266
2647
|
if (!isPlainObject(state)) {
|
|
267
|
-
if (
|
|
2648
|
+
if (isDevelopment8()) {
|
|
268
2649
|
console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
|
|
269
2650
|
}
|
|
270
2651
|
return;
|
|
271
2652
|
}
|
|
272
|
-
const bridge =
|
|
2653
|
+
const bridge = getBridgeWindow8();
|
|
273
2654
|
if (typeof bridge?.saveGameState === "function") {
|
|
274
2655
|
bridge.saveGameState(state);
|
|
275
2656
|
return;
|
|
276
2657
|
}
|
|
277
|
-
|
|
2658
|
+
warnMissingBridge6("saveGameState");
|
|
278
2659
|
}
|
|
279
2660
|
function flushGameState() {
|
|
280
|
-
const bridge =
|
|
2661
|
+
const bridge = getBridgeWindow8();
|
|
281
2662
|
if (typeof bridge?.flushGameState === "function") {
|
|
282
2663
|
bridge.flushGameState();
|
|
283
2664
|
return;
|
|
284
2665
|
}
|
|
285
|
-
|
|
2666
|
+
warnMissingBridge6("flushGameState");
|
|
286
2667
|
}
|
|
287
2668
|
|
|
288
2669
|
// src/lifecycle.ts
|
|
289
|
-
function
|
|
2670
|
+
function isDevelopment9() {
|
|
290
2671
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
291
2672
|
return nodeEnv !== "production";
|
|
292
2673
|
}
|
|
293
2674
|
function addLifecycleListener(eventName, callback) {
|
|
294
2675
|
if (typeof window === "undefined") {
|
|
295
|
-
if (
|
|
2676
|
+
if (isDevelopment9()) {
|
|
296
2677
|
console.warn(
|
|
297
2678
|
"[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
|
|
298
2679
|
);
|
|
@@ -311,253 +2692,733 @@ function onResume(callback) {
|
|
|
311
2692
|
return addLifecycleListener("oasiz:resume", callback);
|
|
312
2693
|
}
|
|
313
2694
|
|
|
314
|
-
// src/
|
|
315
|
-
var
|
|
316
|
-
|
|
2695
|
+
// src/layout.ts
|
|
2696
|
+
var INSET_SIDES = ["top", "right", "bottom", "left"];
|
|
2697
|
+
var SIDE_TO_AXIS = {
|
|
2698
|
+
top: "vertical",
|
|
2699
|
+
right: "horizontal",
|
|
2700
|
+
bottom: "vertical",
|
|
2701
|
+
left: "horizontal"
|
|
2702
|
+
};
|
|
2703
|
+
function createInsetEdges(value) {
|
|
2704
|
+
return {
|
|
2705
|
+
top: value,
|
|
2706
|
+
right: value,
|
|
2707
|
+
bottom: value,
|
|
2708
|
+
left: value
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
function isDevelopment10() {
|
|
317
2712
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
318
2713
|
return nodeEnv !== "production";
|
|
319
2714
|
}
|
|
320
|
-
function
|
|
2715
|
+
function getBridgeWindow9() {
|
|
321
2716
|
if (typeof window === "undefined") {
|
|
322
2717
|
return void 0;
|
|
323
2718
|
}
|
|
324
2719
|
return window;
|
|
325
2720
|
}
|
|
326
|
-
function
|
|
327
|
-
if (
|
|
2721
|
+
function warnMissingBridge7(methodName) {
|
|
2722
|
+
if (isDevelopment10()) {
|
|
328
2723
|
console.warn(
|
|
329
2724
|
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
330
2725
|
);
|
|
331
2726
|
}
|
|
332
2727
|
}
|
|
333
|
-
function
|
|
334
|
-
|
|
335
|
-
return error;
|
|
336
|
-
}
|
|
337
|
-
return new Error(
|
|
338
|
-
typeof error === "string" ? error : "Back button callback failed."
|
|
339
|
-
);
|
|
2728
|
+
function isRecord2(value) {
|
|
2729
|
+
return typeof value === "object" && value !== null;
|
|
340
2730
|
}
|
|
341
|
-
function
|
|
342
|
-
if (typeof
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
"[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
|
|
346
|
-
);
|
|
2731
|
+
function toFiniteNumber(value) {
|
|
2732
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2733
|
+
if (typeof value !== "string") {
|
|
2734
|
+
return void 0;
|
|
347
2735
|
}
|
|
348
|
-
|
|
349
|
-
|
|
2736
|
+
const parsed = Number.parseFloat(value.trim());
|
|
2737
|
+
if (!Number.isFinite(parsed)) {
|
|
2738
|
+
return void 0;
|
|
2739
|
+
}
|
|
2740
|
+
return parsed;
|
|
350
2741
|
}
|
|
351
|
-
|
|
352
|
-
window.addEventListener(eventName, handler);
|
|
353
|
-
return () => window.removeEventListener(eventName, handler);
|
|
2742
|
+
return value;
|
|
354
2743
|
}
|
|
355
|
-
function
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
2744
|
+
function clampInsetPixels(value) {
|
|
2745
|
+
const numeric = toFiniteNumber(value);
|
|
2746
|
+
if (typeof numeric === "undefined") {
|
|
2747
|
+
return void 0;
|
|
2748
|
+
}
|
|
2749
|
+
return Math.max(0, numeric);
|
|
2750
|
+
}
|
|
2751
|
+
function normalizeInsetPercent(value) {
|
|
2752
|
+
const numeric = toFiniteNumber(value);
|
|
2753
|
+
if (typeof numeric === "undefined") {
|
|
2754
|
+
return void 0;
|
|
2755
|
+
}
|
|
2756
|
+
return Math.min(100, Math.max(0, numeric));
|
|
2757
|
+
}
|
|
2758
|
+
function getViewportSize2(bridge, axis) {
|
|
2759
|
+
const visualViewportSize = axis === "vertical" ? bridge.visualViewport?.height : bridge.visualViewport?.width;
|
|
2760
|
+
if (typeof visualViewportSize === "number" && Number.isFinite(visualViewportSize) && visualViewportSize > 0) {
|
|
2761
|
+
return visualViewportSize;
|
|
2762
|
+
}
|
|
2763
|
+
const innerSize = axis === "vertical" ? bridge.innerHeight : bridge.innerWidth;
|
|
2764
|
+
if (typeof innerSize === "number" && Number.isFinite(innerSize) && innerSize > 0) {
|
|
2765
|
+
return innerSize;
|
|
2766
|
+
}
|
|
2767
|
+
const documentSize = axis === "vertical" ? bridge.document?.documentElement?.clientHeight : bridge.document?.documentElement?.clientWidth;
|
|
2768
|
+
if (typeof documentSize === "number" && Number.isFinite(documentSize) && documentSize > 0) {
|
|
2769
|
+
return documentSize;
|
|
2770
|
+
}
|
|
2771
|
+
const bodySize = axis === "vertical" ? bridge.document?.body?.clientHeight : bridge.document?.body?.clientWidth;
|
|
2772
|
+
if (typeof bodySize === "number" && Number.isFinite(bodySize) && bodySize > 0) {
|
|
2773
|
+
return bodySize;
|
|
2774
|
+
}
|
|
2775
|
+
return 0;
|
|
2776
|
+
}
|
|
2777
|
+
function readCssSafeAreaValue(bridge, cssValue) {
|
|
2778
|
+
const doc = bridge.document;
|
|
2779
|
+
const root = doc?.body ?? doc?.documentElement;
|
|
2780
|
+
if (!doc || !root || typeof doc.createElement !== "function" || typeof root.appendChild !== "function" || typeof bridge.getComputedStyle !== "function") {
|
|
2781
|
+
return 0;
|
|
2782
|
+
}
|
|
2783
|
+
const probe = doc.createElement("div");
|
|
2784
|
+
probe.style.position = "fixed";
|
|
2785
|
+
probe.style.top = "0";
|
|
2786
|
+
probe.style.left = "0";
|
|
2787
|
+
probe.style.width = "0";
|
|
2788
|
+
probe.style.height = "0";
|
|
2789
|
+
probe.style.visibility = "hidden";
|
|
2790
|
+
probe.style.pointerEvents = "none";
|
|
2791
|
+
probe.style.paddingTop = cssValue;
|
|
2792
|
+
root.appendChild(probe);
|
|
2793
|
+
try {
|
|
2794
|
+
return clampInsetPixels(bridge.getComputedStyle(probe).paddingTop) ?? 0;
|
|
2795
|
+
} finally {
|
|
2796
|
+
if (typeof probe.remove === "function") {
|
|
2797
|
+
probe.remove();
|
|
369
2798
|
} else {
|
|
370
|
-
|
|
2799
|
+
probe.parentNode?.removeChild(probe);
|
|
371
2800
|
}
|
|
372
2801
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
2802
|
+
}
|
|
2803
|
+
function readCssSafeAreaPixels(bridge, side) {
|
|
2804
|
+
const envPixels = readCssSafeAreaValue(
|
|
2805
|
+
bridge,
|
|
2806
|
+
"env(safe-area-inset-" + side + ")"
|
|
2807
|
+
);
|
|
2808
|
+
if (envPixels > 0) {
|
|
2809
|
+
return envPixels;
|
|
2810
|
+
}
|
|
2811
|
+
return readCssSafeAreaValue(
|
|
2812
|
+
bridge,
|
|
2813
|
+
"constant(safe-area-inset-" + side + ")"
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
function getDevicePixelRatio(bridge) {
|
|
2817
|
+
const dpr = bridge.devicePixelRatio;
|
|
2818
|
+
if (typeof dpr !== "number" || !Number.isFinite(dpr) || dpr <= 0) {
|
|
2819
|
+
return 1;
|
|
2820
|
+
}
|
|
2821
|
+
return dpr;
|
|
2822
|
+
}
|
|
2823
|
+
function roughlyEqualPixels(a, b) {
|
|
2824
|
+
return Math.abs(a - b) <= 2;
|
|
2825
|
+
}
|
|
2826
|
+
function normalizeInsetPixels(value, bridge, side) {
|
|
2827
|
+
const pixels = clampInsetPixels(value);
|
|
2828
|
+
if (typeof pixels === "undefined") {
|
|
2829
|
+
return void 0;
|
|
2830
|
+
}
|
|
2831
|
+
const cssEnvPixels = readCssSafeAreaPixels(bridge, side);
|
|
2832
|
+
if (pixels <= 0) {
|
|
2833
|
+
return cssEnvPixels;
|
|
2834
|
+
}
|
|
2835
|
+
const dpr = getDevicePixelRatio(bridge);
|
|
2836
|
+
if (cssEnvPixels > 0 && dpr > 1 && roughlyEqualPixels(pixels / dpr, cssEnvPixels)) {
|
|
2837
|
+
return cssEnvPixels;
|
|
2838
|
+
}
|
|
2839
|
+
return pixels;
|
|
2840
|
+
}
|
|
2841
|
+
function pixelsToPercentOfViewport(pixels, bridge, side) {
|
|
2842
|
+
const size = getViewportSize2(bridge, SIDE_TO_AXIS[side]);
|
|
2843
|
+
if (size <= 0) {
|
|
2844
|
+
return 0;
|
|
2845
|
+
}
|
|
2846
|
+
return normalizeInsetPercent(pixels / size * 100) ?? 0;
|
|
2847
|
+
}
|
|
2848
|
+
function percentToPixelsOfViewport(percent, bridge, side) {
|
|
2849
|
+
const size = getViewportSize2(bridge, SIDE_TO_AXIS[side]);
|
|
2850
|
+
if (size <= 0) {
|
|
2851
|
+
return 0;
|
|
2852
|
+
}
|
|
2853
|
+
return percent / 100 * size;
|
|
2854
|
+
}
|
|
2855
|
+
function cssSafeAreaPercent(bridge, side) {
|
|
2856
|
+
return pixelsToPercentOfViewport(readCssSafeAreaPixels(bridge, side), bridge, side);
|
|
2857
|
+
}
|
|
2858
|
+
function resolvePercentValue(value, bridge, side) {
|
|
2859
|
+
const percent = normalizeInsetPercent(value);
|
|
2860
|
+
if (typeof percent === "undefined") {
|
|
2861
|
+
return void 0;
|
|
2862
|
+
}
|
|
2863
|
+
return percent > 0 ? percent : cssSafeAreaPercent(bridge, side);
|
|
2864
|
+
}
|
|
2865
|
+
function resolvePixelValue(value, bridge, side) {
|
|
2866
|
+
const numeric = toFiniteNumber(value);
|
|
2867
|
+
if (typeof numeric === "undefined") {
|
|
2868
|
+
return void 0;
|
|
2869
|
+
}
|
|
2870
|
+
return normalizeInsetPixels(numeric, bridge, side);
|
|
2871
|
+
}
|
|
2872
|
+
function sideSuffix(side) {
|
|
2873
|
+
switch (side) {
|
|
2874
|
+
case "top":
|
|
2875
|
+
return "Top";
|
|
2876
|
+
case "right":
|
|
2877
|
+
return "Right";
|
|
2878
|
+
case "bottom":
|
|
2879
|
+
return "Bottom";
|
|
2880
|
+
case "left":
|
|
2881
|
+
return "Left";
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
function callBridgeFunction(bridge, name) {
|
|
2885
|
+
const fn = bridge[name];
|
|
2886
|
+
if (typeof fn !== "function") {
|
|
2887
|
+
return void 0;
|
|
2888
|
+
}
|
|
2889
|
+
try {
|
|
2890
|
+
return fn.call(bridge);
|
|
2891
|
+
} catch (error) {
|
|
2892
|
+
console.error("[oasiz/sdk] " + String(name) + " failed:", error);
|
|
2893
|
+
return void 0;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
function readInsetObjectValue(value, side, group) {
|
|
2897
|
+
if (!isRecord2(value)) {
|
|
2898
|
+
return void 0;
|
|
2899
|
+
}
|
|
2900
|
+
if (group) {
|
|
2901
|
+
return isRecord2(value[group]) ? value[group][side] : void 0;
|
|
2902
|
+
}
|
|
2903
|
+
return value[side];
|
|
2904
|
+
}
|
|
2905
|
+
function firstDefined(...values) {
|
|
2906
|
+
return values.find((value) => typeof value !== "undefined");
|
|
2907
|
+
}
|
|
2908
|
+
function readHostInsetSources(bridge) {
|
|
2909
|
+
return {
|
|
2910
|
+
globalPixels: bridge.__OASIZ_VIEWPORT_INSETS__,
|
|
2911
|
+
globalPercent: bridge.__OASIZ_VIEWPORT_INSETS_PERCENT__,
|
|
2912
|
+
methodPixels: callBridgeFunction(bridge, "getViewportInsets"),
|
|
2913
|
+
methodPercent: callBridgeFunction(bridge, "getViewportInsetsPercent")
|
|
384
2914
|
};
|
|
385
2915
|
}
|
|
386
|
-
function
|
|
387
|
-
|
|
2916
|
+
function readIndividualPercentValue(bridge, side) {
|
|
2917
|
+
const suffix = sideSuffix(side);
|
|
2918
|
+
return firstDefined(
|
|
2919
|
+
callBridgeFunction(
|
|
2920
|
+
bridge,
|
|
2921
|
+
"getSafeArea" + suffix + "Percent"
|
|
2922
|
+
),
|
|
2923
|
+
bridge["__OASIZ_SAFE_AREA_" + side.toUpperCase() + "_PERCENT__"]
|
|
2924
|
+
);
|
|
388
2925
|
}
|
|
389
|
-
function
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
2926
|
+
function readIndividualPixelValue(bridge, side) {
|
|
2927
|
+
const suffix = sideSuffix(side);
|
|
2928
|
+
return firstDefined(
|
|
2929
|
+
callBridgeFunction(
|
|
2930
|
+
bridge,
|
|
2931
|
+
"getSafeArea" + suffix
|
|
2932
|
+
),
|
|
2933
|
+
bridge["__OASIZ_SAFE_AREA_" + side.toUpperCase() + "__"]
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2936
|
+
function resolveInsetSide(bridge, sources, side) {
|
|
2937
|
+
const percentCandidate = firstDefined(
|
|
2938
|
+
readInsetObjectValue(sources.methodPercent, side, "percent"),
|
|
2939
|
+
readInsetObjectValue(sources.methodPercent, side),
|
|
2940
|
+
readInsetObjectValue(sources.globalPercent, side, "percent"),
|
|
2941
|
+
readInsetObjectValue(sources.globalPercent, side),
|
|
2942
|
+
readInsetObjectValue(sources.methodPixels, side, "percent"),
|
|
2943
|
+
readInsetObjectValue(sources.globalPixels, side, "percent"),
|
|
2944
|
+
readIndividualPercentValue(bridge, side)
|
|
2945
|
+
);
|
|
2946
|
+
const percent = resolvePercentValue(percentCandidate, bridge, side);
|
|
2947
|
+
if (typeof percent !== "undefined") {
|
|
2948
|
+
return {
|
|
2949
|
+
pixels: percentToPixelsOfViewport(percent, bridge, side),
|
|
2950
|
+
percent
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
const pixelCandidate = firstDefined(
|
|
2954
|
+
readInsetObjectValue(sources.methodPixels, side, "pixels"),
|
|
2955
|
+
readInsetObjectValue(sources.methodPixels, side),
|
|
2956
|
+
readInsetObjectValue(sources.globalPixels, side, "pixels"),
|
|
2957
|
+
readInsetObjectValue(sources.globalPixels, side),
|
|
2958
|
+
readIndividualPixelValue(bridge, side)
|
|
2959
|
+
);
|
|
2960
|
+
const pixels = resolvePixelValue(pixelCandidate, bridge, side);
|
|
2961
|
+
if (typeof pixels !== "undefined") {
|
|
2962
|
+
return {
|
|
2963
|
+
pixels,
|
|
2964
|
+
percent: pixelsToPercentOfViewport(pixels, bridge, side)
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
const cssPixels = readCssSafeAreaPixels(bridge, side);
|
|
2968
|
+
return {
|
|
2969
|
+
pixels: cssPixels,
|
|
2970
|
+
percent: pixelsToPercentOfViewport(cssPixels, bridge, side)
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2973
|
+
function getViewportInsets() {
|
|
2974
|
+
const bridge = getBridgeWindow9();
|
|
2975
|
+
if (!bridge) {
|
|
2976
|
+
return {
|
|
2977
|
+
pixels: createInsetEdges(0),
|
|
2978
|
+
percent: createInsetEdges(0)
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2981
|
+
const sources = readHostInsetSources(bridge);
|
|
2982
|
+
const pixels = createInsetEdges(0);
|
|
2983
|
+
const percent = createInsetEdges(0);
|
|
2984
|
+
for (const side of INSET_SIDES) {
|
|
2985
|
+
const resolved = resolveInsetSide(bridge, sources, side);
|
|
2986
|
+
pixels[side] = resolved.pixels;
|
|
2987
|
+
percent[side] = resolved.percent;
|
|
2988
|
+
}
|
|
2989
|
+
return { pixels, percent };
|
|
2990
|
+
}
|
|
2991
|
+
function getSafeAreaTop() {
|
|
2992
|
+
const bridge = getBridgeWindow9();
|
|
2993
|
+
if (!bridge) {
|
|
2994
|
+
return 0;
|
|
2995
|
+
}
|
|
2996
|
+
const top = getViewportInsets().percent.top;
|
|
2997
|
+
if (top <= 0) {
|
|
2998
|
+
warnMissingBridge7("getSafeAreaTop");
|
|
2999
|
+
}
|
|
3000
|
+
return top;
|
|
3001
|
+
}
|
|
3002
|
+
function setLeaderboardVisible(visible) {
|
|
3003
|
+
if (typeof visible !== "boolean") {
|
|
3004
|
+
if (isDevelopment10()) {
|
|
3005
|
+
console.warn(
|
|
3006
|
+
"[oasiz/sdk] setLeaderboardVisible expected a boolean:",
|
|
3007
|
+
visible
|
|
3008
|
+
);
|
|
3009
|
+
}
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
const bridge = getBridgeWindow9();
|
|
3013
|
+
if (typeof bridge?.__oasizSetLeaderboardVisible === "function") {
|
|
3014
|
+
bridge.__oasizSetLeaderboardVisible(visible);
|
|
393
3015
|
return;
|
|
394
3016
|
}
|
|
395
|
-
|
|
3017
|
+
warnMissingBridge7("__oasizSetLeaderboardVisible");
|
|
396
3018
|
}
|
|
397
3019
|
|
|
398
|
-
// src/
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
|
|
3020
|
+
// src/performance.ts
|
|
3021
|
+
var DEFAULT_GRAPHICS_PERFORMANCE = {
|
|
3022
|
+
fps: 45,
|
|
3023
|
+
tier: "medium"
|
|
3024
|
+
};
|
|
3025
|
+
var TIER_DEFAULT_FPS = {
|
|
3026
|
+
minimal: 24,
|
|
3027
|
+
low: 30,
|
|
3028
|
+
medium: 45,
|
|
3029
|
+
high: 60
|
|
3030
|
+
};
|
|
3031
|
+
var cachedEstimatedGraphicsPerformance;
|
|
3032
|
+
function getBridgeWindow10() {
|
|
404
3033
|
if (typeof window === "undefined") {
|
|
405
3034
|
return void 0;
|
|
406
3035
|
}
|
|
407
3036
|
return window;
|
|
408
3037
|
}
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
3038
|
+
function isRecord3(value) {
|
|
3039
|
+
return typeof value === "object" && value !== null;
|
|
3040
|
+
}
|
|
3041
|
+
function toFiniteNumber2(value) {
|
|
3042
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3043
|
+
return value;
|
|
3044
|
+
}
|
|
3045
|
+
if (typeof value === "string") {
|
|
3046
|
+
const parsed = Number.parseFloat(value.trim());
|
|
3047
|
+
if (Number.isFinite(parsed)) {
|
|
3048
|
+
return parsed;
|
|
3049
|
+
}
|
|
414
3050
|
}
|
|
3051
|
+
return void 0;
|
|
415
3052
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
3053
|
+
function clampScore(value) {
|
|
3054
|
+
return Math.min(100, Math.max(0, Math.round(value)));
|
|
3055
|
+
}
|
|
3056
|
+
function clampFps(value) {
|
|
3057
|
+
return Math.min(240, Math.max(1, Math.round(value)));
|
|
3058
|
+
}
|
|
3059
|
+
function tierFromScore(score) {
|
|
3060
|
+
if (score < 25) return "minimal";
|
|
3061
|
+
if (score < 40) return "low";
|
|
3062
|
+
if (score < 70) return "medium";
|
|
3063
|
+
return "high";
|
|
3064
|
+
}
|
|
3065
|
+
function tierFromFps(fps) {
|
|
3066
|
+
if (fps < 30) return "minimal";
|
|
3067
|
+
if (fps < 45) return "low";
|
|
3068
|
+
if (fps < 58) return "medium";
|
|
3069
|
+
return "high";
|
|
3070
|
+
}
|
|
3071
|
+
function fpsFromScore(score) {
|
|
3072
|
+
return TIER_DEFAULT_FPS[tierFromScore(score)];
|
|
3073
|
+
}
|
|
3074
|
+
function normalizeTier(value) {
|
|
3075
|
+
if (typeof value !== "string") {
|
|
3076
|
+
return void 0;
|
|
421
3077
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
3078
|
+
const normalized = value.trim().toLowerCase();
|
|
3079
|
+
if (normalized === "minimal" || normalized === "safe") return "minimal";
|
|
3080
|
+
if (normalized === "high") return "high";
|
|
3081
|
+
if (normalized === "medium") return "medium";
|
|
3082
|
+
if (normalized === "low") return "low";
|
|
3083
|
+
return void 0;
|
|
426
3084
|
}
|
|
427
|
-
|
|
428
|
-
return
|
|
3085
|
+
function firstDefined2(...values) {
|
|
3086
|
+
return values.find((value) => typeof value !== "undefined");
|
|
429
3087
|
}
|
|
430
|
-
function
|
|
431
|
-
if (typeof
|
|
432
|
-
return
|
|
433
|
-
};
|
|
3088
|
+
function normalizeMetric(value) {
|
|
3089
|
+
if (typeof value === "undefined" || value === null) {
|
|
3090
|
+
return void 0;
|
|
434
3091
|
}
|
|
435
|
-
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
3092
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
3093
|
+
const numeric = toFiniteNumber2(value);
|
|
3094
|
+
if (typeof numeric !== "undefined") {
|
|
3095
|
+
const fps2 = clampFps(numeric);
|
|
3096
|
+
return { fps: fps2, tier: tierFromFps(fps2) };
|
|
3097
|
+
}
|
|
3098
|
+
const tier2 = normalizeTier(value);
|
|
3099
|
+
if (tier2) {
|
|
3100
|
+
return { fps: TIER_DEFAULT_FPS[tier2], tier: tier2 };
|
|
3101
|
+
}
|
|
3102
|
+
return void 0;
|
|
3103
|
+
}
|
|
3104
|
+
if (!isRecord3(value)) {
|
|
3105
|
+
return void 0;
|
|
3106
|
+
}
|
|
3107
|
+
const fpsCandidate = firstDefined2(
|
|
3108
|
+
value.fps,
|
|
3109
|
+
value.targetFps,
|
|
3110
|
+
value.frameRate,
|
|
3111
|
+
value.framesPerSecond
|
|
3112
|
+
);
|
|
3113
|
+
const legacyScoreCandidate = firstDefined2(
|
|
3114
|
+
value.score,
|
|
3115
|
+
value.metric,
|
|
3116
|
+
value.value,
|
|
3117
|
+
value.performance,
|
|
3118
|
+
value.performanceScore,
|
|
3119
|
+
value.graphicsScore
|
|
3120
|
+
);
|
|
3121
|
+
const tierCandidate = firstDefined2(
|
|
3122
|
+
value.tier,
|
|
3123
|
+
value.graphicsTier,
|
|
3124
|
+
value.performanceTier,
|
|
3125
|
+
value.qualityTier,
|
|
3126
|
+
value.recommendedTier
|
|
3127
|
+
);
|
|
3128
|
+
const tier = normalizeTier(tierCandidate);
|
|
3129
|
+
const fpsNumeric = toFiniteNumber2(fpsCandidate);
|
|
3130
|
+
const scoreNumeric = toFiniteNumber2(legacyScoreCandidate);
|
|
3131
|
+
if (typeof fpsNumeric === "undefined" && typeof scoreNumeric === "undefined" && !tier) {
|
|
3132
|
+
return void 0;
|
|
3133
|
+
}
|
|
3134
|
+
const fps = clampFps(
|
|
3135
|
+
typeof fpsNumeric !== "undefined" ? fpsNumeric : typeof scoreNumeric !== "undefined" ? fpsFromScore(clampScore(scoreNumeric)) : TIER_DEFAULT_FPS[tier]
|
|
3136
|
+
);
|
|
3137
|
+
return {
|
|
3138
|
+
fps,
|
|
3139
|
+
tier: tier ?? tierFromFps(fps)
|
|
442
3140
|
};
|
|
443
3141
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
3142
|
+
function callBridgeMetric(bridge, name) {
|
|
3143
|
+
const fn = bridge[name];
|
|
3144
|
+
if (typeof fn !== "function") {
|
|
3145
|
+
return void 0;
|
|
3146
|
+
}
|
|
3147
|
+
try {
|
|
3148
|
+
return fn.call(bridge);
|
|
3149
|
+
} catch (error) {
|
|
3150
|
+
console.error("[oasiz/sdk] " + String(name) + " failed:", error);
|
|
3151
|
+
return void 0;
|
|
3152
|
+
}
|
|
449
3153
|
}
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
3154
|
+
function readHostGraphicsPerformance(bridge) {
|
|
3155
|
+
const candidates = [
|
|
3156
|
+
callBridgeMetric(bridge, "getGraphicsPerformance"),
|
|
3157
|
+
callBridgeMetric(bridge, "getGraphicsPerformanceMetric"),
|
|
3158
|
+
callBridgeMetric(bridge, "getPerformanceMetric"),
|
|
3159
|
+
bridge.__OASIZ_GRAPHICS_PERFORMANCE__,
|
|
3160
|
+
bridge.__OASIZ_PERFORMANCE_METRIC__
|
|
3161
|
+
];
|
|
3162
|
+
for (const candidate of candidates) {
|
|
3163
|
+
const metric = normalizeMetric(candidate);
|
|
3164
|
+
if (metric) {
|
|
3165
|
+
return metric;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
return void 0;
|
|
453
3169
|
}
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
return
|
|
3170
|
+
function getDevicePixelRatio2(bridge) {
|
|
3171
|
+
const dpr = bridge.devicePixelRatio;
|
|
3172
|
+
return typeof dpr === "number" && Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
457
3173
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return state.entitlements.find((item) => item.productId === productId)?.owned === true;
|
|
3174
|
+
function getNavigatorValue(bridge) {
|
|
3175
|
+
return bridge.navigator;
|
|
461
3176
|
}
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
3177
|
+
function parseIosMajorVersion(userAgent) {
|
|
3178
|
+
const match = /\bOS (\d+)_/i.exec(userAgent);
|
|
3179
|
+
if (!match) {
|
|
3180
|
+
return void 0;
|
|
3181
|
+
}
|
|
3182
|
+
const parsed = Number.parseInt(match[1] ?? "", 10);
|
|
3183
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
465
3184
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
quantity
|
|
470
|
-
});
|
|
3185
|
+
function isAppleMobileDevice(bridge) {
|
|
3186
|
+
const userAgent = bridge.navigator?.userAgent ?? "";
|
|
3187
|
+
return /iP(hone|ad|od)/i.test(userAgent) || /Macintosh/i.test(userAgent) && (bridge.navigator?.maxTouchPoints ?? 0) > 1;
|
|
471
3188
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
3189
|
+
function getLongestScreenEdge(bridge) {
|
|
3190
|
+
const screenWidth = bridge.screen?.width;
|
|
3191
|
+
const screenHeight = bridge.screen?.height;
|
|
3192
|
+
const innerWidth = bridge.innerWidth;
|
|
3193
|
+
const innerHeight = bridge.innerHeight;
|
|
3194
|
+
return Math.max(
|
|
3195
|
+
typeof screenWidth === "number" ? screenWidth : 0,
|
|
3196
|
+
typeof screenHeight === "number" ? screenHeight : 0,
|
|
3197
|
+
typeof innerWidth === "number" ? innerWidth : 0,
|
|
3198
|
+
typeof innerHeight === "number" ? innerHeight : 0
|
|
3199
|
+
);
|
|
477
3200
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
3201
|
+
function isCoarsePointer(bridge) {
|
|
3202
|
+
try {
|
|
3203
|
+
return bridge.matchMedia?.("(pointer: coarse)").matches === true;
|
|
3204
|
+
} catch {
|
|
3205
|
+
return false;
|
|
3206
|
+
}
|
|
481
3207
|
}
|
|
482
|
-
function
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
3208
|
+
function getWebGlInfo(bridge) {
|
|
3209
|
+
const canvas = bridge.document?.createElement?.("canvas");
|
|
3210
|
+
if (!canvas || typeof canvas.getContext !== "function") {
|
|
3211
|
+
return { webglVersion: 0 };
|
|
3212
|
+
}
|
|
3213
|
+
const gl2 = canvas.getContext("webgl2");
|
|
3214
|
+
const gl = gl2 ?? canvas.getContext("webgl") ?? canvas.getContext("experimental-webgl");
|
|
3215
|
+
if (!gl) {
|
|
3216
|
+
return { webglVersion: 0 };
|
|
3217
|
+
}
|
|
3218
|
+
const context = gl;
|
|
3219
|
+
let maxTextureSize;
|
|
3220
|
+
let renderer;
|
|
3221
|
+
try {
|
|
3222
|
+
const value = context.getParameter(context.MAX_TEXTURE_SIZE);
|
|
3223
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3224
|
+
maxTextureSize = value;
|
|
3225
|
+
}
|
|
3226
|
+
} catch {
|
|
3227
|
+
}
|
|
3228
|
+
try {
|
|
3229
|
+
const debugInfo = context.getExtension("WEBGL_debug_renderer_info");
|
|
3230
|
+
const rendererParam = debugInfo?.UNMASKED_RENDERER_WEBGL ?? context.RENDERER;
|
|
3231
|
+
const value = context.getParameter(rendererParam);
|
|
3232
|
+
if (typeof value === "string") {
|
|
3233
|
+
renderer = value;
|
|
3234
|
+
}
|
|
3235
|
+
} catch {
|
|
3236
|
+
}
|
|
3237
|
+
return {
|
|
3238
|
+
maxTextureSize,
|
|
3239
|
+
renderer,
|
|
3240
|
+
webglVersion: gl2 ? 2 : 1
|
|
3241
|
+
};
|
|
486
3242
|
}
|
|
487
|
-
function
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
3243
|
+
function applyMemoryScore(score, memory) {
|
|
3244
|
+
if (typeof memory !== "number" || !Number.isFinite(memory) || memory <= 0) {
|
|
3245
|
+
return score;
|
|
3246
|
+
}
|
|
3247
|
+
if (memory <= 1) return score - 18;
|
|
3248
|
+
if (memory <= 2) return score - 10;
|
|
3249
|
+
if (memory >= 8) return score + 16;
|
|
3250
|
+
if (memory >= 6) return score + 10;
|
|
3251
|
+
return score;
|
|
3252
|
+
}
|
|
3253
|
+
function applyCoreScore(score, cores) {
|
|
3254
|
+
if (typeof cores !== "number" || !Number.isFinite(cores) || cores <= 0) {
|
|
3255
|
+
return score;
|
|
3256
|
+
}
|
|
3257
|
+
if (cores <= 2) return score - 12;
|
|
3258
|
+
if (cores <= 4) return score;
|
|
3259
|
+
if (cores >= 8) return score + 12;
|
|
3260
|
+
return score + 8;
|
|
3261
|
+
}
|
|
3262
|
+
function applyWebGlScore(score, info) {
|
|
3263
|
+
let next = score;
|
|
3264
|
+
if (info.webglVersion === 2) {
|
|
3265
|
+
next += 16;
|
|
3266
|
+
} else if (info.webglVersion === 1) {
|
|
3267
|
+
next += 6;
|
|
3268
|
+
} else {
|
|
3269
|
+
next -= 25;
|
|
3270
|
+
}
|
|
3271
|
+
if (typeof info.maxTextureSize === "number") {
|
|
3272
|
+
if (info.maxTextureSize >= 8192) next += 10;
|
|
3273
|
+
else if (info.maxTextureSize >= 4096) next += 3;
|
|
3274
|
+
else next -= 8;
|
|
3275
|
+
}
|
|
3276
|
+
const renderer = info.renderer?.toLowerCase() ?? "";
|
|
3277
|
+
if (/swiftshader|llvmpipe|software/.test(renderer)) {
|
|
3278
|
+
next = Math.min(next, 35);
|
|
3279
|
+
} else if (/\bm[1-9]\b|apple gpu|a1[6-9]|rtx|radeon|adreno 7|adreno 8/.test(renderer)) {
|
|
3280
|
+
next += 8;
|
|
3281
|
+
}
|
|
3282
|
+
return next;
|
|
3283
|
+
}
|
|
3284
|
+
function applyMobileCostScore(score, bridge) {
|
|
3285
|
+
if (!isCoarsePointer(bridge)) {
|
|
3286
|
+
return score;
|
|
3287
|
+
}
|
|
3288
|
+
const dpr = getDevicePixelRatio2(bridge);
|
|
3289
|
+
const width = bridge.screen?.width ?? bridge.innerWidth ?? 0;
|
|
3290
|
+
const height = bridge.screen?.height ?? bridge.innerHeight ?? 0;
|
|
3291
|
+
const physicalPixels = width * height * dpr * dpr;
|
|
3292
|
+
if (physicalPixels > 4e6) {
|
|
3293
|
+
return score - 5;
|
|
3294
|
+
}
|
|
3295
|
+
return score;
|
|
3296
|
+
}
|
|
3297
|
+
function applyAppleMobileRules(score, bridge) {
|
|
3298
|
+
if (!isAppleMobileDevice(bridge)) {
|
|
3299
|
+
return score;
|
|
3300
|
+
}
|
|
3301
|
+
const userAgent = bridge.navigator?.userAgent ?? "";
|
|
3302
|
+
const iosMajorVersion = parseIosMajorVersion(userAgent);
|
|
3303
|
+
const highDensityDisplay = getDevicePixelRatio2(bridge) >= 3;
|
|
3304
|
+
const longestScreenEdge = getLongestScreenEdge(bridge);
|
|
3305
|
+
if (typeof iosMajorVersion === "number" && iosMajorVersion < 15) {
|
|
3306
|
+
return Math.min(score, 35);
|
|
3307
|
+
}
|
|
3308
|
+
if (highDensityDisplay && longestScreenEdge >= 430 && (typeof iosMajorVersion === "undefined" || iosMajorVersion >= 17)) {
|
|
3309
|
+
return Math.max(score, 78);
|
|
3310
|
+
}
|
|
3311
|
+
if (highDensityDisplay && longestScreenEdge >= 414 && (typeof iosMajorVersion === "undefined" || iosMajorVersion >= 16)) {
|
|
3312
|
+
return Math.max(score, 62);
|
|
3313
|
+
}
|
|
3314
|
+
return Math.min(score, 48);
|
|
3315
|
+
}
|
|
3316
|
+
function estimateGraphicsPerformance(bridge) {
|
|
3317
|
+
const navigatorValue = getNavigatorValue(bridge);
|
|
3318
|
+
let score = 45;
|
|
3319
|
+
score = applyMemoryScore(score, navigatorValue?.deviceMemory);
|
|
3320
|
+
score = applyCoreScore(score, navigatorValue?.hardwareConcurrency);
|
|
3321
|
+
score = applyWebGlScore(score, getWebGlInfo(bridge));
|
|
3322
|
+
score = applyMobileCostScore(score, bridge);
|
|
3323
|
+
score = applyAppleMobileRules(score, bridge);
|
|
3324
|
+
const normalizedScore = clampScore(score);
|
|
3325
|
+
return {
|
|
3326
|
+
fps: fpsFromScore(normalizedScore),
|
|
3327
|
+
tier: tierFromScore(normalizedScore)
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
function getGraphicsPerformance() {
|
|
3331
|
+
const bridge = getBridgeWindow10();
|
|
3332
|
+
if (!bridge) {
|
|
3333
|
+
return { ...DEFAULT_GRAPHICS_PERFORMANCE };
|
|
3334
|
+
}
|
|
3335
|
+
const hostMetric = readHostGraphicsPerformance(bridge);
|
|
3336
|
+
if (hostMetric) {
|
|
3337
|
+
return hostMetric;
|
|
3338
|
+
}
|
|
3339
|
+
cachedEstimatedGraphicsPerformance ??= estimateGraphicsPerformance(bridge);
|
|
3340
|
+
return { ...cachedEstimatedGraphicsPerformance };
|
|
491
3341
|
}
|
|
492
3342
|
|
|
493
3343
|
// src/index.ts
|
|
494
3344
|
var oasiz = {
|
|
495
3345
|
submitScore,
|
|
496
|
-
|
|
3346
|
+
enableAppSimulator,
|
|
3347
|
+
addScore,
|
|
3348
|
+
setScore,
|
|
3349
|
+
getPlayerCharacter,
|
|
497
3350
|
share,
|
|
498
3351
|
triggerHaptic,
|
|
3352
|
+
enableLogOverlay,
|
|
499
3353
|
loadGameState,
|
|
500
3354
|
saveGameState,
|
|
501
3355
|
flushGameState,
|
|
502
3356
|
shareRoomCode,
|
|
3357
|
+
openInviteModal,
|
|
503
3358
|
onPause,
|
|
504
3359
|
onResume,
|
|
3360
|
+
getSafeAreaTop,
|
|
3361
|
+
getViewportInsets,
|
|
3362
|
+
setLeaderboardVisible,
|
|
3363
|
+
getGraphicsPerformance,
|
|
3364
|
+
enableBackButtonTesting,
|
|
505
3365
|
onBackButton,
|
|
506
3366
|
onLeaveGame,
|
|
507
3367
|
leaveGame,
|
|
508
|
-
store: {
|
|
509
|
-
syncProducts,
|
|
510
|
-
getProducts,
|
|
511
|
-
getEntitlements,
|
|
512
|
-
hasEntitlement,
|
|
513
|
-
getQuantity,
|
|
514
|
-
purchase,
|
|
515
|
-
consume,
|
|
516
|
-
getJemBalance,
|
|
517
|
-
onEntitlementsChanged,
|
|
518
|
-
onJemBalanceChanged
|
|
519
|
-
},
|
|
520
3368
|
get gameId() {
|
|
521
3369
|
return getGameId();
|
|
522
3370
|
},
|
|
523
3371
|
get roomCode() {
|
|
524
3372
|
return getRoomCode();
|
|
525
3373
|
},
|
|
3374
|
+
get playerId() {
|
|
3375
|
+
return getPlayerId();
|
|
3376
|
+
},
|
|
526
3377
|
get playerName() {
|
|
527
3378
|
return getPlayerName();
|
|
528
3379
|
},
|
|
529
3380
|
get playerAvatar() {
|
|
530
3381
|
return getPlayerAvatar();
|
|
3382
|
+
},
|
|
3383
|
+
get safeAreaTop() {
|
|
3384
|
+
return getSafeAreaTop();
|
|
3385
|
+
},
|
|
3386
|
+
get viewportInsets() {
|
|
3387
|
+
return getViewportInsets();
|
|
3388
|
+
},
|
|
3389
|
+
get graphicsPerformance() {
|
|
3390
|
+
return getGraphicsPerformance();
|
|
531
3391
|
}
|
|
532
3392
|
};
|
|
533
3393
|
// Annotate the CommonJS export names for ESM import in node:
|
|
534
3394
|
0 && (module.exports = {
|
|
535
|
-
|
|
536
|
-
|
|
3395
|
+
addScore,
|
|
3396
|
+
enableAppSimulator,
|
|
3397
|
+
enableBackButtonTesting,
|
|
3398
|
+
enableLogOverlay,
|
|
537
3399
|
flushGameState,
|
|
538
|
-
getEntitlements,
|
|
539
3400
|
getGameId,
|
|
540
|
-
|
|
3401
|
+
getGraphicsPerformance,
|
|
541
3402
|
getPlayerAvatar,
|
|
3403
|
+
getPlayerCharacter,
|
|
3404
|
+
getPlayerId,
|
|
542
3405
|
getPlayerName,
|
|
543
|
-
getProducts,
|
|
544
|
-
getQuantity,
|
|
545
3406
|
getRoomCode,
|
|
546
|
-
|
|
3407
|
+
getSafeAreaTop,
|
|
3408
|
+
getViewportInsets,
|
|
547
3409
|
leaveGame,
|
|
548
3410
|
loadGameState,
|
|
549
3411
|
oasiz,
|
|
550
3412
|
onBackButton,
|
|
551
|
-
onEntitlementsChanged,
|
|
552
|
-
onJemBalanceChanged,
|
|
553
3413
|
onLeaveGame,
|
|
554
3414
|
onPause,
|
|
555
3415
|
onResume,
|
|
556
|
-
|
|
3416
|
+
openInviteModal,
|
|
557
3417
|
saveGameState,
|
|
3418
|
+
setLeaderboardVisible,
|
|
3419
|
+
setScore,
|
|
558
3420
|
share,
|
|
559
3421
|
shareRoomCode,
|
|
560
3422
|
submitScore,
|
|
561
|
-
syncProducts,
|
|
562
3423
|
triggerHaptic
|
|
563
3424
|
});
|