@ait-co/devtools 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +430 -134
- package/dist/chunk-6PPZTREF.js +569 -0
- package/dist/chunk-6PPZTREF.js.map +1 -0
- package/dist/mock/index.d.ts +22 -6
- package/dist/mock/index.js +87 -272
- package/dist/mock/index.js.map +1 -1
- package/dist/panel/index.d.ts +1 -1
- package/dist/panel/index.js +588 -32
- package/dist/panel/index.js.map +1 -1
- package/dist/unplugin/index.cjs +13 -12
- package/dist/unplugin/index.cjs.map +1 -1
- package/dist/unplugin/index.d.cts +51 -0
- package/dist/unplugin/index.d.ts +51 -0
- package/dist/unplugin/index.js +13 -12
- package/dist/unplugin/index.js.map +1 -1
- package/package.json +19 -10
- package/dist/chunk-YYIIG3JT.js +0 -146
- package/dist/chunk-YYIIG3JT.js.map +0 -1
package/dist/panel/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
aitState
|
|
3
|
-
|
|
2
|
+
aitState,
|
|
3
|
+
getDefaultPlaceholderImages
|
|
4
|
+
} from "../chunk-6PPZTREF.js";
|
|
4
5
|
|
|
5
6
|
// src/panel/styles.ts
|
|
7
|
+
var PANEL_WIDTH = 360;
|
|
8
|
+
var PANEL_HEIGHT = 480;
|
|
6
9
|
var PANEL_STYLES = (
|
|
7
10
|
/* css */
|
|
8
11
|
`
|
|
9
12
|
.ait-panel-toggle {
|
|
10
13
|
position: fixed;
|
|
11
|
-
bottom: 16px;
|
|
12
|
-
right: 16px;
|
|
13
14
|
z-index: 99999;
|
|
14
15
|
width: 48px;
|
|
15
16
|
height: 48px;
|
|
@@ -25,18 +26,18 @@ var PANEL_STYLES = (
|
|
|
25
26
|
color: white;
|
|
26
27
|
transition: transform 0.15s;
|
|
27
28
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
29
|
+
touch-action: none;
|
|
30
|
+
user-select: none;
|
|
28
31
|
}
|
|
29
|
-
.ait-panel-toggle:hover {
|
|
32
|
+
.ait-panel-toggle:hover:not(.dragging) {
|
|
30
33
|
transform: scale(1.1);
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
.ait-panel {
|
|
34
37
|
position: fixed;
|
|
35
|
-
bottom: 72px;
|
|
36
|
-
right: 16px;
|
|
37
38
|
z-index: 99998;
|
|
38
|
-
width:
|
|
39
|
-
|
|
39
|
+
width: ${PANEL_WIDTH}px;
|
|
40
|
+
height: ${PANEL_HEIGHT}px;
|
|
40
41
|
background: #1a1a2e;
|
|
41
42
|
border-radius: 12px;
|
|
42
43
|
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
@@ -61,7 +62,7 @@ var PANEL_STYLES = (
|
|
|
61
62
|
align-items: center;
|
|
62
63
|
border-bottom: 1px solid #2a2a4a;
|
|
63
64
|
}
|
|
64
|
-
.ait-panel-header span {
|
|
65
|
+
.ait-panel-header > span:first-child {
|
|
65
66
|
color: #3182F6;
|
|
66
67
|
}
|
|
67
68
|
|
|
@@ -98,8 +99,8 @@ var PANEL_STYLES = (
|
|
|
98
99
|
.ait-panel-body {
|
|
99
100
|
padding: 12px 16px;
|
|
100
101
|
overflow-y: auto;
|
|
101
|
-
max-height: 400px;
|
|
102
102
|
flex: 1;
|
|
103
|
+
min-height: 0;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
.ait-section {
|
|
@@ -205,6 +206,176 @@ var PANEL_STYLES = (
|
|
|
205
206
|
flex: 1;
|
|
206
207
|
word-break: break-all;
|
|
207
208
|
}
|
|
209
|
+
|
|
210
|
+
/* Device tab */
|
|
211
|
+
.ait-image-grid {
|
|
212
|
+
display: flex;
|
|
213
|
+
flex-wrap: wrap;
|
|
214
|
+
gap: 8px;
|
|
215
|
+
margin-top: 8px;
|
|
216
|
+
}
|
|
217
|
+
.ait-image-thumb {
|
|
218
|
+
position: relative;
|
|
219
|
+
width: 64px;
|
|
220
|
+
height: 64px;
|
|
221
|
+
border-radius: 4px;
|
|
222
|
+
overflow: hidden;
|
|
223
|
+
border: 1px solid #3a3a5a;
|
|
224
|
+
}
|
|
225
|
+
.ait-image-thumb img {
|
|
226
|
+
width: 100%;
|
|
227
|
+
height: 100%;
|
|
228
|
+
object-fit: cover;
|
|
229
|
+
}
|
|
230
|
+
.ait-image-thumb .ait-image-remove {
|
|
231
|
+
position: absolute;
|
|
232
|
+
top: 2px;
|
|
233
|
+
right: 2px;
|
|
234
|
+
width: 18px;
|
|
235
|
+
height: 18px;
|
|
236
|
+
border-radius: 50%;
|
|
237
|
+
background: rgba(231,76,60,0.9);
|
|
238
|
+
color: white;
|
|
239
|
+
border: none;
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
font-size: 10px;
|
|
242
|
+
line-height: 18px;
|
|
243
|
+
text-align: center;
|
|
244
|
+
padding: 0;
|
|
245
|
+
}
|
|
246
|
+
.ait-btn-row {
|
|
247
|
+
display: flex;
|
|
248
|
+
gap: 6px;
|
|
249
|
+
margin-top: 8px;
|
|
250
|
+
}
|
|
251
|
+
.ait-btn-secondary {
|
|
252
|
+
background: #2a2a4a;
|
|
253
|
+
color: #e0e0e0;
|
|
254
|
+
border: 1px solid #3a3a5a;
|
|
255
|
+
border-radius: 4px;
|
|
256
|
+
padding: 4px 8px;
|
|
257
|
+
font-size: 11px;
|
|
258
|
+
cursor: pointer;
|
|
259
|
+
font-family: inherit;
|
|
260
|
+
}
|
|
261
|
+
.ait-btn-secondary:hover {
|
|
262
|
+
background: #3a3a5a;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Prompt notification */
|
|
266
|
+
.ait-prompt-banner {
|
|
267
|
+
background: #2d1b69;
|
|
268
|
+
border: 1px solid #6c3bd5;
|
|
269
|
+
border-radius: 6px;
|
|
270
|
+
padding: 10px 12px;
|
|
271
|
+
margin-bottom: 12px;
|
|
272
|
+
}
|
|
273
|
+
.ait-prompt-banner .ait-prompt-title {
|
|
274
|
+
color: #b388ff;
|
|
275
|
+
font-size: 12px;
|
|
276
|
+
font-weight: 600;
|
|
277
|
+
margin-bottom: 8px;
|
|
278
|
+
}
|
|
279
|
+
.ait-prompt-input-row {
|
|
280
|
+
display: flex;
|
|
281
|
+
gap: 6px;
|
|
282
|
+
align-items: center;
|
|
283
|
+
margin-top: 6px;
|
|
284
|
+
}
|
|
285
|
+
.ait-prompt-input-row input {
|
|
286
|
+
background: #2a2a4a;
|
|
287
|
+
color: #e0e0e0;
|
|
288
|
+
border: 1px solid #3a3a5a;
|
|
289
|
+
border-radius: 4px;
|
|
290
|
+
padding: 4px 8px;
|
|
291
|
+
font-size: 12px;
|
|
292
|
+
width: 80px;
|
|
293
|
+
font-family: inherit;
|
|
294
|
+
}
|
|
295
|
+
.ait-prompt-input-row label {
|
|
296
|
+
color: #aaa;
|
|
297
|
+
font-size: 11px;
|
|
298
|
+
min-width: 30px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.ait-panel-close {
|
|
302
|
+
display: none;
|
|
303
|
+
background: none;
|
|
304
|
+
border: none;
|
|
305
|
+
color: #888;
|
|
306
|
+
font-size: 18px;
|
|
307
|
+
cursor: pointer;
|
|
308
|
+
padding: 0 4px;
|
|
309
|
+
font-family: inherit;
|
|
310
|
+
}
|
|
311
|
+
.ait-panel-close:hover {
|
|
312
|
+
color: #e0e0e0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* Disabled state for monitoring-only mode */
|
|
316
|
+
.ait-select:disabled,
|
|
317
|
+
.ait-input:disabled {
|
|
318
|
+
opacity: 0.5;
|
|
319
|
+
cursor: not-allowed;
|
|
320
|
+
}
|
|
321
|
+
.ait-btn:disabled,
|
|
322
|
+
.ait-btn-secondary:disabled {
|
|
323
|
+
opacity: 0.5;
|
|
324
|
+
cursor: not-allowed;
|
|
325
|
+
}
|
|
326
|
+
.ait-btn-danger:disabled {
|
|
327
|
+
background: #5a5a5a;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* Mock status badge */
|
|
331
|
+
.ait-mock-badge {
|
|
332
|
+
display: inline-block;
|
|
333
|
+
padding: 2px 8px;
|
|
334
|
+
border-radius: 10px;
|
|
335
|
+
font-size: 10px;
|
|
336
|
+
font-weight: 600;
|
|
337
|
+
letter-spacing: 0.3px;
|
|
338
|
+
cursor: pointer;
|
|
339
|
+
}
|
|
340
|
+
.ait-mock-badge-on {
|
|
341
|
+
background: #1a4731;
|
|
342
|
+
color: #4ade80;
|
|
343
|
+
}
|
|
344
|
+
.ait-mock-badge-off {
|
|
345
|
+
background: #4a1a1a;
|
|
346
|
+
color: #f87171;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Monitoring-only notice */
|
|
350
|
+
.ait-monitoring-notice {
|
|
351
|
+
background: #2a1a00;
|
|
352
|
+
border: 1px solid #6b4c00;
|
|
353
|
+
border-radius: 4px;
|
|
354
|
+
padding: 6px 10px;
|
|
355
|
+
margin-bottom: 12px;
|
|
356
|
+
font-size: 11px;
|
|
357
|
+
color: #fbbf24;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
@media (max-width: 480px) {
|
|
361
|
+
.ait-panel.open {
|
|
362
|
+
position: fixed;
|
|
363
|
+
top: 0;
|
|
364
|
+
left: 0;
|
|
365
|
+
right: 0;
|
|
366
|
+
bottom: 0;
|
|
367
|
+
width: 100%;
|
|
368
|
+
height: 100%;
|
|
369
|
+
max-height: 100%;
|
|
370
|
+
border-radius: 0;
|
|
371
|
+
}
|
|
372
|
+
.ait-panel-toggle {
|
|
373
|
+
z-index: 100000;
|
|
374
|
+
}
|
|
375
|
+
.ait-panel-close {
|
|
376
|
+
display: block;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
208
379
|
`
|
|
209
380
|
);
|
|
210
381
|
|
|
@@ -213,6 +384,7 @@ var TABS = [
|
|
|
213
384
|
{ id: "env", label: "Environment" },
|
|
214
385
|
{ id: "permissions", label: "Permissions" },
|
|
215
386
|
{ id: "location", label: "Location" },
|
|
387
|
+
{ id: "device", label: "Device" },
|
|
216
388
|
{ id: "iap", label: "IAP" },
|
|
217
389
|
{ id: "events", label: "Events" },
|
|
218
390
|
{ id: "analytics", label: "Analytics" },
|
|
@@ -231,8 +403,9 @@ function h(tag, attrs, ...children) {
|
|
|
231
403
|
}
|
|
232
404
|
return el;
|
|
233
405
|
}
|
|
234
|
-
function selectRow(label, options, value, onChange) {
|
|
406
|
+
function selectRow(label, options, value, onChange, disabled = false) {
|
|
235
407
|
const select = h("select", { className: "ait-select" });
|
|
408
|
+
if (disabled) select.disabled = true;
|
|
236
409
|
for (const opt of options) {
|
|
237
410
|
const option = h("option", { value: opt }, opt);
|
|
238
411
|
if (opt === value) option.selected = true;
|
|
@@ -241,45 +414,50 @@ function selectRow(label, options, value, onChange) {
|
|
|
241
414
|
select.addEventListener("change", () => onChange(select.value));
|
|
242
415
|
return h("div", { className: "ait-row" }, h("label", {}, label), select);
|
|
243
416
|
}
|
|
244
|
-
function inputRow(label, value, onChange) {
|
|
417
|
+
function inputRow(label, value, onChange, disabled = false) {
|
|
245
418
|
const input = h("input", { className: "ait-input", value });
|
|
419
|
+
if (disabled) input.disabled = true;
|
|
246
420
|
input.addEventListener("change", () => onChange(input.value));
|
|
247
421
|
return h("div", { className: "ait-row" }, h("label", {}, label), input);
|
|
248
422
|
}
|
|
249
423
|
function renderEnvTab() {
|
|
250
424
|
const s = aitState.state;
|
|
425
|
+
const disabled = !s.panelEditable;
|
|
251
426
|
const container = h("div");
|
|
427
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
252
428
|
container.append(
|
|
253
429
|
h(
|
|
254
430
|
"div",
|
|
255
431
|
{ className: "ait-section" },
|
|
256
432
|
h("div", { className: "ait-section-title" }, "Platform"),
|
|
257
|
-
selectRow("OS", ["ios", "android"], s.platform, (v) => aitState.update({ platform: v })),
|
|
258
|
-
inputRow("App Version", s.appVersion, (v) => aitState.update({ appVersion: v })),
|
|
259
|
-
selectRow("Environment", ["toss", "sandbox"], s.environment, (v) => aitState.update({ environment: v })),
|
|
260
|
-
inputRow("Locale", s.locale, (v) => aitState.update({ locale: v }))
|
|
433
|
+
selectRow("OS", ["ios", "android"], s.platform, (v) => aitState.update({ platform: v }), disabled),
|
|
434
|
+
inputRow("App Version", s.appVersion, (v) => aitState.update({ appVersion: v }), disabled),
|
|
435
|
+
selectRow("Environment", ["toss", "sandbox"], s.environment, (v) => aitState.update({ environment: v }), disabled),
|
|
436
|
+
inputRow("Locale", s.locale, (v) => aitState.update({ locale: v }), disabled)
|
|
261
437
|
),
|
|
262
438
|
h(
|
|
263
439
|
"div",
|
|
264
440
|
{ className: "ait-section" },
|
|
265
441
|
h("div", { className: "ait-section-title" }, "Network"),
|
|
266
|
-
selectRow("Status", ["WIFI", "4G", "5G", "3G", "2G", "OFFLINE", "WWAN", "UNKNOWN"], s.networkStatus, (v) => aitState.update({ networkStatus: v }))
|
|
442
|
+
selectRow("Status", ["WIFI", "4G", "5G", "3G", "2G", "OFFLINE", "WWAN", "UNKNOWN"], s.networkStatus, (v) => aitState.update({ networkStatus: v }), disabled)
|
|
267
443
|
),
|
|
268
444
|
h(
|
|
269
445
|
"div",
|
|
270
446
|
{ className: "ait-section" },
|
|
271
447
|
h("div", { className: "ait-section-title" }, "Safe Area Insets"),
|
|
272
|
-
inputRow("Top", String(s.safeAreaInsets.top), (v) => aitState.patch("safeAreaInsets", { top: Number(v) })),
|
|
273
|
-
inputRow("Bottom", String(s.safeAreaInsets.bottom), (v) => aitState.patch("safeAreaInsets", { bottom: Number(v) }))
|
|
448
|
+
inputRow("Top", String(s.safeAreaInsets.top), (v) => aitState.patch("safeAreaInsets", { top: Number(v) }), disabled),
|
|
449
|
+
inputRow("Bottom", String(s.safeAreaInsets.bottom), (v) => aitState.patch("safeAreaInsets", { bottom: Number(v) }), disabled)
|
|
274
450
|
)
|
|
275
451
|
);
|
|
276
452
|
return container;
|
|
277
453
|
}
|
|
278
454
|
function renderPermissionsTab() {
|
|
279
455
|
const s = aitState.state;
|
|
456
|
+
const disabled = !s.panelEditable;
|
|
280
457
|
const container = h("div");
|
|
281
458
|
const names = ["camera", "photos", "geolocation", "clipboard", "contacts", "microphone"];
|
|
282
459
|
const statuses = ["allowed", "denied", "notDetermined"];
|
|
460
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
283
461
|
container.append(
|
|
284
462
|
h(
|
|
285
463
|
"div",
|
|
@@ -288,7 +466,7 @@ function renderPermissionsTab() {
|
|
|
288
466
|
...names.map(
|
|
289
467
|
(name) => selectRow(name, statuses, s.permissions[name], (v) => {
|
|
290
468
|
aitState.patch("permissions", { [name]: v });
|
|
291
|
-
})
|
|
469
|
+
}, disabled)
|
|
292
470
|
)
|
|
293
471
|
)
|
|
294
472
|
);
|
|
@@ -296,7 +474,9 @@ function renderPermissionsTab() {
|
|
|
296
474
|
}
|
|
297
475
|
function renderLocationTab() {
|
|
298
476
|
const s = aitState.state;
|
|
477
|
+
const disabled = !s.panelEditable;
|
|
299
478
|
const container = h("div");
|
|
479
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
300
480
|
container.append(
|
|
301
481
|
h(
|
|
302
482
|
"div",
|
|
@@ -305,23 +485,25 @@ function renderLocationTab() {
|
|
|
305
485
|
inputRow("Latitude", String(s.location.coords.latitude), (v) => {
|
|
306
486
|
const coords = { ...s.location.coords, latitude: Number(v) };
|
|
307
487
|
aitState.patch("location", { coords });
|
|
308
|
-
}),
|
|
488
|
+
}, disabled),
|
|
309
489
|
inputRow("Longitude", String(s.location.coords.longitude), (v) => {
|
|
310
490
|
const coords = { ...s.location.coords, longitude: Number(v) };
|
|
311
491
|
aitState.patch("location", { coords });
|
|
312
|
-
}),
|
|
492
|
+
}, disabled),
|
|
313
493
|
inputRow("Accuracy", String(s.location.coords.accuracy), (v) => {
|
|
314
494
|
const coords = { ...s.location.coords, accuracy: Number(v) };
|
|
315
495
|
aitState.patch("location", { coords });
|
|
316
|
-
})
|
|
496
|
+
}, disabled)
|
|
317
497
|
)
|
|
318
498
|
);
|
|
319
499
|
return container;
|
|
320
500
|
}
|
|
321
501
|
function renderIapTab() {
|
|
322
502
|
const s = aitState.state;
|
|
503
|
+
const disabled = !s.panelEditable;
|
|
323
504
|
const container = h("div");
|
|
324
505
|
const results = ["success", "USER_CANCELED", "INVALID_PRODUCT_ID", "PAYMENT_PENDING", "NETWORK_ERROR", "ITEM_ALREADY_OWNED", "INTERNAL_ERROR"];
|
|
506
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
325
507
|
container.append(
|
|
326
508
|
h(
|
|
327
509
|
"div",
|
|
@@ -329,7 +511,7 @@ function renderIapTab() {
|
|
|
329
511
|
h("div", { className: "ait-section-title" }, "IAP Simulator"),
|
|
330
512
|
selectRow("Next Purchase Result", results, s.iap.nextResult, (v) => {
|
|
331
513
|
aitState.patch("iap", { nextResult: v });
|
|
332
|
-
})
|
|
514
|
+
}, disabled)
|
|
333
515
|
),
|
|
334
516
|
h(
|
|
335
517
|
"div",
|
|
@@ -337,7 +519,7 @@ function renderIapTab() {
|
|
|
337
519
|
h("div", { className: "ait-section-title" }, "TossPay"),
|
|
338
520
|
selectRow("Next Payment Result", ["success", "fail"], s.payment.nextResult, (v) => {
|
|
339
521
|
aitState.patch("payment", { nextResult: v });
|
|
340
|
-
})
|
|
522
|
+
}, disabled)
|
|
341
523
|
),
|
|
342
524
|
h(
|
|
343
525
|
"div",
|
|
@@ -356,11 +538,15 @@ function renderIapTab() {
|
|
|
356
538
|
return container;
|
|
357
539
|
}
|
|
358
540
|
function renderEventsTab() {
|
|
541
|
+
const disabled = !aitState.state.panelEditable;
|
|
359
542
|
const container = h("div");
|
|
543
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
360
544
|
const backBtn = h("button", { className: "ait-btn" }, "Trigger Back Event");
|
|
361
545
|
backBtn.addEventListener("click", () => aitState.trigger("backEvent"));
|
|
546
|
+
if (disabled) backBtn.disabled = true;
|
|
362
547
|
const homeBtn = h("button", { className: "ait-btn" }, "Trigger Home Event");
|
|
363
548
|
homeBtn.addEventListener("click", () => aitState.trigger("homeEvent"));
|
|
549
|
+
if (disabled) homeBtn.disabled = true;
|
|
364
550
|
container.append(
|
|
365
551
|
h(
|
|
366
552
|
"div",
|
|
@@ -374,18 +560,21 @@ function renderEventsTab() {
|
|
|
374
560
|
h("div", { className: "ait-section-title" }, "Login"),
|
|
375
561
|
selectRow("Logged In", ["true", "false"], String(aitState.state.auth.isLoggedIn), (v) => {
|
|
376
562
|
aitState.patch("auth", { isLoggedIn: v === "true" });
|
|
377
|
-
}),
|
|
563
|
+
}, disabled),
|
|
378
564
|
selectRow("Toss Login Integrated", ["true", "false"], String(aitState.state.auth.isTossLoginIntegrated), (v) => {
|
|
379
565
|
aitState.patch("auth", { isTossLoginIntegrated: v === "true" });
|
|
380
|
-
})
|
|
566
|
+
}, disabled)
|
|
381
567
|
)
|
|
382
568
|
);
|
|
383
569
|
return container;
|
|
384
570
|
}
|
|
385
571
|
function renderAnalyticsTab() {
|
|
572
|
+
const disabled = !aitState.state.panelEditable;
|
|
386
573
|
const container = h("div");
|
|
574
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
387
575
|
const logs = aitState.state.analyticsLog;
|
|
388
576
|
const clearBtn = h("button", { className: "ait-btn ait-btn-sm ait-btn-danger" }, "Clear");
|
|
577
|
+
if (disabled) clearBtn.disabled = true;
|
|
389
578
|
clearBtn.addEventListener("click", () => {
|
|
390
579
|
aitState.state.analyticsLog.length = 0;
|
|
391
580
|
refreshPanel();
|
|
@@ -415,7 +604,9 @@ function renderAnalyticsTab() {
|
|
|
415
604
|
return container;
|
|
416
605
|
}
|
|
417
606
|
function renderStorageTab() {
|
|
607
|
+
const disabled = !aitState.state.panelEditable;
|
|
418
608
|
const container = h("div");
|
|
609
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
419
610
|
const prefix = "__ait_storage:";
|
|
420
611
|
const entries = [];
|
|
421
612
|
for (let i = 0; i < localStorage.length; i++) {
|
|
@@ -425,6 +616,7 @@ function renderStorageTab() {
|
|
|
425
616
|
}
|
|
426
617
|
}
|
|
427
618
|
const clearBtn = h("button", { className: "ait-btn ait-btn-sm ait-btn-danger" }, "Clear All");
|
|
619
|
+
if (disabled) clearBtn.disabled = true;
|
|
428
620
|
clearBtn.addEventListener("click", () => {
|
|
429
621
|
entries.forEach(([key]) => localStorage.removeItem(prefix + key));
|
|
430
622
|
refreshPanel();
|
|
@@ -455,15 +647,341 @@ function renderStorageTab() {
|
|
|
455
647
|
);
|
|
456
648
|
return container;
|
|
457
649
|
}
|
|
650
|
+
var pendingPrompt = null;
|
|
651
|
+
if (typeof window !== "undefined") {
|
|
652
|
+
window.addEventListener("__ait:prompt-request", (e) => {
|
|
653
|
+
const detail = e.detail;
|
|
654
|
+
pendingPrompt = { type: detail.type };
|
|
655
|
+
currentTab = "device";
|
|
656
|
+
if (panelEl && !panelEl.classList.contains("open")) {
|
|
657
|
+
panelEl.classList.add("open");
|
|
658
|
+
}
|
|
659
|
+
refreshPanel();
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
function resolvePrompt(type, data) {
|
|
663
|
+
window.dispatchEvent(new CustomEvent("__ait:prompt-response:" + type, { detail: data }));
|
|
664
|
+
pendingPrompt = null;
|
|
665
|
+
refreshPanel();
|
|
666
|
+
}
|
|
667
|
+
function renderPromptBanner() {
|
|
668
|
+
if (!pendingPrompt) return null;
|
|
669
|
+
const banner = h("div", { className: "ait-prompt-banner" });
|
|
670
|
+
if (pendingPrompt.type === "camera") {
|
|
671
|
+
banner.append(
|
|
672
|
+
h("div", { className: "ait-prompt-title" }, "Camera Prompt \u2014 Select an image")
|
|
673
|
+
);
|
|
674
|
+
const input = h("input", { type: "file", accept: "image/*", style: "font-size:11px;color:#aaa" });
|
|
675
|
+
input.addEventListener("change", () => {
|
|
676
|
+
const file = input.files?.[0];
|
|
677
|
+
if (!file) return;
|
|
678
|
+
const reader = new FileReader();
|
|
679
|
+
reader.onload = () => resolvePrompt("camera", reader.result);
|
|
680
|
+
reader.readAsDataURL(file);
|
|
681
|
+
});
|
|
682
|
+
banner.appendChild(input);
|
|
683
|
+
} else if (pendingPrompt.type === "photos") {
|
|
684
|
+
banner.append(
|
|
685
|
+
h("div", { className: "ait-prompt-title" }, "Photos Prompt \u2014 Select images")
|
|
686
|
+
);
|
|
687
|
+
const input = h("input", { type: "file", accept: "image/*", multiple: "", style: "font-size:11px;color:#aaa" });
|
|
688
|
+
input.addEventListener("change", () => {
|
|
689
|
+
const files = Array.from(input.files ?? []);
|
|
690
|
+
if (files.length === 0) return;
|
|
691
|
+
Promise.all(files.map((file) => new Promise((res) => {
|
|
692
|
+
const reader = new FileReader();
|
|
693
|
+
reader.onload = () => res(reader.result);
|
|
694
|
+
reader.readAsDataURL(file);
|
|
695
|
+
}))).then((dataUris) => resolvePrompt("photos", dataUris));
|
|
696
|
+
});
|
|
697
|
+
banner.appendChild(input);
|
|
698
|
+
} else if (pendingPrompt.type === "location" || pendingPrompt.type === "location-update") {
|
|
699
|
+
banner.append(
|
|
700
|
+
h(
|
|
701
|
+
"div",
|
|
702
|
+
{ className: "ait-prompt-title" },
|
|
703
|
+
pendingPrompt.type === "location" ? "Location Prompt \u2014 Enter coordinates" : "Location Update \u2014 Send coordinates"
|
|
704
|
+
)
|
|
705
|
+
);
|
|
706
|
+
const latInput = h("input", { className: "ait-input", value: String(aitState.state.location.coords.latitude), style: "width:80px" });
|
|
707
|
+
const lngInput = h("input", { className: "ait-input", value: String(aitState.state.location.coords.longitude), style: "width:80px" });
|
|
708
|
+
const sendBtn = h("button", { className: "ait-btn ait-btn-sm" }, "Send");
|
|
709
|
+
sendBtn.addEventListener("click", () => {
|
|
710
|
+
const loc = {
|
|
711
|
+
coords: {
|
|
712
|
+
latitude: Number(latInput.value),
|
|
713
|
+
longitude: Number(lngInput.value),
|
|
714
|
+
altitude: 0,
|
|
715
|
+
accuracy: 10,
|
|
716
|
+
altitudeAccuracy: 0,
|
|
717
|
+
heading: 0
|
|
718
|
+
},
|
|
719
|
+
timestamp: Date.now(),
|
|
720
|
+
accessLocation: "FINE"
|
|
721
|
+
};
|
|
722
|
+
resolvePrompt(pendingPrompt.type, loc);
|
|
723
|
+
});
|
|
724
|
+
banner.append(
|
|
725
|
+
h(
|
|
726
|
+
"div",
|
|
727
|
+
{ className: "ait-prompt-input-row" },
|
|
728
|
+
h("label", {}, "Lat"),
|
|
729
|
+
latInput,
|
|
730
|
+
h("label", {}, "Lng"),
|
|
731
|
+
lngInput,
|
|
732
|
+
sendBtn
|
|
733
|
+
)
|
|
734
|
+
);
|
|
735
|
+
} else {
|
|
736
|
+
banner.append(
|
|
737
|
+
h("div", { className: "ait-prompt-title" }, `Prompt: ${pendingPrompt.type}`)
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
const cancelBtn = h("button", { className: "ait-btn ait-btn-sm ait-btn-danger", style: "margin-top:8px" }, "Cancel");
|
|
741
|
+
cancelBtn.addEventListener("click", () => {
|
|
742
|
+
pendingPrompt = null;
|
|
743
|
+
window.dispatchEvent(new CustomEvent("__ait:prompt-cancel"));
|
|
744
|
+
refreshPanel();
|
|
745
|
+
});
|
|
746
|
+
banner.appendChild(cancelBtn);
|
|
747
|
+
return banner;
|
|
748
|
+
}
|
|
749
|
+
function renderDeviceTab() {
|
|
750
|
+
const s = aitState.state;
|
|
751
|
+
const disabled = !s.panelEditable;
|
|
752
|
+
const container = h("div");
|
|
753
|
+
if (disabled) container.appendChild(monitoringNotice());
|
|
754
|
+
if (s.panelEditable) {
|
|
755
|
+
const promptBanner = renderPromptBanner();
|
|
756
|
+
if (promptBanner) container.appendChild(promptBanner);
|
|
757
|
+
}
|
|
758
|
+
const modeEntries = [
|
|
759
|
+
{ label: "Camera", key: "camera", options: ["mock", "web", "prompt"] },
|
|
760
|
+
{ label: "Photos", key: "photos", options: ["mock", "web", "prompt"] },
|
|
761
|
+
{ label: "Location", key: "location", options: ["mock", "web", "prompt"] },
|
|
762
|
+
{ label: "Network", key: "network", options: ["mock", "web"] },
|
|
763
|
+
{ label: "Clipboard", key: "clipboard", options: ["mock", "web"] }
|
|
764
|
+
];
|
|
765
|
+
container.append(
|
|
766
|
+
h(
|
|
767
|
+
"div",
|
|
768
|
+
{ className: "ait-section" },
|
|
769
|
+
h("div", { className: "ait-section-title" }, "Device API Modes"),
|
|
770
|
+
...modeEntries.map(
|
|
771
|
+
(entry) => selectRow(entry.label, entry.options, s.deviceModes[entry.key], (v) => {
|
|
772
|
+
aitState.patch("deviceModes", { [entry.key]: v });
|
|
773
|
+
refreshPanel();
|
|
774
|
+
}, disabled)
|
|
775
|
+
)
|
|
776
|
+
)
|
|
777
|
+
);
|
|
778
|
+
const images = s.mockData.images;
|
|
779
|
+
const imageGrid = h("div", { className: "ait-image-grid" });
|
|
780
|
+
images.forEach((dataUri, idx) => {
|
|
781
|
+
const thumb = h("div", { className: "ait-image-thumb" });
|
|
782
|
+
const img = h("img", { src: dataUri });
|
|
783
|
+
const removeBtn = h("button", { className: "ait-image-remove" }, "x");
|
|
784
|
+
removeBtn.addEventListener("click", () => {
|
|
785
|
+
const newImages = [...aitState.state.mockData.images];
|
|
786
|
+
newImages.splice(idx, 1);
|
|
787
|
+
aitState.patch("mockData", { images: newImages });
|
|
788
|
+
refreshPanel();
|
|
789
|
+
});
|
|
790
|
+
if (disabled) removeBtn.disabled = true;
|
|
791
|
+
thumb.append(img, removeBtn);
|
|
792
|
+
imageGrid.appendChild(thumb);
|
|
793
|
+
});
|
|
794
|
+
const addBtn = h("button", { className: "ait-btn-secondary" }, "+ Add");
|
|
795
|
+
addBtn.addEventListener("click", () => {
|
|
796
|
+
const input = document.createElement("input");
|
|
797
|
+
input.type = "file";
|
|
798
|
+
input.accept = "image/*";
|
|
799
|
+
input.multiple = true;
|
|
800
|
+
input.onchange = () => {
|
|
801
|
+
const files = Array.from(input.files ?? []);
|
|
802
|
+
Promise.all(files.map((file) => new Promise((res) => {
|
|
803
|
+
const reader = new FileReader();
|
|
804
|
+
reader.onload = () => res(reader.result);
|
|
805
|
+
reader.readAsDataURL(file);
|
|
806
|
+
}))).then((dataUris) => {
|
|
807
|
+
aitState.patch("mockData", { images: [...aitState.state.mockData.images, ...dataUris] });
|
|
808
|
+
refreshPanel();
|
|
809
|
+
});
|
|
810
|
+
};
|
|
811
|
+
input.click();
|
|
812
|
+
});
|
|
813
|
+
if (disabled) addBtn.disabled = true;
|
|
814
|
+
const defaultsBtn = h("button", { className: "ait-btn-secondary" }, "Use defaults");
|
|
815
|
+
defaultsBtn.addEventListener("click", () => {
|
|
816
|
+
aitState.patch("mockData", { images: [...getDefaultPlaceholderImages()] });
|
|
817
|
+
refreshPanel();
|
|
818
|
+
});
|
|
819
|
+
if (disabled) defaultsBtn.disabled = true;
|
|
820
|
+
const clearImagesBtn = h("button", { className: "ait-btn-secondary" }, "Clear");
|
|
821
|
+
clearImagesBtn.addEventListener("click", () => {
|
|
822
|
+
aitState.patch("mockData", { images: [] });
|
|
823
|
+
refreshPanel();
|
|
824
|
+
});
|
|
825
|
+
if (disabled) clearImagesBtn.disabled = true;
|
|
826
|
+
container.append(
|
|
827
|
+
h(
|
|
828
|
+
"div",
|
|
829
|
+
{ className: "ait-section" },
|
|
830
|
+
h("div", { className: "ait-section-title" }, `Mock Images (${images.length})`),
|
|
831
|
+
imageGrid,
|
|
832
|
+
h("div", { className: "ait-btn-row" }, addBtn, defaultsBtn, clearImagesBtn)
|
|
833
|
+
)
|
|
834
|
+
);
|
|
835
|
+
return container;
|
|
836
|
+
}
|
|
837
|
+
function monitoringNotice() {
|
|
838
|
+
return h(
|
|
839
|
+
"div",
|
|
840
|
+
{ className: "ait-monitoring-notice" },
|
|
841
|
+
"Read-only \u2014 mock responses are controlled at build time."
|
|
842
|
+
);
|
|
843
|
+
}
|
|
458
844
|
var TAB_RENDERERS = {
|
|
459
845
|
env: renderEnvTab,
|
|
460
846
|
permissions: renderPermissionsTab,
|
|
461
847
|
location: renderLocationTab,
|
|
848
|
+
device: renderDeviceTab,
|
|
462
849
|
iap: renderIapTab,
|
|
463
850
|
events: renderEventsTab,
|
|
464
851
|
analytics: renderAnalyticsTab,
|
|
465
852
|
storage: renderStorageTab
|
|
466
853
|
};
|
|
854
|
+
function makeDraggable(el, onClickOnly) {
|
|
855
|
+
let isDragging = false;
|
|
856
|
+
let startX = 0, startY = 0;
|
|
857
|
+
let startLeft = 0, startTop = 0;
|
|
858
|
+
let hasMoved = false;
|
|
859
|
+
el.addEventListener("pointerdown", (e) => {
|
|
860
|
+
isDragging = true;
|
|
861
|
+
hasMoved = false;
|
|
862
|
+
startX = e.clientX;
|
|
863
|
+
startY = e.clientY;
|
|
864
|
+
const rect = el.getBoundingClientRect();
|
|
865
|
+
startLeft = rect.left;
|
|
866
|
+
startTop = rect.top;
|
|
867
|
+
el.setPointerCapture(e.pointerId);
|
|
868
|
+
e.preventDefault();
|
|
869
|
+
});
|
|
870
|
+
el.addEventListener("pointermove", (e) => {
|
|
871
|
+
if (!isDragging) return;
|
|
872
|
+
const dx = e.clientX - startX;
|
|
873
|
+
const dy = e.clientY - startY;
|
|
874
|
+
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
|
|
875
|
+
hasMoved = true;
|
|
876
|
+
el.classList.add("dragging");
|
|
877
|
+
}
|
|
878
|
+
if (!hasMoved) return;
|
|
879
|
+
el.style.left = startLeft + dx + "px";
|
|
880
|
+
el.style.top = startTop + dy + "px";
|
|
881
|
+
el.style.right = "auto";
|
|
882
|
+
el.style.bottom = "auto";
|
|
883
|
+
});
|
|
884
|
+
el.addEventListener("pointerup", (e) => {
|
|
885
|
+
if (!isDragging) return;
|
|
886
|
+
isDragging = false;
|
|
887
|
+
el.classList.remove("dragging");
|
|
888
|
+
el.releasePointerCapture(e.pointerId);
|
|
889
|
+
if (hasMoved) {
|
|
890
|
+
snapToEdge(el);
|
|
891
|
+
updatePanelPosition(el);
|
|
892
|
+
saveButtonPosition(el);
|
|
893
|
+
} else {
|
|
894
|
+
onClickOnly();
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
el.addEventListener("pointercancel", (e) => {
|
|
898
|
+
isDragging = false;
|
|
899
|
+
el.classList.remove("dragging");
|
|
900
|
+
el.releasePointerCapture(e.pointerId);
|
|
901
|
+
if (hasMoved) {
|
|
902
|
+
snapToEdge(el);
|
|
903
|
+
updatePanelPosition(el);
|
|
904
|
+
saveButtonPosition(el);
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
function snapToEdge(el) {
|
|
909
|
+
const rect = el.getBoundingClientRect();
|
|
910
|
+
const vw = window.innerWidth;
|
|
911
|
+
const vh = window.innerHeight;
|
|
912
|
+
const cx = rect.left + rect.width / 2;
|
|
913
|
+
const margin = 16;
|
|
914
|
+
if (cx < vw / 2) {
|
|
915
|
+
el.style.left = margin + "px";
|
|
916
|
+
el.style.right = "auto";
|
|
917
|
+
} else {
|
|
918
|
+
el.style.left = "auto";
|
|
919
|
+
el.style.right = margin + "px";
|
|
920
|
+
}
|
|
921
|
+
const top = Math.max(margin, Math.min(vh - rect.height - margin, rect.top));
|
|
922
|
+
el.style.top = top + "px";
|
|
923
|
+
el.style.bottom = "auto";
|
|
924
|
+
}
|
|
925
|
+
function updatePanelPosition(toggleEl) {
|
|
926
|
+
if (!panelEl) return;
|
|
927
|
+
const vw = window.innerWidth;
|
|
928
|
+
const vh = window.innerHeight;
|
|
929
|
+
if (vw <= 480) {
|
|
930
|
+
panelEl.style.top = "";
|
|
931
|
+
panelEl.style.left = "";
|
|
932
|
+
panelEl.style.right = "";
|
|
933
|
+
panelEl.style.bottom = "";
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
const rect = toggleEl.getBoundingClientRect();
|
|
937
|
+
const panelWidth = PANEL_WIDTH;
|
|
938
|
+
const panelHeight = PANEL_HEIGHT;
|
|
939
|
+
const margin = 16;
|
|
940
|
+
if (rect.left < vw / 2) {
|
|
941
|
+
panelEl.style.left = margin + "px";
|
|
942
|
+
panelEl.style.right = "auto";
|
|
943
|
+
} else {
|
|
944
|
+
panelEl.style.left = "auto";
|
|
945
|
+
panelEl.style.right = margin + "px";
|
|
946
|
+
}
|
|
947
|
+
if (rect.top < vh / 2) {
|
|
948
|
+
const top = Math.min(rect.bottom + 8, vh - panelHeight - margin);
|
|
949
|
+
panelEl.style.top = Math.max(margin, top) + "px";
|
|
950
|
+
panelEl.style.bottom = "auto";
|
|
951
|
+
} else {
|
|
952
|
+
const bottom = Math.min(vh - rect.top + 8, vh - panelHeight - margin);
|
|
953
|
+
panelEl.style.top = "auto";
|
|
954
|
+
panelEl.style.bottom = Math.max(margin, bottom) + "px";
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
function saveButtonPosition(el) {
|
|
958
|
+
localStorage.setItem("__ait_btn_pos", JSON.stringify({
|
|
959
|
+
left: el.style.left,
|
|
960
|
+
top: el.style.top,
|
|
961
|
+
right: el.style.right,
|
|
962
|
+
bottom: el.style.bottom
|
|
963
|
+
}));
|
|
964
|
+
}
|
|
965
|
+
function restoreButtonPosition(el) {
|
|
966
|
+
const saved = localStorage.getItem("__ait_btn_pos");
|
|
967
|
+
if (saved) {
|
|
968
|
+
try {
|
|
969
|
+
const pos = JSON.parse(saved);
|
|
970
|
+
if (typeof pos !== "object" || pos === null) return;
|
|
971
|
+
const allowedKeys = ["left", "top", "right", "bottom"];
|
|
972
|
+
const validCssValue = /^(\d+px|auto)$/;
|
|
973
|
+
for (const key of allowedKeys) {
|
|
974
|
+
if (key in pos && typeof pos[key] === "string" && validCssValue.test(pos[key])) {
|
|
975
|
+
el.style[key] = pos[key];
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
} catch {
|
|
979
|
+
}
|
|
980
|
+
} else {
|
|
981
|
+
el.style.bottom = "16px";
|
|
982
|
+
el.style.right = "16px";
|
|
983
|
+
}
|
|
984
|
+
}
|
|
467
985
|
var currentTab = "env";
|
|
468
986
|
var panelEl = null;
|
|
469
987
|
var bodyEl = null;
|
|
@@ -484,12 +1002,35 @@ function mount() {
|
|
|
484
1002
|
document.head.appendChild(style);
|
|
485
1003
|
const toggle = h("button", { className: "ait-panel-toggle", title: "AIT DevTools" }, "AIT");
|
|
486
1004
|
let isOpen = false;
|
|
1005
|
+
restoreButtonPosition(toggle);
|
|
487
1006
|
panelEl = h("div", { className: "ait-panel" });
|
|
1007
|
+
const closeBtn = h("button", { className: "ait-panel-close", title: "Close" }, "\xD7");
|
|
1008
|
+
closeBtn.addEventListener("click", () => {
|
|
1009
|
+
isOpen = false;
|
|
1010
|
+
panelEl.classList.remove("open");
|
|
1011
|
+
});
|
|
1012
|
+
const mockBadge = h("span", {
|
|
1013
|
+
className: `ait-mock-badge ${aitState.state.panelEditable ? "ait-mock-badge-on" : "ait-mock-badge-off"}`,
|
|
1014
|
+
title: "Toggle panel edit mode"
|
|
1015
|
+
}, aitState.state.panelEditable ? "EDIT" : "READ-ONLY");
|
|
1016
|
+
mockBadge.addEventListener("click", () => {
|
|
1017
|
+
aitState.update({ panelEditable: !aitState.state.panelEditable });
|
|
1018
|
+
mockBadge.className = `ait-mock-badge ${aitState.state.panelEditable ? "ait-mock-badge-on" : "ait-mock-badge-off"}`;
|
|
1019
|
+
mockBadge.textContent = aitState.state.panelEditable ? "EDIT" : "READ-ONLY";
|
|
1020
|
+
refreshPanel();
|
|
1021
|
+
});
|
|
1022
|
+
const headerRight = h(
|
|
1023
|
+
"span",
|
|
1024
|
+
{ style: "display:flex;align-items:center;gap:6px" },
|
|
1025
|
+
mockBadge,
|
|
1026
|
+
h("span", { style: "font-size:11px;color:#666;font-weight:400" }, `v${"0.0.2"}`),
|
|
1027
|
+
closeBtn
|
|
1028
|
+
);
|
|
488
1029
|
const header = h(
|
|
489
1030
|
"div",
|
|
490
1031
|
{ className: "ait-panel-header" },
|
|
491
1032
|
h("span", {}, "AIT DevTools"),
|
|
492
|
-
|
|
1033
|
+
headerRight
|
|
493
1034
|
);
|
|
494
1035
|
tabsEl = h("div", { className: "ait-panel-tabs" });
|
|
495
1036
|
for (const tab of TABS) {
|
|
@@ -503,13 +1044,28 @@ function mount() {
|
|
|
503
1044
|
bodyEl = h("div", { className: "ait-panel-body" });
|
|
504
1045
|
panelEl.append(header, tabsEl, bodyEl);
|
|
505
1046
|
document.body.append(panelEl, toggle);
|
|
506
|
-
toggle
|
|
1047
|
+
snapToEdge(toggle);
|
|
1048
|
+
saveButtonPosition(toggle);
|
|
1049
|
+
makeDraggable(toggle, () => {
|
|
507
1050
|
isOpen = !isOpen;
|
|
508
1051
|
panelEl.classList.toggle("open", isOpen);
|
|
509
|
-
if (isOpen)
|
|
1052
|
+
if (isOpen) {
|
|
1053
|
+
updatePanelPosition(toggle);
|
|
1054
|
+
refreshPanel();
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
let resizeRaf = 0;
|
|
1058
|
+
window.addEventListener("resize", () => {
|
|
1059
|
+
if (resizeRaf) return;
|
|
1060
|
+
resizeRaf = requestAnimationFrame(() => {
|
|
1061
|
+
resizeRaf = 0;
|
|
1062
|
+
snapToEdge(toggle);
|
|
1063
|
+
saveButtonPosition(toggle);
|
|
1064
|
+
if (isOpen) updatePanelPosition(toggle);
|
|
1065
|
+
});
|
|
510
1066
|
});
|
|
511
1067
|
aitState.subscribe(() => {
|
|
512
|
-
if (isOpen && (currentTab === "analytics" || currentTab === "storage")) {
|
|
1068
|
+
if (isOpen && (currentTab === "analytics" || currentTab === "storage" || currentTab === "device")) {
|
|
513
1069
|
refreshPanel();
|
|
514
1070
|
}
|
|
515
1071
|
});
|