@oasiz/sdk 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -63
- package/dist/index.cjs +235 -936
- package/dist/index.d.cts +104 -40
- package/dist/index.d.ts +104 -40
- package/dist/index.js +224 -934
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,897 +22,6 @@ function triggerHaptic(type) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// src/log-overlay.ts
|
|
26
|
-
var CONSOLE_METHODS = [
|
|
27
|
-
"debug",
|
|
28
|
-
"log",
|
|
29
|
-
"info",
|
|
30
|
-
"warn",
|
|
31
|
-
"error"
|
|
32
|
-
];
|
|
33
|
-
var DEFAULT_MAX_ENTRIES = 200;
|
|
34
|
-
var DEFAULT_TITLE = "SDK Logs";
|
|
35
|
-
var OVERLAY_MARGIN = 12;
|
|
36
|
-
var DEFAULT_COLLAPSED_WIDTH = 156;
|
|
37
|
-
var DEFAULT_COLLAPSED_HEIGHT = 52;
|
|
38
|
-
var DEFAULT_EXPANDED_WIDTH = 565;
|
|
39
|
-
var DEFAULT_EXPANDED_HEIGHT = 372;
|
|
40
|
-
var DRAG_THRESHOLD_PX = 6;
|
|
41
|
-
var MIN_EXPANDED_WIDTH = 160;
|
|
42
|
-
var MIN_EXPANDED_HEIGHT = 110;
|
|
43
|
-
var RESIZE_HOTSPOT_PX = 28;
|
|
44
|
-
var TOP_DRAG_ZONE_PX = 44;
|
|
45
|
-
var NOOP_HANDLE = {
|
|
46
|
-
clear() {
|
|
47
|
-
},
|
|
48
|
-
destroy() {
|
|
49
|
-
},
|
|
50
|
-
hide() {
|
|
51
|
-
},
|
|
52
|
-
isVisible() {
|
|
53
|
-
return false;
|
|
54
|
-
},
|
|
55
|
-
show() {
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
function getBrowserWindow() {
|
|
59
|
-
if (typeof window === "undefined") {
|
|
60
|
-
return void 0;
|
|
61
|
-
}
|
|
62
|
-
return window;
|
|
63
|
-
}
|
|
64
|
-
function getDocument() {
|
|
65
|
-
if (typeof document === "undefined") {
|
|
66
|
-
return void 0;
|
|
67
|
-
}
|
|
68
|
-
return document;
|
|
69
|
-
}
|
|
70
|
-
function clampMaxEntries(value) {
|
|
71
|
-
if (!Number.isFinite(value)) {
|
|
72
|
-
return DEFAULT_MAX_ENTRIES;
|
|
73
|
-
}
|
|
74
|
-
return Math.max(10, Math.floor(value));
|
|
75
|
-
}
|
|
76
|
-
function createConsoleSnapshot() {
|
|
77
|
-
const fallback = console.log.bind(console);
|
|
78
|
-
return {
|
|
79
|
-
debug: typeof console.debug === "function" ? console.debug.bind(console) : fallback,
|
|
80
|
-
log: fallback,
|
|
81
|
-
info: typeof console.info === "function" ? console.info.bind(console) : fallback,
|
|
82
|
-
warn: typeof console.warn === "function" ? console.warn.bind(console) : fallback,
|
|
83
|
-
error: typeof console.error === "function" ? console.error.bind(console) : fallback
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function formatTimestamp(timestamp) {
|
|
87
|
-
const date = new Date(timestamp);
|
|
88
|
-
const hours = String(date.getHours()).padStart(2, "0");
|
|
89
|
-
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
90
|
-
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
91
|
-
const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
|
|
92
|
-
return "[" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + "]";
|
|
93
|
-
}
|
|
94
|
-
function safeStringify(value) {
|
|
95
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
96
|
-
try {
|
|
97
|
-
return JSON.stringify(
|
|
98
|
-
value,
|
|
99
|
-
(_key, candidate) => {
|
|
100
|
-
if (typeof candidate === "bigint") {
|
|
101
|
-
return candidate.toString() + "n";
|
|
102
|
-
}
|
|
103
|
-
if (typeof candidate === "object" && candidate !== null) {
|
|
104
|
-
if (seen.has(candidate)) {
|
|
105
|
-
return "[Circular]";
|
|
106
|
-
}
|
|
107
|
-
seen.add(candidate);
|
|
108
|
-
}
|
|
109
|
-
return candidate;
|
|
110
|
-
},
|
|
111
|
-
2
|
|
112
|
-
) ?? String(value);
|
|
113
|
-
} catch {
|
|
114
|
-
return String(value);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function formatArg(value) {
|
|
118
|
-
if (typeof value === "string") {
|
|
119
|
-
return value;
|
|
120
|
-
}
|
|
121
|
-
if (value instanceof Error) {
|
|
122
|
-
if (value.stack) {
|
|
123
|
-
return value.stack;
|
|
124
|
-
}
|
|
125
|
-
return value.name + ": " + value.message;
|
|
126
|
-
}
|
|
127
|
-
if (typeof value === "undefined") {
|
|
128
|
-
return "undefined";
|
|
129
|
-
}
|
|
130
|
-
if (typeof value === "function") {
|
|
131
|
-
return "[Function " + (value.name || "anonymous") + "]";
|
|
132
|
-
}
|
|
133
|
-
return safeStringify(value);
|
|
134
|
-
}
|
|
135
|
-
function formatEntryMessage(args) {
|
|
136
|
-
const message = args.map(formatArg).join(" ");
|
|
137
|
-
if (message.length <= 4e3) {
|
|
138
|
-
return message;
|
|
139
|
-
}
|
|
140
|
-
return message.slice(0, 3997) + "...";
|
|
141
|
-
}
|
|
142
|
-
function createEntry(level, args, id) {
|
|
143
|
-
return {
|
|
144
|
-
id,
|
|
145
|
-
level,
|
|
146
|
-
message: formatEntryMessage(args),
|
|
147
|
-
timestamp: Date.now()
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function createButton(label) {
|
|
151
|
-
const button = document.createElement("button");
|
|
152
|
-
button.type = "button";
|
|
153
|
-
button.textContent = label;
|
|
154
|
-
button.style.cssText = [
|
|
155
|
-
"appearance:none",
|
|
156
|
-
"border:1px solid rgba(255,255,255,0.18)",
|
|
157
|
-
"background:rgba(255,255,255,0.06)",
|
|
158
|
-
"color:#f8fafc",
|
|
159
|
-
"border-radius:999px",
|
|
160
|
-
"padding:6px 10px",
|
|
161
|
-
"font:600 12px/1.1 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
162
|
-
"cursor:pointer"
|
|
163
|
-
].join(";");
|
|
164
|
-
return button;
|
|
165
|
-
}
|
|
166
|
-
function getLevelAccent(level) {
|
|
167
|
-
if (level === "error") {
|
|
168
|
-
return {
|
|
169
|
-
lineBackground: "rgba(255, 109, 122, 0.08)"
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
if (level === "warn") {
|
|
173
|
-
return {
|
|
174
|
-
lineBackground: "rgba(255, 196, 94, 0.07)"
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
if (level === "info") {
|
|
178
|
-
return {
|
|
179
|
-
lineBackground: "rgba(82, 187, 255, 0.07)"
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
if (level === "debug") {
|
|
183
|
-
return {
|
|
184
|
-
lineBackground: "rgba(166, 137, 255, 0.07)"
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
lineBackground: "rgba(117, 235, 191, 0.06)"
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function getViewportSize() {
|
|
192
|
-
const browserWindow = getBrowserWindow();
|
|
193
|
-
return {
|
|
194
|
-
width: Math.max(320, browserWindow?.innerWidth ?? 1280),
|
|
195
|
-
height: Math.max(240, browserWindow?.innerHeight ?? 720)
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function clampPanelSize(size) {
|
|
199
|
-
const viewport = getViewportSize();
|
|
200
|
-
const maxWidth = Math.max(MIN_EXPANDED_WIDTH, viewport.width - OVERLAY_MARGIN * 2);
|
|
201
|
-
const maxHeight = Math.max(
|
|
202
|
-
MIN_EXPANDED_HEIGHT,
|
|
203
|
-
viewport.height - OVERLAY_MARGIN * 2
|
|
204
|
-
);
|
|
205
|
-
return {
|
|
206
|
-
width: Math.min(maxWidth, Math.max(MIN_EXPANDED_WIDTH, size.width)),
|
|
207
|
-
height: Math.min(maxHeight, Math.max(MIN_EXPANDED_HEIGHT, size.height))
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
function getOverlaySize(state) {
|
|
211
|
-
if (state.expanded && state.panelSize) {
|
|
212
|
-
return state.panelSize;
|
|
213
|
-
}
|
|
214
|
-
const rect = state.ui?.root && typeof state.ui.root.getBoundingClientRect === "function" ? state.ui.root.getBoundingClientRect() : null;
|
|
215
|
-
if (rect && Number.isFinite(rect.width) && Number.isFinite(rect.height)) {
|
|
216
|
-
return {
|
|
217
|
-
width: Math.max(1, rect.width),
|
|
218
|
-
height: Math.max(1, rect.height)
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return state.expanded ? clampPanelSize({
|
|
222
|
-
width: DEFAULT_EXPANDED_WIDTH,
|
|
223
|
-
height: DEFAULT_EXPANDED_HEIGHT
|
|
224
|
-
}) : { width: DEFAULT_COLLAPSED_WIDTH, height: DEFAULT_COLLAPSED_HEIGHT };
|
|
225
|
-
}
|
|
226
|
-
function applyPanelSize(state) {
|
|
227
|
-
if (!state.ui) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (!state.expanded) {
|
|
231
|
-
state.ui.root.style.width = "auto";
|
|
232
|
-
state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
|
|
233
|
-
state.ui.panel.style.height = "auto";
|
|
234
|
-
state.ui.entries.style.maxHeight = "min(36vh, 280px)";
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (!state.panelSize) {
|
|
238
|
-
state.ui.root.style.width = "min(565px, calc(100vw - 24px))";
|
|
239
|
-
state.ui.panel.style.width = "min(565px, calc(100vw - 24px))";
|
|
240
|
-
state.ui.panel.style.height = "auto";
|
|
241
|
-
state.ui.entries.style.maxHeight = "min(36vh, 280px)";
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const nextSize = clampPanelSize(
|
|
245
|
-
state.panelSize
|
|
246
|
-
);
|
|
247
|
-
state.panelSize = nextSize;
|
|
248
|
-
state.ui.root.style.width = nextSize.width + "px";
|
|
249
|
-
state.ui.panel.style.width = "100%";
|
|
250
|
-
state.ui.panel.style.height = nextSize.height + "px";
|
|
251
|
-
state.ui.entries.style.maxHeight = Math.max(72, nextSize.height - 88) + "px";
|
|
252
|
-
}
|
|
253
|
-
function clampPosition(point, state) {
|
|
254
|
-
const viewport = getViewportSize();
|
|
255
|
-
const size = getOverlaySize(state);
|
|
256
|
-
const maxX = Math.max(OVERLAY_MARGIN, viewport.width - size.width - OVERLAY_MARGIN);
|
|
257
|
-
const maxY = Math.max(
|
|
258
|
-
OVERLAY_MARGIN,
|
|
259
|
-
viewport.height - size.height - OVERLAY_MARGIN
|
|
260
|
-
);
|
|
261
|
-
return {
|
|
262
|
-
x: Math.min(maxX, Math.max(OVERLAY_MARGIN, point.x)),
|
|
263
|
-
y: Math.min(maxY, Math.max(OVERLAY_MARGIN, point.y))
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
function applyOverlayPosition(state) {
|
|
267
|
-
if (!state.ui) {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (!state.position) {
|
|
271
|
-
const viewport = getViewportSize();
|
|
272
|
-
const size = getOverlaySize(state);
|
|
273
|
-
state.position = clampPosition(
|
|
274
|
-
{
|
|
275
|
-
x: viewport.width - size.width - OVERLAY_MARGIN,
|
|
276
|
-
y: viewport.height - size.height - OVERLAY_MARGIN
|
|
277
|
-
},
|
|
278
|
-
state
|
|
279
|
-
);
|
|
280
|
-
} else {
|
|
281
|
-
state.position = clampPosition(state.position, state);
|
|
282
|
-
}
|
|
283
|
-
state.ui.root.style.left = state.position.x + "px";
|
|
284
|
-
state.ui.root.style.top = state.position.y + "px";
|
|
285
|
-
}
|
|
286
|
-
function getPointFromMouseEvent(event) {
|
|
287
|
-
return { x: event.clientX, y: event.clientY };
|
|
288
|
-
}
|
|
289
|
-
function getPointFromTouchEvent(event) {
|
|
290
|
-
const touch = event.touches[0] ?? event.changedTouches[0];
|
|
291
|
-
if (!touch) {
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
return { x: touch.clientX, y: touch.clientY };
|
|
295
|
-
}
|
|
296
|
-
function stopDragging(state) {
|
|
297
|
-
if (state.isDragging || state.isResizing) {
|
|
298
|
-
state.suppressToggleClickUntil = Date.now() + 180;
|
|
299
|
-
}
|
|
300
|
-
state.isDragging = false;
|
|
301
|
-
state.dragStartPoint = null;
|
|
302
|
-
state.lastDragPoint = null;
|
|
303
|
-
state.removeDragListeners?.();
|
|
304
|
-
state.removeDragListeners = null;
|
|
305
|
-
if (state.ui) {
|
|
306
|
-
state.ui.panel.style.cursor = "default";
|
|
307
|
-
state.ui.dragZone.style.cursor = "grab";
|
|
308
|
-
state.ui.toggleButton.style.cursor = "grab";
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function stopResizing(state) {
|
|
312
|
-
if (state.isResizing) {
|
|
313
|
-
state.suppressToggleClickUntil = Date.now() + 180;
|
|
314
|
-
}
|
|
315
|
-
state.isResizing = false;
|
|
316
|
-
state.resizeStartPoint = null;
|
|
317
|
-
state.resizeStartSize = null;
|
|
318
|
-
state.removeResizeListeners?.();
|
|
319
|
-
state.removeResizeListeners = null;
|
|
320
|
-
}
|
|
321
|
-
function beginDragTracking(state, startPoint) {
|
|
322
|
-
const doc = getDocument();
|
|
323
|
-
if (!doc) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
stopResizing(state);
|
|
327
|
-
stopDragging(state);
|
|
328
|
-
state.dragMoved = false;
|
|
329
|
-
state.dragStartPoint = startPoint;
|
|
330
|
-
state.lastDragPoint = startPoint;
|
|
331
|
-
const handlePointerMove = (nextPoint) => {
|
|
332
|
-
if (!state.dragStartPoint || !state.lastDragPoint || !nextPoint) {
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if (!state.isDragging) {
|
|
336
|
-
const deltaFromStartX = nextPoint.x - state.dragStartPoint.x;
|
|
337
|
-
const deltaFromStartY = nextPoint.y - state.dragStartPoint.y;
|
|
338
|
-
const distance = Math.sqrt(
|
|
339
|
-
deltaFromStartX * deltaFromStartX + deltaFromStartY * deltaFromStartY
|
|
340
|
-
);
|
|
341
|
-
if (distance < DRAG_THRESHOLD_PX) {
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
state.isDragging = true;
|
|
345
|
-
state.dragMoved = true;
|
|
346
|
-
if (state.ui) {
|
|
347
|
-
state.ui.panel.style.cursor = "grabbing";
|
|
348
|
-
state.ui.dragZone.style.cursor = "grabbing";
|
|
349
|
-
state.ui.toggleButton.style.cursor = "grabbing";
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const currentPosition = state.position ?? { x: OVERLAY_MARGIN, y: OVERLAY_MARGIN };
|
|
353
|
-
state.position = clampPosition(
|
|
354
|
-
{
|
|
355
|
-
x: currentPosition.x + (nextPoint.x - state.lastDragPoint.x),
|
|
356
|
-
y: currentPosition.y + (nextPoint.y - state.lastDragPoint.y)
|
|
357
|
-
},
|
|
358
|
-
state
|
|
359
|
-
);
|
|
360
|
-
state.lastDragPoint = nextPoint;
|
|
361
|
-
applyOverlayPosition(state);
|
|
362
|
-
};
|
|
363
|
-
const handleMouseMove = (event) => {
|
|
364
|
-
handlePointerMove(getPointFromMouseEvent(event));
|
|
365
|
-
};
|
|
366
|
-
const handleTouchMove = (event) => {
|
|
367
|
-
handlePointerMove(getPointFromTouchEvent(event));
|
|
368
|
-
};
|
|
369
|
-
const handleMouseUp = () => {
|
|
370
|
-
stopDragging(state);
|
|
371
|
-
};
|
|
372
|
-
const handleTouchEnd = () => {
|
|
373
|
-
stopDragging(state);
|
|
374
|
-
};
|
|
375
|
-
doc.addEventListener("mousemove", handleMouseMove);
|
|
376
|
-
doc.addEventListener("mouseup", handleMouseUp);
|
|
377
|
-
doc.addEventListener("touchmove", handleTouchMove, { passive: true });
|
|
378
|
-
doc.addEventListener("touchend", handleTouchEnd);
|
|
379
|
-
doc.addEventListener("touchcancel", handleTouchEnd);
|
|
380
|
-
state.removeDragListeners = () => {
|
|
381
|
-
doc.removeEventListener("mousemove", handleMouseMove);
|
|
382
|
-
doc.removeEventListener("mouseup", handleMouseUp);
|
|
383
|
-
doc.removeEventListener("touchmove", handleTouchMove);
|
|
384
|
-
doc.removeEventListener("touchend", handleTouchEnd);
|
|
385
|
-
doc.removeEventListener("touchcancel", handleTouchEnd);
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
function startResizing(state, startPoint) {
|
|
389
|
-
const doc = getDocument();
|
|
390
|
-
if (!doc) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
stopDragging(state);
|
|
394
|
-
stopResizing(state);
|
|
395
|
-
state.isResizing = true;
|
|
396
|
-
state.resizeStartPoint = startPoint;
|
|
397
|
-
state.resizeStartSize = state.panelSize ?? clampPanelSize({ width: DEFAULT_EXPANDED_WIDTH, height: DEFAULT_EXPANDED_HEIGHT });
|
|
398
|
-
const handleResizeMove = (nextPoint) => {
|
|
399
|
-
if (!state.isResizing || !state.resizeStartPoint || !state.resizeStartSize || !nextPoint) {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
state.panelSize = clampPanelSize({
|
|
403
|
-
width: state.resizeStartSize.width + (nextPoint.x - state.resizeStartPoint.x),
|
|
404
|
-
height: state.resizeStartSize.height + (nextPoint.y - state.resizeStartPoint.y)
|
|
405
|
-
});
|
|
406
|
-
applyPanelSize(state);
|
|
407
|
-
applyOverlayPosition(state);
|
|
408
|
-
};
|
|
409
|
-
const handleMouseMove = (event) => {
|
|
410
|
-
handleResizeMove(getPointFromMouseEvent(event));
|
|
411
|
-
};
|
|
412
|
-
const handleTouchMove = (event) => {
|
|
413
|
-
handleResizeMove(getPointFromTouchEvent(event));
|
|
414
|
-
};
|
|
415
|
-
const handleFinish = () => {
|
|
416
|
-
stopResizing(state);
|
|
417
|
-
};
|
|
418
|
-
doc.addEventListener("mousemove", handleMouseMove);
|
|
419
|
-
doc.addEventListener("mouseup", handleFinish);
|
|
420
|
-
doc.addEventListener("touchmove", handleTouchMove, { passive: true });
|
|
421
|
-
doc.addEventListener("touchend", handleFinish);
|
|
422
|
-
doc.addEventListener("touchcancel", handleFinish);
|
|
423
|
-
state.removeResizeListeners = () => {
|
|
424
|
-
doc.removeEventListener("mousemove", handleMouseMove);
|
|
425
|
-
doc.removeEventListener("mouseup", handleFinish);
|
|
426
|
-
doc.removeEventListener("touchmove", handleTouchMove);
|
|
427
|
-
doc.removeEventListener("touchend", handleFinish);
|
|
428
|
-
doc.removeEventListener("touchcancel", handleFinish);
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
function isInBottomRightResizeZone(element, point) {
|
|
432
|
-
const rect = element.getBoundingClientRect();
|
|
433
|
-
return point.x >= rect.right - RESIZE_HOTSPOT_PX && point.x <= rect.right && point.y >= rect.bottom - RESIZE_HOTSPOT_PX && point.y <= rect.bottom;
|
|
434
|
-
}
|
|
435
|
-
function canStartDragFromTarget(target) {
|
|
436
|
-
if (!(target instanceof Element)) {
|
|
437
|
-
return true;
|
|
438
|
-
}
|
|
439
|
-
if (target.closest("button") || target.closest("a") || target.closest("input") || target.closest("textarea") || target.closest("select")) {
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
return true;
|
|
443
|
-
}
|
|
444
|
-
function isInTopDragZone(element, point, zoneHeight) {
|
|
445
|
-
const rect = element.getBoundingClientRect();
|
|
446
|
-
return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.top + zoneHeight;
|
|
447
|
-
}
|
|
448
|
-
function attachDragStartListeners(element, state, options = {}) {
|
|
449
|
-
element.addEventListener("mousedown", (event) => {
|
|
450
|
-
if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
const point = getPointFromMouseEvent(event);
|
|
454
|
-
if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
beginDragTracking(state, point);
|
|
458
|
-
});
|
|
459
|
-
element.addEventListener("touchstart", (event) => {
|
|
460
|
-
if (!options.allowInteractiveTarget && !canStartDragFromTarget(event.target)) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
const point = getPointFromTouchEvent(event);
|
|
464
|
-
if (!point) {
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
if (typeof options.topZoneHeight === "number" && !isInTopDragZone(element, point, options.topZoneHeight)) {
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
beginDragTracking(state, point);
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
function attachCollapsedToggleListeners(element, state) {
|
|
474
|
-
const startToggleInteraction = (startPoint) => {
|
|
475
|
-
const doc = getDocument();
|
|
476
|
-
if (!doc) {
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
beginDragTracking(state, startPoint);
|
|
480
|
-
const finishInteraction = () => {
|
|
481
|
-
releaseListeners();
|
|
482
|
-
if (state.dragMoved || Date.now() < state.suppressToggleClickUntil) {
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
state.expanded = true;
|
|
486
|
-
state.unreadCount = 0;
|
|
487
|
-
renderOverlay(state);
|
|
488
|
-
};
|
|
489
|
-
const releaseListeners = () => {
|
|
490
|
-
doc.removeEventListener("mouseup", finishInteraction);
|
|
491
|
-
doc.removeEventListener("touchend", finishInteraction);
|
|
492
|
-
doc.removeEventListener("touchcancel", finishInteraction);
|
|
493
|
-
};
|
|
494
|
-
doc.addEventListener("mouseup", finishInteraction);
|
|
495
|
-
doc.addEventListener("touchend", finishInteraction);
|
|
496
|
-
doc.addEventListener("touchcancel", finishInteraction);
|
|
497
|
-
};
|
|
498
|
-
element.addEventListener("mousedown", (event) => {
|
|
499
|
-
event.preventDefault();
|
|
500
|
-
startToggleInteraction(getPointFromMouseEvent(event));
|
|
501
|
-
});
|
|
502
|
-
element.addEventListener("touchstart", (event) => {
|
|
503
|
-
const point = getPointFromTouchEvent(event);
|
|
504
|
-
if (!point) {
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
event.preventDefault();
|
|
508
|
-
startToggleInteraction(point);
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
function attachPanelResizeListeners(element, state) {
|
|
512
|
-
element.addEventListener("mousedown", (event) => {
|
|
513
|
-
if (!state.expanded || !canStartDragFromTarget(event.target)) {
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
const point = getPointFromMouseEvent(event);
|
|
517
|
-
if (!isInBottomRightResizeZone(element, point)) {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
event.preventDefault();
|
|
521
|
-
event.stopPropagation();
|
|
522
|
-
startResizing(state, point);
|
|
523
|
-
});
|
|
524
|
-
element.addEventListener("touchstart", (event) => {
|
|
525
|
-
if (!state.expanded || !canStartDragFromTarget(event.target)) {
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
const point = getPointFromTouchEvent(event);
|
|
529
|
-
if (!point || !isInBottomRightResizeZone(element, point)) {
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
event.preventDefault();
|
|
533
|
-
event.stopPropagation();
|
|
534
|
-
startResizing(state, point);
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
function createOverlayUi(state) {
|
|
538
|
-
const root = document.createElement("div");
|
|
539
|
-
root.style.cssText = [
|
|
540
|
-
"position:fixed",
|
|
541
|
-
"left:12px",
|
|
542
|
-
"top:12px",
|
|
543
|
-
"z-index:2147483647",
|
|
544
|
-
"display:flex",
|
|
545
|
-
"flex-direction:column",
|
|
546
|
-
"align-items:stretch",
|
|
547
|
-
"gap:8px",
|
|
548
|
-
"width:min(565px, calc(100vw - 24px))",
|
|
549
|
-
"pointer-events:none"
|
|
550
|
-
].join(";");
|
|
551
|
-
const toggleButton = createButton("Logs");
|
|
552
|
-
toggleButton.style.pointerEvents = "auto";
|
|
553
|
-
toggleButton.style.alignSelf = "flex-end";
|
|
554
|
-
toggleButton.style.display = "inline-flex";
|
|
555
|
-
toggleButton.style.alignItems = "center";
|
|
556
|
-
toggleButton.style.justifyContent = "center";
|
|
557
|
-
toggleButton.style.minHeight = "40px";
|
|
558
|
-
toggleButton.style.minWidth = "76px";
|
|
559
|
-
toggleButton.style.padding = "8px 14px";
|
|
560
|
-
toggleButton.style.textAlign = "center";
|
|
561
|
-
toggleButton.style.border = "1px solid rgba(122, 212, 255, 0.22)";
|
|
562
|
-
toggleButton.style.background = "linear-gradient(180deg, rgba(13,31,54,0.98), rgba(8,19,37,0.98))";
|
|
563
|
-
toggleButton.style.boxShadow = "0 18px 40px rgba(4,12,24,0.34)";
|
|
564
|
-
toggleButton.style.cursor = "grab";
|
|
565
|
-
toggleButton.style.touchAction = "none";
|
|
566
|
-
const panel = document.createElement("div");
|
|
567
|
-
panel.style.cssText = [
|
|
568
|
-
"position:relative",
|
|
569
|
-
"display:flex",
|
|
570
|
-
"flex-direction:column",
|
|
571
|
-
"width:min(565px, calc(100vw - 24px))",
|
|
572
|
-
"max-height:min(48vh, 372px)",
|
|
573
|
-
"border-radius:18px",
|
|
574
|
-
"border:1px solid rgba(116,167,255,0.16)",
|
|
575
|
-
"background:linear-gradient(180deg, rgba(9,19,37,0.98), rgba(5,12,24,0.98))",
|
|
576
|
-
"box-shadow:0 28px 64px rgba(2,8,18,0.46)",
|
|
577
|
-
"backdrop-filter:blur(16px)",
|
|
578
|
-
"overflow:hidden",
|
|
579
|
-
"cursor:default",
|
|
580
|
-
"pointer-events:auto"
|
|
581
|
-
].join(";");
|
|
582
|
-
const dragZone = document.createElement("div");
|
|
583
|
-
dragZone.style.cssText = [
|
|
584
|
-
"position:absolute",
|
|
585
|
-
"top:0",
|
|
586
|
-
"left:0",
|
|
587
|
-
"right:0",
|
|
588
|
-
"height:" + String(TOP_DRAG_ZONE_PX) + "px",
|
|
589
|
-
"z-index:1",
|
|
590
|
-
"cursor:grab",
|
|
591
|
-
"background:transparent",
|
|
592
|
-
"pointer-events:auto"
|
|
593
|
-
].join(";");
|
|
594
|
-
const controls = document.createElement("div");
|
|
595
|
-
controls.style.cssText = [
|
|
596
|
-
"position:absolute",
|
|
597
|
-
"top:22px",
|
|
598
|
-
"right:22px",
|
|
599
|
-
"z-index:2",
|
|
600
|
-
"display:flex",
|
|
601
|
-
"align-items:center",
|
|
602
|
-
"gap:8px",
|
|
603
|
-
"pointer-events:auto"
|
|
604
|
-
].join(";");
|
|
605
|
-
const clearButton = createButton("Clear");
|
|
606
|
-
clearButton.style.background = "rgba(255,255,255,0.1)";
|
|
607
|
-
clearButton.style.border = "1px solid rgba(255,255,255,0.16)";
|
|
608
|
-
clearButton.style.color = "#eef6ff";
|
|
609
|
-
clearButton.style.minHeight = "30px";
|
|
610
|
-
clearButton.style.padding = "4px 9px";
|
|
611
|
-
clearButton.style.fontSize = "11px";
|
|
612
|
-
clearButton.style.backdropFilter = "blur(8px)";
|
|
613
|
-
const collapseButton = createButton("Hide");
|
|
614
|
-
collapseButton.style.background = "rgba(113, 171, 255, 0.12)";
|
|
615
|
-
collapseButton.style.border = "1px solid rgba(113, 171, 255, 0.2)";
|
|
616
|
-
collapseButton.style.color = "#d9ebff";
|
|
617
|
-
collapseButton.style.minHeight = "30px";
|
|
618
|
-
collapseButton.style.padding = "4px 9px";
|
|
619
|
-
collapseButton.style.fontSize = "11px";
|
|
620
|
-
collapseButton.style.backdropFilter = "blur(8px)";
|
|
621
|
-
const body = document.createElement("div");
|
|
622
|
-
body.style.cssText = [
|
|
623
|
-
"position:relative",
|
|
624
|
-
"display:flex",
|
|
625
|
-
"flex-direction:column",
|
|
626
|
-
"padding:12px",
|
|
627
|
-
"background:linear-gradient(180deg, rgba(4,10,20,0.88), rgba(3,8,18,0.98))",
|
|
628
|
-
"flex:1 1 auto",
|
|
629
|
-
"min-height:0"
|
|
630
|
-
].join(";");
|
|
631
|
-
const entries = document.createElement("div");
|
|
632
|
-
entries.style.cssText = [
|
|
633
|
-
"display:flex",
|
|
634
|
-
"flex-direction:column",
|
|
635
|
-
"gap:0",
|
|
636
|
-
"overflow:auto",
|
|
637
|
-
"flex:1 1 auto",
|
|
638
|
-
"min-height:96px",
|
|
639
|
-
"max-height:min(36vh, 280px)",
|
|
640
|
-
"padding:0",
|
|
641
|
-
"border:1px solid rgba(115,153,212,0.14)",
|
|
642
|
-
"border-radius:12px",
|
|
643
|
-
"background:rgba(4,10,20,0.82)"
|
|
644
|
-
].join(";");
|
|
645
|
-
const emptyState = document.createElement("div");
|
|
646
|
-
emptyState.style.cssText = [
|
|
647
|
-
"display:flex",
|
|
648
|
-
"align-items:center",
|
|
649
|
-
"justify-content:center",
|
|
650
|
-
"flex:1 1 auto",
|
|
651
|
-
"min-height:96px",
|
|
652
|
-
"color:rgba(204,222,250,0.6)",
|
|
653
|
-
"font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
654
|
-
"text-align:center",
|
|
655
|
-
"padding:18px"
|
|
656
|
-
].join(";");
|
|
657
|
-
emptyState.textContent = "Console output will appear here.";
|
|
658
|
-
collapseButton.addEventListener("click", (event) => {
|
|
659
|
-
event.stopPropagation();
|
|
660
|
-
state.expanded = false;
|
|
661
|
-
renderOverlay(state);
|
|
662
|
-
});
|
|
663
|
-
clearButton.addEventListener("click", (event) => {
|
|
664
|
-
event.stopPropagation();
|
|
665
|
-
state.entries = [];
|
|
666
|
-
state.unreadCount = 0;
|
|
667
|
-
renderOverlay(state);
|
|
668
|
-
});
|
|
669
|
-
controls.appendChild(clearButton);
|
|
670
|
-
controls.appendChild(collapseButton);
|
|
671
|
-
entries.appendChild(emptyState);
|
|
672
|
-
body.appendChild(entries);
|
|
673
|
-
body.appendChild(controls);
|
|
674
|
-
panel.appendChild(dragZone);
|
|
675
|
-
panel.appendChild(body);
|
|
676
|
-
attachDragStartListeners(dragZone, state);
|
|
677
|
-
attachPanelResizeListeners(panel, state);
|
|
678
|
-
attachCollapsedToggleListeners(toggleButton, state);
|
|
679
|
-
root.appendChild(panel);
|
|
680
|
-
root.appendChild(toggleButton);
|
|
681
|
-
return {
|
|
682
|
-
body,
|
|
683
|
-
clearButton,
|
|
684
|
-
collapseButton,
|
|
685
|
-
controls,
|
|
686
|
-
dragZone,
|
|
687
|
-
emptyState,
|
|
688
|
-
entries,
|
|
689
|
-
panel,
|
|
690
|
-
root,
|
|
691
|
-
toggleButton
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
function renderOverlay(state) {
|
|
695
|
-
if (!state.ui) {
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
state.ui.panel.style.display = state.expanded ? "flex" : "none";
|
|
699
|
-
state.ui.toggleButton.style.display = state.expanded ? "none" : "inline-flex";
|
|
700
|
-
state.ui.toggleButton.textContent = "Logs";
|
|
701
|
-
if (state.entries.length === 0) {
|
|
702
|
-
state.ui.entries.style.display = "flex";
|
|
703
|
-
state.ui.emptyState.style.display = "flex";
|
|
704
|
-
state.ui.entries.replaceChildren(state.ui.emptyState);
|
|
705
|
-
applyPanelSize(state);
|
|
706
|
-
applyOverlayPosition(state);
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
state.ui.entries.style.display = "flex";
|
|
710
|
-
state.ui.emptyState.style.display = "none";
|
|
711
|
-
const nextChildren = state.entries.map((entry) => {
|
|
712
|
-
const accent = getLevelAccent(entry.level);
|
|
713
|
-
const row = document.createElement("div");
|
|
714
|
-
row.style.cssText = [
|
|
715
|
-
"display:flex",
|
|
716
|
-
"align-items:flex-start",
|
|
717
|
-
"gap:0",
|
|
718
|
-
"padding:4px 12px",
|
|
719
|
-
"background:" + accent.lineBackground
|
|
720
|
-
].join(";");
|
|
721
|
-
const line = document.createElement("div");
|
|
722
|
-
line.textContent = formatTimestamp(entry.timestamp) + " " + entry.level.toUpperCase() + " " + entry.message;
|
|
723
|
-
line.style.cssText = [
|
|
724
|
-
"color:#ecf4ff",
|
|
725
|
-
"font:500 12px/1.5 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
726
|
-
"white-space:pre-wrap",
|
|
727
|
-
"word-break:break-word",
|
|
728
|
-
"flex:1 1 auto"
|
|
729
|
-
].join(";");
|
|
730
|
-
row.appendChild(line);
|
|
731
|
-
return row;
|
|
732
|
-
});
|
|
733
|
-
state.ui.entries.replaceChildren(...nextChildren);
|
|
734
|
-
state.ui.entries.scrollTop = state.ui.entries.scrollHeight;
|
|
735
|
-
applyPanelSize(state);
|
|
736
|
-
applyOverlayPosition(state);
|
|
737
|
-
}
|
|
738
|
-
function mountOverlay(state) {
|
|
739
|
-
const doc = getDocument();
|
|
740
|
-
if (!doc?.body || state.ui) {
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
state.ui = createOverlayUi(state);
|
|
744
|
-
doc.body.appendChild(state.ui.root);
|
|
745
|
-
applyPanelSize(state);
|
|
746
|
-
applyOverlayPosition(state);
|
|
747
|
-
renderOverlay(state);
|
|
748
|
-
}
|
|
749
|
-
function enqueueEntry(state, level, args) {
|
|
750
|
-
state.entries.push(createEntry(level, args, state.nextEntryId));
|
|
751
|
-
state.nextEntryId += 1;
|
|
752
|
-
if (state.entries.length > state.maxEntries) {
|
|
753
|
-
state.entries.splice(0, state.entries.length - state.maxEntries);
|
|
754
|
-
}
|
|
755
|
-
if (!state.expanded) {
|
|
756
|
-
state.unreadCount += 1;
|
|
757
|
-
}
|
|
758
|
-
renderOverlay(state);
|
|
759
|
-
}
|
|
760
|
-
function restoreConsole(snapshot) {
|
|
761
|
-
for (const method of CONSOLE_METHODS) {
|
|
762
|
-
console[method] = snapshot[method];
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
function patchConsole(state) {
|
|
766
|
-
for (const method of CONSOLE_METHODS) {
|
|
767
|
-
const original = state.originalConsole[method];
|
|
768
|
-
console[method] = (...args) => {
|
|
769
|
-
enqueueEntry(state, method, args);
|
|
770
|
-
original(...args);
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
function cleanupOverlay(browserWindow, state) {
|
|
775
|
-
restoreConsole(state.originalConsole);
|
|
776
|
-
stopResizing(state);
|
|
777
|
-
stopDragging(state);
|
|
778
|
-
if (state.domReadyHandler) {
|
|
779
|
-
getDocument()?.removeEventListener("DOMContentLoaded", state.domReadyHandler);
|
|
780
|
-
state.domReadyHandler = void 0;
|
|
781
|
-
}
|
|
782
|
-
if (state.resizeHandler && typeof browserWindow.removeEventListener === "function") {
|
|
783
|
-
browserWindow.removeEventListener("resize", state.resizeHandler);
|
|
784
|
-
state.resizeHandler = void 0;
|
|
785
|
-
}
|
|
786
|
-
state.ui?.root.remove();
|
|
787
|
-
state.ui = null;
|
|
788
|
-
delete browserWindow.__oasizLogOverlayController__;
|
|
789
|
-
delete browserWindow.__oasizLogOverlayState__;
|
|
790
|
-
}
|
|
791
|
-
function createController(browserWindow, state) {
|
|
792
|
-
return {
|
|
793
|
-
retain() {
|
|
794
|
-
state.refCount += 1;
|
|
795
|
-
this.ensureMounted();
|
|
796
|
-
},
|
|
797
|
-
ensureMounted() {
|
|
798
|
-
const doc = getDocument();
|
|
799
|
-
if (!doc) {
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
if (doc.body) {
|
|
803
|
-
mountOverlay(state);
|
|
804
|
-
applyOverlayPosition(state);
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
if (!state.domReadyHandler) {
|
|
808
|
-
state.domReadyHandler = () => {
|
|
809
|
-
mountOverlay(state);
|
|
810
|
-
state.domReadyHandler = void 0;
|
|
811
|
-
};
|
|
812
|
-
doc.addEventListener("DOMContentLoaded", state.domReadyHandler, {
|
|
813
|
-
once: true
|
|
814
|
-
});
|
|
815
|
-
}
|
|
816
|
-
},
|
|
817
|
-
clear() {
|
|
818
|
-
state.entries = [];
|
|
819
|
-
state.unreadCount = 0;
|
|
820
|
-
renderOverlay(state);
|
|
821
|
-
},
|
|
822
|
-
show() {
|
|
823
|
-
state.expanded = true;
|
|
824
|
-
state.unreadCount = 0;
|
|
825
|
-
renderOverlay(state);
|
|
826
|
-
},
|
|
827
|
-
hide() {
|
|
828
|
-
state.expanded = false;
|
|
829
|
-
renderOverlay(state);
|
|
830
|
-
},
|
|
831
|
-
isVisible() {
|
|
832
|
-
return state.expanded;
|
|
833
|
-
},
|
|
834
|
-
destroy() {
|
|
835
|
-
state.refCount = Math.max(0, state.refCount - 1);
|
|
836
|
-
if (state.refCount === 0) {
|
|
837
|
-
cleanupOverlay(browserWindow, state);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
function enableLogOverlay(options = {}) {
|
|
843
|
-
if (options.enabled === false) {
|
|
844
|
-
return NOOP_HANDLE;
|
|
845
|
-
}
|
|
846
|
-
const browserWindow = getBrowserWindow();
|
|
847
|
-
const doc = getDocument();
|
|
848
|
-
if (!browserWindow || !doc) {
|
|
849
|
-
return NOOP_HANDLE;
|
|
850
|
-
}
|
|
851
|
-
const existingController = browserWindow.__oasizLogOverlayController__;
|
|
852
|
-
const existingState = browserWindow.__oasizLogOverlayState__;
|
|
853
|
-
if (existingController && existingState) {
|
|
854
|
-
existingController.retain();
|
|
855
|
-
if (typeof options.maxEntries === "number") {
|
|
856
|
-
existingState.maxEntries = clampMaxEntries(options.maxEntries);
|
|
857
|
-
if (existingState.entries.length > existingState.maxEntries) {
|
|
858
|
-
existingState.entries.splice(
|
|
859
|
-
0,
|
|
860
|
-
existingState.entries.length - existingState.maxEntries
|
|
861
|
-
);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
if (typeof options.collapsed === "boolean") {
|
|
865
|
-
existingState.expanded = !options.collapsed;
|
|
866
|
-
applyPanelSize(existingState);
|
|
867
|
-
if (existingState.expanded) {
|
|
868
|
-
existingState.unreadCount = 0;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
if (typeof options.title === "string" && options.title.trim().length > 0) {
|
|
872
|
-
existingState.title = options.title.trim();
|
|
873
|
-
}
|
|
874
|
-
existingController.ensureMounted();
|
|
875
|
-
renderOverlay(existingState);
|
|
876
|
-
return existingController;
|
|
877
|
-
}
|
|
878
|
-
const state = {
|
|
879
|
-
dragMoved: false,
|
|
880
|
-
dragStartPoint: null,
|
|
881
|
-
entries: [],
|
|
882
|
-
expanded: options.collapsed !== true,
|
|
883
|
-
isDragging: false,
|
|
884
|
-
isResizing: false,
|
|
885
|
-
lastDragPoint: null,
|
|
886
|
-
maxEntries: clampMaxEntries(options.maxEntries),
|
|
887
|
-
nextEntryId: 1,
|
|
888
|
-
originalConsole: createConsoleSnapshot(),
|
|
889
|
-
panelSize: null,
|
|
890
|
-
position: null,
|
|
891
|
-
refCount: 1,
|
|
892
|
-
removeDragListeners: null,
|
|
893
|
-
removeResizeListeners: null,
|
|
894
|
-
resizeStartPoint: null,
|
|
895
|
-
resizeStartSize: null,
|
|
896
|
-
suppressToggleClickUntil: 0,
|
|
897
|
-
title: typeof options.title === "string" && options.title.trim().length > 0 ? options.title.trim() : DEFAULT_TITLE,
|
|
898
|
-
resizeHandler: void 0,
|
|
899
|
-
ui: null,
|
|
900
|
-
unreadCount: 0
|
|
901
|
-
};
|
|
902
|
-
const controller = createController(browserWindow, state);
|
|
903
|
-
browserWindow.__oasizLogOverlayState__ = state;
|
|
904
|
-
browserWindow.__oasizLogOverlayController__ = controller;
|
|
905
|
-
patchConsole(state);
|
|
906
|
-
if (typeof browserWindow.addEventListener === "function") {
|
|
907
|
-
state.resizeHandler = () => {
|
|
908
|
-
applyOverlayPosition(state);
|
|
909
|
-
};
|
|
910
|
-
browserWindow.addEventListener("resize", state.resizeHandler);
|
|
911
|
-
}
|
|
912
|
-
controller.ensureMounted();
|
|
913
|
-
return controller;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
25
|
// src/multiplayer.ts
|
|
917
26
|
function isDevelopment2() {
|
|
918
27
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
@@ -924,10 +33,10 @@ function getBridgeWindow2() {
|
|
|
924
33
|
}
|
|
925
34
|
return window;
|
|
926
35
|
}
|
|
927
|
-
function shareRoomCode(roomCode
|
|
36
|
+
function shareRoomCode(roomCode) {
|
|
928
37
|
const bridge = getBridgeWindow2();
|
|
929
38
|
if (typeof bridge?.shareRoomCode === "function") {
|
|
930
|
-
bridge.shareRoomCode(roomCode
|
|
39
|
+
bridge.shareRoomCode(roomCode);
|
|
931
40
|
return;
|
|
932
41
|
}
|
|
933
42
|
if (isDevelopment2()) {
|
|
@@ -936,18 +45,6 @@ function shareRoomCode(roomCode, options) {
|
|
|
936
45
|
);
|
|
937
46
|
}
|
|
938
47
|
}
|
|
939
|
-
function openInviteModal() {
|
|
940
|
-
const bridge = getBridgeWindow2();
|
|
941
|
-
if (typeof bridge?.openInviteModal === "function") {
|
|
942
|
-
bridge.openInviteModal();
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
if (isDevelopment2()) {
|
|
946
|
-
console.warn(
|
|
947
|
-
"[oasiz/sdk] openInviteModal bridge is unavailable. This is expected in local development."
|
|
948
|
-
);
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
48
|
function getGameId() {
|
|
952
49
|
const bridge = getBridgeWindow2();
|
|
953
50
|
return bridge?.__GAME_ID__;
|
|
@@ -1007,7 +104,7 @@ function emitScoreConfig(config) {
|
|
|
1007
104
|
warnMissingBridge("emitScoreConfig");
|
|
1008
105
|
}
|
|
1009
106
|
|
|
1010
|
-
// src/
|
|
107
|
+
// src/share.ts
|
|
1011
108
|
function isDevelopment4() {
|
|
1012
109
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1013
110
|
return nodeEnv !== "production";
|
|
@@ -1018,6 +115,69 @@ function getBridgeWindow4() {
|
|
|
1018
115
|
}
|
|
1019
116
|
return window;
|
|
1020
117
|
}
|
|
118
|
+
function warnMissingBridge2(methodName) {
|
|
119
|
+
if (isDevelopment4()) {
|
|
120
|
+
console.warn(
|
|
121
|
+
"[oasiz/sdk] " + methodName + " share bridge is unavailable. This is expected in local development."
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function isValidImageReference(image) {
|
|
126
|
+
if (/^data:image\/[a-zA-Z0-9.+-]+;base64,/.test(image)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const parsed = new URL(image);
|
|
131
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
132
|
+
} catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function validateRequest(options) {
|
|
137
|
+
const text = typeof options.text === "string" ? options.text.trim() : "";
|
|
138
|
+
const hasText = text.length > 0;
|
|
139
|
+
const hasScore = options.score !== void 0;
|
|
140
|
+
const hasImage = typeof options.image === "string" && options.image.length > 0;
|
|
141
|
+
if (!hasText && !hasScore && !hasImage) {
|
|
142
|
+
throw new Error("Share request requires text, score, or image.");
|
|
143
|
+
}
|
|
144
|
+
if (hasScore) {
|
|
145
|
+
if (typeof options.score !== "number" || !Number.isInteger(options.score) || options.score < 0) {
|
|
146
|
+
throw new Error("Share score must be a non-negative integer.");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (hasImage && !isValidImageReference(options.image)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"Share image must be an http(s) URL or a data:image/... base64 string."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
...hasText ? { text } : {},
|
|
156
|
+
...hasScore ? { score: options.score } : {},
|
|
157
|
+
...hasImage ? { image: options.image } : {}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async function share(options) {
|
|
161
|
+
const request = validateRequest(options);
|
|
162
|
+
const bridge = getBridgeWindow4();
|
|
163
|
+
if (typeof bridge?.__oasizShareRequest !== "function") {
|
|
164
|
+
warnMissingBridge2("__oasizShareRequest");
|
|
165
|
+
throw new Error("Share bridge unavailable");
|
|
166
|
+
}
|
|
167
|
+
await bridge.__oasizShareRequest(request);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/state.ts
|
|
171
|
+
function isDevelopment5() {
|
|
172
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
173
|
+
return nodeEnv !== "production";
|
|
174
|
+
}
|
|
175
|
+
function getBridgeWindow5() {
|
|
176
|
+
if (typeof window === "undefined") {
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
return window;
|
|
180
|
+
}
|
|
1021
181
|
function isPlainObject(value) {
|
|
1022
182
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1023
183
|
return false;
|
|
@@ -1025,22 +185,22 @@ function isPlainObject(value) {
|
|
|
1025
185
|
const proto = Object.getPrototypeOf(value);
|
|
1026
186
|
return proto === Object.prototype || proto === null;
|
|
1027
187
|
}
|
|
1028
|
-
function
|
|
1029
|
-
if (
|
|
188
|
+
function warnMissingBridge3(methodName) {
|
|
189
|
+
if (isDevelopment5()) {
|
|
1030
190
|
console.warn(
|
|
1031
191
|
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
1032
192
|
);
|
|
1033
193
|
}
|
|
1034
194
|
}
|
|
1035
195
|
function loadGameState() {
|
|
1036
|
-
const bridge =
|
|
196
|
+
const bridge = getBridgeWindow5();
|
|
1037
197
|
if (typeof bridge?.loadGameState !== "function") {
|
|
1038
|
-
|
|
198
|
+
warnMissingBridge3("loadGameState");
|
|
1039
199
|
return {};
|
|
1040
200
|
}
|
|
1041
201
|
const state = bridge.loadGameState();
|
|
1042
202
|
if (!isPlainObject(state)) {
|
|
1043
|
-
if (
|
|
203
|
+
if (isDevelopment5()) {
|
|
1044
204
|
console.warn(
|
|
1045
205
|
"[oasiz/sdk] loadGameState returned invalid data. Falling back to empty object."
|
|
1046
206
|
);
|
|
@@ -1051,35 +211,35 @@ function loadGameState() {
|
|
|
1051
211
|
}
|
|
1052
212
|
function saveGameState(state) {
|
|
1053
213
|
if (!isPlainObject(state)) {
|
|
1054
|
-
if (
|
|
214
|
+
if (isDevelopment5()) {
|
|
1055
215
|
console.warn("[oasiz/sdk] saveGameState expected a plain object:", state);
|
|
1056
216
|
}
|
|
1057
217
|
return;
|
|
1058
218
|
}
|
|
1059
|
-
const bridge =
|
|
219
|
+
const bridge = getBridgeWindow5();
|
|
1060
220
|
if (typeof bridge?.saveGameState === "function") {
|
|
1061
221
|
bridge.saveGameState(state);
|
|
1062
222
|
return;
|
|
1063
223
|
}
|
|
1064
|
-
|
|
224
|
+
warnMissingBridge3("saveGameState");
|
|
1065
225
|
}
|
|
1066
226
|
function flushGameState() {
|
|
1067
|
-
const bridge =
|
|
227
|
+
const bridge = getBridgeWindow5();
|
|
1068
228
|
if (typeof bridge?.flushGameState === "function") {
|
|
1069
229
|
bridge.flushGameState();
|
|
1070
230
|
return;
|
|
1071
231
|
}
|
|
1072
|
-
|
|
232
|
+
warnMissingBridge3("flushGameState");
|
|
1073
233
|
}
|
|
1074
234
|
|
|
1075
235
|
// src/lifecycle.ts
|
|
1076
|
-
function
|
|
236
|
+
function isDevelopment6() {
|
|
1077
237
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1078
238
|
return nodeEnv !== "production";
|
|
1079
239
|
}
|
|
1080
240
|
function addLifecycleListener(eventName, callback) {
|
|
1081
241
|
if (typeof window === "undefined") {
|
|
1082
|
-
if (
|
|
242
|
+
if (isDevelopment6()) {
|
|
1083
243
|
console.warn(
|
|
1084
244
|
"[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
|
|
1085
245
|
);
|
|
@@ -1100,26 +260,34 @@ function onResume(callback) {
|
|
|
1100
260
|
|
|
1101
261
|
// src/navigation.ts
|
|
1102
262
|
var activeBackListeners = 0;
|
|
1103
|
-
function
|
|
263
|
+
function isDevelopment7() {
|
|
1104
264
|
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
1105
265
|
return nodeEnv !== "production";
|
|
1106
266
|
}
|
|
1107
|
-
function
|
|
267
|
+
function getBridgeWindow6() {
|
|
1108
268
|
if (typeof window === "undefined") {
|
|
1109
269
|
return void 0;
|
|
1110
270
|
}
|
|
1111
271
|
return window;
|
|
1112
272
|
}
|
|
1113
|
-
function
|
|
1114
|
-
if (
|
|
273
|
+
function warnMissingBridge4(methodName) {
|
|
274
|
+
if (isDevelopment7()) {
|
|
1115
275
|
console.warn(
|
|
1116
276
|
"[oasiz/sdk] " + methodName + " bridge is unavailable. This is expected in local development."
|
|
1117
277
|
);
|
|
1118
278
|
}
|
|
1119
279
|
}
|
|
280
|
+
function normalizeNavigationError(error) {
|
|
281
|
+
if (error instanceof Error) {
|
|
282
|
+
return error;
|
|
283
|
+
}
|
|
284
|
+
return new Error(
|
|
285
|
+
typeof error === "string" ? error : "Back button callback failed."
|
|
286
|
+
);
|
|
287
|
+
}
|
|
1120
288
|
function addNavigationListener(eventName, callback) {
|
|
1121
289
|
if (typeof window === "undefined") {
|
|
1122
|
-
if (
|
|
290
|
+
if (isDevelopment7()) {
|
|
1123
291
|
console.warn(
|
|
1124
292
|
"[oasiz/sdk] " + eventName + " listener registered without a browser window. This is expected in local development."
|
|
1125
293
|
);
|
|
@@ -1132,25 +300,32 @@ function addNavigationListener(eventName, callback) {
|
|
|
1132
300
|
return () => window.removeEventListener(eventName, handler);
|
|
1133
301
|
}
|
|
1134
302
|
function onBackButton(callback) {
|
|
1135
|
-
const off = addNavigationListener("oasiz:back",
|
|
1136
|
-
|
|
303
|
+
const off = addNavigationListener("oasiz:back", () => {
|
|
304
|
+
try {
|
|
305
|
+
callback();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
leaveGame();
|
|
308
|
+
throw normalizeNavigationError(error);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
const bridge = getBridgeWindow6();
|
|
1137
312
|
activeBackListeners += 1;
|
|
1138
313
|
if (activeBackListeners === 1) {
|
|
1139
314
|
if (typeof bridge?.__oasizSetBackOverride === "function") {
|
|
1140
315
|
bridge.__oasizSetBackOverride(true);
|
|
1141
316
|
} else {
|
|
1142
|
-
|
|
317
|
+
warnMissingBridge4("__oasizSetBackOverride");
|
|
1143
318
|
}
|
|
1144
319
|
}
|
|
1145
320
|
return () => {
|
|
1146
321
|
off();
|
|
1147
322
|
activeBackListeners = Math.max(0, activeBackListeners - 1);
|
|
1148
323
|
if (activeBackListeners === 0) {
|
|
1149
|
-
const currentBridge =
|
|
324
|
+
const currentBridge = getBridgeWindow6();
|
|
1150
325
|
if (typeof currentBridge?.__oasizSetBackOverride === "function") {
|
|
1151
326
|
currentBridge.__oasizSetBackOverride(false);
|
|
1152
327
|
} else {
|
|
1153
|
-
|
|
328
|
+
warnMissingBridge4("__oasizSetBackOverride");
|
|
1154
329
|
}
|
|
1155
330
|
}
|
|
1156
331
|
};
|
|
@@ -1159,30 +334,136 @@ function onLeaveGame(callback) {
|
|
|
1159
334
|
return addNavigationListener("oasiz:leave", callback);
|
|
1160
335
|
}
|
|
1161
336
|
function leaveGame() {
|
|
1162
|
-
const bridge =
|
|
337
|
+
const bridge = getBridgeWindow6();
|
|
1163
338
|
if (typeof bridge?.__oasizLeaveGame === "function") {
|
|
1164
339
|
bridge.__oasizLeaveGame();
|
|
1165
340
|
return;
|
|
1166
341
|
}
|
|
1167
|
-
|
|
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
|
+
});
|
|
1168
438
|
}
|
|
1169
439
|
|
|
1170
440
|
// src/index.ts
|
|
1171
441
|
var oasiz = {
|
|
1172
442
|
submitScore,
|
|
1173
443
|
emitScoreConfig,
|
|
444
|
+
share,
|
|
1174
445
|
triggerHaptic,
|
|
1175
|
-
enableLogOverlay,
|
|
1176
446
|
loadGameState,
|
|
1177
447
|
saveGameState,
|
|
1178
448
|
flushGameState,
|
|
1179
449
|
shareRoomCode,
|
|
1180
|
-
openInviteModal,
|
|
1181
450
|
onPause,
|
|
1182
451
|
onResume,
|
|
1183
452
|
onBackButton,
|
|
1184
453
|
onLeaveGame,
|
|
1185
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
|
+
},
|
|
1186
467
|
get gameId() {
|
|
1187
468
|
return getGameId();
|
|
1188
469
|
},
|
|
@@ -1197,23 +478,32 @@ var oasiz = {
|
|
|
1197
478
|
}
|
|
1198
479
|
};
|
|
1199
480
|
export {
|
|
481
|
+
consume,
|
|
1200
482
|
emitScoreConfig,
|
|
1201
|
-
enableLogOverlay,
|
|
1202
483
|
flushGameState,
|
|
484
|
+
getEntitlements,
|
|
1203
485
|
getGameId,
|
|
486
|
+
getJemBalance,
|
|
1204
487
|
getPlayerAvatar,
|
|
1205
488
|
getPlayerName,
|
|
489
|
+
getProducts,
|
|
490
|
+
getQuantity,
|
|
1206
491
|
getRoomCode,
|
|
492
|
+
hasEntitlement,
|
|
1207
493
|
leaveGame,
|
|
1208
494
|
loadGameState,
|
|
1209
495
|
oasiz,
|
|
1210
496
|
onBackButton,
|
|
497
|
+
onEntitlementsChanged,
|
|
498
|
+
onJemBalanceChanged,
|
|
1211
499
|
onLeaveGame,
|
|
1212
500
|
onPause,
|
|
1213
501
|
onResume,
|
|
1214
|
-
|
|
502
|
+
purchase,
|
|
1215
503
|
saveGameState,
|
|
504
|
+
share,
|
|
1216
505
|
shareRoomCode,
|
|
1217
506
|
submitScore,
|
|
507
|
+
syncProducts,
|
|
1218
508
|
triggerHaptic
|
|
1219
509
|
};
|