insikt.js 1.1.0 → 1.2.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/insikt.es.js CHANGED
@@ -1,4 +1,8 @@
1
1
  (function() {
2
+ const VERSION = "1.2.0";
3
+ const BUFFER_KEY = "__insikt_buffer__";
4
+ const SETTINGS_KEY = "__dc_settings";
5
+ const BODY_DISPLAY_LIMIT = 10 * 1024;
2
6
  const css = `
3
7
  @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Syne:wght@400;600;700;800&display=swap');
4
8
 
@@ -21,11 +25,11 @@
21
25
  --dc-sans: 'Syne', sans-serif;
22
26
  }
23
27
 
24
- /* Scope resets strictly to our elements */
25
28
  #dc-panel, #dc-fab { box-sizing: border-box; }
26
29
  #dc-panel *, #dc-fab * { box-sizing: inherit; }
27
30
 
28
31
  /* ── FAB ────────────────────────────── */
32
+
29
33
  #dc-fab {
30
34
  position: fixed;
31
35
  bottom: 20px;
@@ -49,7 +53,11 @@
49
53
  margin: 0;
50
54
  }
51
55
 
52
- #dc-fab:hover { transform: scale(1.05); box-shadow: 0 6px 30px rgba(124,106,247,0.7); }
56
+ #dc-fab:hover {
57
+ transform: scale(1.05);
58
+ box-shadow: 0 6px 30px rgba(124,106,247,0.7);
59
+ }
60
+
53
61
  #dc-fab:active { transform: scale(0.95); }
54
62
 
55
63
  .dc-fab-badge {
@@ -70,7 +78,8 @@
70
78
  font-family: var(--dc-mono);
71
79
  }
72
80
 
73
- /* ── CONSOLE PANEL ───────────────────── */
81
+ /* ── PANEL ───────────────────────────── */
82
+
74
83
  #dc-panel {
75
84
  position: fixed;
76
85
  bottom: 0;
@@ -98,10 +107,11 @@
98
107
 
99
108
  @keyframes dcSlideUp {
100
109
  from { transform: translateY(100%); opacity: 0; }
101
- to { transform: translateY(0); opacity: 1; }
110
+ to { transform: translateY(0); opacity: 1; }
102
111
  }
103
112
 
104
- /* Drag handle */
113
+ /* ── DRAG HANDLE ─────────────────────── */
114
+
105
115
  #dc-drag {
106
116
  width: 100%;
107
117
  height: 22px;
@@ -127,7 +137,8 @@
127
137
 
128
138
  #dc-drag:hover::before { background: var(--dc-accent); }
129
139
 
130
- /* Header bar */
140
+ /* ── HEADER ──────────────────────────── */
141
+
131
142
  #dc-header {
132
143
  display: flex;
133
144
  align-items: center;
@@ -157,8 +168,14 @@
157
168
  }
158
169
 
159
170
  @keyframes dcPulse {
160
- 0%,100% { opacity: 1; box-shadow: 0 0 0 0 rgba(61,255,160,0.4); }
161
- 50% { opacity: 0.6; box-shadow: 0 0 0 5px rgba(61,255,160,0); }
171
+ 0%,100% {
172
+ opacity: 1;
173
+ box-shadow: 0 0 0 0 rgba(61,255,160,0.4);
174
+ }
175
+ 50% {
176
+ opacity: 0.6;
177
+ box-shadow: 0 0 0 5px rgba(61,255,160,0);
178
+ }
162
179
  }
163
180
 
164
181
  .dc-hbtn {
@@ -177,9 +194,13 @@
177
194
  padding: 0;
178
195
  }
179
196
 
180
- .dc-hbtn:hover { background: var(--dc-border); color: var(--dc-text0); }
197
+ .dc-hbtn:hover {
198
+ background: var(--dc-border);
199
+ color: var(--dc-text0);
200
+ }
201
+
202
+ /* ── TABS ────────────────────────────── */
181
203
 
182
- /* Tabs */
183
204
  #dc-tabs {
184
205
  display: flex;
185
206
  background: var(--dc-bg0);
@@ -229,7 +250,8 @@
229
250
  margin-left: 5px;
230
251
  }
231
252
 
232
- /* Tab panels */
253
+ /* ── PANEL CONTENT ───────────────────── */
254
+
233
255
  .dc-panel-content {
234
256
  display: none;
235
257
  flex-direction: column;
@@ -248,12 +270,16 @@
248
270
  }
249
271
 
250
272
  .dc-scroll::-webkit-scrollbar { width: 4px; }
251
- .dc-scroll::-webkit-scrollbar-thumb { background: var(--dc-border); border-radius: 2px; }
273
+ .dc-scroll::-webkit-scrollbar-thumb {
274
+ background: var(--dc-border);
275
+ border-radius: 2px;
276
+ }
252
277
 
253
278
  /* ── LOG ENTRIES ──────────────────────── */
279
+
254
280
  .dc-entry {
255
281
  display: flex;
256
- align-items: baseline;
282
+ align-items: flex-start;
257
283
  gap: 8px;
258
284
  padding: 5px 8px;
259
285
  border-radius: 5px;
@@ -262,15 +288,91 @@
262
288
  line-height: 1.5;
263
289
  word-break: break-word;
264
290
  animation: dcFadeIn 0.1s ease;
291
+ position: relative;
292
+ }
293
+
294
+ @keyframes dcFadeIn {
295
+ from {
296
+ opacity: 0;
297
+ transform: translateY(2px);
298
+ }
299
+ to {
300
+ opacity: 1;
301
+ transform: none;
302
+ }
303
+ }
304
+
305
+ .dc-entry-log {
306
+ color: var(--dc-text0);
307
+ }
308
+
309
+ .dc-entry-warn {
310
+ color: var(--dc-orange);
311
+ background: rgba(255,179,71,0.07);
312
+ border-left: 2px solid var(--dc-orange);
313
+ }
314
+
315
+ .dc-entry-error {
316
+ color: var(--dc-red);
317
+ background: rgba(255,92,110,0.07);
318
+ border-left: 2px solid var(--dc-red);
319
+ }
320
+
321
+ .dc-entry-info {
322
+ color: var(--dc-blue);
323
+ background: rgba(91,206,250,0.06);
324
+ border-left: 2px solid var(--dc-blue);
265
325
  }
266
326
 
267
- @keyframes dcFadeIn { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
327
+ .dc-entry-cmd {
328
+ color: var(--dc-accent);
329
+ background: rgba(124,106,247,0.06);
330
+ }
331
+
332
+ .dc-entry-table {
333
+ color: var(--dc-text0);
334
+ background: rgba(124,106,247,0.04);
335
+ border-left: 2px solid var(--dc-accent);
336
+ display: block;
337
+ }
338
+
339
+ .dc-entry-group {
340
+ color: var(--dc-accent);
341
+ background: rgba(124,106,247,0.05);
342
+ cursor: pointer;
343
+ user-select: none;
344
+ font-weight: 600;
345
+ }
346
+
347
+ .dc-entry-group:hover {
348
+ background: rgba(124,106,247,0.10);
349
+ }
350
+
351
+ .dc-group-arrow {
352
+ display: inline-block;
353
+ width: 12px;
354
+ color: var(--dc-text2);
355
+ font-size: 9px;
356
+ transition: transform 0.15s;
357
+ }
358
+
359
+ .dc-group-arrow.open {
360
+ transform: rotate(90deg);
361
+ }
362
+
363
+ .dc-group-children {
364
+ display: block;
365
+ }
366
+
367
+ .dc-group-children.collapsed {
368
+ display: none;
369
+ }
268
370
 
269
- .dc-entry-log { color: var(--dc-text0); }
270
- .dc-entry-warn { color: var(--dc-orange); background: rgba(255,179,71,0.07); border-left: 2px solid var(--dc-orange); }
271
- .dc-entry-error { color: var(--dc-red); background: rgba(255,92,110,0.07); border-left: 2px solid var(--dc-red); }
272
- .dc-entry-info { color: var(--dc-blue); background: rgba(91,206,250,0.06); border-left: 2px solid var(--dc-blue); }
273
- .dc-entry-cmd { color: var(--dc-accent); background: rgba(124,106,247,0.06); }
371
+ .dc-grouped {
372
+ margin-left: 18px;
373
+ border-left: 1px solid var(--dc-border);
374
+ border-radius: 0;
375
+ }
274
376
 
275
377
  .dc-ts {
276
378
  color: var(--dc-text2);
@@ -278,21 +380,135 @@
278
380
  font-weight: 400;
279
381
  flex-shrink: 0;
280
382
  letter-spacing: 0;
383
+ white-space: nowrap;
281
384
  }
282
385
 
283
- .dc-entry pre {
284
- white-space: pre-wrap;
285
- word-break: break-all;
286
- background: var(--dc-bg0);
287
- padding: 6px 8px;
288
- border-radius: 5px;
386
+ .dc-entry-body {
387
+ min-width: 0;
388
+ flex: 1;
389
+ }
390
+
391
+ /* ── OBJECT INSPECTOR ───────────────── */
392
+
393
+ .dc-value {
394
+ font-family: var(--dc-mono);
395
+ font-size: inherit;
396
+ line-height: 1.5;
397
+ }
398
+
399
+ .dc-primitive-string { color: var(--dc-green); }
400
+ .dc-primitive-number { color: var(--dc-blue); }
401
+ .dc-primitive-boolean { color: var(--dc-orange); }
402
+ .dc-primitive-null,
403
+ .dc-primitive-undefined {
404
+ color: var(--dc-text2);
405
+ }
406
+
407
+ .dc-primitive-bigint { color: var(--dc-blue); }
408
+ .dc-primitive-symbol { color: var(--dc-accent2); }
409
+ .dc-primitive-function { color: var(--dc-accent2); }
410
+
411
+ .dc-object-node {
412
+ display: inline;
413
+ }
414
+
415
+ .dc-object-toggle {
416
+ display: inline-flex;
417
+ align-items: center;
418
+ gap: 3px;
419
+ cursor: pointer;
420
+ user-select: none;
421
+ color: var(--dc-text1);
422
+ border-radius: 3px;
423
+ padding: 0 2px;
424
+ }
425
+
426
+ .dc-object-toggle:hover {
427
+ background: var(--dc-bg3);
428
+ }
429
+
430
+ .dc-object-arrow {
431
+ color: var(--dc-text2);
432
+ width: 10px;
433
+ font-size: 9px;
434
+ display: inline-block;
435
+ }
436
+
437
+ .dc-object-summary {
438
+ color: var(--dc-text1);
439
+ }
440
+
441
+ .dc-object-children {
442
+ display: none;
443
+ margin-left: 14px;
444
+ border-left: 1px solid var(--dc-border);
445
+ padding-left: 8px;
446
+ margin-top: 2px;
447
+ margin-bottom: 2px;
448
+ }
449
+
450
+ .dc-object-children.open {
451
+ display: block;
452
+ }
453
+
454
+ .dc-object-row {
455
+ display: block;
456
+ padding: 1px 0;
457
+ }
458
+
459
+ .dc-object-key {
460
+ color: var(--dc-accent2);
461
+ }
462
+
463
+ .dc-object-key-index {
464
+ color: var(--dc-text2);
465
+ }
466
+
467
+ .dc-object-more {
468
+ color: var(--dc-text2);
469
+ font-style: italic;
470
+ }
471
+
472
+ /* ── TABLE ───────────────────────────── */
473
+
474
+ .dc-table-wrap {
475
+ overflow-x: auto;
289
476
  margin-top: 4px;
290
- font-size: 11px;
291
- color: var(--dc-green);
477
+ border: 1px solid var(--dc-border);
478
+ border-radius: 6px;
479
+ }
480
+
481
+ .dc-console-table {
292
482
  width: 100%;
483
+ border-collapse: collapse;
484
+ font-family: var(--dc-mono);
485
+ font-size: 10px;
486
+ min-width: 280px;
487
+ }
488
+
489
+ .dc-console-table th {
490
+ background: var(--dc-bg3);
491
+ color: var(--dc-accent);
492
+ font-weight: 600;
493
+ text-align: left;
494
+ padding: 5px 7px;
495
+ border-bottom: 1px solid var(--dc-border);
496
+ white-space: nowrap;
497
+ }
498
+
499
+ .dc-console-table td {
500
+ padding: 5px 7px;
501
+ border-bottom: 1px solid var(--dc-border);
502
+ color: var(--dc-text1);
503
+ vertical-align: top;
504
+ }
505
+
506
+ .dc-console-table tr:last-child td {
507
+ border-bottom: none;
293
508
  }
294
509
 
295
- /* ── REPL INPUT ───────────────────────── */
510
+ /* ── REPL ────────────────────────────── */
511
+
296
512
  .dc-repl {
297
513
  display: flex;
298
514
  gap: 8px;
@@ -315,7 +531,10 @@
315
531
  transition: border-color 0.15s;
316
532
  }
317
533
 
318
- .dc-input:focus { border-color: var(--dc-accent); box-shadow: 0 0 0 2px rgba(124,106,247,0.15); }
534
+ .dc-input:focus {
535
+ border-color: var(--dc-accent);
536
+ box-shadow: 0 0 0 2px rgba(124,106,247,0.15);
537
+ }
319
538
 
320
539
  .dc-run-btn {
321
540
  padding: 8px 14px;
@@ -334,7 +553,29 @@
334
553
  .dc-run-btn:hover { background: #8f7ffb; }
335
554
  .dc-run-btn:active { transform: scale(0.96); }
336
555
 
337
- /* ── NETWORK TAB ──────────────────────── */
556
+ /* ── RELOAD SEPARATOR ────────────────── */
557
+
558
+ .dc-reload-separator {
559
+ display: flex;
560
+ align-items: center;
561
+ gap: 8px;
562
+ margin: 10px 0;
563
+ color: var(--dc-text2);
564
+ font-size: 9px;
565
+ letter-spacing: 0.1em;
566
+ text-transform: uppercase;
567
+ }
568
+
569
+ .dc-reload-separator::before,
570
+ .dc-reload-separator::after {
571
+ content: '';
572
+ height: 1px;
573
+ background: var(--dc-border);
574
+ flex: 1;
575
+ }
576
+
577
+ /* ── NETWORK ─────────────────────────── */
578
+
338
579
  .dc-net-filter {
339
580
  display: flex;
340
581
  gap: 8px;
@@ -369,7 +610,10 @@
369
610
  cursor: pointer;
370
611
  }
371
612
 
372
- .dc-clear-btn:hover { border-color: var(--dc-red); color: var(--dc-red); }
613
+ .dc-clear-btn:hover {
614
+ border-color: var(--dc-red);
615
+ color: var(--dc-red);
616
+ }
373
617
 
374
618
  .dc-req {
375
619
  padding: 9px 10px;
@@ -416,9 +660,9 @@
416
660
  }
417
661
 
418
662
  .dc-req-status { flex-shrink: 0; }
419
- .s-ok { color: var(--dc-green); }
663
+ .s-ok { color: var(--dc-green); }
420
664
  .s-redir { color: var(--dc-orange); }
421
- .s-err { color: var(--dc-red); }
665
+ .s-err { color: var(--dc-red); }
422
666
 
423
667
  .dc-req-meta {
424
668
  color: var(--dc-text2);
@@ -439,11 +683,76 @@
439
683
  .dc-req-detail.open { display: block; }
440
684
 
441
685
  .dc-detail-section { margin-bottom: 10px; }
442
- .dc-detail-title { color: var(--dc-accent); font-weight: 700; margin-bottom: 4px; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; }
443
- .dc-detail-row { padding: 2px 0; word-break: break-all; }
686
+
687
+ .dc-detail-title {
688
+ color: var(--dc-accent);
689
+ font-weight: 700;
690
+ margin-bottom: 4px;
691
+ font-size: 10px;
692
+ letter-spacing: 0.08em;
693
+ text-transform: uppercase;
694
+ }
695
+
696
+ .dc-detail-row {
697
+ padding: 2px 0;
698
+ word-break: break-all;
699
+ }
700
+
444
701
  .dc-detail-key { color: var(--dc-text2); }
445
702
 
446
- /* ── STORAGE TAB ──────────────────────── */
703
+ .dc-body-block {
704
+ background: var(--dc-bg1);
705
+ border: 1px solid var(--dc-border);
706
+ border-radius: 5px;
707
+ padding: 7px;
708
+ margin-top: 5px;
709
+ }
710
+
711
+ .dc-body-pre {
712
+ white-space: pre-wrap;
713
+ word-break: break-word;
714
+ max-height: 160px;
715
+ overflow: auto;
716
+ margin: 0;
717
+ font-family: var(--dc-mono);
718
+ font-size: 10px;
719
+ color: var(--dc-green);
720
+ }
721
+
722
+ .dc-body-truncated {
723
+ color: var(--dc-orange);
724
+ font-size: 9px;
725
+ margin-top: 5px;
726
+ }
727
+
728
+ .dc-body-full {
729
+ display: none;
730
+ white-space: pre-wrap;
731
+ word-break: break-word;
732
+ max-height: 400px;
733
+ overflow: auto;
734
+ margin: 6px 0 0;
735
+ font-family: var(--dc-mono);
736
+ font-size: 10px;
737
+ color: var(--dc-green);
738
+ }
739
+
740
+ .dc-body-full.open { display: block; }
741
+
742
+ .dc-body-toggle {
743
+ margin-top: 5px;
744
+ padding: 4px 7px;
745
+ background: var(--dc-bg3);
746
+ border: 1px solid var(--dc-border);
747
+ border-radius: 4px;
748
+ color: var(--dc-text1);
749
+ font-family: var(--dc-mono);
750
+ font-size: 9px;
751
+ cursor: pointer;
752
+ }
753
+
754
+ /* ── STORAGE ─────────────────────────── */
755
+
447
756
  .dc-storage-nav {
448
757
  display: flex;
449
758
  gap: 4px;
@@ -465,8 +774,16 @@
465
774
  transition: all 0.15s;
466
775
  }
467
776
 
468
- .dc-st-btn.active { background: var(--dc-accent); border-color: var(--dc-accent); color: #fff; }
469
- .dc-st-btn:hover:not(.active) { border-color: var(--dc-text2); color: var(--dc-text1); }
777
+ .dc-st-btn.active {
778
+ background: var(--dc-accent);
779
+ border-color: var(--dc-accent);
780
+ color: #fff;
781
+ }
782
+
783
+ .dc-st-btn:hover:not(.active) {
784
+ border-color: var(--dc-text2);
785
+ color: var(--dc-text1);
786
+ }
470
787
 
471
788
  .dc-storage-actions {
472
789
  display: flex;
@@ -495,10 +812,23 @@
495
812
 
496
813
  .dc-db-header:hover { background: var(--dc-border); }
497
814
 
498
- .dc-db-name { color: var(--dc-blue); font-size: 12px; font-weight: 700; }
499
- .dc-db-meta { color: var(--dc-text2); font-size: 10px; margin-top: 2px; }
815
+ .dc-db-name {
816
+ color: var(--dc-blue);
817
+ font-size: 12px;
818
+ font-weight: 700;
819
+ }
820
+
821
+ .dc-db-meta {
822
+ color: var(--dc-text2);
823
+ font-size: 10px;
824
+ margin-top: 2px;
825
+ }
826
+
827
+ .dc-db-body {
828
+ display: none;
829
+ padding: 10px;
830
+ }
500
831
 
501
- .dc-db-body { display: none; padding: 10px; }
502
832
  .dc-db-card.open .dc-db-body { display: block; }
503
833
 
504
834
  .dc-store-item {
@@ -512,8 +842,18 @@
512
842
  }
513
843
 
514
844
  .dc-store-item:hover { border-color: var(--dc-border); }
515
- .dc-store-name { color: var(--dc-text0); font-size: 12px; font-weight: 600; }
516
- .dc-store-meta { color: var(--dc-text2); font-size: 10px; margin-top: 2px; }
845
+
846
+ .dc-store-name {
847
+ color: var(--dc-text0);
848
+ font-size: 12px;
849
+ font-weight: 600;
850
+ }
851
+
852
+ .dc-store-meta {
853
+ color: var(--dc-text2);
854
+ font-size: 10px;
855
+ margin-top: 2px;
856
+ }
517
857
 
518
858
  .dc-data-viewer {
519
859
  display: none;
@@ -530,8 +870,16 @@
530
870
  font-size: 11px;
531
871
  }
532
872
 
533
- .dc-data-key { color: #ffd700; font-weight: 600; }
534
- .dc-data-val { color: var(--dc-blue); margin-top: 3px; word-break: break-all; }
873
+ .dc-data-key {
874
+ color: #ffd700;
875
+ font-weight: 600;
876
+ }
877
+
878
+ .dc-data-val {
879
+ color: var(--dc-blue);
880
+ margin-top: 3px;
881
+ word-break: break-all;
882
+ }
535
883
 
536
884
  .dc-kv-row {
537
885
  display: flex;
@@ -542,8 +890,18 @@
542
890
  font-size: 11px;
543
891
  }
544
892
 
545
- .dc-kv-key { color: #ffd700; font-weight: 600; min-width: 100px; word-break: break-all; }
546
- .dc-kv-val { color: var(--dc-blue); flex: 1; word-break: break-all; }
893
+ .dc-kv-key {
894
+ color: #ffd700;
895
+ font-weight: 600;
896
+ min-width: 100px;
897
+ word-break: break-all;
898
+ }
899
+
900
+ .dc-kv-val {
901
+ color: var(--dc-blue);
902
+ flex: 1;
903
+ word-break: break-all;
904
+ }
547
905
 
548
906
  .dc-empty {
549
907
  padding: 24px;
@@ -552,7 +910,8 @@
552
910
  font-size: 12px;
553
911
  }
554
912
 
555
- /* ── DOM TAB ──────────────────────────────────── */
913
+ /* ── DOM ─────────────────────────────── */
914
+
556
915
  .dc-dom-toolbar {
557
916
  display: flex;
558
917
  gap: 8px;
@@ -573,8 +932,16 @@
573
932
  transition: all 0.15s;
574
933
  }
575
934
 
576
- .dc-dom-btn:hover { border-color: var(--dc-accent); color: var(--dc-accent); }
577
- .dc-dom-btn.picking { background: var(--dc-accent2); border-color: var(--dc-accent2); color: #fff; }
935
+ .dc-dom-btn:hover {
936
+ border-color: var(--dc-accent);
937
+ color: var(--dc-accent);
938
+ }
939
+
940
+ .dc-dom-btn.picking {
941
+ background: var(--dc-accent2);
942
+ border-color: var(--dc-accent2);
943
+ color: #fff;
944
+ }
578
945
 
579
946
  .dc-tree { font-size: 12px; }
580
947
 
@@ -594,29 +961,51 @@
594
961
 
595
962
  .dc-node-label:hover { background: var(--dc-bg3); }
596
963
 
597
- .dc-arrow { color: var(--dc-text2); width: 12px; flex-shrink: 0; font-size: 9px; }
598
- .dc-tag { color: var(--dc-accent2); font-weight: 600; }
599
- .dc-id { color: var(--dc-blue); }
600
- .dc-cls { color: var(--dc-orange); }
601
- .dc-txt-preview { color: var(--dc-text2); font-size: 10px; max-width: 150px; overflow: hidden; text-overflow: ellipsis; }
602
-
603
- .dc-children { margin-left: 16px; display: none; }
604
- .dc-children.open { display: block; }
964
+ .dc-arrow {
965
+ color: var(--dc-text2);
966
+ width: 12px;
967
+ flex-shrink: 0;
968
+ font-size: 9px;
969
+ }
605
970
 
606
- .dc-node-detail {
607
- background: var(--dc-bg0);
608
- border: 1px solid var(--dc-border);
609
- border-radius: 6px;
610
- padding: 12px;
611
- margin: 8px 0;
612
- font-size: 11px;
971
+ .dc-tag {
972
+ color: var(--dc-accent2);
973
+ font-weight: 600;
974
+ }
975
+
976
+ .dc-id { color: var(--dc-blue); }
977
+ .dc-cls { color: var(--dc-orange); }
978
+
979
+ .dc-txt-preview {
980
+ color: var(--dc-text2);
981
+ font-size: 10px;
982
+ max-width: 150px;
983
+ overflow: hidden;
984
+ text-overflow: ellipsis;
985
+ }
986
+
987
+ .dc-children {
988
+ margin-left: 16px;
989
+ display: none;
990
+ }
991
+
992
+ .dc-children.open { display: block; }
993
+
994
+ .dc-node-detail {
995
+ background: var(--dc-bg0);
996
+ border: 1px solid var(--dc-border);
997
+ border-radius: 6px;
998
+ padding: 12px;
999
+ margin: 8px 0;
1000
+ font-size: 11px;
613
1001
  }
614
1002
 
615
1003
  .dc-attr-row { padding: 3px 0; }
616
1004
  .dc-attr-name { color: var(--dc-blue); }
617
- .dc-attr-val { color: var(--dc-green); }
1005
+ .dc-attr-val { color: var(--dc-green); }
1006
+
1007
+ /* ── SYSTEM ──────────────────────────── */
618
1008
 
619
- /* ── SYSTEM TAB ───────────────────────────────── */
620
1009
  .dc-sys-grid {
621
1010
  display: grid;
622
1011
  grid-template-columns: 1fr 1fr;
@@ -648,7 +1037,8 @@
648
1037
 
649
1038
  .dc-sys-full { grid-column: 1 / -1; }
650
1039
 
651
- /* ── SETTINGS TAB ─────────────────────────────── */
1040
+ /* ── SETTINGS ───────────────────────── */
1041
+
652
1042
  .dc-settings-body { padding: 10px 12px; }
653
1043
 
654
1044
  .dc-setting-row {
@@ -662,8 +1052,18 @@
662
1052
  .dc-setting-row:last-child { border-bottom: none; }
663
1053
 
664
1054
  .dc-setting-info { flex: 1; }
665
- .dc-setting-name { color: var(--dc-text0); font-size: 12px; font-weight: 600; }
666
- .dc-setting-desc { color: var(--dc-text2); font-size: 10px; margin-top: 2px; }
1055
+
1056
+ .dc-setting-name {
1057
+ color: var(--dc-text0);
1058
+ font-size: 12px;
1059
+ font-weight: 600;
1060
+ }
1061
+
1062
+ .dc-setting-desc {
1063
+ color: var(--dc-text2);
1064
+ font-size: 10px;
1065
+ margin-top: 2px;
1066
+ }
667
1067
 
668
1068
  .dc-toggle {
669
1069
  position: relative;
@@ -672,18 +1072,27 @@
672
1072
  flex-shrink: 0;
673
1073
  }
674
1074
 
675
- .dc-toggle input { opacity: 0; width: 0; height: 0; }
1075
+ .dc-toggle input {
1076
+ opacity: 0;
1077
+ width: 0;
1078
+ height: 0;
1079
+ }
676
1080
 
677
1081
  .dc-toggle-track {
678
1082
  position: absolute;
679
- top: 0; left: 0; right: 0; bottom: 0;
1083
+ top: 0;
1084
+ left: 0;
1085
+ right: 0;
1086
+ bottom: 0;
680
1087
  background: var(--dc-border);
681
1088
  border-radius: 11px;
682
1089
  cursor: pointer;
683
1090
  transition: background 0.2s;
684
1091
  }
685
1092
 
686
- .dc-toggle input:checked + .dc-toggle-track { background: var(--dc-accent); }
1093
+ .dc-toggle input:checked + .dc-toggle-track {
1094
+ background: var(--dc-accent);
1095
+ }
687
1096
 
688
1097
  .dc-toggle-track::after {
689
1098
  content: '';
@@ -692,11 +1101,14 @@
692
1101
  height: 16px;
693
1102
  background: white;
694
1103
  border-radius: 50%;
695
- top: 3px; left: 3px;
1104
+ top: 3px;
1105
+ left: 3px;
696
1106
  transition: transform 0.2s;
697
1107
  }
698
1108
 
699
- .dc-toggle input:checked + .dc-toggle-track::after { transform: translateX(18px); }
1109
+ .dc-toggle input:checked + .dc-toggle-track::after {
1110
+ transform: translateX(18px);
1111
+ }
700
1112
 
701
1113
  .dc-select {
702
1114
  background: var(--dc-bg0);
@@ -708,7 +1120,8 @@
708
1120
  outline: none;
709
1121
  }
710
1122
 
711
- /* ── PICK highlight ───────────────────────────── */
1123
+ /* ── PICK ─────────────────────────────── */
1124
+
712
1125
  .dc-pick-highlight {
713
1126
  outline: 2px dashed var(--dc-accent2) !important;
714
1127
  outline-offset: 2px;
@@ -719,24 +1132,29 @@
719
1132
  style.textContent = css;
720
1133
  document.head.appendChild(style);
721
1134
  const html = `
722
- <button id="dc-fab" title="DevConsole">
1135
+ <button id="dc-fab" title="INSIKT">
723
1136
  <span id="dc-fab-icon">⌥</span>
724
1137
  <span class="dc-fab-badge" id="dc-error-badge">0</span>
725
1138
  </button>
726
1139
 
727
1140
  <div id="dc-panel">
728
1141
  <div id="dc-drag"></div>
1142
+
729
1143
  <div id="dc-header">
730
- <span id="dc-title">DevConsole</span>
1144
+ <span id="dc-title">INSIKT</span>
731
1145
  <span class="dc-pulse"></span>
732
1146
  <button class="dc-hbtn" id="dc-clear-btn" title="Clear active tab">🗑</button>
733
- <button class="dc-hbtn" id="dc-copy-btn" title="Copy logs to clipboard">📋</button>
1147
+ <button class="dc-hbtn" id="dc-copy-btn" title="Copy active tab">📋</button>
734
1148
  <button class="dc-hbtn" id="dc-minimize-btn" title="Minimize">−</button>
735
1149
  </div>
736
1150
 
737
1151
  <div id="dc-tabs">
738
- <button class="dc-tab active" data-tab="console">Console<span class="dc-tab-count" id="cnt-console" style="display:none"></span></button>
739
- <button class="dc-tab" data-tab="network">Network<span class="dc-tab-count" id="cnt-network" style="display:none"></span></button>
1152
+ <button class="dc-tab active" data-tab="console">
1153
+ Console<span class="dc-tab-count" id="cnt-console" style="display:none"></span>
1154
+ </button>
1155
+ <button class="dc-tab" data-tab="network">
1156
+ Network<span class="dc-tab-count" id="cnt-network" style="display:none"></span>
1157
+ </button>
740
1158
  <button class="dc-tab" data-tab="storage">Storage</button>
741
1159
  <button class="dc-tab" data-tab="dom">DOM</button>
742
1160
  <button class="dc-tab" data-tab="system">System</button>
@@ -745,15 +1163,28 @@
745
1163
 
746
1164
  <div class="dc-panel-content active" id="panel-console">
747
1165
  <div class="dc-scroll" id="console-output"></div>
1166
+
748
1167
  <div class="dc-repl">
749
- <input class="dc-input" id="repl-input" placeholder="▶ execute javascript…" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
1168
+ <input
1169
+ class="dc-input"
1170
+ id="repl-input"
1171
+ placeholder="▶ execute javascript…"
1172
+ autocomplete="off"
1173
+ autocorrect="off"
1174
+ autocapitalize="off"
1175
+ spellcheck="false"
1176
+ >
750
1177
  <button class="dc-run-btn" id="repl-run">RUN</button>
751
1178
  </div>
752
1179
  </div>
753
1180
 
754
1181
  <div class="dc-panel-content" id="panel-network">
755
1182
  <div class="dc-net-filter">
756
- <input class="dc-filter-input" id="net-filter" placeholder="Filter by URL or method…">
1183
+ <input
1184
+ class="dc-filter-input"
1185
+ id="net-filter"
1186
+ placeholder="Filter by URL or method…"
1187
+ >
757
1188
  <button class="dc-clear-btn" id="net-clear">Clear</button>
758
1189
  </div>
759
1190
  <div class="dc-scroll" id="network-output"></div>
@@ -765,9 +1196,11 @@
765
1196
  <button class="dc-st-btn" data-storage="localstorage">LocalStorage</button>
766
1197
  <button class="dc-st-btn" data-storage="sessionstorage">SessionStorage</button>
767
1198
  </div>
1199
+
768
1200
  <div class="dc-storage-actions">
769
1201
  <button class="dc-clear-btn" id="storage-refresh">↺ Refresh</button>
770
1202
  </div>
1203
+
771
1204
  <div class="dc-scroll" id="storage-output"></div>
772
1205
  </div>
773
1206
 
@@ -802,6 +1235,8 @@
802
1235
  replHistory: [],
803
1236
  replHistoryIdx: -1,
804
1237
  errorCount: 0,
1238
+ groupDepth: 0,
1239
+ nextLogId: 1,
805
1240
  settings: {
806
1241
  timestamps: true,
807
1242
  autoScroll: true,
@@ -809,19 +1244,23 @@
809
1244
  fontSize: 12,
810
1245
  monitorNetwork: true,
811
1246
  captureErrors: true,
1247
+ persistBuffer: false,
812
1248
  theme: "dark"
813
1249
  }
814
1250
  };
815
1251
  function saveSettings() {
816
1252
  try {
817
- localStorage.setItem("__dc_settings", JSON.stringify(S.settings));
1253
+ localStorage.setItem(SETTINGS_KEY, JSON.stringify(S.settings));
818
1254
  } catch (e) {
819
1255
  }
820
1256
  }
821
1257
  function loadSettings() {
822
1258
  try {
823
- const saved = localStorage.getItem("__dc_settings");
824
- if (saved) S.settings = { ...S.settings, ...JSON.parse(saved) };
1259
+ const saved = localStorage.getItem(SETTINGS_KEY);
1260
+ if (saved) {
1261
+ const parsed = JSON.parse(saved);
1262
+ S.settings = { ...S.settings, ...parsed };
1263
+ }
825
1264
  } catch (e) {
826
1265
  }
827
1266
  }
@@ -846,20 +1285,7 @@
846
1285
  return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}.${String(d.getMilliseconds()).padStart(3, "0")}`;
847
1286
  }
848
1287
  function escHtml(s) {
849
- return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
850
- }
851
- function formatArg(a) {
852
- if (a === null) return '<span style="color:#aaa">null</span>';
853
- if (a === void 0) return '<span style="color:#aaa">undefined</span>';
854
- if (typeof a === "object") {
855
- try {
856
- const s = JSON.stringify(a, null, 2);
857
- return `<pre>${escHtml(s)}</pre>`;
858
- } catch (e) {
859
- return String(a);
860
- }
861
- }
862
- return escHtml(String(a));
1288
+ return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
863
1289
  }
864
1290
  function truncUrl(url, max = 55) {
865
1291
  if (!url || url.length <= max) return url;
@@ -880,15 +1306,337 @@
880
1306
  function methodClass(m) {
881
1307
  return `m-${["GET", "POST", "PUT", "DELETE", "PATCH"].includes(m) ? m : "UNK"}`;
882
1308
  }
1309
+ function updateTabCount(tab, n) {
1310
+ const el2 = document.getElementById(`cnt-${tab}`);
1311
+ if (!el2) return;
1312
+ if (n > 0) {
1313
+ el2.textContent = n > 999 ? "999+" : n;
1314
+ el2.style.display = "inline-flex";
1315
+ } else {
1316
+ el2.style.display = "none";
1317
+ }
1318
+ }
1319
+ const SNAPSHOT_DEPTH = 8;
1320
+ const SNAPSHOT_KEYS = 100;
1321
+ function snapshotValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
1322
+ if (value === null) {
1323
+ return { kind: "null" };
1324
+ }
1325
+ if (value === void 0) {
1326
+ return { kind: "undefined" };
1327
+ }
1328
+ const type = typeof value;
1329
+ if (type === "string") {
1330
+ return { kind: "string", value };
1331
+ }
1332
+ if (type === "number") {
1333
+ if (Number.isNaN(value)) return { kind: "number", value: "NaN" };
1334
+ if (value === Infinity) return { kind: "number", value: "Infinity" };
1335
+ if (value === -Infinity) return { kind: "number", value: "-Infinity" };
1336
+ return { kind: "number", value };
1337
+ }
1338
+ if (type === "boolean") {
1339
+ return { kind: "boolean", value };
1340
+ }
1341
+ if (type === "bigint") {
1342
+ return { kind: "bigint", value: value.toString() + "n" };
1343
+ }
1344
+ if (type === "symbol") {
1345
+ return { kind: "symbol", value: String(value) };
1346
+ }
1347
+ if (type === "function") {
1348
+ return {
1349
+ kind: "function",
1350
+ value: value.name ? `[Function ${value.name}]` : "[Function]"
1351
+ };
1352
+ }
1353
+ if (type !== "object") {
1354
+ return { kind: "string", value: String(value) };
1355
+ }
1356
+ if (seen.has(value)) {
1357
+ return { kind: "circular" };
1358
+ }
1359
+ if (depth >= SNAPSHOT_DEPTH) {
1360
+ return {
1361
+ kind: "object",
1362
+ ctor: getConstructorName(value),
1363
+ entries: [],
1364
+ truncated: true
1365
+ };
1366
+ }
1367
+ seen.add(value);
1368
+ if (value instanceof Date) {
1369
+ return {
1370
+ kind: "date",
1371
+ value: isNaN(value.getTime()) ? "Invalid Date" : value.toISOString()
1372
+ };
1373
+ }
1374
+ if (value instanceof RegExp) {
1375
+ return {
1376
+ kind: "regexp",
1377
+ value: String(value)
1378
+ };
1379
+ }
1380
+ if (value instanceof Error) {
1381
+ const result2 = {
1382
+ kind: "error",
1383
+ ctor: value.name || "Error",
1384
+ message: String(value.message || ""),
1385
+ stack: value.stack ? String(value.stack) : "",
1386
+ entries: []
1387
+ };
1388
+ try {
1389
+ Object.keys(value).slice(0, SNAPSHOT_KEYS).forEach((key) => {
1390
+ result2.entries.push({
1391
+ key,
1392
+ value: snapshotValue(value[key], depth + 1, seen)
1393
+ });
1394
+ });
1395
+ } catch (e) {
1396
+ }
1397
+ return result2;
1398
+ }
1399
+ if (Array.isArray(value)) {
1400
+ const result2 = {
1401
+ kind: "array",
1402
+ length: value.length,
1403
+ entries: [],
1404
+ truncated: false
1405
+ };
1406
+ const limit2 = Math.min(value.length, SNAPSHOT_KEYS);
1407
+ for (let i = 0; i < limit2; i++) {
1408
+ try {
1409
+ result2.entries.push({
1410
+ key: String(i),
1411
+ index: true,
1412
+ value: snapshotValue(value[i], depth + 1, seen)
1413
+ });
1414
+ } catch (e) {
1415
+ result2.entries.push({
1416
+ key: String(i),
1417
+ index: true,
1418
+ value: { kind: "string", value: "[Uninspectable]" }
1419
+ });
1420
+ }
1421
+ }
1422
+ if (value.length > limit2) result2.truncated = true;
1423
+ seen.delete(value);
1424
+ return result2;
1425
+ }
1426
+ const result = {
1427
+ kind: "object",
1428
+ ctor: getConstructorName(value),
1429
+ entries: [],
1430
+ truncated: false
1431
+ };
1432
+ let keys = [];
1433
+ try {
1434
+ keys = Object.keys(value);
1435
+ } catch (e) {
1436
+ }
1437
+ const limit = Math.min(keys.length, SNAPSHOT_KEYS);
1438
+ for (let i = 0; i < limit; i++) {
1439
+ const key = keys[i];
1440
+ try {
1441
+ result.entries.push({
1442
+ key,
1443
+ value: snapshotValue(value[key], depth + 1, seen)
1444
+ });
1445
+ } catch (e) {
1446
+ result.entries.push({
1447
+ key,
1448
+ value: { kind: "string", value: "[Uninspectable]" }
1449
+ });
1450
+ }
1451
+ }
1452
+ if (keys.length > limit) result.truncated = true;
1453
+ seen.delete(value);
1454
+ return result;
1455
+ }
1456
+ function getConstructorName(value) {
1457
+ try {
1458
+ if (value && value.constructor && value.constructor.name) {
1459
+ return value.constructor.name;
1460
+ }
1461
+ } catch (e) {
1462
+ }
1463
+ return "Object";
1464
+ }
1465
+ function snapshotArgs(args) {
1466
+ return args.map((a) => snapshotValue(a));
1467
+ }
1468
+ function renderValue(value, options = {}) {
1469
+ const node = document.createElement("span");
1470
+ node.className = "dc-value";
1471
+ if (!value || typeof value.kind !== "string") {
1472
+ node.textContent = String(value);
1473
+ return node;
1474
+ }
1475
+ switch (value.kind) {
1476
+ case "null":
1477
+ node.classList.add("dc-primitive-null");
1478
+ node.textContent = "null";
1479
+ return node;
1480
+ case "undefined":
1481
+ node.classList.add("dc-primitive-undefined");
1482
+ node.textContent = "undefined";
1483
+ return node;
1484
+ case "string":
1485
+ node.classList.add("dc-primitive-string");
1486
+ node.textContent = `"${value.value}"`;
1487
+ return node;
1488
+ case "number":
1489
+ node.classList.add("dc-primitive-number");
1490
+ node.textContent = String(value.value);
1491
+ return node;
1492
+ case "boolean":
1493
+ node.classList.add("dc-primitive-boolean");
1494
+ node.textContent = String(value.value);
1495
+ return node;
1496
+ case "bigint":
1497
+ node.classList.add("dc-primitive-bigint");
1498
+ node.textContent = String(value.value);
1499
+ return node;
1500
+ case "symbol":
1501
+ node.classList.add("dc-primitive-symbol");
1502
+ node.textContent = String(value.value);
1503
+ return node;
1504
+ case "function":
1505
+ node.classList.add("dc-primitive-function");
1506
+ node.textContent = String(value.value);
1507
+ return node;
1508
+ case "date":
1509
+ node.classList.add("dc-primitive-string");
1510
+ node.textContent = `Date("${value.value}")`;
1511
+ return node;
1512
+ case "regexp":
1513
+ node.classList.add("dc-primitive-string");
1514
+ node.textContent = value.value;
1515
+ return node;
1516
+ case "circular":
1517
+ node.classList.add("dc-primitive-null");
1518
+ node.textContent = "[Circular]";
1519
+ return node;
1520
+ case "error":
1521
+ return renderExpandableValue(value, {
1522
+ summary: `${value.ctor || "Error"}: ${value.message || ""}`
1523
+ });
1524
+ case "array":
1525
+ return renderExpandableValue(value, {
1526
+ summary: `[${value.length != null ? value.length + " items" : "…"}]`
1527
+ });
1528
+ case "object":
1529
+ return renderExpandableValue(value, {
1530
+ summary: `{${value.ctor && value.ctor !== "Object" ? value.ctor + " " : ""}…}`
1531
+ });
1532
+ default:
1533
+ node.textContent = "[Unknown]";
1534
+ return node;
1535
+ }
1536
+ }
1537
+ function renderExpandableValue(value, options = {}) {
1538
+ const wrap = document.createElement("span");
1539
+ wrap.className = "dc-object-node";
1540
+ const toggle = document.createElement("span");
1541
+ toggle.className = "dc-object-toggle";
1542
+ const arrow = document.createElement("span");
1543
+ arrow.className = "dc-object-arrow";
1544
+ arrow.textContent = "▶";
1545
+ const summary = document.createElement("span");
1546
+ summary.className = "dc-object-summary";
1547
+ summary.textContent = options.summary || "{…}";
1548
+ toggle.appendChild(arrow);
1549
+ toggle.appendChild(summary);
1550
+ const children = document.createElement("div");
1551
+ children.className = "dc-object-children";
1552
+ const entries = Array.isArray(value.entries) ? value.entries : [];
1553
+ entries.forEach((entry) => {
1554
+ const row = document.createElement("div");
1555
+ row.className = "dc-object-row";
1556
+ const key = document.createElement("span");
1557
+ key.className = entry.index ? "dc-object-key-index" : "dc-object-key";
1558
+ key.textContent = entry.index ? `[${entry.key}] ` : `${entry.key}: `;
1559
+ row.appendChild(key);
1560
+ row.appendChild(renderValue(entry.value, {
1561
+ depth: (options.depth || 0) + 1
1562
+ }));
1563
+ children.appendChild(row);
1564
+ });
1565
+ if (value.truncated) {
1566
+ const more = document.createElement("div");
1567
+ more.className = "dc-object-more";
1568
+ more.textContent = "… more";
1569
+ children.appendChild(more);
1570
+ }
1571
+ if (value.kind === "error" && value.stack) {
1572
+ const stack = document.createElement("div");
1573
+ stack.className = "dc-object-more";
1574
+ stack.style.whiteSpace = "pre-wrap";
1575
+ stack.style.marginTop = "4px";
1576
+ stack.textContent = value.stack;
1577
+ children.appendChild(stack);
1578
+ }
1579
+ toggle.addEventListener("click", (e) => {
1580
+ e.stopPropagation();
1581
+ const open = children.classList.toggle("open");
1582
+ arrow.textContent = open ? "▼" : "▶";
1583
+ });
1584
+ wrap.appendChild(toggle);
1585
+ if (entries.length || value.truncated || value.stack) {
1586
+ wrap.appendChild(children);
1587
+ }
1588
+ return wrap;
1589
+ }
1590
+ function renderArgsInto(container, args) {
1591
+ if (!args || !args.length) {
1592
+ container.textContent = "";
1593
+ return;
1594
+ }
1595
+ args.forEach((arg, index) => {
1596
+ if (index > 0) {
1597
+ container.appendChild(document.createTextNode(" "));
1598
+ }
1599
+ container.appendChild(renderValue(arg));
1600
+ });
1601
+ }
883
1602
  const _orig = {};
1603
+ ["log", "warn", "error", "info", "table", "group", "groupCollapsed", "groupEnd"].forEach((level) => {
1604
+ if (typeof console[level] === "function") {
1605
+ _orig[level] = console[level].bind(console);
1606
+ }
1607
+ });
884
1608
  ["log", "warn", "error", "info"].forEach((level) => {
885
- _orig[level] = console[level].bind(console);
1609
+ if (!_orig[level]) return;
886
1610
  console[level] = (...args) => {
887
1611
  _orig[level](...args);
888
1612
  addLog(level, args);
889
1613
  };
890
1614
  });
891
- function addLog(type, args) {
1615
+ if (_orig.table) {
1616
+ console.table = (...args) => {
1617
+ _orig.table(...args);
1618
+ addTableLog(args);
1619
+ };
1620
+ }
1621
+ if (_orig.group) {
1622
+ console.group = (...args) => {
1623
+ _orig.group(...args);
1624
+ addGroupLog(false, args);
1625
+ };
1626
+ }
1627
+ if (_orig.groupCollapsed) {
1628
+ console.groupCollapsed = (...args) => {
1629
+ _orig.groupCollapsed(...args);
1630
+ addGroupLog(true, args);
1631
+ };
1632
+ }
1633
+ if (_orig.groupEnd) {
1634
+ console.groupEnd = (...args) => {
1635
+ _orig.groupEnd(...args);
1636
+ S.groupDepth = Math.max(0, S.groupDepth - 1);
1637
+ };
1638
+ }
1639
+ function addLog(type, args, options = {}) {
892
1640
  if (type === "error") {
893
1641
  S.errorCount++;
894
1642
  const b = el.badge();
@@ -897,139 +1645,763 @@
897
1645
  b.style.display = "flex";
898
1646
  }
899
1647
  }
900
- S.logs.push({ type, args, ts: ts() });
901
- if (S.logs.length > S.settings.maxEntries) S.logs.shift();
1648
+ const entry = {
1649
+ id: S.nextLogId++,
1650
+ type,
1651
+ args: snapshotArgs(args),
1652
+ ts: ts(),
1653
+ groupDepth: options.groupDepth != null ? options.groupDepth : S.groupDepth,
1654
+ restored: false
1655
+ };
1656
+ S.logs.push(entry);
1657
+ trimLogs();
1658
+ persistBuffer();
1659
+ updateTabCount("console", S.logs.length);
1660
+ if (S.activeTab === "console") {
1661
+ renderConsole();
1662
+ }
1663
+ }
1664
+ function addTableLog(args) {
1665
+ const source = args.length > 0 ? args[0] : void 0;
1666
+ const entry = {
1667
+ id: S.nextLogId++,
1668
+ type: "table",
1669
+ args: snapshotArgs(args),
1670
+ table: buildTableSnapshot(source),
1671
+ ts: ts(),
1672
+ groupDepth: S.groupDepth,
1673
+ restored: false
1674
+ };
1675
+ S.logs.push(entry);
1676
+ trimLogs();
1677
+ persistBuffer();
1678
+ updateTabCount("console", S.logs.length);
1679
+ if (S.activeTab === "console") {
1680
+ renderConsole();
1681
+ }
1682
+ }
1683
+ function addGroupLog(collapsed, args) {
1684
+ const depth = S.groupDepth;
1685
+ const entry = {
1686
+ id: S.nextLogId++,
1687
+ type: "group",
1688
+ args: snapshotArgs(args),
1689
+ ts: ts(),
1690
+ groupDepth: depth,
1691
+ collapsed: !!collapsed,
1692
+ restored: false
1693
+ };
1694
+ S.logs.push(entry);
1695
+ S.groupDepth++;
1696
+ trimLogs();
1697
+ persistBuffer();
902
1698
  updateTabCount("console", S.logs.length);
903
- if (S.activeTab === "console") renderConsole();
1699
+ if (S.activeTab === "console") {
1700
+ renderConsole();
1701
+ }
1702
+ }
1703
+ function buildTableSnapshot(source) {
1704
+ if (!Array.isArray(source)) {
1705
+ return {
1706
+ tabular: false,
1707
+ value: snapshotValue(source)
1708
+ };
1709
+ }
1710
+ const rows = source.slice(0, SNAPSHOT_KEYS).map((row, index) => {
1711
+ const cells = {};
1712
+ if (row && typeof row === "object" && !Array.isArray(row)) {
1713
+ Object.keys(row).slice(0, SNAPSHOT_KEYS).forEach((key) => {
1714
+ cells[key] = snapshotValue(row[key]);
1715
+ });
1716
+ } else {
1717
+ cells.value = snapshotValue(row);
1718
+ }
1719
+ return {
1720
+ index,
1721
+ cells
1722
+ };
1723
+ });
1724
+ return {
1725
+ tabular: true,
1726
+ rows,
1727
+ truncated: source.length > rows.length
1728
+ };
1729
+ }
1730
+ function trimLogs() {
1731
+ const max = Math.max(1, Number(S.settings.maxEntries) || 500);
1732
+ while (S.logs.length > max) {
1733
+ S.logs.shift();
1734
+ }
904
1735
  }
905
1736
  function renderConsole() {
906
1737
  const o = el.consOut();
907
1738
  if (!o) return;
1739
+ o.innerHTML = "";
1740
+ if (!S.logs.length) {
1741
+ o.innerHTML = '<div class="dc-empty">No console entries yet</div>';
1742
+ return;
1743
+ }
908
1744
  const frag = document.createDocumentFragment();
909
- S.logs.forEach((log) => {
910
- const d = document.createElement("div");
911
- d.className = `dc-entry dc-entry-${log.type}`;
912
- const tsHtml = S.settings.timestamps ? `<span class="dc-ts">${log.ts}</span>` : "";
913
- const msgHtml = log.args.map(formatArg).join(" ");
914
- d.innerHTML = `${tsHtml}<span>${msgHtml}</span>`;
915
- d.style.fontSize = S.settings.fontSize + "px";
916
- frag.appendChild(d);
1745
+ let previousRestored = null;
1746
+ let skipUntil = -1;
1747
+ S.logs.forEach((log, index) => {
1748
+ if (!log.restored && previousRestored === true) {
1749
+ frag.appendChild(createReloadSeparator());
1750
+ }
1751
+ previousRestored = !!log.restored;
1752
+ if (index < skipUntil) return;
1753
+ const entry = createConsoleEntry(log, index);
1754
+ if (entry) {
1755
+ frag.appendChild(entry);
1756
+ if (log.type === "group" && entry.dataset.groupEnd) {
1757
+ skipUntil = parseInt(entry.dataset.groupEnd, 10);
1758
+ }
1759
+ }
917
1760
  });
918
- o.innerHTML = "";
919
1761
  o.appendChild(frag);
920
- if (S.settings.autoScroll) o.scrollTop = o.scrollHeight;
1762
+ if (S.settings.autoScroll) {
1763
+ o.scrollTop = o.scrollHeight;
1764
+ }
921
1765
  }
922
- if (S.settings.captureErrors) {
923
- window.onerror = (msg, src, line, col) => {
924
- addLog("error", [`${msg} @ ${src}:${line}:${col}`]);
1766
+ function createReloadSeparator() {
1767
+ const separator = document.createElement("div");
1768
+ separator.className = "dc-reload-separator";
1769
+ separator.textContent = "reloaded";
1770
+ return separator;
1771
+ }
1772
+ function createConsoleEntry(log, index) {
1773
+ if (log.type === "group") {
1774
+ return createGroupEntry(log, index);
1775
+ }
1776
+ if (log.type === "table") {
1777
+ return createTableEntry(log);
1778
+ }
1779
+ const d = document.createElement("div");
1780
+ d.className = `dc-entry dc-entry-${log.type}`;
1781
+ if (log.groupDepth > 0) {
1782
+ d.classList.add("dc-grouped");
1783
+ }
1784
+ d.style.fontSize = S.settings.fontSize + "px";
1785
+ if (S.settings.timestamps) {
1786
+ const timestamp = document.createElement("span");
1787
+ timestamp.className = "dc-ts";
1788
+ timestamp.textContent = log.ts;
1789
+ d.appendChild(timestamp);
1790
+ }
1791
+ const body = document.createElement("span");
1792
+ body.className = "dc-entry-body";
1793
+ renderArgsInto(body, log.args);
1794
+ d.appendChild(body);
1795
+ return d;
1796
+ }
1797
+ function createGroupEntry(log, index) {
1798
+ const d = document.createElement("div");
1799
+ d.className = "dc-entry dc-entry-group";
1800
+ d.style.fontSize = S.settings.fontSize + "px";
1801
+ const arrow = document.createElement("span");
1802
+ arrow.className = "dc-group-arrow";
1803
+ arrow.textContent = "▶";
1804
+ const label = document.createElement("span");
1805
+ label.className = "dc-entry-body";
1806
+ renderArgsInto(label, log.args);
1807
+ if (S.settings.timestamps) {
1808
+ const timestamp = document.createElement("span");
1809
+ timestamp.className = "dc-ts";
1810
+ timestamp.textContent = log.ts;
1811
+ d.appendChild(timestamp);
1812
+ }
1813
+ d.appendChild(arrow);
1814
+ d.appendChild(label);
1815
+ const children = document.createElement("div");
1816
+ children.className = "dc-group-children";
1817
+ const end = findGroupEnd(index, log.groupDepth);
1818
+ if (end > index + 1) {
1819
+ const childFrag = document.createDocumentFragment();
1820
+ for (let i = index + 1; i < end; i++) {
1821
+ const child = createConsoleEntry(S.logs[i], i);
1822
+ if (child) {
1823
+ childFrag.appendChild(child);
1824
+ }
1825
+ }
1826
+ children.appendChild(childFrag);
1827
+ if (log.collapsed) {
1828
+ children.classList.add("collapsed");
1829
+ } else {
1830
+ arrow.classList.add("open");
1831
+ }
1832
+ d.addEventListener("click", (e) => {
1833
+ e.stopPropagation();
1834
+ const collapsed = children.classList.toggle("collapsed");
1835
+ arrow.classList.toggle("open", !collapsed);
1836
+ });
1837
+ d.dataset.groupEnd = String(end);
1838
+ d.appendChild(children);
1839
+ }
1840
+ return d;
1841
+ }
1842
+ function findGroupEnd(index, depth) {
1843
+ for (let i = index + 1; i < S.logs.length; i++) {
1844
+ const candidate = S.logs[i];
1845
+ if (candidate.type === "group" && candidate.groupDepth <= depth) {
1846
+ return i;
1847
+ }
1848
+ if (candidate.groupDepth < depth) {
1849
+ return i;
1850
+ }
1851
+ }
1852
+ return S.logs.length;
1853
+ }
1854
+ function createTableEntry(log) {
1855
+ const d = document.createElement("div");
1856
+ d.className = "dc-entry dc-entry-table";
1857
+ if (log.groupDepth > 0) {
1858
+ d.classList.add("dc-grouped");
1859
+ }
1860
+ d.style.fontSize = S.settings.fontSize + "px";
1861
+ if (S.settings.timestamps) {
1862
+ const timestamp = document.createElement("span");
1863
+ timestamp.className = "dc-ts";
1864
+ timestamp.textContent = log.ts;
1865
+ d.appendChild(timestamp);
1866
+ }
1867
+ const body = document.createElement("div");
1868
+ body.className = "dc-entry-body";
1869
+ const tableData = log.table;
1870
+ if (!tableData || !tableData.tabular) {
1871
+ renderArgsInto(body, log.args);
1872
+ d.appendChild(body);
1873
+ return d;
1874
+ }
1875
+ const wrap = document.createElement("div");
1876
+ wrap.className = "dc-table-wrap";
1877
+ const table = document.createElement("table");
1878
+ table.className = "dc-console-table";
1879
+ const columns = /* @__PURE__ */ new Set();
1880
+ tableData.rows.forEach((row) => {
1881
+ Object.keys(row.cells).forEach((key) => columns.add(key));
1882
+ });
1883
+ const columnList = [...columns];
1884
+ const thead = document.createElement("thead");
1885
+ const headRow = document.createElement("tr");
1886
+ const indexHead = document.createElement("th");
1887
+ indexHead.textContent = "(index)";
1888
+ headRow.appendChild(indexHead);
1889
+ columnList.forEach((column) => {
1890
+ const th = document.createElement("th");
1891
+ th.textContent = column;
1892
+ headRow.appendChild(th);
1893
+ });
1894
+ thead.appendChild(headRow);
1895
+ table.appendChild(thead);
1896
+ const tbody = document.createElement("tbody");
1897
+ tableData.rows.forEach((row) => {
1898
+ const tr = document.createElement("tr");
1899
+ const indexCell = document.createElement("td");
1900
+ indexCell.textContent = String(row.index);
1901
+ tr.appendChild(indexCell);
1902
+ columnList.forEach((column) => {
1903
+ const td = document.createElement("td");
1904
+ if (row.cells[column]) {
1905
+ td.appendChild(renderValue(row.cells[column]));
1906
+ } else {
1907
+ td.textContent = "—";
1908
+ }
1909
+ tr.appendChild(td);
1910
+ });
1911
+ tbody.appendChild(tr);
1912
+ });
1913
+ table.appendChild(tbody);
1914
+ wrap.appendChild(table);
1915
+ body.appendChild(wrap);
1916
+ if (tableData.truncated) {
1917
+ const note = document.createElement("div");
1918
+ note.className = "dc-object-more";
1919
+ note.textContent = "… more rows";
1920
+ body.appendChild(note);
1921
+ }
1922
+ d.appendChild(body);
1923
+ return d;
1924
+ }
1925
+ let _origOnError = window.onerror;
1926
+ function installErrorCapture() {
1927
+ if (!S.settings.captureErrors) return;
1928
+ _origOnError = window.onerror;
1929
+ window.onerror = function(msg, src, line, col, error) {
1930
+ addLog("error", [
1931
+ error || `${msg} @ ${src}:${line}:${col}`
1932
+ ]);
1933
+ if (typeof _origOnError === "function") {
1934
+ try {
1935
+ return _origOnError.apply(this, arguments);
1936
+ } catch (e) {
1937
+ }
1938
+ }
925
1939
  return false;
926
1940
  };
927
- window.addEventListener("unhandledrejection", (e) => {
928
- addLog("error", [`Unhandled Promise Rejection: ${e.reason}`]);
929
- });
1941
+ window.addEventListener("unhandledrejection", onUnhandledRejection);
1942
+ }
1943
+ function onUnhandledRejection(e) {
1944
+ addLog("error", [
1945
+ e.reason instanceof Error ? e.reason : `Unhandled Promise Rejection: ${String(e.reason)}`
1946
+ ]);
930
1947
  }
1948
+ installErrorCapture();
931
1949
  const _origFetch = window.fetch;
932
- window.fetch = async (...args) => {
933
- const startTime = Date.now();
934
- let method = "GET";
935
- let url = args[0] instanceof Request ? args[0].url : String(args[0]);
936
- if (args[1] && args[1].method) method = args[1].method.toUpperCase();
937
- if (args[0] instanceof Request) method = args[0].method.toUpperCase();
938
- const req = { method, url, ts: ts(), startTime, status: null, duration: null, headers: {}, error: null };
1950
+ function isBinaryContentType(contentType) {
1951
+ if (!contentType) return false;
1952
+ const type = contentType.toLowerCase();
1953
+ return type.includes("application/octet-stream") || type.includes("image/") || type.includes("audio/") || type.includes("video/") || type.includes("application/pdf") || type.includes("application/zip") || type.includes("application/gzip") || type.includes("application/wasm");
1954
+ }
1955
+ function shouldCaptureTextBody(contentType) {
1956
+ if (!contentType) return true;
1957
+ return !isBinaryContentType(contentType);
1958
+ }
1959
+ async function captureRequestBody(input, init) {
939
1960
  try {
940
- const res = await _origFetch(...args);
941
- req.status = res.status;
942
- req.statusText = res.statusText;
943
- req.duration = Date.now() - startTime;
1961
+ if (init && init.body != null) {
1962
+ return serializeRequestBody(init.body);
1963
+ }
1964
+ if (input instanceof Request) {
1965
+ const clone = input.clone();
1966
+ if (clone.bodyUsed || !shouldCaptureTextBody(clone.headers.get("content-type"))) {
1967
+ return null;
1968
+ }
1969
+ return await clone.text();
1970
+ }
1971
+ } catch (e) {
1972
+ }
1973
+ return null;
1974
+ }
1975
+ function serializeRequestBody(body) {
1976
+ if (body == null) return null;
1977
+ if (typeof body === "string") {
1978
+ return body;
1979
+ }
1980
+ if (body instanceof URLSearchParams) {
1981
+ return body.toString();
1982
+ }
1983
+ if (body instanceof FormData) {
1984
+ const result = {};
944
1985
  try {
945
- res.headers.forEach((v, k) => {
946
- req.headers[k] = v;
1986
+ body.forEach((value, key) => {
1987
+ if (value instanceof File) {
1988
+ result[key] = `[File ${value.name}, ${value.size} bytes]`;
1989
+ } else {
1990
+ result[key] = String(value);
1991
+ }
947
1992
  });
948
1993
  } catch (e) {
1994
+ return "[FormData]";
949
1995
  }
950
- addNetReq(req);
951
- return res;
1996
+ return JSON.stringify(result, null, 2);
1997
+ }
1998
+ if (body instanceof Blob) {
1999
+ return isBinaryContentType(body.type) ? null : `[Blob ${body.type || "unknown"}, ${body.size} bytes]`;
2000
+ }
2001
+ if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
2002
+ return null;
2003
+ }
2004
+ try {
2005
+ return JSON.stringify(body, null, 2);
952
2006
  } catch (e) {
953
- req.error = e.message;
954
- req.duration = Date.now() - startTime;
955
- addNetReq(req);
956
- throw e;
2007
+ return String(body);
957
2008
  }
958
- };
2009
+ }
2010
+ async function captureResponseBody(response) {
2011
+ try {
2012
+ const contentType = response.headers.get("content-type") || "";
2013
+ if (!shouldCaptureTextBody(contentType)) {
2014
+ return null;
2015
+ }
2016
+ const clone = response.clone();
2017
+ if (clone.bodyUsed) return null;
2018
+ return await clone.text();
2019
+ } catch (e) {
2020
+ return null;
2021
+ }
2022
+ }
2023
+ if (_origFetch) {
2024
+ window.fetch = async (...args) => {
2025
+ if (!S.settings.monitorNetwork) {
2026
+ return _origFetch.apply(window, args);
2027
+ }
2028
+ const startTime = Date.now();
2029
+ let method = "GET";
2030
+ let url = "";
2031
+ try {
2032
+ url = args[0] instanceof Request ? args[0].url : String(args[0]);
2033
+ if (args[1] && args[1].method) {
2034
+ method = String(args[1].method).toUpperCase();
2035
+ }
2036
+ if (args[0] instanceof Request) {
2037
+ method = args[0].method.toUpperCase();
2038
+ }
2039
+ } catch (e) {
2040
+ url = String(args[0]);
2041
+ }
2042
+ const req = {
2043
+ method,
2044
+ url,
2045
+ ts: ts(),
2046
+ startTime,
2047
+ status: null,
2048
+ statusText: "",
2049
+ duration: null,
2050
+ headers: {},
2051
+ requestHeaders: {},
2052
+ requestBody: null,
2053
+ responseBody: null,
2054
+ error: null
2055
+ };
2056
+ try {
2057
+ const request = args[0] instanceof Request ? args[0] : new Request(url, args[1]);
2058
+ request.headers.forEach((v, k) => {
2059
+ req.requestHeaders[k] = v;
2060
+ });
2061
+ } catch (e) {
2062
+ }
2063
+ req.requestBody = await captureRequestBody(args[0], args[1]);
2064
+ try {
2065
+ const res = await _origFetch.apply(window, args);
2066
+ req.status = res.status;
2067
+ req.statusText = res.statusText;
2068
+ req.duration = Date.now() - startTime;
2069
+ try {
2070
+ res.headers.forEach((v, k) => {
2071
+ req.headers[k] = v;
2072
+ });
2073
+ } catch (e) {
2074
+ }
2075
+ addNetReq(req);
2076
+ captureResponseBody(res).then((body) => {
2077
+ if (body == null) return;
2078
+ req.responseBody = body;
2079
+ if (S.activeTab === "network") {
2080
+ renderNetwork();
2081
+ }
2082
+ persistBuffer();
2083
+ }).catch(() => {
2084
+ });
2085
+ return res;
2086
+ } catch (e) {
2087
+ req.error = e && e.message ? e.message : String(e);
2088
+ req.duration = Date.now() - startTime;
2089
+ addNetReq(req);
2090
+ throw e;
2091
+ }
2092
+ };
2093
+ }
959
2094
  const _origOpen = XMLHttpRequest.prototype.open;
960
2095
  const _origSend = XMLHttpRequest.prototype.send;
961
2096
  XMLHttpRequest.prototype.open = function(method, url) {
962
- this.__dcReq = { method: method.toUpperCase(), url: String(url), ts: ts(), startTime: 0 };
2097
+ if (!S.settings.monitorNetwork) {
2098
+ return _origOpen.apply(this, arguments);
2099
+ }
2100
+ this.__dcReq = {
2101
+ method: String(method).toUpperCase(),
2102
+ url: String(url),
2103
+ ts: ts(),
2104
+ startTime: 0,
2105
+ requestBody: null,
2106
+ responseBody: null,
2107
+ requestHeaders: {}
2108
+ };
963
2109
  return _origOpen.apply(this, arguments);
964
2110
  };
965
- XMLHttpRequest.prototype.send = function(body) {
2111
+ const _origSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
2112
+ XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
966
2113
  if (this.__dcReq) {
967
- const req = this.__dcReq;
968
- req.startTime = Date.now();
969
- this.addEventListener("loadend", () => {
970
- req.status = this.status;
971
- req.statusText = this.statusText;
972
- req.duration = Date.now() - req.startTime;
973
- req.headers = {};
974
- const raw = this.getAllResponseHeaders();
975
- if (raw) raw.split("\r\n").filter(Boolean).forEach((line) => {
976
- const i = line.indexOf(": ");
977
- if (i > 0) req.headers[line.slice(0, i)] = line.slice(i + 2);
978
- });
979
- req.error = this.status === 0 ? "Request failed" : null;
980
- addNetReq(req);
981
- });
2114
+ this.__dcReq.requestHeaders[name] = String(value);
982
2115
  }
2116
+ return _origSetRequestHeader.apply(this, arguments);
2117
+ };
2118
+ XMLHttpRequest.prototype.send = function(body) {
2119
+ if (!S.settings.monitorNetwork || !this.__dcReq) {
2120
+ return _origSend.apply(this, arguments);
2121
+ }
2122
+ const req = this.__dcReq;
2123
+ req.startTime = Date.now();
2124
+ req.requestBody = serializeRequestBody(body);
2125
+ this.addEventListener("loadend", () => {
2126
+ req.status = this.status;
2127
+ req.statusText = this.statusText;
2128
+ req.duration = Date.now() - req.startTime;
2129
+ req.headers = {};
2130
+ try {
2131
+ const raw = this.getAllResponseHeaders();
2132
+ if (raw) {
2133
+ raw.split("\r\n").filter(Boolean).forEach((line) => {
2134
+ const i = line.indexOf(": ");
2135
+ if (i > 0) {
2136
+ req.headers[line.slice(0, i)] = line.slice(i + 2);
2137
+ }
2138
+ });
2139
+ }
2140
+ } catch (e) {
2141
+ }
2142
+ req.error = this.status === 0 ? "Request failed" : null;
2143
+ try {
2144
+ const responseType = this.responseType || "";
2145
+ if (responseType === "" || responseType === "text") {
2146
+ const contentType = req.headers["content-type"] || "";
2147
+ if (shouldCaptureTextBody(contentType)) {
2148
+ req.responseBody = this.responseText || null;
2149
+ }
2150
+ }
2151
+ } catch (e) {
2152
+ }
2153
+ addNetReq(req);
2154
+ });
983
2155
  return _origSend.apply(this, arguments);
984
2156
  };
985
2157
  function addNetReq(req) {
986
2158
  S.network.unshift(req);
987
- if (S.network.length > S.settings.maxEntries) S.network.pop();
2159
+ if (S.network.length > S.settings.maxEntries) {
2160
+ S.network.pop();
2161
+ }
2162
+ persistBuffer();
988
2163
  updateTabCount("network", S.network.length);
989
- if (S.activeTab === "network") renderNetwork();
2164
+ if (S.activeTab === "network") {
2165
+ renderNetwork();
2166
+ }
990
2167
  }
991
2168
  function renderNetwork(filter) {
992
2169
  const o = el.netOut();
993
2170
  if (!o) return;
994
2171
  filter = filter !== void 0 ? filter : el.netFlt() ? el.netFlt().value : "";
995
- const filtered = filter ? S.network.filter((r) => r.url.toLowerCase().includes(filter.toLowerCase()) || r.method.toLowerCase().includes(filter.toLowerCase())) : S.network;
996
- o.innerHTML = filtered.map((req, i) => {
997
- const sc = req.error ? "s-err" : statusClass(req.status);
998
- const mc = methodClass(req.method);
999
- const statusLabel = req.status ? `<span class="dc-req-status ${sc}">${req.status}</span>` : req.error ? `<span class="dc-req-status s-err">ERR</span>` : '<span class="dc-req-status" style="color:var(--dc-text2)">…</span>';
1000
- const durLabel = req.duration != null ? `${req.duration}ms · ` : "";
1001
- const headersHtml = Object.keys(req.headers).length ? Object.entries(req.headers).map(([k, v]) => `<div class="dc-detail-row"><span class="dc-detail-key">${escHtml(k)}: </span>${escHtml(v)}</div>`).join("") : '<div class="dc-detail-row" style="color:var(--dc-text2)">none</div>';
1002
- return `<div class="dc-req" id="req-${i}" onclick="window.__dcToggleReq(${i})">
1003
- <div class="dc-req-top">
1004
- <span class="dc-method ${mc}">${req.method}</span>
1005
- <span class="dc-req-url" title="${escHtml(req.url)}">${escHtml(truncUrl(req.url))}</span>
1006
- ${statusLabel}
1007
- </div>
1008
- <div class="dc-req-meta">${durLabel}${req.ts}${req.error ? " · " + escHtml(req.error) : ""}</div>
1009
- <div class="dc-req-detail" id="req-detail-${i}">
1010
- <div class="dc-detail-section">
1011
- <div class="dc-detail-title">General</div>
1012
- <div class="dc-detail-row"><span class="dc-detail-key">URL: </span>${escHtml(req.url)}</div>
1013
- <div class="dc-detail-row"><span class="dc-detail-key">Method: </span>${req.method}</div>
1014
- <div class="dc-detail-row"><span class="dc-detail-key">Status: </span>${req.status || req.error || "–"} ${req.statusText || ""}</div>
1015
- <div class="dc-detail-row"><span class="dc-detail-key">Duration: </span>${req.duration != null ? req.duration + "ms" : "–"}</div>
1016
- </div>
1017
- <div class="dc-detail-section">
1018
- <div class="dc-detail-title">Response Headers</div>
1019
- ${headersHtml}
1020
- </div>
1021
- </div>
1022
- </div>`;
1023
- }).join("") || '<div class="dc-empty">No requests captured yet</div>';
1024
- }
1025
- window.__dcToggleReq = function(i) {
1026
- const detail = document.getElementById(`req-detail-${i}`);
1027
- const card = document.getElementById(`req-${i}`);
1028
- if (!detail) return;
1029
- const isOpen = detail.classList.contains("open");
1030
- detail.classList.toggle("open", !isOpen);
1031
- card.classList.toggle("expanded", !isOpen);
1032
- };
2172
+ const needle = filter.trim().toLowerCase();
2173
+ const filtered = needle ? S.network.filter(
2174
+ (r) => String(r.url).toLowerCase().includes(needle) || String(r.method).toLowerCase().includes(needle)
2175
+ ) : S.network;
2176
+ o.innerHTML = "";
2177
+ if (!filtered.length) {
2178
+ o.innerHTML = '<div class="dc-empty">No requests captured yet</div>';
2179
+ return;
2180
+ }
2181
+ filtered.forEach((req) => {
2182
+ o.appendChild(createNetworkEntry(req));
2183
+ });
2184
+ }
2185
+ function createNetworkEntry(req) {
2186
+ const card = document.createElement("div");
2187
+ card.className = "dc-req";
2188
+ const top = document.createElement("div");
2189
+ top.className = "dc-req-top";
2190
+ const method = document.createElement("span");
2191
+ method.className = `dc-method ${methodClass(req.method)}`;
2192
+ method.textContent = req.method;
2193
+ const url = document.createElement("span");
2194
+ url.className = "dc-req-url";
2195
+ url.title = req.url;
2196
+ url.textContent = truncUrl(req.url);
2197
+ const status = document.createElement("span");
2198
+ status.className = `dc-req-status ${req.error ? "s-err" : statusClass(req.status)}`;
2199
+ status.textContent = req.status ? String(req.status) : req.error ? "ERR" : "…";
2200
+ top.appendChild(method);
2201
+ top.appendChild(url);
2202
+ top.appendChild(status);
2203
+ const meta = document.createElement("div");
2204
+ meta.className = "dc-req-meta";
2205
+ const dur = req.duration != null ? `${req.duration}ms · ` : "";
2206
+ meta.textContent = `${dur}${req.ts}` + (req.error ? ` · ${req.error}` : "");
2207
+ const detail = document.createElement("div");
2208
+ detail.className = "dc-req-detail";
2209
+ buildNetworkDetail(detail, req);
2210
+ card.appendChild(top);
2211
+ card.appendChild(meta);
2212
+ card.appendChild(detail);
2213
+ card.addEventListener("click", (e) => {
2214
+ if (e.target.closest(".dc-body-toggle")) return;
2215
+ const open = detail.classList.toggle("open");
2216
+ card.classList.toggle("expanded", open);
2217
+ });
2218
+ return card;
2219
+ }
2220
+ function buildNetworkDetail(detail, req) {
2221
+ addDetailSection(detail, "General", [
2222
+ ["URL", req.url],
2223
+ ["Method", req.method],
2224
+ ["Status", `${req.status || req.error || "–"} ${req.statusText || ""}`],
2225
+ ["Duration", req.duration != null ? `${req.duration}ms` : "–"]
2226
+ ]);
2227
+ addHeaderSection(
2228
+ detail,
2229
+ "Request Headers",
2230
+ req.requestHeaders
2231
+ );
2232
+ addBodySection(
2233
+ detail,
2234
+ "Request Body",
2235
+ req.requestBody
2236
+ );
2237
+ addHeaderSection(
2238
+ detail,
2239
+ "Response Headers",
2240
+ req.headers
2241
+ );
2242
+ addBodySection(
2243
+ detail,
2244
+ "Response Body",
2245
+ req.responseBody
2246
+ );
2247
+ }
2248
+ function addDetailSection(parent, title, rows) {
2249
+ const section = document.createElement("div");
2250
+ section.className = "dc-detail-section";
2251
+ const heading = document.createElement("div");
2252
+ heading.className = "dc-detail-title";
2253
+ heading.textContent = title;
2254
+ section.appendChild(heading);
2255
+ rows.forEach(([key, value]) => {
2256
+ const row = document.createElement("div");
2257
+ row.className = "dc-detail-row";
2258
+ const keyEl = document.createElement("span");
2259
+ keyEl.className = "dc-detail-key";
2260
+ keyEl.textContent = `${key}: `;
2261
+ row.appendChild(keyEl);
2262
+ row.appendChild(document.createTextNode(String(value ?? "–")));
2263
+ section.appendChild(row);
2264
+ });
2265
+ parent.appendChild(section);
2266
+ }
2267
+ function addHeaderSection(parent, title, headers) {
2268
+ const section = document.createElement("div");
2269
+ section.className = "dc-detail-section";
2270
+ const heading = document.createElement("div");
2271
+ heading.className = "dc-detail-title";
2272
+ heading.textContent = title;
2273
+ section.appendChild(heading);
2274
+ const entries = headers ? Object.entries(headers) : [];
2275
+ if (!entries.length) {
2276
+ const empty = document.createElement("div");
2277
+ empty.className = "dc-detail-row";
2278
+ empty.style.color = "var(--dc-text2)";
2279
+ empty.textContent = "none";
2280
+ section.appendChild(empty);
2281
+ } else {
2282
+ entries.forEach(([key, value]) => {
2283
+ const row = document.createElement("div");
2284
+ row.className = "dc-detail-row";
2285
+ const keyEl = document.createElement("span");
2286
+ keyEl.className = "dc-detail-key";
2287
+ keyEl.textContent = `${key}: `;
2288
+ row.appendChild(keyEl);
2289
+ row.appendChild(document.createTextNode(String(value)));
2290
+ section.appendChild(row);
2291
+ });
2292
+ }
2293
+ parent.appendChild(section);
2294
+ }
2295
+ function addBodySection(parent, title, body) {
2296
+ if (body == null || body === "") return;
2297
+ const section = document.createElement("div");
2298
+ section.className = "dc-detail-section";
2299
+ const heading = document.createElement("div");
2300
+ heading.className = "dc-detail-title";
2301
+ heading.textContent = title;
2302
+ section.appendChild(heading);
2303
+ const block = document.createElement("div");
2304
+ block.className = "dc-body-block";
2305
+ const text = String(body);
2306
+ if (text.length <= BODY_DISPLAY_LIMIT) {
2307
+ const pre = document.createElement("pre");
2308
+ pre.className = "dc-body-pre";
2309
+ pre.textContent = formatBodyForDisplay(text);
2310
+ block.appendChild(pre);
2311
+ } else {
2312
+ const preview = document.createElement("pre");
2313
+ preview.className = "dc-body-pre";
2314
+ preview.textContent = formatBodyForDisplay(text.slice(0, BODY_DISPLAY_LIMIT)) + "\n…";
2315
+ const full = document.createElement("pre");
2316
+ full.className = "dc-body-full";
2317
+ full.textContent = formatBodyForDisplay(text);
2318
+ const note = document.createElement("div");
2319
+ note.className = "dc-body-truncated";
2320
+ note.textContent = `Showing first 10 KB of ${formatBytes(text.length)}.`;
2321
+ const toggle = document.createElement("button");
2322
+ toggle.className = "dc-body-toggle";
2323
+ toggle.textContent = "Show full";
2324
+ toggle.addEventListener("click", (e) => {
2325
+ e.stopPropagation();
2326
+ const open = full.classList.toggle("open");
2327
+ toggle.textContent = open ? "Hide full" : "Show full";
2328
+ });
2329
+ block.appendChild(preview);
2330
+ block.appendChild(note);
2331
+ block.appendChild(toggle);
2332
+ block.appendChild(full);
2333
+ }
2334
+ section.appendChild(block);
2335
+ parent.appendChild(section);
2336
+ }
2337
+ function formatBodyForDisplay(text) {
2338
+ const trimmed = text.trim();
2339
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
2340
+ try {
2341
+ return JSON.stringify(JSON.parse(trimmed), null, 2);
2342
+ } catch (e) {
2343
+ }
2344
+ }
2345
+ return text;
2346
+ }
2347
+ function formatBytes(bytes) {
2348
+ if (bytes < 1024) return `${bytes} B`;
2349
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
2350
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
2351
+ }
2352
+ function persistBuffer() {
2353
+ if (!S.settings.persistBuffer) return;
2354
+ try {
2355
+ const payload = {
2356
+ version: VERSION,
2357
+ savedAt: Date.now(),
2358
+ logs: S.logs.slice(-S.settings.maxEntries),
2359
+ network: S.network.slice(0, S.settings.maxEntries)
2360
+ };
2361
+ sessionStorage.setItem(
2362
+ BUFFER_KEY,
2363
+ JSON.stringify(payload)
2364
+ );
2365
+ } catch (e) {
2366
+ }
2367
+ }
2368
+ function restoreBuffer() {
2369
+ if (!S.settings.persistBuffer) return;
2370
+ try {
2371
+ const raw = sessionStorage.getItem(BUFFER_KEY);
2372
+ if (!raw) return;
2373
+ const payload = JSON.parse(raw);
2374
+ if (!payload || typeof payload !== "object") return;
2375
+ if (Array.isArray(payload.logs)) {
2376
+ S.logs = payload.logs.map((log) => ({
2377
+ ...log,
2378
+ restored: true
2379
+ }));
2380
+ }
2381
+ if (Array.isArray(payload.network)) {
2382
+ S.network = payload.network;
2383
+ }
2384
+ S.logs.forEach((log) => {
2385
+ if (log.id >= S.nextLogId) {
2386
+ S.nextLogId = log.id + 1;
2387
+ }
2388
+ });
2389
+ updateTabCount("console", S.logs.length);
2390
+ updateTabCount("network", S.network.length);
2391
+ } catch (e) {
2392
+ try {
2393
+ sessionStorage.removeItem(BUFFER_KEY);
2394
+ } catch (err) {
2395
+ }
2396
+ }
2397
+ }
2398
+ function clearPersistentBuffer() {
2399
+ try {
2400
+ sessionStorage.removeItem(BUFFER_KEY);
2401
+ } catch (e) {
2402
+ }
2403
+ }
2404
+ restoreBuffer();
1033
2405
  async function loadStorage() {
1034
2406
  S.dbs = [];
1035
2407
  if (!window.indexedDB) return;
@@ -1053,7 +2425,11 @@
1053
2425
  req.onerror = () => rej(req.error);
1054
2426
  req.onsuccess = () => {
1055
2427
  const db = req.result;
1056
- const info = { name: db.name, version: db.version, stores: [] };
2428
+ const info = {
2429
+ name: db.name,
2430
+ version: db.version,
2431
+ stores: []
2432
+ };
1057
2433
  const storeNames = [...db.objectStoreNames];
1058
2434
  let pending = storeNames.length;
1059
2435
  if (!pending) {
@@ -1070,7 +2446,12 @@
1070
2446
  res(info);
1071
2447
  }
1072
2448
  }).catch(() => {
1073
- info.stores.push({ name: storeName, count: 0, keyPath: null, data: [] });
2449
+ info.stores.push({
2450
+ name: storeName,
2451
+ count: 0,
2452
+ keyPath: null,
2453
+ data: []
2454
+ });
1074
2455
  if (--pending === 0) {
1075
2456
  db.close();
1076
2457
  S.dbs.push(info);
@@ -1087,7 +2468,12 @@
1087
2468
  const store = tx.objectStore(storeName);
1088
2469
  const cntReq = store.count();
1089
2470
  cntReq.onsuccess = () => {
1090
- const info = { name: storeName, count: cntReq.result, keyPath: store.keyPath, data: [] };
2471
+ const info = {
2472
+ name: storeName,
2473
+ count: cntReq.result,
2474
+ keyPath: store.keyPath,
2475
+ data: []
2476
+ };
1091
2477
  const getReq = store.getAll(void 0, 20);
1092
2478
  getReq.onsuccess = () => {
1093
2479
  info.data = getReq.result;
@@ -1095,7 +2481,12 @@
1095
2481
  };
1096
2482
  getReq.onerror = () => res(info);
1097
2483
  };
1098
- cntReq.onerror = () => res({ name: storeName, count: 0, keyPath: null, data: [] });
2484
+ cntReq.onerror = () => res({
2485
+ name: storeName,
2486
+ count: 0,
2487
+ keyPath: null,
2488
+ data: []
2489
+ });
1099
2490
  });
1100
2491
  }
1101
2492
  function renderStorage() {
@@ -1118,29 +2509,46 @@
1118
2509
  o.innerHTML = '<div class="dc-empty">No IndexedDB databases found</div>';
1119
2510
  return;
1120
2511
  }
1121
- o.innerHTML = S.dbs.map((db, di) => `<div class="dc-db-card" id="db-card-${di}">
2512
+ o.innerHTML = S.dbs.map((db, di) => `
2513
+ <div class="dc-db-card" id="db-card-${di}">
1122
2514
  <div class="dc-db-header" onclick="window.__dcToggleDb(${di})">
1123
2515
  <div>
1124
2516
  <div class="dc-db-name">${escHtml(db.name)}</div>
1125
- <div class="dc-db-meta">v${db.version} · ${db.stores.length} store${db.stores.length !== 1 ? "s" : ""}</div>
2517
+ <div class="dc-db-meta">
2518
+ v${db.version} ·
2519
+ ${db.stores.length}
2520
+ store${db.stores.length !== 1 ? "s" : ""}
2521
+ </div>
1126
2522
  </div>
1127
2523
  <span style="color:var(--dc-text2);font-size:11px">▼</span>
1128
2524
  </div>
2525
+
1129
2526
  <div class="dc-db-body">
1130
2527
  ${db.stores.map((st, si) => `
1131
- <div class="dc-store-item" onclick="window.__dcToggleStore(${di},${si})">
1132
- <div class="dc-store-name">${escHtml(st.name)}</div>
1133
- <div class="dc-store-meta">${st.count} records · keyPath: ${st.keyPath || "none"}</div>
1134
- <div class="dc-data-viewer" id="store-${di}-${si}">
1135
- ${st.data.length ? st.data.slice(0, 20).map((item) => `
1136
- <div class="dc-data-row">
1137
- <div class="dc-data-key">· ${escHtml(getKey(item, st.keyPath))}</div>
1138
- <div class="dc-data-val">${escHtml(fmtVal(item))}</div>
1139
- </div>`).join("") : '<div style="color:var(--dc-text2);font-size:11px;padding:8px">Empty store</div>'}
2528
+ <div class="dc-store-item" onclick="window.__dcToggleStore(${di},${si})">
2529
+ <div class="dc-store-name">${escHtml(st.name)}</div>
2530
+ <div class="dc-store-meta">
2531
+ ${st.count} records ·
2532
+ keyPath: ${st.keyPath || "none"}
2533
+ </div>
2534
+
2535
+ <div class="dc-data-viewer" id="store-${di}-${si}">
2536
+ ${st.data.length ? st.data.slice(0, 20).map((item) => `
2537
+ <div class="dc-data-row">
2538
+ <div class="dc-data-key">
2539
+ · ${escHtml(getKey(item, st.keyPath))}
2540
+ </div>
2541
+ <div class="dc-data-val">
2542
+ ${escHtml(fmtVal(item))}
2543
+ </div>
2544
+ </div>
2545
+ `).join("") : '<div style="color:var(--dc-text2);font-size:11px;padding:8px">Empty store</div>'}
2546
+ </div>
1140
2547
  </div>
1141
- </div>`).join("")}
2548
+ `).join("")}
1142
2549
  </div>
1143
- </div>`).join("");
2550
+ </div>
2551
+ `).join("");
1144
2552
  }
1145
2553
  function getKey(obj, keyPath) {
1146
2554
  if (!keyPath) return "—";
@@ -1148,35 +2556,27 @@
1148
2556
  }
1149
2557
  function fmtVal(v) {
1150
2558
  if (typeof v === "object" && v !== null) {
1151
- const s2 = JSON.stringify(v, null, 2);
1152
- return s2.length > 300 ? s2.slice(0, 300) + "…" : s2;
2559
+ try {
2560
+ const s2 = JSON.stringify(v, null, 2);
2561
+ return s2.length > 300 ? s2.slice(0, 300) + "…" : s2;
2562
+ } catch (e) {
2563
+ return "[Unserializable]";
2564
+ }
1153
2565
  }
1154
2566
  const s = String(v);
1155
2567
  return s.length > 200 ? s.slice(0, 200) + "…" : s;
1156
2568
  }
1157
- function renderKV(o, storage, name) {
1158
- const items = [];
1159
- for (let i = 0; i < storage.length; i++) {
1160
- const k = storage.key(i);
1161
- items.push({ k, v: storage.getItem(k) });
1162
- }
1163
- if (!items.length) {
1164
- o.innerHTML = `<div class="dc-empty">No ${name} items</div>`;
1165
- return;
1166
- }
1167
- o.innerHTML = `<div style="padding:4px 0">` + items.map(({ k, v }) => `
1168
- <div class="dc-kv-row">
1169
- <span class="dc-kv-key">${escHtml(k)}</span>
1170
- <span class="dc-kv-val">${escHtml(fmtVal(v))}</span>
1171
- </div>`).join("") + `</div>`;
1172
- }
1173
2569
  window.__dcToggleDb = function(di) {
1174
2570
  const card = document.getElementById(`db-card-${di}`);
1175
- if (card) card.classList.toggle("open");
2571
+ if (card) {
2572
+ card.classList.toggle("open");
2573
+ }
1176
2574
  };
1177
2575
  window.__dcToggleStore = function(di, si) {
1178
2576
  const viewer = document.getElementById(`store-${di}-${si}`);
1179
- if (viewer) viewer.classList.toggle("open");
2577
+ if (viewer) {
2578
+ viewer.classList.toggle("open");
2579
+ }
1180
2580
  };
1181
2581
  function renderDomTree() {
1182
2582
  const o = el.domOut();
@@ -1218,7 +2618,9 @@
1218
2618
  close.className = "dc-tag";
1219
2619
  close.textContent = ">";
1220
2620
  label.appendChild(close);
1221
- const txtContent = nodeEl.childNodes.length ? [...nodeEl.childNodes].find((n) => n.nodeType === 3 && n.textContent.trim()) : null;
2621
+ const txtContent = nodeEl.childNodes.length ? [...nodeEl.childNodes].find(
2622
+ (n) => n.nodeType === 3 && n.textContent.trim()
2623
+ ) : null;
1222
2624
  if (txtContent) {
1223
2625
  const preview = document.createElement("span");
1224
2626
  preview.className = "dc-txt-preview";
@@ -1230,7 +2632,9 @@
1230
2632
  if (hasChildren) {
1231
2633
  const childDiv = wrap.querySelector(":scope > .dc-children");
1232
2634
  const isOpen = childDiv && childDiv.classList.contains("open");
1233
- if (childDiv) childDiv.classList.toggle("open", !isOpen);
2635
+ if (childDiv) {
2636
+ childDiv.classList.toggle("open", !isOpen);
2637
+ }
1234
2638
  arrow.textContent = !isOpen ? "▼" : "▶";
1235
2639
  }
1236
2640
  showNodeDetail(nodeEl);
@@ -1239,7 +2643,9 @@
1239
2643
  if (hasChildren) {
1240
2644
  const childDiv = document.createElement("div");
1241
2645
  childDiv.className = "dc-children";
1242
- children.forEach((child) => childDiv.appendChild(buildNode(child)));
2646
+ children.forEach((child) => {
2647
+ childDiv.appendChild(buildNode(child));
2648
+ });
1243
2649
  wrap.appendChild(childDiv);
1244
2650
  }
1245
2651
  return wrap;
@@ -1253,46 +2659,109 @@
1253
2659
  const tag = `<${targetEl.tagName.toLowerCase()}>`;
1254
2660
  const attrs = [...targetEl.attributes];
1255
2661
  const styles = window.getComputedStyle(targetEl);
1256
- d.innerHTML = `<div style="color:var(--dc-accent2);font-size:12px;font-weight:700;margin-bottom:8px">${escHtml(tag)}</div>
1257
- ${attrs.length ? `<div class="dc-detail-title">Attributes</div>${attrs.map((a) => `<div class="dc-attr-row"><span class="dc-attr-name">${escHtml(a.name)}</span>=<span class="dc-attr-val">"${escHtml(a.value)}"</span></div>`).join("")}` : ""}
1258
- <div class="dc-detail-title" style="margin-top:8px">Geometry</div>
1259
- <div class="dc-attr-row"><span class="dc-attr-name">size: </span><span class="dc-attr-val">${Math.round(targetEl.offsetWidth)}×${Math.round(targetEl.offsetHeight)}</span></div>
1260
- <div class="dc-attr-row"><span class="dc-attr-name">display: </span><span class="dc-attr-val">${styles.display}</span></div>
1261
- <div class="dc-attr-row"><span class="dc-attr-name">position: </span><span class="dc-attr-val">${styles.position}</span></div>`;
2662
+ d.innerHTML = `
2663
+ <div style="color:var(--dc-accent2);font-size:12px;font-weight:700;margin-bottom:8px">
2664
+ ${escHtml(tag)}
2665
+ </div>
2666
+
2667
+ ${attrs.length ? `
2668
+ <div class="dc-detail-title">Attributes</div>
2669
+ ${attrs.map((a) => `
2670
+ <div class="dc-attr-row">
2671
+ <span class="dc-attr-name">${escHtml(a.name)}</span>=
2672
+ <span class="dc-attr-val">"${escHtml(a.value)}"</span>
2673
+ </div>
2674
+ `).join("")}
2675
+ ` : ""}
2676
+
2677
+ <div class="dc-detail-title" style="margin-top:8px">
2678
+ Geometry
2679
+ </div>
2680
+
2681
+ <div class="dc-attr-row">
2682
+ <span class="dc-attr-name">size: </span>
2683
+ <span class="dc-attr-val">
2684
+ ${Math.round(targetEl.offsetWidth)}×${Math.round(targetEl.offsetHeight)}
2685
+ </span>
2686
+ </div>
2687
+
2688
+ <div class="dc-attr-row">
2689
+ <span class="dc-attr-name">display: </span>
2690
+ <span class="dc-attr-val">${styles.display}</span>
2691
+ </div>
2692
+
2693
+ <div class="dc-attr-row">
2694
+ <span class="dc-attr-name">position: </span>
2695
+ <span class="dc-attr-val">${styles.position}</span>
2696
+ </div>
2697
+ `;
1262
2698
  el.domOut().prepend(d);
1263
2699
  }
1264
2700
  let _pickHandler = null;
2701
+ let _pickHoverHandler = null;
1265
2702
  let _pickedEl = null;
1266
2703
  function startPicking() {
1267
2704
  S.isPicking = true;
1268
- document.getElementById("dom-pick-btn").classList.add("picking");
2705
+ const button = document.getElementById("dom-pick-btn");
2706
+ if (button) button.classList.add("picking");
1269
2707
  _pickHandler = (e) => {
1270
- if (e.target.closest("#dc-panel") || e.target.closest("#dc-fab")) return;
2708
+ if (e.target.closest("#dc-panel") || e.target.closest("#dc-fab")) {
2709
+ return;
2710
+ }
1271
2711
  e.preventDefault();
1272
2712
  e.stopPropagation();
1273
- if (_pickedEl) _pickedEl.classList.remove("dc-pick-highlight");
2713
+ if (_pickedEl) {
2714
+ _pickedEl.classList.remove("dc-pick-highlight");
2715
+ }
1274
2716
  _pickedEl = e.target;
1275
2717
  _pickedEl.classList.add("dc-pick-highlight");
1276
2718
  showNodeDetail(_pickedEl);
1277
2719
  stopPicking();
1278
2720
  };
1279
- document.addEventListener("click", _pickHandler, true);
1280
- document.addEventListener("mouseover", (e) => {
2721
+ _pickHoverHandler = (e) => {
1281
2722
  if (!S.isPicking) return;
1282
- if (e.target.closest("#dc-panel") || e.target.closest("#dc-fab")) return;
1283
- if (_pickedEl) _pickedEl.classList.remove("dc-pick-highlight");
2723
+ if (e.target.closest("#dc-panel") || e.target.closest("#dc-fab")) {
2724
+ return;
2725
+ }
2726
+ if (_pickedEl) {
2727
+ _pickedEl.classList.remove("dc-pick-highlight");
2728
+ }
1284
2729
  _pickedEl = e.target;
1285
2730
  _pickedEl.classList.add("dc-pick-highlight");
1286
- }, true);
2731
+ };
2732
+ document.addEventListener(
2733
+ "click",
2734
+ _pickHandler,
2735
+ true
2736
+ );
2737
+ document.addEventListener(
2738
+ "mouseover",
2739
+ _pickHoverHandler,
2740
+ true
2741
+ );
1287
2742
  }
1288
2743
  function stopPicking() {
1289
2744
  S.isPicking = false;
1290
- const btn = document.getElementById("dom-pick-btn");
1291
- if (btn) btn.classList.remove("picking");
2745
+ const button = document.getElementById("dom-pick-btn");
2746
+ if (button) {
2747
+ button.classList.remove("picking");
2748
+ }
1292
2749
  if (_pickHandler) {
1293
- document.removeEventListener("click", _pickHandler, true);
2750
+ document.removeEventListener(
2751
+ "click",
2752
+ _pickHandler,
2753
+ true
2754
+ );
1294
2755
  _pickHandler = null;
1295
2756
  }
2757
+ if (_pickHoverHandler) {
2758
+ document.removeEventListener(
2759
+ "mouseover",
2760
+ _pickHoverHandler,
2761
+ true
2762
+ );
2763
+ _pickHoverHandler = null;
2764
+ }
1296
2765
  }
1297
2766
  function renderSystem() {
1298
2767
  const o = el.sysOut();
@@ -1311,126 +2780,161 @@
1311
2780
  ["HW Concurrency", nav.hardwareConcurrency || "—"]
1312
2781
  ];
1313
2782
  if (conn) {
1314
- cards.push(["Connection", `${conn.effectiveType || "—"} · ${conn.downlink || "—"} Mbps`]);
1315
- cards.push(["RTT", conn.rtt != null ? `${conn.rtt}ms` : "—"]);
2783
+ cards.push([
2784
+ "Connection",
2785
+ `${conn.effectiveType || "—"} · ${conn.downlink || "—"} Mbps`
2786
+ ]);
2787
+ cards.push([
2788
+ "RTT",
2789
+ conn.rtt != null ? `${conn.rtt}ms` : "—"
2790
+ ]);
1316
2791
  }
1317
2792
  if (mem) {
1318
2793
  const mb = (v) => (v / 1048576).toFixed(1) + " MB";
1319
- cards.push(["Heap Used", mb(mem.usedJSHeapSize)]);
1320
- cards.push(["Heap Limit", mb(mem.jsHeapSizeLimit)]);
2794
+ cards.push([
2795
+ "Heap Used",
2796
+ mb(mem.usedJSHeapSize)
2797
+ ]);
2798
+ cards.push([
2799
+ "Heap Limit",
2800
+ mb(mem.jsHeapSizeLimit)
2801
+ ]);
1321
2802
  }
1322
2803
  if (perf) {
1323
2804
  const nav2 = perf.getEntriesByType && perf.getEntriesByType("navigation")[0];
1324
2805
  if (nav2) {
1325
- cards.push(["Page Load", `${Math.round(nav2.loadEventEnd)}ms`]);
1326
- cards.push(["DOM Ready", `${Math.round(nav2.domContentLoadedEventEnd)}ms`]);
2806
+ cards.push([
2807
+ "Page Load",
2808
+ `${Math.round(nav2.loadEventEnd)}ms`
2809
+ ]);
2810
+ cards.push([
2811
+ "DOM Ready",
2812
+ `${Math.round(nav2.domContentLoadedEventEnd)}ms`
2813
+ ]);
1327
2814
  }
1328
2815
  }
1329
- o.innerHTML = cards.map(
1330
- ([label, val]) => `<div class="dc-sys-card"><div class="dc-sys-label">${label}</div><div class="dc-sys-val">${escHtml(String(val))}</div></div>`
1331
- ).join("") + `<div class="dc-sys-card dc-sys-full"><div class="dc-sys-label">User Agent</div><div class="dc-sys-val" style="word-break:break-all;font-size:10px">${escHtml(nav.userAgent)}</div></div>`;
2816
+ o.innerHTML = cards.map(([label, val]) => `
2817
+ <div class="dc-sys-card">
2818
+ <div class="dc-sys-label">${escHtml(label)}</div>
2819
+ <div class="dc-sys-val">${escHtml(String(val))}</div>
2820
+ </div>
2821
+ `).join("") + `
2822
+ <div class="dc-sys-card dc-sys-full">
2823
+ <div class="dc-sys-label">User Agent</div>
2824
+ <div
2825
+ class="dc-sys-val"
2826
+ style="word-break:break-all;font-size:10px"
2827
+ >
2828
+ ${escHtml(nav.userAgent)}
2829
+ </div>
2830
+ </div>
2831
+ `;
2832
+ }
2833
+ function makeToggle(id, name, desc, checked) {
2834
+ return `
2835
+ <div class="dc-setting-row">
2836
+ <div class="dc-setting-info">
2837
+ <div class="dc-setting-name">${name}</div>
2838
+ <div class="dc-setting-desc">${desc}</div>
2839
+ </div>
2840
+ <label class="dc-toggle">
2841
+ <input type="checkbox" id="${id}" ${checked ? "checked" : ""}>
2842
+ <span class="dc-toggle-track"></span>
2843
+ </label>
2844
+ </div>`;
2845
+ }
2846
+ function makeSelect(id, name, desc, options, current) {
2847
+ return `
2848
+ <div class="dc-setting-row">
2849
+ <div class="dc-setting-info">
2850
+ <div class="dc-setting-name">${name}</div>
2851
+ <div class="dc-setting-desc">${desc}</div>
2852
+ </div>
2853
+ <select class="dc-select" id="${id}">
2854
+ ${options.map((v) => `<option value="${v}" ${current === v ? "selected" : ""}>${v}</option>`).join("")}
2855
+ </select>
2856
+ </div>`;
1332
2857
  }
1333
2858
  function renderSettings() {
1334
2859
  const o = el.settOut();
1335
2860
  if (!o) return;
1336
- o.innerHTML = `
1337
- <div class="dc-setting-row">
1338
- <div class="dc-setting-info">
1339
- <div class="dc-setting-name">Timestamps</div>
1340
- <div class="dc-setting-desc">Show time prefix on each log entry</div>
1341
- </div>
1342
- <label class="dc-toggle"><input type="checkbox" id="s-ts" ${S.settings.timestamps ? "checked" : ""}><span class="dc-toggle-track"></span></label>
1343
- </div>
1344
- <div class="dc-setting-row">
1345
- <div class="dc-setting-info">
1346
- <div class="dc-setting-name">Auto-scroll</div>
1347
- <div class="dc-setting-desc">Scroll to latest entry automatically</div>
1348
- </div>
1349
- <label class="dc-toggle"><input type="checkbox" id="s-as" ${S.settings.autoScroll ? "checked" : ""}><span class="dc-toggle-track"></span></label>
1350
- </div>
1351
- <div class="dc-setting-row">
1352
- <div class="dc-setting-info">
1353
- <div class="dc-setting-name">Capture Global Errors</div>
1354
- <div class="dc-setting-desc">window.onerror + unhandledrejection</div>
1355
- </div>
1356
- <label class="dc-toggle"><input type="checkbox" id="s-ce" ${S.settings.captureErrors ? "checked" : ""}><span class="dc-toggle-track"></span></label>
1357
- </div>
1358
- <div class="dc-setting-row">
1359
- <div class="dc-setting-info">
1360
- <div class="dc-setting-name">Monitor Network</div>
1361
- <div class="dc-setting-desc">Intercept fetch + XHR requests</div>
1362
- </div>
1363
- <label class="dc-toggle"><input type="checkbox" id="s-mn" ${S.settings.monitorNetwork ? "checked" : ""}><span class="dc-toggle-track"></span></label>
1364
- </div>
1365
- <div class="dc-setting-row">
1366
- <div class="dc-setting-info">
1367
- <div class="dc-setting-name">Font Size</div>
1368
- <div class="dc-setting-desc">Console output font size (px)</div>
1369
- </div>
1370
- <select class="dc-select" id="s-fs">
1371
- ${[10, 11, 12, 13, 14].map((s) => `<option value="${s}" ${S.settings.fontSize === s ? "selected" : ""}>${s}px</option>`).join("")}
1372
- </select>
1373
- </div>
1374
- <div class="dc-setting-row">
1375
- <div class="dc-setting-info">
1376
- <div class="dc-setting-name">Max Log Entries</div>
1377
- <div class="dc-setting-desc">Older entries are discarded</div>
1378
- </div>
1379
- <select class="dc-select" id="s-me">
1380
- ${[100, 250, 500, 1e3, 2e3].map((s) => `<option value="${s}" ${S.settings.maxEntries === s ? "selected" : ""}>${s}</option>`).join("")}
1381
- </select>
1382
- </div>
1383
- <div class="dc-setting-row" style="border:none">
1384
- <div class="dc-setting-info">
1385
- <div class="dc-setting-name" style="color:var(--dc-red)">Clear All Data</div>
1386
- <div class="dc-setting-desc">Wipe logs and network history</div>
1387
- </div>
1388
- <button class="dc-clear-btn" onclick="window.__dcClearAll()">Clear</button>
1389
- </div>`;
2861
+ const ss = S.settings;
2862
+ o.innerHTML = makeToggle("s-ts", "Timestamps", "Show time prefix on each log entry", ss.timestamps) + makeToggle("s-as", "Auto-scroll", "Scroll to latest entry automatically", ss.autoScroll) + makeToggle("s-ce", "Capture Global Errors", "window.onerror + unhandledrejection", ss.captureErrors) + makeToggle("s-mn", "Monitor Network", "Intercept fetch + XHR requests", ss.monitorNetwork) + makeToggle("s-pb", "Persist Across Reload", "Keep console and network history in sessionStorage", ss.persistBuffer) + makeSelect("s-fs", "Font Size", "Console output font size (px)", [10, 11, 12, 13, 14], ss.fontSize) + makeSelect("s-me", "Max Log Entries", "Older entries are discarded", [100, 250, 500, 1e3, 2e3], ss.maxEntries) + `<div class="dc-setting-row" style="border:none">
2863
+ <div class="dc-setting-info">
2864
+ <div class="dc-setting-name" style="color:var(--dc-red)">Clear All Data</div>
2865
+ <div class="dc-setting-desc">Wipe logs, network history and persistent buffer</div>
2866
+ </div>
2867
+ <button class="dc-clear-btn" id="dc-settings-clear">Clear</button>
2868
+ </div>`;
1390
2869
  document.getElementById("s-ts").onchange = (e) => {
1391
- S.settings.timestamps = e.target.checked;
2870
+ ss.timestamps = e.target.checked;
1392
2871
  saveSettings();
1393
2872
  renderConsole();
1394
2873
  };
1395
2874
  document.getElementById("s-as").onchange = (e) => {
1396
- S.settings.autoScroll = e.target.checked;
2875
+ ss.autoScroll = e.target.checked;
1397
2876
  saveSettings();
1398
2877
  };
1399
2878
  document.getElementById("s-ce").onchange = (e) => {
1400
- S.settings.captureErrors = e.target.checked;
2879
+ ss.captureErrors = e.target.checked;
1401
2880
  saveSettings();
1402
2881
  };
1403
2882
  document.getElementById("s-mn").onchange = (e) => {
1404
- S.settings.monitorNetwork = e.target.checked;
2883
+ ss.monitorNetwork = e.target.checked;
2884
+ saveSettings();
2885
+ };
2886
+ document.getElementById("s-pb").onchange = (e) => {
2887
+ ss.persistBuffer = e.target.checked;
1405
2888
  saveSettings();
2889
+ ss.persistBuffer ? persistBuffer() : clearPersistentBuffer();
1406
2890
  };
1407
2891
  document.getElementById("s-fs").onchange = (e) => {
1408
- S.settings.fontSize = parseInt(e.target.value);
2892
+ ss.fontSize = parseInt(e.target.value, 10);
1409
2893
  saveSettings();
1410
2894
  if (S.activeTab === "console") renderConsole();
1411
2895
  };
1412
2896
  document.getElementById("s-me").onchange = (e) => {
1413
- S.settings.maxEntries = parseInt(e.target.value);
2897
+ ss.maxEntries = parseInt(e.target.value, 10);
2898
+ trimLogs();
2899
+ if (S.network.length > ss.maxEntries) S.network = S.network.slice(0, ss.maxEntries);
1414
2900
  saveSettings();
2901
+ persistBuffer();
2902
+ updateTabCount("console", S.logs.length);
2903
+ updateTabCount("network", S.network.length);
2904
+ renderConsole();
1415
2905
  };
2906
+ document.getElementById("dc-settings-clear").addEventListener("click", window.__dcClearAll);
1416
2907
  }
1417
2908
  window.__dcClearAll = function() {
1418
2909
  S.logs = [];
1419
2910
  S.network = [];
1420
2911
  S.errorCount = 0;
2912
+ S.groupDepth = 0;
2913
+ clearPersistentBuffer();
1421
2914
  const b = el.badge();
1422
2915
  if (b) {
1423
2916
  b.style.display = "none";
2917
+ b.textContent = "0";
1424
2918
  }
1425
2919
  updateTabCount("console", 0);
1426
2920
  updateTabCount("network", 0);
1427
- if (S.activeTab === "console") renderConsole();
1428
- if (S.activeTab === "network") renderNetwork();
2921
+ renderConsole();
2922
+ renderNetwork();
1429
2923
  };
1430
2924
  function switchTab(name) {
1431
2925
  S.activeTab = name;
1432
- document.querySelectorAll(".dc-tab").forEach((b) => b.classList.toggle("active", b.dataset.tab === name));
1433
- document.querySelectorAll(".dc-panel-content").forEach((p) => p.classList.toggle("active", p.id === `panel-${name}`));
2926
+ document.querySelectorAll(".dc-tab").forEach(
2927
+ (b) => b.classList.toggle(
2928
+ "active",
2929
+ b.dataset.tab === name
2930
+ )
2931
+ );
2932
+ document.querySelectorAll(".dc-panel-content").forEach(
2933
+ (p) => p.classList.toggle(
2934
+ "active",
2935
+ p.id === `panel-${name}`
2936
+ )
2937
+ );
1434
2938
  switch (name) {
1435
2939
  case "console":
1436
2940
  renderConsole();
@@ -1452,15 +2956,12 @@
1452
2956
  break;
1453
2957
  }
1454
2958
  }
1455
- function updateTabCount(tab, n) {
1456
- const el2 = document.getElementById(`cnt-${tab}`);
1457
- if (!el2) return;
1458
- if (n > 0) {
1459
- el2.textContent = n > 999 ? "999+" : n;
1460
- el2.style.display = "inline-flex";
1461
- } else el2.style.display = "none";
1462
- }
1463
2959
  let _panelOpen = false;
2960
+ function updateFab() {
2961
+ const fab = el.fab();
2962
+ if (!fab) return;
2963
+ fab.innerHTML = `${_panelOpen ? "✕" : "⌥"}<span class="dc-fab-badge" id="dc-error-badge" style="display:${S.errorCount > 0 ? "flex" : "none"}">${S.errorCount}</span>`;
2964
+ }
1464
2965
  function togglePanel() {
1465
2966
  _panelOpen = !_panelOpen;
1466
2967
  const panel = el.panel();
@@ -1473,19 +2974,19 @@
1473
2974
  } else {
1474
2975
  panel.classList.remove("visible");
1475
2976
  setTimeout(() => {
1476
- panel.style.display = "none";
2977
+ if (!_panelOpen) {
2978
+ panel.style.display = "none";
2979
+ }
1477
2980
  }, 260);
1478
2981
  }
1479
- if (_panelOpen) {
1480
- el.fab().innerHTML = `✕<span class="dc-fab-badge" id="dc-error-badge" style="display:${S.errorCount > 0 ? "flex" : "none"}">${S.errorCount}</span>`;
1481
- } else {
1482
- el.fab().innerHTML = `⌥<span class="dc-fab-badge" id="dc-error-badge" style="display:${S.errorCount > 0 ? "flex" : "none"}">${S.errorCount}</span>`;
1483
- }
2982
+ updateFab();
1484
2983
  }
1485
2984
  (function initDrag() {
1486
2985
  const handle = document.getElementById("dc-drag");
1487
2986
  const panel = document.getElementById("dc-panel");
1488
- let dragging = false, startY = 0, startH = 0;
2987
+ let dragging = false;
2988
+ let startY = 0;
2989
+ let startH = 0;
1489
2990
  function onStart(e) {
1490
2991
  dragging = true;
1491
2992
  startY = e.clientY || e.touches && e.touches[0].clientY;
@@ -1495,19 +2996,45 @@
1495
2996
  function onMove(e) {
1496
2997
  if (!dragging) return;
1497
2998
  const y = e.clientY || e.touches && e.touches[0].clientY;
1498
- const newH = Math.max(120, Math.min(window.innerHeight * 0.85, startH + (startY - y)));
2999
+ const newH = Math.max(
3000
+ 120,
3001
+ Math.min(
3002
+ window.innerHeight * 0.85,
3003
+ startH + (startY - y)
3004
+ )
3005
+ );
1499
3006
  panel.style.height = newH + "px";
1500
3007
  }
1501
3008
  function onEnd() {
1502
3009
  dragging = false;
1503
3010
  document.body.style.userSelect = "";
1504
3011
  }
1505
- handle.addEventListener("mousedown", onStart);
1506
- handle.addEventListener("touchstart", onStart, { passive: true });
1507
- document.addEventListener("mousemove", onMove);
1508
- document.addEventListener("touchmove", onMove, { passive: true });
1509
- document.addEventListener("mouseup", onEnd);
1510
- document.addEventListener("touchend", onEnd);
3012
+ handle.addEventListener(
3013
+ "mousedown",
3014
+ onStart
3015
+ );
3016
+ handle.addEventListener(
3017
+ "touchstart",
3018
+ onStart,
3019
+ { passive: true }
3020
+ );
3021
+ document.addEventListener(
3022
+ "mousemove",
3023
+ onMove
3024
+ );
3025
+ document.addEventListener(
3026
+ "touchmove",
3027
+ onMove,
3028
+ { passive: true }
3029
+ );
3030
+ document.addEventListener(
3031
+ "mouseup",
3032
+ onEnd
3033
+ );
3034
+ document.addEventListener(
3035
+ "touchend",
3036
+ onEnd
3037
+ );
1511
3038
  })();
1512
3039
  function initRepl() {
1513
3040
  const input = el.replIn();
@@ -1516,12 +3043,23 @@
1516
3043
  const code = input.value.trim();
1517
3044
  if (!code) return;
1518
3045
  S.replHistory.unshift(code);
1519
- if (S.replHistory.length > 50) S.replHistory.pop();
3046
+ if (S.replHistory.length > 50) {
3047
+ S.replHistory.pop();
3048
+ }
1520
3049
  S.replHistoryIdx = -1;
1521
3050
  const entry = document.createElement("div");
1522
3051
  entry.className = "dc-entry dc-entry-cmd";
1523
3052
  entry.style.fontSize = S.settings.fontSize + "px";
1524
- entry.innerHTML = `<span class="dc-ts">${ts()}</span><span>▶ ${escHtml(code)}</span>`;
3053
+ if (S.settings.timestamps) {
3054
+ const timestamp = document.createElement("span");
3055
+ timestamp.className = "dc-ts";
3056
+ timestamp.textContent = ts();
3057
+ entry.appendChild(timestamp);
3058
+ }
3059
+ const body = document.createElement("span");
3060
+ body.className = "dc-entry-body";
3061
+ body.textContent = `▶ ${code}`;
3062
+ entry.appendChild(body);
1525
3063
  el.consOut().appendChild(entry);
1526
3064
  try {
1527
3065
  const result = (0, eval)(code);
@@ -1529,18 +3067,42 @@
1529
3067
  const resEntry = document.createElement("div");
1530
3068
  resEntry.className = "dc-entry dc-entry-log";
1531
3069
  resEntry.style.fontSize = S.settings.fontSize + "px";
1532
- resEntry.innerHTML = `<span class="dc-ts">${ts()}</span><span>${formatArg(result)}</span>`;
3070
+ if (S.settings.timestamps) {
3071
+ const timestamp = document.createElement("span");
3072
+ timestamp.className = "dc-ts";
3073
+ timestamp.textContent = ts();
3074
+ resEntry.appendChild(timestamp);
3075
+ }
3076
+ const resultBody = document.createElement("span");
3077
+ resultBody.className = "dc-entry-body";
3078
+ resultBody.appendChild(
3079
+ renderValue(snapshotValue(result))
3080
+ );
3081
+ resEntry.appendChild(resultBody);
1533
3082
  el.consOut().appendChild(resEntry);
1534
3083
  }
1535
3084
  } catch (err) {
1536
3085
  const errEntry = document.createElement("div");
1537
3086
  errEntry.className = "dc-entry dc-entry-error";
1538
3087
  errEntry.style.fontSize = S.settings.fontSize + "px";
1539
- errEntry.innerHTML = `<span class="dc-ts">${ts()}</span><span>${escHtml(err.toString())}</span>`;
3088
+ if (S.settings.timestamps) {
3089
+ const timestamp = document.createElement("span");
3090
+ timestamp.className = "dc-ts";
3091
+ timestamp.textContent = ts();
3092
+ errEntry.appendChild(timestamp);
3093
+ }
3094
+ const body2 = document.createElement("span");
3095
+ body2.className = "dc-entry-body";
3096
+ body2.appendChild(
3097
+ renderValue(snapshotValue(err))
3098
+ );
3099
+ errEntry.appendChild(body2);
1540
3100
  el.consOut().appendChild(errEntry);
1541
3101
  }
1542
3102
  input.value = "";
1543
- if (S.settings.autoScroll) el.consOut().scrollTop = el.consOut().scrollHeight;
3103
+ if (S.settings.autoScroll) {
3104
+ el.consOut().scrollTop = el.consOut().scrollHeight;
3105
+ }
1544
3106
  switchTab("console");
1545
3107
  }
1546
3108
  input.addEventListener("keydown", (e) => {
@@ -1550,100 +3112,241 @@
1550
3112
  }
1551
3113
  if (e.key === "ArrowUp") {
1552
3114
  e.preventDefault();
1553
- S.replHistoryIdx = Math.min(S.replHistoryIdx + 1, S.replHistory.length - 1);
3115
+ S.replHistoryIdx = Math.min(
3116
+ S.replHistoryIdx + 1,
3117
+ S.replHistory.length - 1
3118
+ );
1554
3119
  input.value = S.replHistory[S.replHistoryIdx] || "";
1555
3120
  }
1556
3121
  if (e.key === "ArrowDown") {
1557
3122
  e.preventDefault();
1558
- S.replHistoryIdx = Math.max(S.replHistoryIdx - 1, -1);
3123
+ S.replHistoryIdx = Math.max(
3124
+ S.replHistoryIdx - 1,
3125
+ -1
3126
+ );
1559
3127
  input.value = S.replHistoryIdx >= 0 ? S.replHistory[S.replHistoryIdx] : "";
1560
3128
  }
1561
3129
  });
1562
3130
  const runBtn = document.getElementById("repl-run");
1563
- if (runBtn) runBtn.addEventListener("click", execute);
3131
+ if (runBtn) {
3132
+ runBtn.addEventListener(
3133
+ "click",
3134
+ execute
3135
+ );
3136
+ }
1564
3137
  }
1565
- document.getElementById("dc-fab").addEventListener("click", togglePanel);
1566
- document.getElementById("dc-minimize-btn").addEventListener("click", togglePanel);
3138
+ document.getElementById("dc-fab").addEventListener(
3139
+ "click",
3140
+ togglePanel
3141
+ );
3142
+ document.getElementById("dc-minimize-btn").addEventListener(
3143
+ "click",
3144
+ togglePanel
3145
+ );
1567
3146
  document.querySelectorAll(".dc-tab").forEach((btn) => {
1568
- btn.addEventListener("click", () => switchTab(btn.dataset.tab));
3147
+ btn.addEventListener(
3148
+ "click",
3149
+ () => switchTab(btn.dataset.tab)
3150
+ );
1569
3151
  });
1570
- document.getElementById("dc-clear-btn").addEventListener("click", () => {
1571
- switch (S.activeTab) {
1572
- case "console":
1573
- S.logs = [];
1574
- updateTabCount("console", 0);
1575
- renderConsole();
1576
- break;
1577
- case "network":
1578
- S.network = [];
1579
- updateTabCount("network", 0);
1580
- renderNetwork();
1581
- break;
3152
+ document.getElementById("dc-clear-btn").addEventListener(
3153
+ "click",
3154
+ () => {
3155
+ switch (S.activeTab) {
3156
+ case "console":
3157
+ S.logs = [];
3158
+ S.groupDepth = 0;
3159
+ clearPersistentBuffer();
3160
+ updateTabCount("console", 0);
3161
+ renderConsole();
3162
+ break;
3163
+ case "network":
3164
+ S.network = [];
3165
+ clearPersistentBuffer();
3166
+ updateTabCount("network", 0);
3167
+ renderNetwork();
3168
+ break;
3169
+ }
1582
3170
  }
1583
- });
1584
- document.getElementById("dc-copy-btn").addEventListener("click", () => {
1585
- let text = "";
1586
- if (S.activeTab === "console") {
1587
- text = S.logs.map((l) => `[${l.ts}] [${l.type.toUpperCase()}] ${l.args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}`).join("\n");
1588
- } else if (S.activeTab === "network") {
1589
- text = S.network.map((r) => `[${r.ts}] ${r.method} ${r.url} → ${r.status || "ERR"} (${r.duration}ms)`).join("\n");
3171
+ );
3172
+ document.getElementById("dc-copy-btn").addEventListener(
3173
+ "click",
3174
+ () => {
3175
+ let text = "";
3176
+ if (S.activeTab === "console") {
3177
+ text = S.logs.map(
3178
+ (l) => `[${l.ts}] [${String(l.type).toUpperCase()}] ${formatLogForCopy(l)}`
3179
+ ).join("\n");
3180
+ } else if (S.activeTab === "network") {
3181
+ text = S.network.map(
3182
+ (r) => `[${r.ts}] ${r.method} ${r.url} → ${r.status || "ERR"} (${r.duration}ms)` + (r.requestBody ? `
3183
+ Request: ${r.requestBody}` : "") + (r.responseBody ? `
3184
+ Response: ${r.responseBody}` : "")
3185
+ ).join("\n\n");
3186
+ }
3187
+ if (!text) return;
3188
+ if (navigator.clipboard && navigator.clipboard.writeText) {
3189
+ navigator.clipboard.writeText(text).catch(() => {
3190
+ });
3191
+ }
1590
3192
  }
1591
- if (text) {
1592
- navigator.clipboard ? navigator.clipboard.writeText(text).catch(() => {
1593
- }) : /* @__PURE__ */ (() => {
1594
- })();
1595
- console.log("📋 Copied to clipboard");
3193
+ );
3194
+ function formatLogForCopy(log) {
3195
+ if (log.type === "table" && log.table) {
3196
+ try {
3197
+ return JSON.stringify(log.table, null, 2);
3198
+ } catch (e) {
3199
+ }
1596
3200
  }
1597
- });
1598
- el.netFlt().addEventListener("input", (e) => renderNetwork(e.target.value));
1599
- document.getElementById("net-clear").addEventListener("click", () => {
1600
- S.network = [];
1601
- updateTabCount("network", 0);
1602
- renderNetwork();
1603
- });
3201
+ return (log.args || []).map(snapshotToText).join(" ");
3202
+ }
3203
+ function snapshotToText(value) {
3204
+ if (!value) return "";
3205
+ switch (value.kind) {
3206
+ case "string":
3207
+ return `"${value.value}"`;
3208
+ case "null":
3209
+ case "undefined":
3210
+ case "number":
3211
+ case "boolean":
3212
+ case "bigint":
3213
+ case "symbol":
3214
+ case "function":
3215
+ return String(value.value ?? value.kind);
3216
+ case "date":
3217
+ case "regexp":
3218
+ case "circular":
3219
+ return String(value.value || `[${value.kind}]`);
3220
+ case "error":
3221
+ return `${value.ctor}: ${value.message}`;
3222
+ case "array":
3223
+ case "object":
3224
+ try {
3225
+ return JSON.stringify(value, null, 2);
3226
+ } catch (e) {
3227
+ return "[Object]";
3228
+ }
3229
+ default:
3230
+ return "[Unknown]";
3231
+ }
3232
+ }
3233
+ el.netFlt().addEventListener(
3234
+ "input",
3235
+ (e) => renderNetwork(e.target.value)
3236
+ );
3237
+ document.getElementById("net-clear").addEventListener(
3238
+ "click",
3239
+ () => {
3240
+ S.network = [];
3241
+ clearPersistentBuffer();
3242
+ updateTabCount("network", 0);
3243
+ renderNetwork();
3244
+ }
3245
+ );
1604
3246
  document.querySelectorAll(".dc-st-btn").forEach((btn) => {
1605
- btn.addEventListener("click", () => {
1606
- S.activeStorage = btn.dataset.storage;
1607
- document.querySelectorAll(".dc-st-btn").forEach((b) => b.classList.toggle("active", b === btn));
1608
- renderStorage();
1609
- });
1610
- });
1611
- document.getElementById("storage-refresh").addEventListener("click", loadStorage);
1612
- document.getElementById("dom-refresh-btn").addEventListener("click", renderDomTree);
1613
- document.getElementById("dom-pick-btn").addEventListener("click", () => {
1614
- S.isPicking ? stopPicking() : startPicking();
1615
- });
1616
- document.getElementById("dom-collapse-btn").addEventListener("click", () => {
1617
- document.querySelectorAll(".dc-children.open").forEach((c) => c.classList.remove("open"));
1618
- document.querySelectorAll(".dc-arrow").forEach((a) => {
1619
- if (a.textContent === "▼") a.textContent = "▶";
1620
- });
3247
+ btn.addEventListener(
3248
+ "click",
3249
+ () => {
3250
+ S.activeStorage = btn.dataset.storage;
3251
+ document.querySelectorAll(".dc-st-btn").forEach(
3252
+ (b) => b.classList.toggle(
3253
+ "active",
3254
+ b === btn
3255
+ )
3256
+ );
3257
+ renderStorage();
3258
+ }
3259
+ );
1621
3260
  });
3261
+ document.getElementById("storage-refresh").addEventListener(
3262
+ "click",
3263
+ loadStorage
3264
+ );
3265
+ document.getElementById("dom-refresh-btn").addEventListener(
3266
+ "click",
3267
+ renderDomTree
3268
+ );
3269
+ document.getElementById("dom-pick-btn").addEventListener(
3270
+ "click",
3271
+ () => {
3272
+ S.isPicking ? stopPicking() : startPicking();
3273
+ }
3274
+ );
3275
+ document.getElementById("dom-collapse-btn").addEventListener(
3276
+ "click",
3277
+ () => {
3278
+ document.querySelectorAll(".dc-children.open").forEach(
3279
+ (c) => c.classList.remove("open")
3280
+ );
3281
+ document.querySelectorAll(".dc-arrow").forEach((a) => {
3282
+ if (a.textContent === "▼") {
3283
+ a.textContent = "▶";
3284
+ }
3285
+ });
3286
+ }
3287
+ );
1622
3288
  let _lastUrl = location.href;
1623
3289
  setInterval(() => {
1624
3290
  if (location.href !== _lastUrl) {
1625
3291
  _lastUrl = location.href;
1626
- addLog("info", ["🔄 SPA navigation detected:", location.href]);
3292
+ addLog(
3293
+ "info",
3294
+ ["🔄 SPA navigation detected:", location.href]
3295
+ );
1627
3296
  }
1628
3297
  }, 1e3);
1629
3298
  initRepl();
1630
3299
  renderConsole();
1631
- addLog("log", ["✅ DevConsole v2.0 loaded — click ⌥ to open"]);
3300
+ if (!S.logs.length) {
3301
+ addLog(
3302
+ "log",
3303
+ [`✅ INSIKT v${VERSION} loaded — click ⌥ to open`]
3304
+ );
3305
+ } else {
3306
+ S.logs.forEach((log) => {
3307
+ log.restored = true;
3308
+ });
3309
+ addLog(
3310
+ "info",
3311
+ [`🔄 INSIKT v${VERSION} resumed after reload`]
3312
+ );
3313
+ }
1632
3314
  window.insikt = {
3315
+ version: VERSION,
1633
3316
  toggle: togglePanel,
1634
3317
  clear: window.__dcClearAll,
1635
3318
  init: () => {
1636
3319
  },
1637
3320
  destroy: () => {
1638
- const fab = el.fab(), panel = el.panel();
3321
+ const fab = el.fab();
3322
+ const panel = el.panel();
1639
3323
  if (fab) fab.remove();
1640
3324
  if (panel) panel.remove();
1641
- ["log", "warn", "error", "info"].forEach((l) => {
1642
- if (_orig[l]) console[l] = _orig[l];
3325
+ ["log", "warn", "error", "info", "table", "group", "groupCollapsed", "groupEnd"].forEach((level) => {
3326
+ if (_orig[level]) {
3327
+ console[level] = _orig[level];
3328
+ }
1643
3329
  });
1644
- window.fetch = _origFetch;
3330
+ if (_origFetch) {
3331
+ window.fetch = _origFetch;
3332
+ }
1645
3333
  XMLHttpRequest.prototype.open = _origOpen;
1646
3334
  XMLHttpRequest.prototype.send = _origSend;
3335
+ XMLHttpRequest.prototype.setRequestHeader = _origSetRequestHeader;
3336
+ window.onerror = _origOnError;
3337
+ window.removeEventListener(
3338
+ "unhandledrejection",
3339
+ onUnhandledRejection
3340
+ );
3341
+ stopPicking();
3342
+ if (style && style.parentNode) {
3343
+ style.parentNode.removeChild(style);
3344
+ }
3345
+ delete window.__dcToggleReq;
3346
+ delete window.__dcToggleDb;
3347
+ delete window.__dcToggleStore;
3348
+ delete window.__dcClearAll;
3349
+ delete window.insikt;
1647
3350
  }
1648
3351
  };
1649
3352
  })();