@oasiz/sdk 1.5.5 → 1.6.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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // src/character.ts
1
+ // src/haptics.ts
2
2
  function isDevelopment() {
3
3
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
4
4
  return nodeEnv !== "production";
@@ -9,1014 +9,72 @@ function getBridgeWindow() {
9
9
  }
10
10
  return window;
11
11
  }
12
- function warnMissingBridge(methodName) {
13
- if (isDevelopment()) {
14
- console.warn(
15
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
16
- );
17
- }
18
- }
19
- async function getPlayerCharacter() {
20
- const bridge = getBridgeWindow();
21
- if (typeof bridge?.__oasizGetPlayerCharacter !== "function") {
22
- warnMissingBridge("getPlayerCharacter");
23
- return null;
24
- }
25
- try {
26
- const result = await bridge.__oasizGetPlayerCharacter();
27
- return result ?? null;
28
- } catch (error) {
29
- if (isDevelopment()) {
30
- console.error("[oasiz/sdk] getPlayerCharacter failed:", error);
31
- }
32
- return null;
33
- }
34
- }
35
-
36
- // src/haptics.ts
37
- function isDevelopment2() {
38
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
39
- return nodeEnv !== "production";
40
- }
41
- function getBridgeWindow2() {
42
- if (typeof window === "undefined") {
43
- return void 0;
44
- }
45
- return window;
46
- }
47
12
  function triggerHaptic(type) {
48
- const bridge = getBridgeWindow2();
13
+ const bridge = getBridgeWindow();
49
14
  if (typeof bridge?.triggerHaptic === "function") {
50
15
  bridge.triggerHaptic(type);
51
16
  return;
52
17
  }
53
- if (isDevelopment2()) {
18
+ if (isDevelopment()) {
54
19
  console.warn(
55
20
  "[oasiz/sdk] triggerHaptic bridge is unavailable. This is expected in local development."
56
21
  );
57
22
  }
58
23
  }
59
24
 
60
- // src/log-overlay.ts
61
- var CONSOLE_METHODS = [
62
- "debug",
63
- "log",
64
- "info",
65
- "warn",
66
- "error"
67
- ];
68
- var DEFAULT_MAX_ENTRIES = 200;
69
- var DEFAULT_TITLE = "SDK Logs";
70
- var OVERLAY_MARGIN = 12;
71
- var DEFAULT_COLLAPSED_WIDTH = 156;
72
- var DEFAULT_COLLAPSED_HEIGHT = 52;
73
- var DEFAULT_EXPANDED_WIDTH = 565;
74
- var DEFAULT_EXPANDED_HEIGHT = 372;
75
- var DRAG_THRESHOLD_PX = 6;
76
- var MIN_EXPANDED_WIDTH = 160;
77
- var MIN_EXPANDED_HEIGHT = 110;
78
- var RESIZE_HOTSPOT_PX = 28;
79
- var TOP_DRAG_ZONE_PX = 44;
80
- var NOOP_HANDLE = {
81
- clear() {
82
- },
83
- destroy() {
84
- },
85
- hide() {
86
- },
87
- isVisible() {
88
- return false;
89
- },
90
- show() {
91
- }
92
- };
93
- function getBrowserWindow() {
94
- if (typeof window === "undefined") {
95
- return void 0;
96
- }
97
- return window;
98
- }
99
- function getDocument() {
100
- if (typeof document === "undefined") {
101
- return void 0;
102
- }
103
- return document;
104
- }
105
- function clampMaxEntries(value) {
106
- if (!Number.isFinite(value)) {
107
- return DEFAULT_MAX_ENTRIES;
108
- }
109
- return Math.max(10, Math.floor(value));
110
- }
111
- function createConsoleSnapshot() {
112
- const fallback = console.log.bind(console);
113
- return {
114
- debug: typeof console.debug === "function" ? console.debug.bind(console) : fallback,
115
- log: fallback,
116
- info: typeof console.info === "function" ? console.info.bind(console) : fallback,
117
- warn: typeof console.warn === "function" ? console.warn.bind(console) : fallback,
118
- error: typeof console.error === "function" ? console.error.bind(console) : fallback
119
- };
120
- }
121
- function formatTimestamp(timestamp) {
122
- const date = new Date(timestamp);
123
- const hours = String(date.getHours()).padStart(2, "0");
124
- const minutes = String(date.getMinutes()).padStart(2, "0");
125
- const seconds = String(date.getSeconds()).padStart(2, "0");
126
- const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
127
- return "[" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + "]";
128
- }
129
- function safeStringify(value) {
130
- const seen = /* @__PURE__ */ new WeakSet();
131
- try {
132
- return JSON.stringify(
133
- value,
134
- (_key, candidate) => {
135
- if (typeof candidate === "bigint") {
136
- return candidate.toString() + "n";
137
- }
138
- if (typeof candidate === "object" && candidate !== null) {
139
- if (seen.has(candidate)) {
140
- return "[Circular]";
141
- }
142
- seen.add(candidate);
143
- }
144
- return candidate;
145
- },
146
- 2
147
- ) ?? String(value);
148
- } catch {
149
- return String(value);
150
- }
151
- }
152
- function formatArg(value) {
153
- if (typeof value === "string") {
154
- return value;
155
- }
156
- if (value instanceof Error) {
157
- if (value.stack) {
158
- return value.stack;
159
- }
160
- return value.name + ": " + value.message;
161
- }
162
- if (typeof value === "undefined") {
163
- return "undefined";
164
- }
165
- if (typeof value === "function") {
166
- return "[Function " + (value.name || "anonymous") + "]";
167
- }
168
- return safeStringify(value);
169
- }
170
- function formatEntryMessage(args) {
171
- const message = args.map(formatArg).join(" ");
172
- if (message.length <= 4e3) {
173
- return message;
174
- }
175
- return message.slice(0, 3997) + "...";
176
- }
177
- function createEntry(level, args, id) {
178
- return {
179
- id,
180
- level,
181
- message: formatEntryMessage(args),
182
- timestamp: Date.now()
183
- };
184
- }
185
- function createButton(label) {
186
- const button = document.createElement("button");
187
- button.type = "button";
188
- button.textContent = label;
189
- button.style.cssText = [
190
- "appearance:none",
191
- "border:1px solid rgba(255,255,255,0.18)",
192
- "background:rgba(255,255,255,0.06)",
193
- "color:#f8fafc",
194
- "border-radius:999px",
195
- "padding:6px 10px",
196
- "font:600 12px/1.1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
197
- "cursor:pointer"
198
- ].join(";");
199
- return button;
200
- }
201
- function getLevelAccent(level) {
202
- if (level === "error") {
203
- return {
204
- lineBackground: "rgba(255, 109, 122, 0.08)"
205
- };
206
- }
207
- if (level === "warn") {
208
- return {
209
- lineBackground: "rgba(255, 196, 94, 0.07)"
210
- };
211
- }
212
- if (level === "info") {
213
- return {
214
- lineBackground: "rgba(82, 187, 255, 0.07)"
215
- };
216
- }
217
- if (level === "debug") {
218
- return {
219
- lineBackground: "rgba(166, 137, 255, 0.07)"
220
- };
221
- }
222
- return {
223
- lineBackground: "rgba(117, 235, 191, 0.06)"
224
- };
225
- }
226
- function getViewportSize() {
227
- const browserWindow = getBrowserWindow();
228
- return {
229
- width: Math.max(320, browserWindow?.innerWidth ?? 1280),
230
- height: Math.max(240, browserWindow?.innerHeight ?? 720)
231
- };
232
- }
233
- function clampPanelSize(size) {
234
- const viewport = getViewportSize();
235
- const maxWidth = Math.max(MIN_EXPANDED_WIDTH, viewport.width - OVERLAY_MARGIN * 2);
236
- const maxHeight = Math.max(
237
- MIN_EXPANDED_HEIGHT,
238
- viewport.height - OVERLAY_MARGIN * 2
239
- );
240
- return {
241
- width: Math.min(maxWidth, Math.max(MIN_EXPANDED_WIDTH, size.width)),
242
- height: Math.min(maxHeight, Math.max(MIN_EXPANDED_HEIGHT, size.height))
243
- };
244
- }
245
- function getOverlaySize(state) {
246
- if (state.expanded && state.panelSize) {
247
- return state.panelSize;
248
- }
249
- const rect = state.ui?.root && typeof state.ui.root.getBoundingClientRect === "function" ? state.ui.root.getBoundingClientRect() : null;
250
- if (rect && Number.isFinite(rect.width) && Number.isFinite(rect.height)) {
251
- return {
252
- width: Math.max(1, rect.width),
253
- height: Math.max(1, rect.height)
254
- };
255
- }
256
- return state.expanded ? clampPanelSize({
257
- width: DEFAULT_EXPANDED_WIDTH,
258
- height: DEFAULT_EXPANDED_HEIGHT
259
- }) : { width: DEFAULT_COLLAPSED_WIDTH, height: DEFAULT_COLLAPSED_HEIGHT };
260
- }
261
- function applyPanelSize(state) {
262
- if (!state.ui) {
263
- return;
264
- }
265
- if (!state.expanded) {
266
- state.ui.root.style.width = "auto";
267
- state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
268
- state.ui.panel.style.height = "auto";
269
- state.ui.entries.style.maxHeight = "min(36vh, 280px)";
270
- return;
271
- }
272
- if (!state.panelSize) {
273
- state.ui.root.style.width = "min(565px, calc(100vw - 24px))";
274
- state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
275
- state.ui.panel.style.height = "auto";
276
- state.ui.entries.style.maxHeight = "min(36vh, 280px)";
277
- return;
278
- }
279
- const nextSize = clampPanelSize(
280
- state.panelSize
281
- );
282
- state.panelSize = nextSize;
283
- state.ui.root.style.width = nextSize.width + "px";
284
- state.ui.panel.style.width = "100%";
285
- state.ui.panel.style.height = nextSize.height + "px";
286
- state.ui.entries.style.maxHeight = Math.max(72, nextSize.height - 88) + "px";
287
- }
288
- function clampPosition(point, state) {
289
- const viewport = getViewportSize();
290
- const size = getOverlaySize(state);
291
- const maxX = Math.max(OVERLAY_MARGIN, viewport.width - size.width - OVERLAY_MARGIN);
292
- const maxY = Math.max(
293
- OVERLAY_MARGIN,
294
- viewport.height - size.height - OVERLAY_MARGIN
295
- );
296
- return {
297
- x: Math.min(maxX, Math.max(OVERLAY_MARGIN, point.x)),
298
- y: Math.min(maxY, Math.max(OVERLAY_MARGIN, point.y))
299
- };
300
- }
301
- function applyOverlayPosition(state) {
302
- if (!state.ui) {
303
- return;
304
- }
305
- if (!state.position) {
306
- const viewport = getViewportSize();
307
- const size = getOverlaySize(state);
308
- state.position = clampPosition(
309
- {
310
- x: viewport.width - size.width - OVERLAY_MARGIN,
311
- y: viewport.height - size.height - OVERLAY_MARGIN
312
- },
313
- state
314
- );
315
- } else {
316
- state.position = clampPosition(state.position, state);
317
- }
318
- state.ui.root.style.left = state.position.x + "px";
319
- state.ui.root.style.top = state.position.y + "px";
320
- }
321
- function getPointFromMouseEvent(event) {
322
- return { x: event.clientX, y: event.clientY };
323
- }
324
- function getPointFromTouchEvent(event) {
325
- const touch = event.touches[0] ?? event.changedTouches[0];
326
- if (!touch) {
327
- return null;
328
- }
329
- return { x: touch.clientX, y: touch.clientY };
330
- }
331
- function stopDragging(state) {
332
- if (state.isDragging || state.isResizing) {
333
- state.suppressToggleClickUntil = Date.now() + 180;
334
- }
335
- state.isDragging = false;
336
- state.dragStartPoint = null;
337
- state.lastDragPoint = null;
338
- state.removeDragListeners?.();
339
- state.removeDragListeners = null;
340
- if (state.ui) {
341
- state.ui.panel.style.cursor = "default";
342
- state.ui.dragZone.style.cursor = "grab";
343
- state.ui.toggleButton.style.cursor = "grab";
344
- }
345
- }
346
- function stopResizing(state) {
347
- if (state.isResizing) {
348
- state.suppressToggleClickUntil = Date.now() + 180;
349
- }
350
- state.isResizing = false;
351
- state.resizeStartPoint = null;
352
- state.resizeStartSize = null;
353
- state.removeResizeListeners?.();
354
- state.removeResizeListeners = null;
355
- }
356
- function beginDragTracking(state, startPoint) {
357
- const doc = getDocument();
358
- if (!doc) {
359
- return;
360
- }
361
- stopResizing(state);
362
- stopDragging(state);
363
- state.dragMoved = false;
364
- state.dragStartPoint = startPoint;
365
- state.lastDragPoint = startPoint;
366
- const handlePointerMove = (nextPoint) => {
367
- if (!state.dragStartPoint || !state.lastDragPoint || !nextPoint) {
368
- return;
369
- }
370
- if (!state.isDragging) {
371
- const deltaFromStartX = nextPoint.x - state.dragStartPoint.x;
372
- const deltaFromStartY = nextPoint.y - state.dragStartPoint.y;
373
- const distance = Math.sqrt(
374
- deltaFromStartX * deltaFromStartX + deltaFromStartY * deltaFromStartY
375
- );
376
- if (distance < DRAG_THRESHOLD_PX) {
377
- return;
378
- }
379
- state.isDragging = true;
380
- state.dragMoved = true;
381
- if (state.ui) {
382
- state.ui.panel.style.cursor = "grabbing";
383
- state.ui.dragZone.style.cursor = "grabbing";
384
- state.ui.toggleButton.style.cursor = "grabbing";
385
- }
386
- }
387
- const currentPosition = state.position ?? { x: OVERLAY_MARGIN, y: OVERLAY_MARGIN };
388
- state.position = clampPosition(
389
- {
390
- x: currentPosition.x + (nextPoint.x - state.lastDragPoint.x),
391
- y: currentPosition.y + (nextPoint.y - state.lastDragPoint.y)
392
- },
393
- state
394
- );
395
- state.lastDragPoint = nextPoint;
396
- applyOverlayPosition(state);
397
- };
398
- const handleMouseMove = (event) => {
399
- handlePointerMove(getPointFromMouseEvent(event));
400
- };
401
- const handleTouchMove = (event) => {
402
- handlePointerMove(getPointFromTouchEvent(event));
403
- };
404
- const handleMouseUp = () => {
405
- stopDragging(state);
406
- };
407
- const handleTouchEnd = () => {
408
- stopDragging(state);
409
- };
410
- doc.addEventListener("mousemove", handleMouseMove);
411
- doc.addEventListener("mouseup", handleMouseUp);
412
- doc.addEventListener("touchmove", handleTouchMove, { passive: true });
413
- doc.addEventListener("touchend", handleTouchEnd);
414
- doc.addEventListener("touchcancel", handleTouchEnd);
415
- state.removeDragListeners = () => {
416
- doc.removeEventListener("mousemove", handleMouseMove);
417
- doc.removeEventListener("mouseup", handleMouseUp);
418
- doc.removeEventListener("touchmove", handleTouchMove);
419
- doc.removeEventListener("touchend", handleTouchEnd);
420
- doc.removeEventListener("touchcancel", handleTouchEnd);
421
- };
422
- }
423
- function startResizing(state, startPoint) {
424
- const doc = getDocument();
425
- if (!doc) {
426
- return;
427
- }
428
- stopDragging(state);
429
- stopResizing(state);
430
- state.isResizing = true;
431
- state.resizeStartPoint = startPoint;
432
- state.resizeStartSize = state.panelSize ?? clampPanelSize({ width: DEFAULT_EXPANDED_WIDTH, height: DEFAULT_EXPANDED_HEIGHT });
433
- const handleResizeMove = (nextPoint) => {
434
- if (!state.isResizing || !state.resizeStartPoint || !state.resizeStartSize || !nextPoint) {
435
- return;
436
- }
437
- state.panelSize = clampPanelSize({
438
- width: state.resizeStartSize.width + (nextPoint.x - state.resizeStartPoint.x),
439
- height: state.resizeStartSize.height + (nextPoint.y - state.resizeStartPoint.y)
440
- });
441
- applyPanelSize(state);
442
- applyOverlayPosition(state);
443
- };
444
- const handleMouseMove = (event) => {
445
- handleResizeMove(getPointFromMouseEvent(event));
446
- };
447
- const handleTouchMove = (event) => {
448
- handleResizeMove(getPointFromTouchEvent(event));
449
- };
450
- const handleFinish = () => {
451
- stopResizing(state);
452
- };
453
- doc.addEventListener("mousemove", handleMouseMove);
454
- doc.addEventListener("mouseup", handleFinish);
455
- doc.addEventListener("touchmove", handleTouchMove, { passive: true });
456
- doc.addEventListener("touchend", handleFinish);
457
- doc.addEventListener("touchcancel", handleFinish);
458
- state.removeResizeListeners = () => {
459
- doc.removeEventListener("mousemove", handleMouseMove);
460
- doc.removeEventListener("mouseup", handleFinish);
461
- doc.removeEventListener("touchmove", handleTouchMove);
462
- doc.removeEventListener("touchend", handleFinish);
463
- doc.removeEventListener("touchcancel", handleFinish);
464
- };
465
- }
466
- function isInBottomRightResizeZone(element, point) {
467
- const rect = element.getBoundingClientRect();
468
- return point.x >= rect.right - RESIZE_HOTSPOT_PX && point.x <= rect.right && point.y >= rect.bottom - RESIZE_HOTSPOT_PX && point.y <= rect.bottom;
469
- }
470
- function canStartDragFromTarget(target) {
471
- if (!(target instanceof Element)) {
472
- return true;
473
- }
474
- if (target.closest("button") || target.closest("a") || target.closest("input") || target.closest("textarea") || target.closest("select")) {
475
- return false;
476
- }
477
- return true;
478
- }
479
- function isInTopDragZone(element, point, zoneHeight) {
480
- const rect = element.getBoundingClientRect();
481
- return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.top + zoneHeight;
482
- }
483
- function attachDragStartListeners(element, state, options = {}) {
484
- element.addEventListener("mousedown", (event) => {
485
- if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
486
- return;
487
- }
488
- const point = getPointFromMouseEvent(event);
489
- if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
490
- return;
491
- }
492
- beginDragTracking(state, point);
493
- });
494
- element.addEventListener("touchstart", (event) => {
495
- if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
496
- return;
497
- }
498
- const point = getPointFromTouchEvent(event);
499
- if (!point) {
500
- return;
501
- }
502
- if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
503
- return;
504
- }
505
- beginDragTracking(state, point);
506
- });
507
- }
508
- function attachCollapsedToggleListeners(element, state) {
509
- const startToggleInteraction = (startPoint) => {
510
- const doc = getDocument();
511
- if (!doc) {
512
- return;
513
- }
514
- beginDragTracking(state, startPoint);
515
- const finishInteraction = () => {
516
- releaseListeners();
517
- if (state.dragMoved || Date.now() < state.suppressToggleClickUntil) {
518
- return;
519
- }
520
- state.expanded = true;
521
- state.unreadCount = 0;
522
- renderOverlay(state);
523
- };
524
- const releaseListeners = () => {
525
- doc.removeEventListener("mouseup", finishInteraction);
526
- doc.removeEventListener("touchend", finishInteraction);
527
- doc.removeEventListener("touchcancel", finishInteraction);
528
- };
529
- doc.addEventListener("mouseup", finishInteraction);
530
- doc.addEventListener("touchend", finishInteraction);
531
- doc.addEventListener("touchcancel", finishInteraction);
532
- };
533
- element.addEventListener("mousedown", (event) => {
534
- event.preventDefault();
535
- startToggleInteraction(getPointFromMouseEvent(event));
536
- });
537
- element.addEventListener("touchstart", (event) => {
538
- const point = getPointFromTouchEvent(event);
539
- if (!point) {
540
- return;
541
- }
542
- event.preventDefault();
543
- startToggleInteraction(point);
544
- });
545
- }
546
- function attachPanelResizeListeners(element, state) {
547
- element.addEventListener("mousedown", (event) => {
548
- if (!state.expanded || !canStartDragFromTarget(event.target)) {
549
- return;
550
- }
551
- const point = getPointFromMouseEvent(event);
552
- if (!isInBottomRightResizeZone(element, point)) {
553
- return;
554
- }
555
- event.preventDefault();
556
- event.stopPropagation();
557
- startResizing(state, point);
558
- });
559
- element.addEventListener("touchstart", (event) => {
560
- if (!state.expanded || !canStartDragFromTarget(event.target)) {
561
- return;
562
- }
563
- const point = getPointFromTouchEvent(event);
564
- if (!point || !isInBottomRightResizeZone(element, point)) {
565
- return;
566
- }
567
- event.preventDefault();
568
- event.stopPropagation();
569
- startResizing(state, point);
570
- });
571
- }
572
- function createOverlayUi(state) {
573
- const root = document.createElement("div");
574
- root.style.cssText = [
575
- "position:fixed",
576
- "left:12px",
577
- "top:12px",
578
- "z-index:2147483647",
579
- "display:flex",
580
- "flex-direction:column",
581
- "align-items:stretch",
582
- "gap:8px",
583
- "width:min(565px, calc(100vw - 24px))",
584
- "pointer-events:none"
585
- ].join(";");
586
- const toggleButton = createButton("Logs");
587
- toggleButton.style.pointerEvents = "auto";
588
- toggleButton.style.alignSelf = "flex-end";
589
- toggleButton.style.display = "inline-flex";
590
- toggleButton.style.alignItems = "center";
591
- toggleButton.style.justifyContent = "center";
592
- toggleButton.style.minHeight = "40px";
593
- toggleButton.style.minWidth = "76px";
594
- toggleButton.style.padding = "8px 14px";
595
- toggleButton.style.textAlign = "center";
596
- toggleButton.style.border = "1px solid rgba(122, 212, 255, 0.22)";
597
- toggleButton.style.background = "linear-gradient(180deg, rgba(13,31,54,0.98), rgba(8,19,37,0.98))";
598
- toggleButton.style.boxShadow = "0 18px 40px rgba(4,12,24,0.34)";
599
- toggleButton.style.cursor = "grab";
600
- toggleButton.style.touchAction = "none";
601
- const panel = document.createElement("div");
602
- panel.style.cssText = [
603
- "position:relative",
604
- "display:flex",
605
- "flex-direction:column",
606
- "width:min(565px, calc(100vw - 24px))",
607
- "max-height:min(48vh, 372px)",
608
- "border-radius:18px",
609
- "border:1px solid rgba(116,167,255,0.16)",
610
- "background:linear-gradient(180deg, rgba(9,19,37,0.98), rgba(5,12,24,0.98))",
611
- "box-shadow:0 28px 64px rgba(2,8,18,0.46)",
612
- "backdrop-filter:blur(16px)",
613
- "overflow:hidden",
614
- "cursor:default",
615
- "pointer-events:auto"
616
- ].join(";");
617
- const dragZone = document.createElement("div");
618
- dragZone.style.cssText = [
619
- "position:absolute",
620
- "top:0",
621
- "left:0",
622
- "right:0",
623
- "height:" + String(TOP_DRAG_ZONE_PX) + "px",
624
- "z-index:1",
625
- "cursor:grab",
626
- "background:transparent",
627
- "pointer-events:auto"
628
- ].join(";");
629
- const controls = document.createElement("div");
630
- controls.style.cssText = [
631
- "position:absolute",
632
- "top:22px",
633
- "right:22px",
634
- "z-index:2",
635
- "display:flex",
636
- "align-items:center",
637
- "gap:8px",
638
- "pointer-events:auto"
639
- ].join(";");
640
- const clearButton = createButton("Clear");
641
- clearButton.style.background = "rgba(255,255,255,0.1)";
642
- clearButton.style.border = "1px solid rgba(255,255,255,0.16)";
643
- clearButton.style.color = "#eef6ff";
644
- clearButton.style.minHeight = "30px";
645
- clearButton.style.padding = "4px 9px";
646
- clearButton.style.fontSize = "11px";
647
- clearButton.style.backdropFilter = "blur(8px)";
648
- const collapseButton = createButton("Hide");
649
- collapseButton.style.background = "rgba(113, 171, 255, 0.12)";
650
- collapseButton.style.border = "1px solid rgba(113, 171, 255, 0.2)";
651
- collapseButton.style.color = "#d9ebff";
652
- collapseButton.style.minHeight = "30px";
653
- collapseButton.style.padding = "4px 9px";
654
- collapseButton.style.fontSize = "11px";
655
- collapseButton.style.backdropFilter = "blur(8px)";
656
- const body = document.createElement("div");
657
- body.style.cssText = [
658
- "position:relative",
659
- "display:flex",
660
- "flex-direction:column",
661
- "padding:12px",
662
- "background:linear-gradient(180deg, rgba(4,10,20,0.88), rgba(3,8,18,0.98))",
663
- "flex:1 1 auto",
664
- "min-height:0"
665
- ].join(";");
666
- const entries = document.createElement("div");
667
- entries.style.cssText = [
668
- "display:flex",
669
- "flex-direction:column",
670
- "gap:0",
671
- "overflow:auto",
672
- "flex:1 1 auto",
673
- "min-height:96px",
674
- "max-height:min(36vh, 280px)",
675
- "padding:0",
676
- "border:1px solid rgba(115,153,212,0.14)",
677
- "border-radius:12px",
678
- "background:rgba(4,10,20,0.82)"
679
- ].join(";");
680
- const emptyState = document.createElement("div");
681
- emptyState.style.cssText = [
682
- "display:flex",
683
- "align-items:center",
684
- "justify-content:center",
685
- "flex:1 1 auto",
686
- "min-height:96px",
687
- "color:rgba(204,222,250,0.6)",
688
- "font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
689
- "text-align:center",
690
- "padding:18px"
691
- ].join(";");
692
- emptyState.textContent = "Console output will appear here.";
693
- collapseButton.addEventListener("click", (event) => {
694
- event.stopPropagation();
695
- state.expanded = false;
696
- renderOverlay(state);
697
- });
698
- clearButton.addEventListener("click", (event) => {
699
- event.stopPropagation();
700
- state.entries = [];
701
- state.unreadCount = 0;
702
- renderOverlay(state);
703
- });
704
- controls.appendChild(clearButton);
705
- controls.appendChild(collapseButton);
706
- entries.appendChild(emptyState);
707
- body.appendChild(entries);
708
- body.appendChild(controls);
709
- panel.appendChild(dragZone);
710
- panel.appendChild(body);
711
- attachDragStartListeners(dragZone, state);
712
- attachPanelResizeListeners(panel, state);
713
- attachCollapsedToggleListeners(toggleButton, state);
714
- root.appendChild(panel);
715
- root.appendChild(toggleButton);
716
- return {
717
- body,
718
- clearButton,
719
- collapseButton,
720
- controls,
721
- dragZone,
722
- emptyState,
723
- entries,
724
- panel,
725
- root,
726
- toggleButton
727
- };
728
- }
729
- function renderOverlay(state) {
730
- if (!state.ui) {
731
- return;
732
- }
733
- state.ui.panel.style.display = state.expanded ? "flex" : "none";
734
- state.ui.toggleButton.style.display = state.expanded ? "none" : "inline-flex";
735
- state.ui.toggleButton.textContent = "Logs";
736
- if (state.entries.length === 0) {
737
- state.ui.entries.style.display = "flex";
738
- state.ui.emptyState.style.display = "flex";
739
- state.ui.entries.replaceChildren(state.ui.emptyState);
740
- applyPanelSize(state);
741
- applyOverlayPosition(state);
742
- return;
743
- }
744
- state.ui.entries.style.display = "flex";
745
- state.ui.emptyState.style.display = "none";
746
- const nextChildren = state.entries.map((entry) => {
747
- const accent = getLevelAccent(entry.level);
748
- const row = document.createElement("div");
749
- row.style.cssText = [
750
- "display:flex",
751
- "align-items:flex-start",
752
- "gap:0",
753
- "padding:4px 12px",
754
- "background:" + accent.lineBackground
755
- ].join(";");
756
- const line = document.createElement("div");
757
- line.textContent = formatTimestamp(entry.timestamp) + " " + entry.level.toUpperCase() + " " + entry.message;
758
- line.style.cssText = [
759
- "color:#ecf4ff",
760
- "font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
761
- "white-space:pre-wrap",
762
- "word-break:break-word",
763
- "flex:1 1 auto"
764
- ].join(";");
765
- row.appendChild(line);
766
- return row;
767
- });
768
- state.ui.entries.replaceChildren(...nextChildren);
769
- state.ui.entries.scrollTop = state.ui.entries.scrollHeight;
770
- applyPanelSize(state);
771
- applyOverlayPosition(state);
772
- }
773
- function mountOverlay(state) {
774
- const doc = getDocument();
775
- if (!doc?.body || state.ui) {
776
- return;
777
- }
778
- state.ui = createOverlayUi(state);
779
- doc.body.appendChild(state.ui.root);
780
- applyPanelSize(state);
781
- applyOverlayPosition(state);
782
- renderOverlay(state);
783
- }
784
- function enqueueEntry(state, level, args) {
785
- state.entries.push(createEntry(level, args, state.nextEntryId));
786
- state.nextEntryId += 1;
787
- if (state.entries.length > state.maxEntries) {
788
- state.entries.splice(0, state.entries.length - state.maxEntries);
789
- }
790
- if (!state.expanded) {
791
- state.unreadCount += 1;
792
- }
793
- renderOverlay(state);
794
- }
795
- function restoreConsole(snapshot) {
796
- for (const method of CONSOLE_METHODS) {
797
- console[method] = snapshot[method];
798
- }
799
- }
800
- function patchConsole(state) {
801
- for (const method of CONSOLE_METHODS) {
802
- const original = state.originalConsole[method];
803
- console[method] = (...args) => {
804
- enqueueEntry(state, method, args);
805
- original(...args);
806
- };
807
- }
808
- }
809
- function cleanupOverlay(browserWindow, state) {
810
- restoreConsole(state.originalConsole);
811
- stopResizing(state);
812
- stopDragging(state);
813
- if (state.domReadyHandler) {
814
- getDocument()?.removeEventListener("DOMContentLoaded", state.domReadyHandler);
815
- state.domReadyHandler = void 0;
816
- }
817
- if (state.resizeHandler && typeof browserWindow.removeEventListener === "function") {
818
- browserWindow.removeEventListener("resize", state.resizeHandler);
819
- state.resizeHandler = void 0;
820
- }
821
- state.ui?.root.remove();
822
- state.ui = null;
823
- delete browserWindow.__oasizLogOverlayController__;
824
- delete browserWindow.__oasizLogOverlayState__;
825
- }
826
- function createController(browserWindow, state) {
827
- return {
828
- retain() {
829
- state.refCount += 1;
830
- this.ensureMounted();
831
- },
832
- ensureMounted() {
833
- const doc = getDocument();
834
- if (!doc) {
835
- return;
836
- }
837
- if (doc.body) {
838
- mountOverlay(state);
839
- applyOverlayPosition(state);
840
- return;
841
- }
842
- if (!state.domReadyHandler) {
843
- state.domReadyHandler = () => {
844
- mountOverlay(state);
845
- state.domReadyHandler = void 0;
846
- };
847
- doc.addEventListener("DOMContentLoaded", state.domReadyHandler, {
848
- once: true
849
- });
850
- }
851
- },
852
- clear() {
853
- state.entries = [];
854
- state.unreadCount = 0;
855
- renderOverlay(state);
856
- },
857
- show() {
858
- state.expanded = true;
859
- state.unreadCount = 0;
860
- renderOverlay(state);
861
- },
862
- hide() {
863
- state.expanded = false;
864
- renderOverlay(state);
865
- },
866
- isVisible() {
867
- return state.expanded;
868
- },
869
- destroy() {
870
- state.refCount = Math.max(0, state.refCount - 1);
871
- if (state.refCount === 0) {
872
- cleanupOverlay(browserWindow, state);
873
- }
874
- }
875
- };
876
- }
877
- function enableLogOverlay(options = {}) {
878
- if (options.enabled === false) {
879
- return NOOP_HANDLE;
880
- }
881
- const browserWindow = getBrowserWindow();
882
- const doc = getDocument();
883
- if (!browserWindow || !doc) {
884
- return NOOP_HANDLE;
885
- }
886
- const existingController = browserWindow.__oasizLogOverlayController__;
887
- const existingState = browserWindow.__oasizLogOverlayState__;
888
- if (existingController && existingState) {
889
- existingController.retain();
890
- if (typeof options.maxEntries === "number") {
891
- existingState.maxEntries = clampMaxEntries(options.maxEntries);
892
- if (existingState.entries.length > existingState.maxEntries) {
893
- existingState.entries.splice(
894
- 0,
895
- existingState.entries.length - existingState.maxEntries
896
- );
897
- }
898
- }
899
- if (typeof options.collapsed === "boolean") {
900
- existingState.expanded = !options.collapsed;
901
- applyPanelSize(existingState);
902
- if (existingState.expanded) {
903
- existingState.unreadCount = 0;
904
- }
905
- }
906
- if (typeof options.title === "string" && options.title.trim().length > 0) {
907
- existingState.title = options.title.trim();
908
- }
909
- existingController.ensureMounted();
910
- renderOverlay(existingState);
911
- return existingController;
912
- }
913
- const state = {
914
- dragMoved: false,
915
- dragStartPoint: null,
916
- entries: [],
917
- expanded: options.collapsed !== true,
918
- isDragging: false,
919
- isResizing: false,
920
- lastDragPoint: null,
921
- maxEntries: clampMaxEntries(options.maxEntries),
922
- nextEntryId: 1,
923
- originalConsole: createConsoleSnapshot(),
924
- panelSize: null,
925
- position: null,
926
- refCount: 1,
927
- removeDragListeners: null,
928
- removeResizeListeners: null,
929
- resizeStartPoint: null,
930
- resizeStartSize: null,
931
- suppressToggleClickUntil: 0,
932
- title: typeof options.title === "string" && options.title.trim().length > 0 ? options.title.trim() : DEFAULT_TITLE,
933
- resizeHandler: void 0,
934
- ui: null,
935
- unreadCount: 0
936
- };
937
- const controller = createController(browserWindow, state);
938
- browserWindow.__oasizLogOverlayState__ = state;
939
- browserWindow.__oasizLogOverlayController__ = controller;
940
- patchConsole(state);
941
- if (typeof browserWindow.addEventListener === "function") {
942
- state.resizeHandler = () => {
943
- applyOverlayPosition(state);
944
- };
945
- browserWindow.addEventListener("resize", state.resizeHandler);
946
- }
947
- controller.ensureMounted();
948
- return controller;
949
- }
950
-
951
25
  // src/multiplayer.ts
952
- function isDevelopment3() {
26
+ function isDevelopment2() {
953
27
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
954
28
  return nodeEnv !== "production";
955
29
  }
956
- function getBridgeWindow3() {
30
+ function getBridgeWindow2() {
957
31
  if (typeof window === "undefined") {
958
32
  return void 0;
959
33
  }
960
34
  return window;
961
35
  }
962
- function shareRoomCode(roomCode, options) {
963
- const bridge = getBridgeWindow3();
36
+ function shareRoomCode(roomCode) {
37
+ const bridge = getBridgeWindow2();
964
38
  if (typeof bridge?.shareRoomCode === "function") {
965
- bridge.shareRoomCode(roomCode, options);
39
+ bridge.shareRoomCode(roomCode);
966
40
  return;
967
41
  }
968
- if (isDevelopment3()) {
42
+ if (isDevelopment2()) {
969
43
  console.warn(
970
44
  "[oasiz/sdk] shareRoomCode bridge is unavailable. This is expected in local development."
971
45
  );
972
46
  }
973
47
  }
974
- function openInviteModal() {
975
- const bridge = getBridgeWindow3();
976
- if (typeof bridge?.openInviteModal === "function") {
977
- bridge.openInviteModal();
978
- return;
979
- }
980
- if (isDevelopment3()) {
981
- console.warn(
982
- "[oasiz/sdk] openInviteModal bridge is unavailable. This is expected in local development."
983
- );
984
- }
985
- }
986
48
  function getGameId() {
987
- const bridge = getBridgeWindow3();
49
+ const bridge = getBridgeWindow2();
988
50
  return bridge?.__GAME_ID__;
989
51
  }
990
52
  function getRoomCode() {
991
- const bridge = getBridgeWindow3();
53
+ const bridge = getBridgeWindow2();
992
54
  return bridge?.__ROOM_CODE__;
993
55
  }
994
- function getPlayerId() {
995
- const bridge = getBridgeWindow3();
996
- return bridge?.__PLAYER_ID__;
997
- }
998
56
  function getPlayerName() {
999
- const bridge = getBridgeWindow3();
57
+ const bridge = getBridgeWindow2();
1000
58
  return bridge?.__PLAYER_NAME__;
1001
59
  }
1002
60
  function getPlayerAvatar() {
1003
- const bridge = getBridgeWindow3();
61
+ const bridge = getBridgeWindow2();
1004
62
  return bridge?.__PLAYER_AVATAR__;
1005
63
  }
1006
64
 
1007
65
  // src/score.ts
1008
- function isDevelopment4() {
66
+ function isDevelopment3() {
1009
67
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1010
68
  return nodeEnv !== "production";
1011
69
  }
1012
- function warnMissingBridge2(methodName) {
1013
- if (isDevelopment4()) {
70
+ function warnMissingBridge(methodName) {
71
+ if (isDevelopment3()) {
1014
72
  console.warn(
1015
73
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1016
74
  );
1017
75
  }
1018
76
  }
1019
- function getBridgeWindow4() {
77
+ function getBridgeWindow3() {
1020
78
  if (typeof window === "undefined") {
1021
79
  return void 0;
1022
80
  }
@@ -1024,92 +82,41 @@ function getBridgeWindow4() {
1024
82
  }
1025
83
  function submitScore(score) {
1026
84
  if (!Number.isFinite(score)) {
1027
- if (isDevelopment4()) {
85
+ if (isDevelopment3()) {
1028
86
  console.warn("[oasiz/sdk] submitScore expected a finite number:", score);
1029
87
  }
1030
88
  return;
1031
89
  }
1032
- const bridge = getBridgeWindow4();
90
+ const bridge = getBridgeWindow3();
1033
91
  const normalizedScore = Math.max(0, Math.floor(score));
1034
92
  if (typeof bridge?.submitScore === "function") {
1035
93
  bridge.submitScore(normalizedScore);
1036
94
  return;
1037
95
  }
1038
- warnMissingBridge2("submitScore");
1039
- }
1040
-
1041
- // src/score-edit.ts
1042
- function isDevelopment5() {
1043
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1044
- return nodeEnv !== "production";
1045
- }
1046
- function getBridgeWindow5() {
1047
- if (typeof window === "undefined") {
1048
- return void 0;
1049
- }
1050
- return window;
1051
- }
1052
- function warnMissingBridge3(methodName) {
1053
- if (isDevelopment5()) {
1054
- console.warn(
1055
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1056
- );
1057
- }
1058
- }
1059
- async function editScore(payload, methodName) {
1060
- const bridge = getBridgeWindow5();
1061
- if (typeof bridge?.__oasizEditScore !== "function") {
1062
- warnMissingBridge3(methodName);
1063
- return null;
1064
- }
1065
- try {
1066
- const result = await bridge.__oasizEditScore(payload);
1067
- return result ?? null;
1068
- } catch (error) {
1069
- if (isDevelopment5()) {
1070
- console.error("[oasiz/sdk] " + methodName + " failed:", error);
1071
- }
1072
- return null;
1073
- }
96
+ warnMissingBridge("submitScore");
1074
97
  }
1075
- async function addScore(delta) {
1076
- if (!Number.isInteger(delta)) {
1077
- if (isDevelopment5()) {
1078
- console.warn("[oasiz/sdk] addScore expected an integer:", delta);
1079
- }
1080
- return null;
1081
- }
1082
- if (delta === 0) {
1083
- return null;
1084
- }
1085
- return editScore({ delta }, "addScore");
1086
- }
1087
- async function setScore(score) {
1088
- if (!Number.isInteger(score) || score < 0) {
1089
- if (isDevelopment5()) {
1090
- console.warn(
1091
- "[oasiz/sdk] setScore expected a non-negative integer:",
1092
- score
1093
- );
1094
- }
1095
- return null;
98
+ function emitScoreConfig(config) {
99
+ const bridge = getBridgeWindow3();
100
+ if (typeof bridge?.emitScoreConfig === "function") {
101
+ bridge.emitScoreConfig(config);
102
+ return;
1096
103
  }
1097
- return editScore({ score }, "setScore");
104
+ warnMissingBridge("emitScoreConfig");
1098
105
  }
1099
106
 
1100
107
  // src/share.ts
1101
- function isDevelopment6() {
108
+ function isDevelopment4() {
1102
109
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1103
110
  return nodeEnv !== "production";
1104
111
  }
1105
- function getBridgeWindow6() {
112
+ function getBridgeWindow4() {
1106
113
  if (typeof window === "undefined") {
1107
114
  return void 0;
1108
115
  }
1109
116
  return window;
1110
117
  }
1111
- function warnMissingBridge4(methodName) {
1112
- if (isDevelopment6()) {
118
+ function warnMissingBridge2(methodName) {
119
+ if (isDevelopment4()) {
1113
120
  console.warn(
1114
121
  "[oasiz/sdk] " + methodName + " share bridge is unavailable. This is expected in local development."
1115
122
  );
@@ -1152,20 +159,20 @@ function validateRequest(options) {
1152
159
  }
1153
160
  async function share(options) {
1154
161
  const request = validateRequest(options);
1155
- const bridge = getBridgeWindow6();
162
+ const bridge = getBridgeWindow4();
1156
163
  if (typeof bridge?.__oasizShareRequest !== "function") {
1157
- warnMissingBridge4("__oasizShareRequest");
164
+ warnMissingBridge2("__oasizShareRequest");
1158
165
  throw new Error("Share bridge unavailable");
1159
166
  }
1160
167
  await bridge.__oasizShareRequest(request);
1161
168
  }
1162
169
 
1163
170
  // src/state.ts
1164
- function isDevelopment7() {
171
+ function isDevelopment5() {
1165
172
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1166
173
  return nodeEnv !== "production";
1167
174
  }
1168
- function getBridgeWindow7() {
175
+ function getBridgeWindow5() {
1169
176
  if (typeof window === "undefined") {
1170
177
  return void 0;
1171
178
  }
@@ -1178,22 +185,22 @@ function isPlainObject(value) {
1178
185
  const proto = Object.getPrototypeOf(value);
1179
186
  return proto === Object.prototype || proto === null;
1180
187
  }
1181
- function warnMissingBridge5(methodName) {
1182
- if (isDevelopment7()) {
188
+ function warnMissingBridge3(methodName) {
189
+ if (isDevelopment5()) {
1183
190
  console.warn(
1184
191
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1185
192
  );
1186
193
  }
1187
194
  }
1188
195
  function loadGameState() {
1189
- const bridge = getBridgeWindow7();
196
+ const bridge = getBridgeWindow5();
1190
197
  if (typeof bridge?.loadGameState !== "function") {
1191
- warnMissingBridge5("loadGameState");
198
+ warnMissingBridge3("loadGameState");
1192
199
  return {};
1193
200
  }
1194
201
  const state = bridge.loadGameState();
1195
202
  if (!isPlainObject(state)) {
1196
- if (isDevelopment7()) {
203
+ if (isDevelopment5()) {
1197
204
  console.warn(
1198
205
  "[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
1199
206
  );
@@ -1204,35 +211,35 @@ function loadGameState() {
1204
211
  }
1205
212
  function saveGameState(state) {
1206
213
  if (!isPlainObject(state)) {
1207
- if (isDevelopment7()) {
214
+ if (isDevelopment5()) {
1208
215
  console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
1209
216
  }
1210
217
  return;
1211
218
  }
1212
- const bridge = getBridgeWindow7();
219
+ const bridge = getBridgeWindow5();
1213
220
  if (typeof bridge?.saveGameState === "function") {
1214
221
  bridge.saveGameState(state);
1215
222
  return;
1216
223
  }
1217
- warnMissingBridge5("saveGameState");
224
+ warnMissingBridge3("saveGameState");
1218
225
  }
1219
226
  function flushGameState() {
1220
- const bridge = getBridgeWindow7();
227
+ const bridge = getBridgeWindow5();
1221
228
  if (typeof bridge?.flushGameState === "function") {
1222
229
  bridge.flushGameState();
1223
230
  return;
1224
231
  }
1225
- warnMissingBridge5("flushGameState");
232
+ warnMissingBridge3("flushGameState");
1226
233
  }
1227
234
 
1228
235
  // src/lifecycle.ts
1229
- function isDevelopment8() {
236
+ function isDevelopment6() {
1230
237
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1231
238
  return nodeEnv !== "production";
1232
239
  }
1233
240
  function addLifecycleListener(eventName, callback) {
1234
241
  if (typeof window === "undefined") {
1235
- if (isDevelopment8()) {
242
+ if (isDevelopment6()) {
1236
243
  console.warn(
1237
244
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
1238
245
  );
@@ -1251,217 +258,20 @@ function onResume(callback) {
1251
258
  return addLifecycleListener("oasiz:resume", callback);
1252
259
  }
1253
260
 
1254
- // src/layout.ts
1255
- function isDevelopment9() {
1256
- const nodeEnv = globalThis.process?.env?.NODE_ENV;
1257
- return nodeEnv !== "production";
1258
- }
1259
- function getBridgeWindow8() {
1260
- if (typeof window === "undefined") {
1261
- return void 0;
1262
- }
1263
- return window;
1264
- }
1265
- function warnMissingBridge6(methodName) {
1266
- if (isDevelopment9()) {
1267
- console.warn(
1268
- "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1269
- );
1270
- }
1271
- }
1272
- function toFiniteNumber(value) {
1273
- if (typeof value !== "number" || !Number.isFinite(value)) {
1274
- if (typeof value !== "string") {
1275
- return void 0;
1276
- }
1277
- const parsed = Number.parseFloat(value.trim());
1278
- if (!Number.isFinite(parsed)) {
1279
- return void 0;
1280
- }
1281
- return parsed;
1282
- }
1283
- return value;
1284
- }
1285
- function clampSafeAreaTopPixels(value) {
1286
- const numeric = toFiniteNumber(value);
1287
- if (typeof numeric === "undefined") {
1288
- return 0;
1289
- }
1290
- return Math.max(0, numeric);
1291
- }
1292
- function normalizeSafeAreaTopPercent(value) {
1293
- const numeric = toFiniteNumber(value);
1294
- if (typeof numeric === "undefined") {
1295
- return void 0;
1296
- }
1297
- return Math.min(100, Math.max(0, numeric));
1298
- }
1299
- function getViewportHeight(bridge) {
1300
- const visualViewportHeight = bridge.visualViewport?.height;
1301
- if (typeof visualViewportHeight === "number" && Number.isFinite(visualViewportHeight) && visualViewportHeight > 0) {
1302
- return visualViewportHeight;
1303
- }
1304
- const innerHeight = bridge.innerHeight;
1305
- if (typeof innerHeight === "number" && Number.isFinite(innerHeight) && innerHeight > 0) {
1306
- return innerHeight;
1307
- }
1308
- const documentHeight = bridge.document?.documentElement?.clientHeight;
1309
- if (typeof documentHeight === "number" && Number.isFinite(documentHeight) && documentHeight > 0) {
1310
- return documentHeight;
1311
- }
1312
- const bodyHeight = bridge.document?.body?.clientHeight;
1313
- if (typeof bodyHeight === "number" && Number.isFinite(bodyHeight) && bodyHeight > 0) {
1314
- return bodyHeight;
1315
- }
1316
- return 0;
1317
- }
1318
- function readCssSafeAreaValue(bridge, cssValue) {
1319
- const doc = bridge.document;
1320
- const root = doc?.body ?? doc?.documentElement;
1321
- if (!doc || !root || typeof doc.createElement !== "function" || typeof root.appendChild !== "function" || typeof bridge.getComputedStyle !== "function") {
1322
- return 0;
1323
- }
1324
- const probe = doc.createElement("div");
1325
- probe.style.position = "fixed";
1326
- probe.style.top = "0";
1327
- probe.style.left = "0";
1328
- probe.style.width = "0";
1329
- probe.style.height = "0";
1330
- probe.style.visibility = "hidden";
1331
- probe.style.pointerEvents = "none";
1332
- probe.style.paddingTop = cssValue;
1333
- root.appendChild(probe);
1334
- try {
1335
- return clampSafeAreaTopPixels(bridge.getComputedStyle(probe).paddingTop);
1336
- } finally {
1337
- if (typeof probe.remove === "function") {
1338
- probe.remove();
1339
- } else {
1340
- probe.parentNode?.removeChild(probe);
1341
- }
1342
- }
1343
- }
1344
- function readCssSafeAreaTopPixels(bridge) {
1345
- const envPixels = readCssSafeAreaValue(bridge, "env(safe-area-inset-top)");
1346
- if (envPixels > 0) {
1347
- return envPixels;
1348
- }
1349
- return readCssSafeAreaValue(bridge, "constant(safe-area-inset-top)");
1350
- }
1351
- function getDevicePixelRatio(bridge) {
1352
- const dpr = bridge.devicePixelRatio;
1353
- if (typeof dpr !== "number" || !Number.isFinite(dpr) || dpr <= 0) {
1354
- return 1;
1355
- }
1356
- return dpr;
1357
- }
1358
- function roughlyEqualPixels(a, b) {
1359
- return Math.abs(a - b) <= 2;
1360
- }
1361
- function normalizeSafeAreaTopPixels(value, bridge) {
1362
- const pixels = clampSafeAreaTopPixels(value);
1363
- const cssEnvPixels = readCssSafeAreaTopPixels(bridge);
1364
- if (pixels <= 0) {
1365
- return cssEnvPixels;
1366
- }
1367
- const dpr = getDevicePixelRatio(bridge);
1368
- if (cssEnvPixels > 0 && dpr > 1 && roughlyEqualPixels(pixels / dpr, cssEnvPixels)) {
1369
- return cssEnvPixels;
1370
- }
1371
- return pixels;
1372
- }
1373
- function pixelsTopToPercentOfViewport(pixels, bridge) {
1374
- const h = getViewportHeight(bridge);
1375
- if (h <= 0) {
1376
- return 0;
1377
- }
1378
- return normalizeSafeAreaTopPercent(pixels / h * 100) ?? 0;
1379
- }
1380
- function cssSafeAreaTopPercent(bridge) {
1381
- return pixelsTopToPercentOfViewport(readCssSafeAreaTopPixels(bridge), bridge);
1382
- }
1383
- function resolvePercentValue(value, bridge) {
1384
- const percent = normalizeSafeAreaTopPercent(value);
1385
- if (typeof percent === "undefined") {
1386
- return void 0;
1387
- }
1388
- return percent > 0 ? percent : cssSafeAreaTopPercent(bridge);
1389
- }
1390
- function resolvePixelValue(value, bridge) {
1391
- const numeric = toFiniteNumber(value);
1392
- if (typeof numeric === "undefined") {
1393
- return void 0;
1394
- }
1395
- return pixelsTopToPercentOfViewport(normalizeSafeAreaTopPixels(numeric, bridge), bridge);
1396
- }
1397
- function getSafeAreaTop() {
1398
- const bridge = getBridgeWindow8();
1399
- if (!bridge) {
1400
- return 0;
1401
- }
1402
- if (typeof bridge.getSafeAreaTopPercent === "function") {
1403
- const percent = resolvePercentValue(bridge.getSafeAreaTopPercent(), bridge);
1404
- if (typeof percent !== "undefined") {
1405
- return percent;
1406
- }
1407
- }
1408
- if (typeof bridge.__OASIZ_SAFE_AREA_TOP_PERCENT__ !== "undefined") {
1409
- const percent = resolvePercentValue(bridge.__OASIZ_SAFE_AREA_TOP_PERCENT__, bridge);
1410
- if (typeof percent !== "undefined") {
1411
- return percent;
1412
- }
1413
- }
1414
- if (typeof bridge.getSafeAreaTop === "function") {
1415
- const percent = resolvePixelValue(bridge.getSafeAreaTop(), bridge);
1416
- if (typeof percent !== "undefined") {
1417
- return percent;
1418
- }
1419
- }
1420
- if (typeof bridge.__OASIZ_SAFE_AREA_TOP__ !== "undefined") {
1421
- const percent = resolvePixelValue(bridge.__OASIZ_SAFE_AREA_TOP__, bridge);
1422
- if (typeof percent !== "undefined") {
1423
- return percent;
1424
- }
1425
- }
1426
- const cssPercent = cssSafeAreaTopPercent(bridge);
1427
- if (cssPercent > 0) {
1428
- return cssPercent;
1429
- }
1430
- warnMissingBridge6("getSafeAreaTop");
1431
- return 0;
1432
- }
1433
- function setLeaderboardVisible(visible) {
1434
- if (typeof visible !== "boolean") {
1435
- if (isDevelopment9()) {
1436
- console.warn(
1437
- "[oasiz/sdk] setLeaderboardVisible expected a boolean:",
1438
- visible
1439
- );
1440
- }
1441
- return;
1442
- }
1443
- const bridge = getBridgeWindow8();
1444
- if (typeof bridge?.__oasizSetLeaderboardVisible === "function") {
1445
- bridge.__oasizSetLeaderboardVisible(visible);
1446
- return;
1447
- }
1448
- warnMissingBridge6("__oasizSetLeaderboardVisible");
1449
- }
1450
-
1451
261
  // src/navigation.ts
1452
262
  var activeBackListeners = 0;
1453
- function isDevelopment10() {
263
+ function isDevelopment7() {
1454
264
  const nodeEnv = globalThis.process?.env?.NODE_ENV;
1455
265
  return nodeEnv !== "production";
1456
266
  }
1457
- function getBridgeWindow9() {
267
+ function getBridgeWindow6() {
1458
268
  if (typeof window === "undefined") {
1459
269
  return void 0;
1460
270
  }
1461
271
  return window;
1462
272
  }
1463
- function warnMissingBridge7(methodName) {
1464
- if (isDevelopment10()) {
273
+ function warnMissingBridge4(methodName) {
274
+ if (isDevelopment7()) {
1465
275
  console.warn(
1466
276
  "[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
1467
277
  );
@@ -1477,7 +287,7 @@ function normalizeNavigationError(error) {
1477
287
  }
1478
288
  function addNavigationListener(eventName, callback) {
1479
289
  if (typeof window === "undefined") {
1480
- if (isDevelopment10()) {
290
+ if (isDevelopment7()) {
1481
291
  console.warn(
1482
292
  "[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
1483
293
  );
@@ -1498,24 +308,24 @@ function onBackButton(callback) {
1498
308
  throw normalizeNavigationError(error);
1499
309
  }
1500
310
  });
1501
- const bridge = getBridgeWindow9();
311
+ const bridge = getBridgeWindow6();
1502
312
  activeBackListeners += 1;
1503
313
  if (activeBackListeners === 1) {
1504
314
  if (typeof bridge?.__oasizSetBackOverride === "function") {
1505
315
  bridge.__oasizSetBackOverride(true);
1506
316
  } else {
1507
- warnMissingBridge7("__oasizSetBackOverride");
317
+ warnMissingBridge4("__oasizSetBackOverride");
1508
318
  }
1509
319
  }
1510
320
  return () => {
1511
321
  off();
1512
322
  activeBackListeners = Math.max(0, activeBackListeners - 1);
1513
323
  if (activeBackListeners === 0) {
1514
- const currentBridge = getBridgeWindow9();
324
+ const currentBridge = getBridgeWindow6();
1515
325
  if (typeof currentBridge?.__oasizSetBackOverride === "function") {
1516
326
  currentBridge.__oasizSetBackOverride(false);
1517
327
  } else {
1518
- warnMissingBridge7("__oasizSetBackOverride");
328
+ warnMissingBridge4("__oasizSetBackOverride");
1519
329
  }
1520
330
  }
1521
331
  };
@@ -1524,78 +334,176 @@ function onLeaveGame(callback) {
1524
334
  return addNavigationListener("oasiz:leave", callback);
1525
335
  }
1526
336
  function leaveGame() {
1527
- const bridge = getBridgeWindow9();
337
+ const bridge = getBridgeWindow6();
1528
338
  if (typeof bridge?.__oasizLeaveGame === "function") {
1529
339
  bridge.__oasizLeaveGame();
1530
340
  return;
1531
341
  }
1532
- warnMissingBridge7("__oasizLeaveGame");
342
+ warnMissingBridge4("__oasizLeaveGame");
343
+ }
344
+
345
+ // src/store.ts
346
+ function isDevelopment8() {
347
+ const nodeEnv = globalThis.process?.env?.NODE_ENV;
348
+ return nodeEnv !== "production";
349
+ }
350
+ function getBridgeWindow7() {
351
+ if (typeof window === "undefined") {
352
+ return void 0;
353
+ }
354
+ return window;
355
+ }
356
+ function warnMissingBridge5(methodName) {
357
+ if (isDevelopment8()) {
358
+ console.warn(
359
+ "[oasiz/sdk] " + methodName + " store bridge is unavailable. This is expected in local development."
360
+ );
361
+ }
362
+ }
363
+ async function requestStoreBridge(action, payload) {
364
+ const bridge = getBridgeWindow7();
365
+ if (typeof bridge?.__oasizStoreBridgeRequest !== "function") {
366
+ warnMissingBridge5(action);
367
+ throw new Error("Store bridge unavailable");
368
+ }
369
+ return await bridge.__oasizStoreBridgeRequest({
370
+ action,
371
+ payload
372
+ });
373
+ }
374
+ async function getFreshState() {
375
+ return await requestStoreBridge("getStoreState");
376
+ }
377
+ function subscribeToStoreEvent(eventName, callback) {
378
+ if (typeof window === "undefined") {
379
+ return () => {
380
+ };
381
+ }
382
+ const handler = (event) => {
383
+ const customEvent = event;
384
+ callback(customEvent.detail);
385
+ };
386
+ window.addEventListener(eventName, handler);
387
+ return () => {
388
+ window.removeEventListener(eventName, handler);
389
+ };
390
+ }
391
+ async function syncProducts(products, expectedVersion) {
392
+ return await requestStoreBridge("syncProducts", {
393
+ expectedVersion: expectedVersion ?? null,
394
+ products
395
+ });
396
+ }
397
+ async function getProducts() {
398
+ const state = await getFreshState();
399
+ return state.products;
400
+ }
401
+ async function getEntitlements() {
402
+ const state = await getFreshState();
403
+ return state.entitlements;
404
+ }
405
+ async function hasEntitlement(productId) {
406
+ const state = await getFreshState();
407
+ return state.entitlements.find((item) => item.productId === productId)?.owned === true;
408
+ }
409
+ async function getQuantity(productId) {
410
+ const state = await getFreshState();
411
+ return state.entitlements.find((item) => item.productId === productId)?.quantity ?? 0;
412
+ }
413
+ async function purchase(productId, quantity = 1) {
414
+ return await requestStoreBridge("purchaseProduct", {
415
+ productId,
416
+ quantity
417
+ });
418
+ }
419
+ async function consume(productId, quantity = 1) {
420
+ return await requestStoreBridge("consumeProduct", {
421
+ productId,
422
+ quantity
423
+ });
424
+ }
425
+ async function getJemBalance() {
426
+ const state = await getFreshState();
427
+ return state.jemBalance;
428
+ }
429
+ function onEntitlementsChanged(callback) {
430
+ return subscribeToStoreEvent("oasiz:entitlements-changed", (state) => {
431
+ callback(state.entitlements);
432
+ });
433
+ }
434
+ function onJemBalanceChanged(callback) {
435
+ return subscribeToStoreEvent("oasiz:jem-balance-changed", (state) => {
436
+ callback(state.jemBalance);
437
+ });
1533
438
  }
1534
439
 
1535
440
  // src/index.ts
1536
441
  var oasiz = {
1537
442
  submitScore,
1538
- addScore,
1539
- setScore,
1540
- getPlayerCharacter,
443
+ emitScoreConfig,
1541
444
  share,
1542
445
  triggerHaptic,
1543
- enableLogOverlay,
1544
446
  loadGameState,
1545
447
  saveGameState,
1546
448
  flushGameState,
1547
449
  shareRoomCode,
1548
- openInviteModal,
1549
450
  onPause,
1550
451
  onResume,
1551
- getSafeAreaTop,
1552
- setLeaderboardVisible,
1553
452
  onBackButton,
1554
453
  onLeaveGame,
1555
454
  leaveGame,
455
+ store: {
456
+ syncProducts,
457
+ getProducts,
458
+ getEntitlements,
459
+ hasEntitlement,
460
+ getQuantity,
461
+ purchase,
462
+ consume,
463
+ getJemBalance,
464
+ onEntitlementsChanged,
465
+ onJemBalanceChanged
466
+ },
1556
467
  get gameId() {
1557
468
  return getGameId();
1558
469
  },
1559
470
  get roomCode() {
1560
471
  return getRoomCode();
1561
472
  },
1562
- get playerId() {
1563
- return getPlayerId();
1564
- },
1565
473
  get playerName() {
1566
474
  return getPlayerName();
1567
475
  },
1568
476
  get playerAvatar() {
1569
477
  return getPlayerAvatar();
1570
- },
1571
- get safeAreaTop() {
1572
- return getSafeAreaTop();
1573
478
  }
1574
479
  };
1575
480
  export {
1576
- addScore,
1577
- enableLogOverlay,
481
+ consume,
482
+ emitScoreConfig,
1578
483
  flushGameState,
484
+ getEntitlements,
1579
485
  getGameId,
486
+ getJemBalance,
1580
487
  getPlayerAvatar,
1581
- getPlayerCharacter,
1582
- getPlayerId,
1583
488
  getPlayerName,
489
+ getProducts,
490
+ getQuantity,
1584
491
  getRoomCode,
1585
- getSafeAreaTop,
492
+ hasEntitlement,
1586
493
  leaveGame,
1587
494
  loadGameState,
1588
495
  oasiz,
1589
496
  onBackButton,
497
+ onEntitlementsChanged,
498
+ onJemBalanceChanged,
1590
499
  onLeaveGame,
1591
500
  onPause,
1592
501
  onResume,
1593
- openInviteModal,
502
+ purchase,
1594
503
  saveGameState,
1595
- setLeaderboardVisible,
1596
- setScore,
1597
504
  share,
1598
505
  shareRoomCode,
1599
506
  submitScore,
507
+ syncProducts,
1600
508
  triggerHaptic
1601
509
  };