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