@c15t/dev-tools 2.0.0-rc.2 → 2.0.0-rc.4

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/panel.d.ts +1 -0
  3. package/dist/components/panel.d.ts.map +1 -1
  4. package/dist/components/tabs.d.ts.map +1 -1
  5. package/dist/components/ui.d.ts +8 -0
  6. package/dist/components/ui.d.ts.map +1 -1
  7. package/dist/core/debug-bundle.d.ts +14 -0
  8. package/dist/core/debug-bundle.d.ts.map +1 -0
  9. package/dist/core/devtools.d.ts.map +1 -1
  10. package/dist/core/override-storage.d.ts +7 -0
  11. package/dist/core/override-storage.d.ts.map +1 -0
  12. package/dist/core/panel-renderer.d.ts +5 -0
  13. package/dist/core/panel-renderer.d.ts.map +1 -1
  14. package/dist/core/state-manager.d.ts +1 -1
  15. package/dist/core/state-manager.d.ts.map +1 -1
  16. package/dist/core/store-connector.d.ts +20 -0
  17. package/dist/core/store-connector.d.ts.map +1 -1
  18. package/dist/core/store-instrumentation.d.ts +13 -0
  19. package/dist/core/store-instrumentation.d.ts.map +1 -0
  20. package/dist/index.cjs +2469 -845
  21. package/dist/index.js +2472 -848
  22. package/dist/panels/actions.d.ts +1 -0
  23. package/dist/panels/actions.d.ts.map +1 -1
  24. package/dist/panels/consents.d.ts.map +1 -1
  25. package/dist/panels/dom-scanner.d.ts.map +1 -1
  26. package/dist/panels/events.d.ts.map +1 -1
  27. package/dist/panels/iab.d.ts +6 -0
  28. package/dist/panels/iab.d.ts.map +1 -1
  29. package/dist/panels/location.d.ts +9 -6
  30. package/dist/panels/location.d.ts.map +1 -1
  31. package/dist/panels/scripts.d.ts +2 -0
  32. package/dist/panels/scripts.d.ts.map +1 -1
  33. package/dist/react.cjs +2392 -752
  34. package/dist/react.js +2376 -736
  35. package/dist/tanstack.cjs +2197 -555
  36. package/dist/tanstack.js +2195 -553
  37. package/dist/utils/preference-trigger.d.ts +2 -2
  38. package/dist/utils/preference-trigger.d.ts.map +1 -1
  39. package/dist/version.d.ts +2 -0
  40. package/dist/version.d.ts.map +1 -0
  41. package/package.json +16 -14
  42. package/tsconfig.json +9 -1
  43. package/dist/__tests__/components/ui.test.d.ts +0 -2
  44. package/dist/__tests__/components/ui.test.d.ts.map +0 -1
  45. package/dist/__tests__/core/renderer.test.d.ts +0 -2
  46. package/dist/__tests__/core/renderer.test.d.ts.map +0 -1
  47. package/dist/__tests__/core/reset-consents.test.d.ts +0 -2
  48. package/dist/__tests__/core/reset-consents.test.d.ts.map +0 -1
  49. package/dist/__tests__/core/state-manager.test.d.ts +0 -2
  50. package/dist/__tests__/core/state-manager.test.d.ts.map +0 -1
  51. package/dist/__tests__/panels/dom-scanner.test.d.ts +0 -2
  52. package/dist/__tests__/panels/dom-scanner.test.d.ts.map +0 -1
package/dist/tanstack.js CHANGED
@@ -259,21 +259,21 @@ var __webpack_modules__ = {
259
259
  module.id,
260
260
  `.toggle-bPZtik {
261
261
  border-radius: var(--c15t-radius-full, 9999px);
262
- background-color: var(--c15t-switch-track, #ccc);
262
+ background-color: var(--c15t-switch-track, #d9d9d9);
263
263
  cursor: pointer;
264
- width: 36px;
265
- height: 20px;
266
- transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
264
+ width: 2rem;
265
+ height: 1.25rem;
266
+ transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), box-shadow var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
267
267
  border: none;
268
268
  align-items: center;
269
- padding: 0;
269
+ padding: .125rem;
270
270
  display: inline-flex;
271
271
  position: relative;
272
272
  }
273
273
 
274
274
  .toggle-bPZtik:focus-visible {
275
- outline: 2px solid var(--c15t-primary, #335cff);
276
- outline-offset: 2px;
275
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
276
+ outline: none;
277
277
  }
278
278
 
279
279
  .toggleActive-Ldlasg {
@@ -283,16 +283,16 @@ var __webpack_modules__ = {
283
283
  .toggleThumb-hjGfoX {
284
284
  border-radius: var(--c15t-radius-full, 9999px);
285
285
  background-color: var(--c15t-switch-thumb, #fff);
286
- width: 16px;
287
- height: 16px;
288
- transition: transform var(--c15t-duration-fast, .1s) var(--c15t-easing-spring, cubic-bezier(.34, 1.56, .64, 1));
286
+ width: .75rem;
287
+ height: .75rem;
288
+ box-shadow: 0 0 0 1px var(--c15t-border, #e3e3e3);
289
+ transition: transform var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
289
290
  position: absolute;
290
291
  left: 2px;
291
- box-shadow: 0 1px 2px #0003;
292
292
  }
293
293
 
294
294
  .toggleActive-Ldlasg .toggleThumb-hjGfoX {
295
- transform: translateX(16px);
295
+ transform: translateX(1rem);
296
296
  }
297
297
 
298
298
  .toggle-bPZtik:disabled, .toggleDisabled-ZcD8nZ {
@@ -305,13 +305,14 @@ var __webpack_modules__ = {
305
305
  }
306
306
 
307
307
  .badge-yA0giZ {
308
- border-radius: var(--c15t-radius-sm, .25rem);
308
+ border-radius: var(--c15t-radius-full, 9999px);
309
309
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
310
310
  font-weight: var(--c15t-font-weight-medium, 500);
311
+ line-height: var(--c15t-line-height-tight, 1.25);
311
312
  white-space: nowrap;
313
+ justify-content: center;
312
314
  align-items: center;
313
- padding: 2px 6px;
314
- line-height: 1;
315
+ padding: .1875rem .4375rem;
315
316
  display: inline-flex;
316
317
  }
317
318
 
@@ -344,38 +345,49 @@ var __webpack_modules__ = {
344
345
  justify-content: center;
345
346
  align-items: center;
346
347
  gap: var(--c15t-space-xs, .25rem);
347
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-sm, .5rem);
348
- border: 1px solid var(--c15t-border, #e3e3e3);
348
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
349
349
  border-radius: var(--c15t-radius-md, .5rem);
350
- background-color: var(--c15t-surface, #fff);
350
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
351
+ min-height: 2rem;
351
352
  color: var(--c15t-text, #171717);
352
- font-family: inherit;
353
- font-size: var(--c15t-devtools-font-size-xs, .75rem);
353
+ font-family: var(--c15t-font-family, system-ui, -apple-system, sans-serif);
354
+ font-size: var(--c15t-font-size-sm, .875rem);
354
355
  font-weight: var(--c15t-font-weight-medium, 500);
356
+ line-height: var(--c15t-line-height-tight, 1.25);
357
+ box-shadow: var(--c15t-shadow-sm, 0 1px 2px #0000000d);
355
358
  cursor: pointer;
356
- transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
359
+ transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), box-shadow var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
360
+ padding: .375rem .625rem;
357
361
  display: inline-flex;
358
362
  }
359
363
 
360
364
  .btn-evRVlh:hover {
361
- background-color: var(--c15t-surface-hover, #f7f7f7);
365
+ background-color: var(--c15t-devtools-surface-subtle, #f7f7f7);
362
366
  border-color: var(--c15t-border-hover, #c9c9c9);
367
+ box-shadow: var(--c15t-shadow-md, 0 4px 12px #00000014);
363
368
  }
364
369
 
365
370
  .btn-evRVlh:focus-visible {
366
- outline: 2px solid var(--c15t-primary, #335cff);
367
- outline-offset: 1px;
371
+ box-shadow: var(--c15t-shadow-sm, 0 1px 2px #0000000d), 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
372
+ outline: none;
373
+ }
374
+
375
+ .btn-evRVlh:active {
376
+ box-shadow: var(--c15t-shadow-sm, 0 1px 2px #0000000d);
377
+ transform: scale(.98);
368
378
  }
369
379
 
370
380
  .btn-evRVlh:disabled {
371
381
  opacity: .5;
372
382
  cursor: not-allowed;
383
+ box-shadow: none;
373
384
  }
374
385
 
375
386
  .btnPrimary-dA6nqY {
376
387
  background-color: var(--c15t-primary, #335cff);
377
388
  border-color: var(--c15t-primary, #335cff);
378
389
  color: var(--c15t-text-on-primary, #fff);
390
+ box-shadow: none;
379
391
  }
380
392
 
381
393
  .btnPrimary-dA6nqY:hover {
@@ -387,6 +399,7 @@ var __webpack_modules__ = {
387
399
  background-color: var(--c15t-devtools-badge-error, #ef4343);
388
400
  border-color: var(--c15t-devtools-badge-error, #ef4343);
389
401
  color: var(--c15t-text-on-primary, #fff);
402
+ box-shadow: none;
390
403
  }
391
404
 
392
405
  .btnDanger-eDnqOX:hover {
@@ -395,8 +408,10 @@ var __webpack_modules__ = {
395
408
  }
396
409
 
397
410
  .btnSmall-TjXoqZ {
398
- padding: 2px var(--c15t-space-xs, .25rem);
399
- font-size: 10px;
411
+ min-height: 1.75rem;
412
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
413
+ border-radius: var(--c15t-radius-sm, .375rem);
414
+ padding: .25rem .5rem;
400
415
  }
401
416
 
402
417
  .btnIcon-fiYQAh {
@@ -406,19 +421,22 @@ var __webpack_modules__ = {
406
421
  }
407
422
 
408
423
  .input-IeTcCs {
409
- width: 100%;
410
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-sm, .5rem);
411
- border: 1px solid var(--c15t-border, #e3e3e3);
424
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
412
425
  border-radius: var(--c15t-radius-md, .5rem);
413
- background-color: var(--c15t-surface, #fff);
426
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
427
+ width: 100%;
428
+ min-height: 2rem;
414
429
  color: var(--c15t-text, #171717);
415
- font-family: inherit;
416
- font-size: var(--c15t-font-size-sm, .875rem);
417
- transition: border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
430
+ font-family: var(--c15t-font-family, system-ui, -apple-system, sans-serif);
431
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
432
+ line-height: var(--c15t-line-height-tight, 1.25);
433
+ transition: border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), box-shadow var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
434
+ padding: .375rem .625rem;
418
435
  }
419
436
 
420
437
  .input-IeTcCs:focus {
421
- border-color: var(--c15t-primary, #335cff);
438
+ border-color: var(--c15t-devtools-focus-ring, #335cff);
439
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--c15t-devtools-focus-ring, #335cff) 25%, transparent);
422
440
  outline: none;
423
441
  }
424
442
 
@@ -427,30 +445,35 @@ var __webpack_modules__ = {
427
445
  }
428
446
 
429
447
  .inputSmall-pJyXcL {
430
- padding: 2px var(--c15t-space-xs, .25rem);
448
+ min-height: 1.625rem;
431
449
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
450
+ padding: .25rem .4375rem;
432
451
  }
433
452
 
434
453
  .select-byJ1WM {
435
- width: 100%;
436
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-sm, .5rem);
437
- border: 1px solid var(--c15t-border, #e3e3e3);
454
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
438
455
  border-radius: var(--c15t-radius-md, .5rem);
439
- background-color: var(--c15t-surface, #fff);
456
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
457
+ width: 100%;
458
+ min-height: 2rem;
440
459
  color: var(--c15t-text, #171717);
441
- font-family: inherit;
460
+ font-family: var(--c15t-font-family, system-ui, -apple-system, sans-serif);
442
461
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
462
+ line-height: var(--c15t-line-height-tight, 1.25);
443
463
  cursor: pointer;
464
+ transition: border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), box-shadow var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
465
+ padding: .375rem .625rem;
444
466
  }
445
467
 
446
468
  .select-byJ1WM:focus {
447
- border-color: var(--c15t-primary, #335cff);
469
+ border-color: var(--c15t-devtools-focus-ring, #335cff);
470
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--c15t-devtools-focus-ring, #335cff) 25%, transparent);
448
471
  outline: none;
449
472
  }
450
473
 
451
474
  .grid-LlrmEz {
452
475
  gap: var(--c15t-space-sm, .5rem);
453
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, 1rem);
476
+ padding: var(--c15t-space-md, 1rem);
454
477
  display: grid;
455
478
  }
456
479
 
@@ -463,42 +486,51 @@ var __webpack_modules__ = {
463
486
  }
464
487
 
465
488
  .gridCard-Qm5xxI {
466
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, .75rem);
467
- border: 1px solid var(--c15t-border, #e3e3e3);
468
- border-radius: var(--c15t-radius-md, .5rem);
469
- background-color: var(--c15t-surface, #fff);
470
- transition: border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
471
489
  justify-content: space-between;
472
490
  align-items: center;
491
+ gap: var(--c15t-space-sm, .5rem);
492
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
493
+ border-radius: var(--c15t-radius-md, .5rem);
494
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
495
+ min-height: 2.75rem;
496
+ box-shadow: var(--c15t-shadow-sm, 0 1px 2px #0000000d);
497
+ transition: border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
498
+ padding: .5625rem .75rem;
473
499
  display: flex;
474
500
  }
475
501
 
476
502
  .gridCard-Qm5xxI:hover {
477
503
  border-color: var(--c15t-border-hover, #c9c9c9);
504
+ background-color: var(--c15t-devtools-surface-subtle, #fafafa);
478
505
  }
479
506
 
480
507
  .gridCardTitle-HjXETp {
481
508
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
482
509
  font-weight: var(--c15t-font-weight-medium, 500);
483
510
  color: var(--c15t-text, #171717);
511
+ line-height: var(--c15t-line-height-tight, 1.25);
484
512
  }
485
513
 
486
514
  .listItem-XUKGIo {
487
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-md, 1rem);
488
- border-bottom: 1px solid var(--c15t-border, #e3e3e3);
489
515
  justify-content: space-between;
490
516
  align-items: center;
517
+ gap: var(--c15t-space-sm, .5rem);
518
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
519
+ border-radius: var(--c15t-radius-md, .5rem);
520
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
521
+ margin-bottom: .375rem;
522
+ padding: .625rem .75rem;
491
523
  display: flex;
492
524
  }
493
525
 
494
526
  .listItem-XUKGIo:last-child {
495
- border-bottom: none;
527
+ margin-bottom: 0;
496
528
  }
497
529
 
498
530
  .listItemContent-WDBF1N {
499
531
  flex-direction: column;
500
532
  flex: 1;
501
- gap: 2px;
533
+ gap: .1875rem;
502
534
  min-width: 0;
503
535
  display: flex;
504
536
  }
@@ -512,6 +544,7 @@ var __webpack_modules__ = {
512
544
  .listItemDescription-E6JHyZ {
513
545
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
514
546
  color: var(--c15t-text-muted, #737373);
547
+ line-height: var(--c15t-line-height-tight, 1.25);
515
548
  text-overflow: ellipsis;
516
549
  white-space: nowrap;
517
550
  overflow: hidden;
@@ -525,8 +558,8 @@ var __webpack_modules__ = {
525
558
  }
526
559
 
527
560
  .section-a197cB {
528
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, 1rem);
529
- border-bottom: 1px solid var(--c15t-border, #e3e3e3);
561
+ border-bottom: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
562
+ padding: .75rem 1rem;
530
563
  }
531
564
 
532
565
  .section-a197cB:last-child {
@@ -534,9 +567,10 @@ var __webpack_modules__ = {
534
567
  }
535
568
 
536
569
  .sectionHeader-Xcljcw {
537
- margin-bottom: var(--c15t-space-sm, .5rem);
538
570
  justify-content: space-between;
539
571
  align-items: center;
572
+ gap: .5rem;
573
+ margin-bottom: .625rem;
540
574
  display: flex;
541
575
  }
542
576
 
@@ -545,13 +579,60 @@ var __webpack_modules__ = {
545
579
  font-weight: var(--c15t-font-weight-semibold, 600);
546
580
  color: var(--c15t-text-muted, #737373);
547
581
  text-transform: uppercase;
548
- letter-spacing: .5px;
582
+ letter-spacing: .04em;
583
+ }
584
+
585
+ .overrideField-keNdpJ {
586
+ flex-direction: column;
587
+ gap: .3125rem;
588
+ margin-bottom: 0;
589
+ display: flex;
590
+ }
591
+
592
+ .overrideLabel-ApMoTw {
593
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
594
+ font-weight: var(--c15t-font-weight-semibold, 600);
595
+ color: var(--c15t-text-muted, #737373);
596
+ line-height: var(--c15t-line-height-tight, 1.25);
597
+ }
598
+
599
+ .overrideHint-yCfwGt {
600
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
601
+ color: var(--c15t-text-muted, #737373);
602
+ line-height: var(--c15t-line-height-tight, 1.25);
603
+ margin-top: .5rem;
604
+ }
605
+
606
+ .overrideActions-imdcn7 {
607
+ border-top: 1px dashed var(--c15t-devtools-border-strong, #e3e3e3);
608
+ justify-content: space-between;
609
+ align-items: center;
610
+ gap: .5rem;
611
+ margin-top: .625rem;
612
+ padding-top: .625rem;
613
+ display: flex;
614
+ }
615
+
616
+ .overrideActionButtons-gYOx1e {
617
+ flex-wrap: wrap;
618
+ gap: .375rem;
619
+ display: flex;
620
+ }
621
+
622
+ .overrideStatus-sty_qS {
623
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
624
+ color: var(--c15t-text-muted, #737373);
625
+ }
626
+
627
+ .overrideStatusDirty-OUdDMw {
628
+ color: var(--c15t-devtools-badge-warning, #f59f0a);
549
629
  }
550
630
 
551
631
  .infoRow-RlB_0h {
552
- padding: var(--c15t-space-xs, .25rem) 0;
553
632
  justify-content: space-between;
554
633
  align-items: center;
634
+ gap: .5rem;
635
+ padding: .25rem 0;
555
636
  display: flex;
556
637
  }
557
638
 
@@ -564,6 +645,7 @@ var __webpack_modules__ = {
564
645
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
565
646
  font-weight: var(--c15t-font-weight-medium, 500);
566
647
  color: var(--c15t-text, #171717);
648
+ font-variant-numeric: tabular-nums;
567
649
  font-family: ui-monospace, Cascadia Code, Source Code Pro, Menlo, Consolas, DejaVu Sans Mono, monospace;
568
650
  }
569
651
 
@@ -574,18 +656,27 @@ var __webpack_modules__ = {
574
656
  flex-direction: column;
575
657
  justify-content: center;
576
658
  align-items: center;
659
+ gap: .375rem;
577
660
  display: flex;
578
661
  }
579
662
 
580
663
  .emptyStateIcon-WHFkX8 {
664
+ opacity: .55;
581
665
  width: 32px;
582
666
  height: 32px;
583
- margin-bottom: var(--c15t-space-sm, .5rem);
584
- opacity: .5;
585
667
  }
586
668
 
587
669
  .emptyStateText-TaLvAJ {
588
670
  font-size: var(--c15t-font-size-sm, .875rem);
671
+ line-height: var(--c15t-line-height-normal, 1.5);
672
+ }
673
+
674
+ .disconnectedState-dOtZBG {
675
+ padding: var(--c15t-space-xl, 2rem);
676
+ text-align: center;
677
+ font-size: var(--c15t-font-size-sm, .875rem);
678
+ color: var(--c15t-text-muted, #737373);
679
+ line-height: var(--c15t-line-height-normal, 1.5);
589
680
  }
590
681
 
591
682
  @media (prefers-reduced-motion: reduce) {
@@ -640,12 +731,20 @@ var __webpack_modules__ = {
640
731
  section: "section-a197cB",
641
732
  sectionHeader: "sectionHeader-Xcljcw",
642
733
  sectionTitle: "sectionTitle-RUiFld",
734
+ overrideField: "overrideField-keNdpJ",
735
+ overrideLabel: "overrideLabel-ApMoTw",
736
+ overrideHint: "overrideHint-yCfwGt",
737
+ overrideActions: "overrideActions-imdcn7",
738
+ overrideActionButtons: "overrideActionButtons-gYOx1e",
739
+ overrideStatus: "overrideStatus-sty_qS",
740
+ overrideStatusDirty: "overrideStatusDirty-OUdDMw",
643
741
  infoRow: "infoRow-RlB_0h",
644
742
  infoLabel: "infoLabel-_pbK33",
645
743
  infoValue: "infoValue-flMl_e",
646
744
  emptyState: "emptyState-QcmzTQ",
647
745
  emptyStateIcon: "emptyStateIcon-WHFkX8",
648
- emptyStateText: "emptyStateText-TaLvAJ"
746
+ emptyStateText: "emptyStateText-TaLvAJ",
747
+ disconnectedState: "disconnectedState-dOtZBG"
649
748
  };
650
749
  const __rspack_default_export = ___CSS_LOADER_EXPORT___;
651
750
  },
@@ -674,9 +773,9 @@ var __webpack_modules__ = {
674
773
  .floatingButton-Gw8MtJ {
675
774
  width: var(--c15t-devtools-button-size, 40px);
676
775
  height: var(--c15t-devtools-button-size, 40px);
677
- border: 1px solid var(--c15t-border, #e3e3e3);
776
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
678
777
  border-radius: var(--c15t-radius-full, 9999px);
679
- background-color: var(--c15t-surface, #fff);
778
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
680
779
  box-shadow: var(--c15t-shadow-lg, 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a);
681
780
  cursor: grab;
682
781
  z-index: var(--c15t-devtools-z-index, 99999);
@@ -704,13 +803,13 @@ var __webpack_modules__ = {
704
803
  }
705
804
 
706
805
  .floatingButton-Gw8MtJ:focus-visible {
707
- outline: 2px solid var(--c15t-primary, #335cff);
708
- outline-offset: 2px;
806
+ box-shadow: var(--c15t-shadow-lg, 0 8px 24px #0000001f), 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
807
+ outline: none;
709
808
  }
710
809
 
711
810
  .floatingButton-Gw8MtJ:active {
712
811
  cursor: grabbing;
713
- transform: scale(1.02);
812
+ transform: scale(.98);
714
813
  }
715
814
 
716
815
  .floatingButtonIcon-cHWefk {
@@ -751,6 +850,7 @@ var __webpack_modules__ = {
751
850
 
752
851
  .backdrop-LhVMB5 {
753
852
  background-color: var(--c15t-overlay, #00000080);
853
+ backdrop-filter: blur(1px);
754
854
  z-index: calc(var(--c15t-devtools-z-index, 99999) + 1);
755
855
  position: fixed;
756
856
  inset: 0;
@@ -759,10 +859,10 @@ var __webpack_modules__ = {
759
859
  .panel-jtWove {
760
860
  width: var(--c15t-devtools-panel-width, 480px);
761
861
  max-height: var(--c15t-devtools-panel-max-height, 560px);
762
- background-color: var(--c15t-surface, #fff);
763
- border: 1px solid var(--c15t-border, #e3e3e3);
862
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
863
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
764
864
  border-radius: var(--c15t-radius-lg, .75rem);
765
- box-shadow: var(--c15t-shadow-lg, 0 8px 24px #0000001f);
865
+ box-shadow: var(--c15t-shadow-lg, 0 10px 28px #00000029);
766
866
  z-index: calc(var(--c15t-devtools-z-index, 99999) + 2);
767
867
  flex-direction: column;
768
868
  display: flex;
@@ -795,11 +895,11 @@ var __webpack_modules__ = {
795
895
  }
796
896
 
797
897
  .header-xluoTr {
798
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, 1rem);
799
- border-bottom: 1px solid var(--c15t-border, #e3e3e3);
898
+ border-bottom: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
800
899
  background-color: var(--c15t-devtools-surface-muted, #f5f5f5);
801
900
  justify-content: space-between;
802
901
  align-items: center;
902
+ padding: .6875rem .875rem;
803
903
  display: flex;
804
904
  }
805
905
 
@@ -808,6 +908,7 @@ var __webpack_modules__ = {
808
908
  gap: var(--c15t-space-sm, .5rem);
809
909
  font-size: var(--c15t-font-size-sm, .875rem);
810
910
  font-weight: var(--c15t-font-weight-semibold, 600);
911
+ line-height: var(--c15t-line-height-tight, 1.25);
811
912
  color: var(--c15t-text, #171717);
812
913
  display: flex;
813
914
  }
@@ -819,9 +920,9 @@ var __webpack_modules__ = {
819
920
  }
820
921
 
821
922
  .closeButton-Yto0Nb {
822
- border-radius: var(--c15t-radius-sm, .25rem);
823
- width: 28px;
824
- height: 28px;
923
+ border-radius: var(--c15t-radius-md, .5rem);
924
+ width: 2rem;
925
+ height: 2rem;
825
926
  color: var(--c15t-text-muted, #737373);
826
927
  cursor: pointer;
827
928
  transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
@@ -834,13 +935,13 @@ var __webpack_modules__ = {
834
935
  }
835
936
 
836
937
  .closeButton-Yto0Nb:hover {
837
- background-color: var(--c15t-surface-hover, #f7f7f7);
938
+ background-color: var(--c15t-devtools-surface-subtle, #f7f7f7);
838
939
  color: var(--c15t-text, #171717);
839
940
  }
840
941
 
841
942
  .closeButton-Yto0Nb:focus-visible {
842
- outline: 2px solid var(--c15t-primary, #335cff);
843
- outline-offset: 1px;
943
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
944
+ outline: none;
844
945
  }
845
946
 
846
947
  .closeButtonIcon-fVlR1I {
@@ -848,33 +949,71 @@ var __webpack_modules__ = {
848
949
  height: 16px;
849
950
  }
850
951
 
952
+ .inlineActionButton-Ky8BmN {
953
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
954
+ border-radius: var(--c15t-radius-sm, .375rem);
955
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
956
+ min-height: 1.625rem;
957
+ color: var(--c15t-text, #171717);
958
+ font-family: var(--c15t-font-family, system-ui, -apple-system, sans-serif);
959
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
960
+ font-weight: var(--c15t-font-weight-medium, 500);
961
+ line-height: var(--c15t-line-height-tight, 1.25);
962
+ cursor: pointer;
963
+ transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
964
+ justify-content: center;
965
+ align-items: center;
966
+ padding: .25rem .5rem;
967
+ display: inline-flex;
968
+ }
969
+
970
+ .inlineActionButton-Ky8BmN:hover {
971
+ background-color: var(--c15t-devtools-surface-subtle, #f7f7f7);
972
+ border-color: var(--c15t-border-hover, #c9c9c9);
973
+ }
974
+
975
+ .inlineActionButton-Ky8BmN:focus-visible {
976
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
977
+ outline: none;
978
+ }
979
+
851
980
  .content-yDMYfG {
981
+ scrollbar-gutter: stable;
852
982
  overscroll-behavior: contain;
853
983
  -webkit-overflow-scrolling: touch;
984
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
854
985
  flex: auto;
855
986
  min-height: 200px;
856
- overflow: hidden auto;
987
+ overflow: hidden scroll;
857
988
  }
858
989
 
859
990
  .footer-ESbmwQ {
860
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-md, 1rem);
861
- border-top: 1px solid var(--c15t-border, #e3e3e3);
991
+ border-top: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
862
992
  background-color: var(--c15t-devtools-surface-muted, #f5f5f5);
863
993
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
864
994
  color: var(--c15t-text-muted, #737373);
865
995
  justify-content: space-between;
866
996
  align-items: center;
997
+ gap: .5rem;
998
+ padding: .5rem .75rem;
867
999
  display: flex;
868
1000
  }
869
1001
 
870
1002
  .footerStatus-rlb99A {
871
1003
  align-items: center;
872
- gap: var(--c15t-space-xs, .25rem);
1004
+ gap: .375rem;
1005
+ min-width: 0;
873
1006
  display: flex;
874
1007
  }
875
1008
 
1009
+ .footerMeta-Vdtxdk {
1010
+ opacity: .75;
1011
+ white-space: nowrap;
1012
+ }
1013
+
876
1014
  .statusDot-hYJoej {
877
1015
  border-radius: var(--c15t-radius-full, 9999px);
1016
+ flex-shrink: 0;
878
1017
  width: 6px;
879
1018
  height: 6px;
880
1019
  }
@@ -913,6 +1052,7 @@ var __webpack_modules__ = {
913
1052
  font-size: var(--c15t-font-size-sm, .875rem);
914
1053
  color: var(--c15t-text-muted, #737373);
915
1054
  max-width: 280px;
1055
+ line-height: var(--c15t-line-height-normal, 1.5);
916
1056
  }
917
1057
 
918
1058
  @media (prefers-reduced-motion: reduce) {
@@ -929,17 +1069,17 @@ var __webpack_modules__ = {
929
1069
  }
930
1070
 
931
1071
  .dropdownMenu-aKK18l {
932
- min-width: 200px;
933
- padding: var(--c15t-space-xs, .25rem);
934
- border: 1px solid var(--c15t-border, #e3e3e3);
1072
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
935
1073
  border-radius: var(--c15t-radius-lg, .75rem);
936
- background-color: var(--c15t-surface, #fff);
1074
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
1075
+ min-width: 200px;
937
1076
  box-shadow: var(--c15t-shadow-lg, 0 8px 24px #0000001f);
938
1077
  z-index: calc(var(--c15t-devtools-z-index, 99999) + 1);
939
1078
  opacity: 0;
940
1079
  transform-origin: 0 100%;
941
1080
  pointer-events: none;
942
1081
  transition: opacity var(--c15t-duration-fast, .1s) var(--c15t-easing-out, cubic-bezier(.215, .61, .355, 1)), transform var(--c15t-duration-fast, .1s) var(--c15t-easing-out, cubic-bezier(.215, .61, .355, 1));
1082
+ padding: .375rem;
943
1083
  position: fixed;
944
1084
  transform: scale(.95)translateY(8px);
945
1085
  }
@@ -967,34 +1107,35 @@ var __webpack_modules__ = {
967
1107
  }
968
1108
 
969
1109
  .menuItem-kBbHRP {
970
- align-items: center;
971
- gap: var(--c15t-space-sm, .5rem);
972
- width: 100%;
973
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, .75rem);
974
1110
  border-radius: var(--c15t-radius-md, .5rem);
1111
+ width: 100%;
975
1112
  color: var(--c15t-text, #171717);
976
- font-size: var(--c15t-font-size-sm, .875rem);
1113
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
977
1114
  font-weight: var(--c15t-font-weight-medium, 500);
1115
+ line-height: var(--c15t-line-height-tight, 1.25);
978
1116
  text-align: left;
979
1117
  cursor: pointer;
980
1118
  transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
981
1119
  background: none;
982
1120
  border: none;
1121
+ align-items: center;
1122
+ gap: .625rem;
1123
+ padding: .5rem .625rem;
983
1124
  display: flex;
984
1125
  }
985
1126
 
986
1127
  .menuItem-kBbHRP:hover {
987
- background-color: var(--c15t-surface-hover, #f2f2f2);
1128
+ background-color: var(--c15t-devtools-surface-subtle, #f2f2f2);
988
1129
  }
989
1130
 
990
1131
  .menuItem-kBbHRP:focus-visible {
991
- outline: 2px solid var(--c15t-primary, #335cff);
992
- outline-offset: -2px;
1132
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
1133
+ outline: none;
993
1134
  }
994
1135
 
995
1136
  .menuItemIcon-P3pP5K {
996
- width: 20px;
997
- height: 20px;
1137
+ width: 1rem;
1138
+ height: 1rem;
998
1139
  color: var(--c15t-text-muted, #737373);
999
1140
  flex-shrink: 0;
1000
1141
  }
@@ -1007,6 +1148,7 @@ var __webpack_modules__ = {
1007
1148
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
1008
1149
  color: var(--c15t-text-muted, #737373);
1009
1150
  font-weight: var(--c15t-font-weight-normal, 400);
1151
+ margin-top: .125rem;
1010
1152
  }
1011
1153
 
1012
1154
  .menuItemContent-hBlruV {
@@ -1022,8 +1164,8 @@ var __webpack_modules__ = {
1022
1164
  .menuItemToggleTrack-gDp_f3 {
1023
1165
  background-color: var(--c15t-switch-track, #d9d9d9);
1024
1166
  border-radius: var(--c15t-radius-full, 9999px);
1025
- width: 36px;
1026
- height: 20px;
1167
+ width: 2rem;
1168
+ height: 1.25rem;
1027
1169
  transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
1028
1170
  position: relative;
1029
1171
  }
@@ -1031,21 +1173,22 @@ var __webpack_modules__ = {
1031
1173
  .menuItemToggleThumb-ioqqyc {
1032
1174
  background-color: var(--c15t-switch-thumb, #fff);
1033
1175
  border-radius: var(--c15t-radius-full, 9999px);
1034
- width: 16px;
1035
- height: 16px;
1036
- box-shadow: var(--c15t-shadow-sm, 0 1px 2px #0000001a);
1176
+ width: .75rem;
1177
+ height: .75rem;
1178
+ box-shadow: 0 0 0 1px var(--c15t-border, #e3e3e3);
1037
1179
  transition: transform var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
1038
1180
  position: absolute;
1039
- top: 2px;
1181
+ top: 50%;
1040
1182
  left: 2px;
1183
+ transform: translateY(-50%);
1041
1184
  }
1042
1185
 
1043
1186
  .menuItemToggleChecked-K3BPtk .menuItemToggleTrack-gDp_f3 {
1044
- background-color: var(--c15t-switch-track-checked, #335cff);
1187
+ background-color: var(--c15t-switch-track-active, #335cff);
1045
1188
  }
1046
1189
 
1047
1190
  .menuItemToggleChecked-K3BPtk .menuItemToggleThumb-ioqqyc {
1048
- transform: translateX(16px);
1191
+ transform: translate(1rem, -50%);
1049
1192
  }
1050
1193
 
1051
1194
  .menuDivider-JIBdhU {
@@ -1081,9 +1224,11 @@ var __webpack_modules__ = {
1081
1224
  headerLogo: "headerLogo-PxJ_w1",
1082
1225
  closeButton: "closeButton-Yto0Nb",
1083
1226
  closeButtonIcon: "closeButtonIcon-fVlR1I",
1227
+ inlineActionButton: "inlineActionButton-Ky8BmN",
1084
1228
  content: "content-yDMYfG",
1085
1229
  footer: "footer-ESbmwQ",
1086
1230
  footerStatus: "footerStatus-rlb99A",
1231
+ footerMeta: "footerMeta-Vdtxdk",
1087
1232
  statusDot: "statusDot-hYJoej",
1088
1233
  statusConnected: "statusConnected-hPSUgS",
1089
1234
  statusDisconnected: "statusDisconnected-HIpcee",
@@ -1122,55 +1267,63 @@ var __webpack_modules__ = {
1122
1267
  ___CSS_LOADER_EXPORT___.push([
1123
1268
  module.id,
1124
1269
  `.tabList-IyuiBE {
1125
- gap: var(--c15t-space-xs, .25rem);
1126
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, 1rem);
1127
- border-bottom: 1px solid var(--c15t-border, #e3e3e3);
1128
- background-color: var(--c15t-surface, #fff);
1129
- scrollbar-width: none;
1130
- -ms-overflow-style: none;
1270
+ border-bottom: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
1271
+ background-color: var(--c15t-devtools-surface-subtle, #fafafa);
1272
+ align-items: center;
1273
+ gap: .375rem;
1274
+ padding: .75rem;
1131
1275
  display: flex;
1132
- overflow-x: auto;
1133
1276
  }
1134
1277
 
1135
- .tabList-IyuiBE::-webkit-scrollbar {
1136
- display: none;
1278
+ .tabStrip-_KrWe4 {
1279
+ align-items: center;
1280
+ gap: var(--c15t-space-xs, .25rem);
1281
+ flex: auto;
1282
+ min-width: 0;
1283
+ display: flex;
1284
+ overflow: hidden;
1137
1285
  }
1138
1286
 
1139
1287
  .tab-yfDEqg {
1140
- align-items: center;
1141
- gap: var(--c15t-space-xs, .25rem);
1142
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-sm, .5rem);
1143
1288
  border-radius: var(--c15t-radius-md, .5rem);
1289
+ min-height: 1.875rem;
1144
1290
  color: var(--c15t-text-muted, #737373);
1145
- font-family: inherit;
1291
+ font-family: var(--c15t-font-family, system-ui, -apple-system, sans-serif);
1146
1292
  font-size: var(--c15t-devtools-font-size-xs, .75rem);
1147
1293
  font-weight: var(--c15t-font-weight-medium, 500);
1294
+ line-height: var(--c15t-line-height-tight, 1.25);
1148
1295
  cursor: pointer;
1149
1296
  white-space: nowrap;
1150
- transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
1297
+ transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), box-shadow var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
1151
1298
  background-color: #0000;
1152
- border: none;
1299
+ border: 1px solid #0000;
1300
+ flex-shrink: 0;
1301
+ align-items: center;
1302
+ gap: .375rem;
1303
+ padding: .25rem .625rem;
1153
1304
  display: flex;
1154
1305
  }
1155
1306
 
1156
1307
  .tab-yfDEqg:hover {
1157
- background-color: var(--c15t-surface-hover, #f7f7f7);
1308
+ background-color: var(--c15t-devtools-surface-muted, #f7f7f7);
1158
1309
  color: var(--c15t-text, #171717);
1159
1310
  }
1160
1311
 
1161
1312
  .tab-yfDEqg:focus-visible {
1162
- outline: 2px solid var(--c15t-primary, #335cff);
1163
- outline-offset: 1px;
1313
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
1314
+ outline: none;
1164
1315
  }
1165
1316
 
1166
1317
  .tabActive-r4hing {
1167
- background-color: var(--c15t-primary, #335cff);
1168
- color: var(--c15t-text-on-primary, #fff);
1318
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
1319
+ border-color: var(--c15t-devtools-border-strong, #e3e3e3);
1320
+ color: var(--c15t-text, #171717);
1321
+ box-shadow: var(--c15t-shadow-sm, 0 1px 2px #0000000d);
1169
1322
  }
1170
1323
 
1171
1324
  .tabActive-r4hing:hover {
1172
- background-color: var(--c15t-primary-hover, #03f);
1173
- color: var(--c15t-text-on-primary, #fff);
1325
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
1326
+ color: var(--c15t-text, #171717);
1174
1327
  }
1175
1328
 
1176
1329
  .tabIcon-U9tnu0 {
@@ -1179,6 +1332,131 @@ var __webpack_modules__ = {
1179
1332
  height: 14px;
1180
1333
  }
1181
1334
 
1335
+ .tabHidden-HBXYSd {
1336
+ display: none;
1337
+ }
1338
+
1339
+ .overflowContainer-TTw9DO {
1340
+ flex-shrink: 0;
1341
+ position: relative;
1342
+ }
1343
+
1344
+ .overflowContainerHidden-sQa_XZ {
1345
+ display: none;
1346
+ }
1347
+
1348
+ .overflowButton-tKq4FF {
1349
+ border-radius: var(--c15t-radius-md, .5rem);
1350
+ width: 1.875rem;
1351
+ height: 1.875rem;
1352
+ color: var(--c15t-text-muted, #737373);
1353
+ cursor: pointer;
1354
+ transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), border-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
1355
+ background-color: #0000;
1356
+ border: 1px solid #0000;
1357
+ justify-content: center;
1358
+ align-items: center;
1359
+ padding: 0;
1360
+ display: inline-flex;
1361
+ }
1362
+
1363
+ .overflowButton-tKq4FF:hover {
1364
+ background-color: var(--c15t-devtools-surface-muted, #f7f7f7);
1365
+ color: var(--c15t-text, #171717);
1366
+ }
1367
+
1368
+ .overflowButton-tKq4FF:focus-visible {
1369
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
1370
+ outline: none;
1371
+ }
1372
+
1373
+ .overflowButtonIcon-FSurfC {
1374
+ justify-content: center;
1375
+ align-items: center;
1376
+ width: 14px;
1377
+ height: 14px;
1378
+ display: inline-flex;
1379
+ }
1380
+
1381
+ .overflowMenu-TST0eZ {
1382
+ border: 1px solid var(--c15t-devtools-border-strong, #e3e3e3);
1383
+ border-radius: var(--c15t-radius-md, .5rem);
1384
+ background-color: var(--c15t-devtools-surface-elevated, #fff);
1385
+ min-width: 10rem;
1386
+ box-shadow: var(--c15t-shadow-md, 0 4px 12px #00000014);
1387
+ opacity: 0;
1388
+ transform-origin: 100% 0;
1389
+ pointer-events: none;
1390
+ z-index: 10;
1391
+ transition: opacity var(--c15t-duration-fast, .1s) var(--c15t-easing-out, cubic-bezier(.215, .61, .355, 1)), transform var(--c15t-duration-fast, .1s) var(--c15t-easing-out, cubic-bezier(.215, .61, .355, 1));
1392
+ flex-direction: column;
1393
+ gap: .125rem;
1394
+ padding: .3125rem;
1395
+ display: flex;
1396
+ position: absolute;
1397
+ top: calc(100% + .375rem);
1398
+ right: 0;
1399
+ transform: translateY(-4px)scale(.98);
1400
+ }
1401
+
1402
+ .overflowMenu-TST0eZ[data-state="open"] {
1403
+ opacity: 1;
1404
+ pointer-events: auto;
1405
+ transform: translateY(0)scale(1);
1406
+ }
1407
+
1408
+ .overflowItem-y5Pz7k {
1409
+ border-radius: var(--c15t-radius-sm, .375rem);
1410
+ min-height: 1.75rem;
1411
+ color: var(--c15t-text, #171717);
1412
+ font-family: var(--c15t-font-family, system-ui, -apple-system, sans-serif);
1413
+ font-size: var(--c15t-devtools-font-size-xs, .75rem);
1414
+ font-weight: var(--c15t-font-weight-medium, 500);
1415
+ line-height: var(--c15t-line-height-tight, 1.25);
1416
+ text-align: left;
1417
+ cursor: pointer;
1418
+ white-space: nowrap;
1419
+ transition: background-color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1)), color var(--c15t-duration-fast, .1s) var(--c15t-easing, cubic-bezier(.4, 0, .2, 1));
1420
+ background-color: #0000;
1421
+ border: none;
1422
+ align-items: center;
1423
+ gap: .5rem;
1424
+ padding: .3125rem .5rem;
1425
+ display: inline-flex;
1426
+ }
1427
+
1428
+ .overflowItem-y5Pz7k:hover {
1429
+ background-color: var(--c15t-devtools-surface-subtle, #fafafa);
1430
+ }
1431
+
1432
+ .overflowItem-y5Pz7k:focus-visible {
1433
+ box-shadow: 0 0 0 2px var(--c15t-devtools-focus-ring, #335cff);
1434
+ outline: none;
1435
+ }
1436
+
1437
+ .overflowItemActive-mzVG1T {
1438
+ background-color: var(--c15t-devtools-accent-soft, #ebefff);
1439
+ color: var(--c15t-text, #171717);
1440
+ }
1441
+
1442
+ .overflowItemDisabled-dcHX3K {
1443
+ opacity: .5;
1444
+ cursor: not-allowed;
1445
+ }
1446
+
1447
+ .overflowItemIcon-fz291V {
1448
+ flex-shrink: 0;
1449
+ justify-content: center;
1450
+ align-items: center;
1451
+ width: 14px;
1452
+ height: 14px;
1453
+ display: inline-flex;
1454
+ }
1455
+
1456
+ .overflowItemHidden-k4aawi {
1457
+ display: none;
1458
+ }
1459
+
1182
1460
  .tabDisabled-lDuv5l {
1183
1461
  opacity: .5;
1184
1462
  cursor: not-allowed;
@@ -1199,7 +1477,7 @@ var __webpack_modules__ = {
1199
1477
  }
1200
1478
 
1201
1479
  @media (prefers-reduced-motion: reduce) {
1202
- .tab-yfDEqg {
1480
+ .tab-yfDEqg, .overflowButton-tKq4FF, .overflowMenu-TST0eZ, .overflowItem-y5Pz7k {
1203
1481
  transition: none;
1204
1482
  }
1205
1483
  }
@@ -1215,9 +1493,21 @@ var __webpack_modules__ = {
1215
1493
  ]);
1216
1494
  ___CSS_LOADER_EXPORT___.locals = {
1217
1495
  tabList: "tabList-IyuiBE",
1496
+ tabStrip: "tabStrip-_KrWe4",
1218
1497
  tab: "tab-yfDEqg",
1219
1498
  tabActive: "tabActive-r4hing",
1220
1499
  tabIcon: "tabIcon-U9tnu0",
1500
+ tabHidden: "tabHidden-HBXYSd",
1501
+ overflowContainer: "overflowContainer-TTw9DO",
1502
+ overflowContainerHidden: "overflowContainerHidden-sQa_XZ",
1503
+ overflowButton: "overflowButton-tKq4FF",
1504
+ overflowButtonIcon: "overflowButtonIcon-FSurfC",
1505
+ overflowMenu: "overflowMenu-TST0eZ",
1506
+ overflowItem: "overflowItem-y5Pz7k",
1507
+ overflowItemActive: "overflowItemActive-mzVG1T",
1508
+ overflowItemDisabled: "overflowItemDisabled-dcHX3K",
1509
+ overflowItemIcon: "overflowItemIcon-fz291V",
1510
+ overflowItemHidden: "overflowItemHidden-k4aawi",
1221
1511
  tabDisabled: "tabDisabled-lDuv5l",
1222
1512
  tabPanel: "tabPanel-QKO8FX",
1223
1513
  tabPanelActive: "tabPanelActive-mrNlGE"
@@ -1241,6 +1531,15 @@ var __webpack_modules__ = {
1241
1531
  --c15t-devtools-button-size: 40px;
1242
1532
  --c15t-devtools-z-index: 99999;
1243
1533
  --c15t-devtools-surface-muted: var(--c15t-surface-hover, #f7f7f7);
1534
+ --c15t-devtools-surface-subtle: #fafafa;
1535
+ --c15t-devtools-surface-elevated: var(--c15t-surface, #fff);
1536
+ --c15t-devtools-border-strong: var(--c15t-border, #e3e3e3);
1537
+ --c15t-devtools-code-surface: #f7f7f7;
1538
+ --c15t-devtools-accent-soft: #ebefff;
1539
+ --c15t-devtools-focus-ring: var(--c15t-primary, #335cff);
1540
+ --c15t-devtools-text-muted: var(--c15t-text-muted, #737373);
1541
+ --c15t-devtools-font-size-sm: var(--c15t-font-size-sm, .875rem);
1542
+ --c15t-surface-muted: var(--c15t-devtools-surface-muted, #f7f7f7);
1244
1543
  --c15t-devtools-font-size-xs: .75rem;
1245
1544
  --c15t-devtools-badge-success: #21c45d;
1246
1545
  --c15t-devtools-badge-success-bg: #e4fbed;
@@ -1254,8 +1553,37 @@ var __webpack_modules__ = {
1254
1553
  --c15t-devtools-badge-neutral-bg: #f0f0f0;
1255
1554
  }
1256
1555
 
1556
+ :is(:global(.c15t-light), :global(.light)) {
1557
+ --c15t-devtools-surface-muted: var(--c15t-surface-hover, #f7f7f7);
1558
+ --c15t-devtools-surface-subtle: #fafafa;
1559
+ --c15t-devtools-surface-elevated: var(--c15t-surface, #fff);
1560
+ --c15t-devtools-border-strong: var(--c15t-border, #e3e3e3);
1561
+ --c15t-devtools-code-surface: #f7f7f7;
1562
+ --c15t-devtools-accent-soft: #ebefff;
1563
+ --c15t-devtools-badge-success-bg: #e4fbed;
1564
+ --c15t-devtools-badge-error-bg: #fde7e7;
1565
+ --c15t-devtools-badge-warning-bg: #fef7dc;
1566
+ --c15t-devtools-badge-info-bg: #dcebfe;
1567
+ --c15t-devtools-badge-neutral-bg: #f0f0f0;
1568
+ }
1569
+
1570
+ @supports (color: color-mix(in srgb, white, black)) {
1571
+ :root {
1572
+ --c15t-devtools-surface-muted: color-mix(in srgb, var(--c15t-surface-hover, #f7f7f7) 85%, var(--c15t-surface, #fff));
1573
+ --c15t-devtools-surface-subtle: color-mix(in srgb, var(--c15t-surface-hover, #f7f7f7) 55%, var(--c15t-surface, #fff));
1574
+ --c15t-devtools-border-strong: color-mix(in srgb, var(--c15t-border, #e3e3e3) 80%, var(--c15t-text-muted, #737373));
1575
+ --c15t-devtools-code-surface: color-mix(in srgb, var(--c15t-surface-hover, #f7f7f7) 70%, var(--c15t-surface, #fff));
1576
+ --c15t-devtools-accent-soft: color-mix(in srgb, var(--c15t-primary, #335cff) 12%, transparent);
1577
+ }
1578
+ }
1579
+
1257
1580
  :is(:global(.c15t-dark), :global(.dark)) {
1258
1581
  --c15t-devtools-surface-muted: var(--c15t-surface-hover, #292929);
1582
+ --c15t-devtools-surface-subtle: #242424;
1583
+ --c15t-devtools-surface-elevated: var(--c15t-surface, #1a1a1a);
1584
+ --c15t-devtools-border-strong: var(--c15t-border, #3d3d3d);
1585
+ --c15t-devtools-code-surface: #212121;
1586
+ --c15t-devtools-accent-soft: #335cff33;
1259
1587
  --c15t-devtools-badge-success-bg: #21c45d33;
1260
1588
  --c15t-devtools-badge-error-bg: #ef434333;
1261
1589
  --c15t-devtools-badge-warning-bg: #f59f0a33;
@@ -1264,8 +1592,13 @@ var __webpack_modules__ = {
1264
1592
  }
1265
1593
 
1266
1594
  @media (prefers-color-scheme: dark) {
1267
- :root {
1595
+ :global(:root:not(.light):not(.c15t-light)) {
1268
1596
  --c15t-devtools-surface-muted: var(--c15t-surface-hover, #292929);
1597
+ --c15t-devtools-surface-subtle: #242424;
1598
+ --c15t-devtools-surface-elevated: var(--c15t-surface, #1a1a1a);
1599
+ --c15t-devtools-border-strong: var(--c15t-border, #3d3d3d);
1600
+ --c15t-devtools-code-surface: #212121;
1601
+ --c15t-devtools-accent-soft: #335cff33;
1269
1602
  --c15t-devtools-badge-success-bg: #21c45d33;
1270
1603
  --c15t-devtools-badge-error-bg: #ef434333;
1271
1604
  --c15t-devtools-badge-warning-bg: #f59f0a33;
@@ -1655,7 +1988,7 @@ panel_module_options.domAPI = styleDomAPI_default();
1655
1988
  panel_module_options.insertStyleElement = insertStyleElement_default();
1656
1989
  injectStylesIntoStyleTag_default()(panel_module.A, panel_module_options);
1657
1990
  panel_module.A && panel_module.A.locals && panel_module.A.locals;
1658
- '[data-c15t-trigger], [aria-label*="privacy settings"], [aria-label*="preference"]';
1991
+ '[data-c15t-trigger], [aria-label*="privacy settings" i], [aria-label*="preference" i]';
1659
1992
  var tabs_module = __webpack_require__("../../node_modules/.bun/@rsbuild+core@1.6.12/node_modules/@rsbuild/core/compiled/css-loader/index.js??ruleSet[1].rules[1].use[1]!builtin:lightningcss-loader??ruleSet[1].rules[1].use[2]!./src/styles/tabs.module.css");
1660
1993
  var tabs_module_options = {};
1661
1994
  tabs_module_options.styleTagTransform = styleTagTransform_default();
@@ -1690,6 +2023,11 @@ const EVENTS_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
1690
2023
  <path d="M12 20h9"></path>
1691
2024
  <path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"></path>
1692
2025
  </svg>`;
2026
+ const MORE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
2027
+ <circle cx="12" cy="5" r="1.75"></circle>
2028
+ <circle cx="12" cy="12" r="1.75"></circle>
2029
+ <circle cx="12" cy="19" r="1.75"></circle>
2030
+ </svg>`;
1693
2031
  const TABS = [
1694
2032
  {
1695
2033
  id: 'location',
@@ -1725,12 +2063,56 @@ const TABS = [
1725
2063
  function tabs_createTabs(options) {
1726
2064
  const { onTabChange, disabledTabs = [] } = options;
1727
2065
  let activeTab = options.activeTab;
2066
+ let isOverflowMenuOpen = false;
2067
+ let visibleTabIds = [];
2068
+ let hiddenTabIds = [];
1728
2069
  const tabButtons = new Map();
2070
+ const overflowButtons = new Map();
1729
2071
  const tabList = renderer_div({
1730
- className: styles_tabs_module.tabList,
2072
+ className: styles_tabs_module.tabList
2073
+ });
2074
+ const tabStrip = renderer_div({
2075
+ className: styles_tabs_module.tabStrip,
1731
2076
  role: 'tablist',
1732
2077
  ariaLabel: 'DevTools tabs'
1733
2078
  });
2079
+ tabList.appendChild(tabStrip);
2080
+ const overflowMenu = renderer_div({
2081
+ className: styles_tabs_module.overflowMenu,
2082
+ role: 'menu',
2083
+ ariaLabel: 'All tabs'
2084
+ });
2085
+ overflowMenu.dataset.state = 'closed';
2086
+ const overflowButton = renderer_button({
2087
+ className: styles_tabs_module.overflowButton,
2088
+ ariaLabel: 'More tabs',
2089
+ ariaExpanded: 'false',
2090
+ onClick: ()=>toggleOverflowMenu(),
2091
+ onKeyDown: (e)=>{
2092
+ if ('ArrowDown' === e.key || 'Enter' === e.key || ' ' === e.key) {
2093
+ e.preventDefault();
2094
+ openOverflowMenu();
2095
+ focusFirstEnabledOverflowItem();
2096
+ }
2097
+ }
2098
+ });
2099
+ overflowButton.setAttribute('aria-haspopup', 'menu');
2100
+ const overflowIcon = renderer_div({
2101
+ className: styles_tabs_module.overflowButtonIcon
2102
+ });
2103
+ overflowIcon.appendChild(renderer_createSvgElement(MORE_ICON, {
2104
+ width: 14,
2105
+ height: 14
2106
+ }));
2107
+ overflowButton.appendChild(overflowIcon);
2108
+ const overflowContainer = renderer_div({
2109
+ className: styles_tabs_module.overflowContainer,
2110
+ children: [
2111
+ overflowButton,
2112
+ overflowMenu
2113
+ ]
2114
+ });
2115
+ tabList.appendChild(overflowContainer);
1734
2116
  for (const tab of TABS){
1735
2117
  const isActive = tab.id === activeTab;
1736
2118
  const isDisabled = disabledTabs.includes(tab.id);
@@ -1744,6 +2126,7 @@ function tabs_createTabs(options) {
1744
2126
  disabled: isDisabled,
1745
2127
  onClick: ()=>{
1746
2128
  if (!isDisabled) {
2129
+ closeOverflowMenu();
1747
2130
  setActiveTab(tab.id);
1748
2131
  onTabChange(tab.id);
1749
2132
  }
@@ -1760,16 +2143,193 @@ function tabs_createTabs(options) {
1760
2143
  tabButton.appendChild(iconWrapper);
1761
2144
  tabButton.appendChild(document.createTextNode(tab.label));
1762
2145
  tabButtons.set(tab.id, tabButton);
1763
- tabList.appendChild(tabButton);
1764
- }
1765
- function handleKeyDown(e, currentTab) {
1766
- const tabIds = TABS.map((t)=>t.id);
1767
- const enabledTabIds = tabIds.filter((id)=>!disabledTabs.includes(id));
1768
- const currentIndex = enabledTabIds.indexOf(currentTab);
1769
- let newIndex = currentIndex;
1770
- switch(e.key){
1771
- case 'ArrowLeft':
1772
- newIndex = currentIndex > 0 ? currentIndex - 1 : enabledTabIds.length - 1;
2146
+ tabStrip.appendChild(tabButton);
2147
+ const overflowItem = renderer_button({
2148
+ className: `${styles_tabs_module.overflowItem} ${isActive ? styles_tabs_module.overflowItemActive : ''} ${isDisabled ? styles_tabs_module.overflowItemDisabled : ''}`,
2149
+ role: 'menuitemradio',
2150
+ ariaChecked: isActive ? 'true' : 'false',
2151
+ disabled: isDisabled,
2152
+ onClick: ()=>{
2153
+ if (!isDisabled) {
2154
+ setActiveTab(tab.id);
2155
+ onTabChange(tab.id);
2156
+ closeOverflowMenu();
2157
+ tabButtons.get(tab.id)?.focus();
2158
+ }
2159
+ },
2160
+ onKeyDown: (e)=>handleOverflowKeyDown(e, tab.id)
2161
+ });
2162
+ const overflowItemIcon = renderer_div({
2163
+ className: styles_tabs_module.overflowItemIcon
2164
+ });
2165
+ overflowItemIcon.appendChild(renderer_createSvgElement(tab.icon, {
2166
+ width: 14,
2167
+ height: 14
2168
+ }));
2169
+ overflowItem.appendChild(overflowItemIcon);
2170
+ overflowItem.appendChild(document.createTextNode(tab.label));
2171
+ overflowButtons.set(tab.id, overflowItem);
2172
+ overflowMenu.appendChild(overflowItem);
2173
+ }
2174
+ function applyActiveState(tab) {
2175
+ for (const [tabId, tabButton] of tabButtons){
2176
+ const isActive = tabId === tab;
2177
+ if (styles_tabs_module.tabActive) tabButton.classList.toggle(styles_tabs_module.tabActive, isActive);
2178
+ tabButton.setAttribute('aria-selected', isActive ? 'true' : 'false');
2179
+ tabButton.tabIndex = isActive ? 0 : -1;
2180
+ }
2181
+ for (const [tabId, overflowItem] of overflowButtons){
2182
+ const isActive = tabId === tab;
2183
+ if (styles_tabs_module.overflowItemActive) overflowItem.classList.toggle(styles_tabs_module.overflowItemActive, isActive);
2184
+ overflowItem.setAttribute('aria-checked', isActive ? 'true' : 'false');
2185
+ }
2186
+ }
2187
+ function updateVisibleTabs() {
2188
+ const allTabIds = TABS.map((t)=>t.id);
2189
+ const iabEnabled = !disabledTabs.includes('iab');
2190
+ const preferredSecondTab = iabEnabled ? 'iab' : 'consents';
2191
+ const overflowSecondTab = iabEnabled ? 'consents' : 'iab';
2192
+ const showOverflowSecondTabInStrip = activeTab === overflowSecondTab;
2193
+ const stripSecondTab = showOverflowSecondTabInStrip ? overflowSecondTab : preferredSecondTab;
2194
+ const forcedOverflowTab = showOverflowSecondTabInStrip ? preferredSecondTab : overflowSecondTab;
2195
+ const layoutTabIds = [
2196
+ 'location',
2197
+ stripSecondTab,
2198
+ "scripts",
2199
+ 'actions',
2200
+ 'events',
2201
+ forcedOverflowTab
2202
+ ];
2203
+ const forcedOverflowTabIds = new Set();
2204
+ forcedOverflowTabIds.add(forcedOverflowTab);
2205
+ for (const [index, tabId] of layoutTabIds.entries()){
2206
+ const tabButton = tabButtons.get(tabId);
2207
+ if (tabButton) tabButton.style.order = String(index);
2208
+ const overflowItem = overflowButtons.get(tabId);
2209
+ if (overflowItem) overflowItem.style.order = String(index);
2210
+ }
2211
+ for (const tabId of allTabIds){
2212
+ const tabButton = tabButtons.get(tabId);
2213
+ if (tabButton && styles_tabs_module.tabHidden) tabButton.classList.remove(styles_tabs_module.tabHidden);
2214
+ }
2215
+ if (styles_tabs_module.overflowContainerHidden) overflowContainer.classList.remove(styles_tabs_module.overflowContainerHidden);
2216
+ const stripGap = Number.parseFloat(getComputedStyle(tabStrip).gap || '0');
2217
+ const calculateVisibleTabs = (availableWidth)=>{
2218
+ if (availableWidth <= 0) return [];
2219
+ const nextVisible = [];
2220
+ let usedWidth = 0;
2221
+ for (const tabId of layoutTabIds){
2222
+ if (forcedOverflowTabIds.has(tabId)) continue;
2223
+ const tabButton = tabButtons.get(tabId);
2224
+ if (!tabButton) continue;
2225
+ const width = tabButton.getBoundingClientRect().width;
2226
+ const nextUsed = 0 === nextVisible.length ? width : usedWidth + stripGap + width;
2227
+ if (nextUsed <= availableWidth) {
2228
+ nextVisible.push(tabId);
2229
+ usedWidth = nextUsed;
2230
+ } else break;
2231
+ }
2232
+ return nextVisible;
2233
+ };
2234
+ const measureStripWidth = ()=>tabStrip.getBoundingClientRect().width;
2235
+ const showOverflowContainer = ()=>{
2236
+ if (styles_tabs_module.overflowContainerHidden) overflowContainer.classList.remove(styles_tabs_module.overflowContainerHidden);
2237
+ };
2238
+ const hideOverflowContainer = ()=>{
2239
+ if (styles_tabs_module.overflowContainerHidden) overflowContainer.classList.add(styles_tabs_module.overflowContainerHidden);
2240
+ };
2241
+ const measureVisibleWidth = (tabIds)=>{
2242
+ let width = 0;
2243
+ for (const [index, tabId] of tabIds.entries()){
2244
+ const tabButton = tabButtons.get(tabId);
2245
+ if (tabButton) {
2246
+ width += tabButton.getBoundingClientRect().width;
2247
+ if (index > 0) width += stripGap;
2248
+ }
2249
+ }
2250
+ return width;
2251
+ };
2252
+ if (0 === forcedOverflowTabIds.size) {
2253
+ hideOverflowContainer();
2254
+ const visibleWithoutOverflow = calculateVisibleTabs(measureStripWidth());
2255
+ if (visibleWithoutOverflow.length === layoutTabIds.length) visibleTabIds = visibleWithoutOverflow;
2256
+ else {
2257
+ showOverflowContainer();
2258
+ visibleTabIds = calculateVisibleTabs(measureStripWidth());
2259
+ }
2260
+ } else {
2261
+ showOverflowContainer();
2262
+ const withOverflow = calculateVisibleTabs(measureStripWidth());
2263
+ visibleTabIds = withOverflow.length > 0 ? withOverflow : [
2264
+ activeTab
2265
+ ];
2266
+ }
2267
+ if (!visibleTabIds.includes(activeTab) && !disabledTabs.includes(activeTab)) if (visibleTabIds.length > 0) visibleTabIds[visibleTabIds.length - 1] = activeTab;
2268
+ else visibleTabIds = [
2269
+ activeTab
2270
+ ];
2271
+ visibleTabIds = [
2272
+ ...new Set(visibleTabIds)
2273
+ ];
2274
+ const maxStripWidth = measureStripWidth();
2275
+ while(visibleTabIds.length > 1 && measureVisibleWidth(visibleTabIds) > maxStripWidth + 0.5){
2276
+ let removeIndex = visibleTabIds.length - 1;
2277
+ if (visibleTabIds[removeIndex] === activeTab) removeIndex = Math.max(0, removeIndex - 1);
2278
+ visibleTabIds.splice(removeIndex, 1);
2279
+ }
2280
+ hiddenTabIds = layoutTabIds.filter((tabId)=>!visibleTabIds.includes(tabId) || forcedOverflowTabIds.has(tabId) && tabId !== activeTab);
2281
+ for (const tabId of allTabIds){
2282
+ const tabButton = tabButtons.get(tabId);
2283
+ if (tabButton) {
2284
+ if (styles_tabs_module.tabHidden) tabButton.classList.toggle(styles_tabs_module.tabHidden, hiddenTabIds.includes(tabId));
2285
+ }
2286
+ }
2287
+ for (const tabId of allTabIds){
2288
+ const overflowItem = overflowButtons.get(tabId);
2289
+ if (overflowItem) {
2290
+ if (styles_tabs_module.overflowItemHidden) overflowItem.classList.toggle(styles_tabs_module.overflowItemHidden, !hiddenTabIds.includes(tabId));
2291
+ }
2292
+ }
2293
+ if (styles_tabs_module.overflowContainerHidden) overflowContainer.classList.toggle(styles_tabs_module.overflowContainerHidden, 0 === hiddenTabIds.length);
2294
+ if (0 === hiddenTabIds.length) closeOverflowMenu();
2295
+ }
2296
+ function focusFirstEnabledOverflowItem() {
2297
+ const firstEnabled = hiddenTabIds.find((tabId)=>!disabledTabs.includes(tabId));
2298
+ if (firstEnabled) overflowButtons.get(firstEnabled)?.focus();
2299
+ }
2300
+ function openOverflowMenu() {
2301
+ if (isOverflowMenuOpen || 0 === hiddenTabIds.length) return;
2302
+ isOverflowMenuOpen = true;
2303
+ overflowMenu.dataset.state = 'open';
2304
+ overflowButton.setAttribute('aria-expanded', 'true');
2305
+ document.addEventListener('click', handleOutsideClick);
2306
+ document.addEventListener('keydown', handleEscapeKey);
2307
+ }
2308
+ function closeOverflowMenu() {
2309
+ if (!isOverflowMenuOpen) return;
2310
+ isOverflowMenuOpen = false;
2311
+ overflowMenu.dataset.state = 'closed';
2312
+ overflowButton.setAttribute('aria-expanded', 'false');
2313
+ document.removeEventListener('click', handleOutsideClick);
2314
+ document.removeEventListener('keydown', handleEscapeKey);
2315
+ }
2316
+ function toggleOverflowMenu() {
2317
+ if (isOverflowMenuOpen) closeOverflowMenu();
2318
+ else openOverflowMenu();
2319
+ }
2320
+ function handleOutsideClick(e) {
2321
+ if (!overflowContainer.contains(e.target)) closeOverflowMenu();
2322
+ }
2323
+ function handleEscapeKey(e) {
2324
+ if ('Escape' === e.key) closeOverflowMenu();
2325
+ }
2326
+ function handleKeyDown(e, currentTab) {
2327
+ const enabledTabIds = visibleTabIds.filter((tabId)=>!disabledTabs.includes(tabId));
2328
+ const currentIndex = enabledTabIds.indexOf(currentTab);
2329
+ let newIndex = currentIndex;
2330
+ switch(e.key){
2331
+ case 'ArrowLeft':
2332
+ newIndex = currentIndex > 0 ? currentIndex - 1 : enabledTabIds.length - 1;
1773
2333
  break;
1774
2334
  case 'ArrowRight':
1775
2335
  newIndex = currentIndex < enabledTabIds.length - 1 ? currentIndex + 1 : 0;
@@ -1791,23 +2351,152 @@ function tabs_createTabs(options) {
1791
2351
  tabButtons.get(newTab)?.focus();
1792
2352
  }
1793
2353
  }
2354
+ function handleOverflowKeyDown(e, currentTab) {
2355
+ const enabledTabIds = hiddenTabIds.filter((tabId)=>!disabledTabs.includes(tabId));
2356
+ const currentIndex = enabledTabIds.indexOf(currentTab);
2357
+ if ('Escape' === e.key) {
2358
+ e.preventDefault();
2359
+ closeOverflowMenu();
2360
+ overflowButton.focus();
2361
+ return;
2362
+ }
2363
+ let newIndex = currentIndex;
2364
+ switch(e.key){
2365
+ case 'ArrowDown':
2366
+ newIndex = (currentIndex + 1) % enabledTabIds.length;
2367
+ break;
2368
+ case 'ArrowUp':
2369
+ newIndex = currentIndex > 0 ? currentIndex - 1 : enabledTabIds.length - 1;
2370
+ break;
2371
+ default:
2372
+ return;
2373
+ }
2374
+ e.preventDefault();
2375
+ const newTab = enabledTabIds[newIndex];
2376
+ if (newTab) overflowButtons.get(newTab)?.focus();
2377
+ }
1794
2378
  function setActiveTab(tab) {
1795
2379
  activeTab = tab;
1796
- for (const [tabId, tabButton] of tabButtons){
1797
- const isActive = tabId === tab;
1798
- if (styles_tabs_module.tabActive) tabButton.classList.toggle(styles_tabs_module.tabActive, isActive);
1799
- tabButton.setAttribute('aria-selected', isActive ? 'true' : 'false');
1800
- tabButton.tabIndex = isActive ? 0 : -1;
1801
- }
2380
+ applyActiveState(tab);
2381
+ updateVisibleTabs();
1802
2382
  }
2383
+ const handleWindowResize = ()=>{
2384
+ updateVisibleTabs();
2385
+ };
2386
+ let resizeObserver = null;
2387
+ if ('undefined' != typeof ResizeObserver) {
2388
+ resizeObserver = new ResizeObserver(()=>{
2389
+ updateVisibleTabs();
2390
+ });
2391
+ resizeObserver.observe(tabList);
2392
+ } else window.addEventListener('resize', handleWindowResize);
2393
+ applyActiveState(activeTab);
2394
+ requestAnimationFrame(()=>{
2395
+ updateVisibleTabs();
2396
+ });
1803
2397
  return {
1804
2398
  element: tabList,
1805
2399
  setActiveTab,
1806
2400
  destroy: ()=>{
2401
+ closeOverflowMenu();
2402
+ if (resizeObserver) {
2403
+ resizeObserver.disconnect();
2404
+ resizeObserver = null;
2405
+ } else window.removeEventListener('resize', handleWindowResize);
1807
2406
  tabButtons.clear();
2407
+ overflowButtons.clear();
1808
2408
  }
1809
2409
  };
1810
2410
  }
2411
+ function debug_bundle_createDebugBundle(payload) {
2412
+ const { namespace, devToolsState, connection, recentEvents, storeState } = payload;
2413
+ const bundle = {
2414
+ generatedAt: new Date().toISOString(),
2415
+ namespace,
2416
+ devToolsState,
2417
+ connection,
2418
+ recentEvents,
2419
+ storeState,
2420
+ overrides: storeState?.overrides ?? null,
2421
+ iab: storeState?.iab ? {
2422
+ tcString: (storeState?.iab).tcString ?? null,
2423
+ purposeCount: Object.keys((storeState?.iab).purposeConsents ?? {}).length,
2424
+ vendorCount: Object.keys((storeState?.iab).vendorConsents ?? {}).length
2425
+ } : null
2426
+ };
2427
+ return JSON.stringify(bundle, null, 2);
2428
+ }
2429
+ function debug_bundle_sanitizeStoreState(state) {
2430
+ if (!state) return null;
2431
+ try {
2432
+ return JSON.parse(JSON.stringify(state, (_key, value)=>'function' == typeof value ? void 0 : value));
2433
+ } catch {
2434
+ return {
2435
+ error: 'Unable to serialize store state'
2436
+ };
2437
+ }
2438
+ }
2439
+ function debug_bundle_downloadDebugBundle(content) {
2440
+ const blob = new Blob([
2441
+ content
2442
+ ], {
2443
+ type: 'application/json'
2444
+ });
2445
+ const url = URL.createObjectURL(blob);
2446
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
2447
+ const anchor = document.createElement('a');
2448
+ anchor.href = url;
2449
+ anchor.download = `c15t-debug-bundle-${timestamp}.json`;
2450
+ anchor.click();
2451
+ URL.revokeObjectURL(url);
2452
+ }
2453
+ const DEVTOOLS_OVERRIDES_STORAGE_KEY = 'c15t-devtools-overrides';
2454
+ function normalizeStringValue(value) {
2455
+ if ('string' != typeof value) return;
2456
+ const normalized = value.trim();
2457
+ return normalized.length > 0 ? normalized : void 0;
2458
+ }
2459
+ function normalizeBooleanValue(value) {
2460
+ return 'boolean' == typeof value ? value : void 0;
2461
+ }
2462
+ function normalizeOverrides(value) {
2463
+ if (!value || 'object' != typeof value) return null;
2464
+ const source = value;
2465
+ const overrides = {
2466
+ country: normalizeStringValue(source.country),
2467
+ region: normalizeStringValue(source.region),
2468
+ language: normalizeStringValue(source.language),
2469
+ gpc: normalizeBooleanValue(source.gpc)
2470
+ };
2471
+ return hasPersistedOverrides(overrides) ? overrides : null;
2472
+ }
2473
+ function hasPersistedOverrides(overrides) {
2474
+ return Boolean(overrides.country || overrides.region || overrides.language || void 0 !== overrides.gpc);
2475
+ }
2476
+ function override_storage_loadPersistedOverrides(storageKey = DEVTOOLS_OVERRIDES_STORAGE_KEY) {
2477
+ if ('undefined' == typeof window) return null;
2478
+ try {
2479
+ const stored = localStorage.getItem(storageKey);
2480
+ if (!stored) return null;
2481
+ const parsed = JSON.parse(stored);
2482
+ return normalizeOverrides(parsed);
2483
+ } catch {
2484
+ return null;
2485
+ }
2486
+ }
2487
+ function override_storage_persistOverrides(overrides, storageKey = DEVTOOLS_OVERRIDES_STORAGE_KEY) {
2488
+ if ('undefined' == typeof window) return;
2489
+ try {
2490
+ if (!hasPersistedOverrides(overrides)) return void localStorage.removeItem(storageKey);
2491
+ localStorage.setItem(storageKey, JSON.stringify(overrides));
2492
+ } catch {}
2493
+ }
2494
+ function override_storage_clearPersistedOverrides(storageKey = DEVTOOLS_OVERRIDES_STORAGE_KEY) {
2495
+ if ('undefined' == typeof window) return;
2496
+ try {
2497
+ localStorage.removeItem(storageKey);
2498
+ } catch {}
2499
+ }
1811
2500
  var components_module = __webpack_require__("../../node_modules/.bun/@rsbuild+core@1.6.12/node_modules/@rsbuild/core/compiled/css-loader/index.js??ruleSet[1].rules[1].use[1]!builtin:lightningcss-loader??ruleSet[1].rules[1].use[2]!./src/styles/components.module.css");
1812
2501
  var components_module_options = {};
1813
2502
  components_module_options.styleTagTransform = styleTagTransform_default();
@@ -1875,31 +2564,19 @@ function createButton(options) {
1875
2564
  btn.appendChild(document.createTextNode(text));
1876
2565
  return btn;
1877
2566
  }
1878
- function createListItem(options) {
1879
- const { title, description, actions = [] } = options;
1880
- const content = renderer_div({
1881
- className: styles_components_module.listItemContent,
1882
- children: [
1883
- renderer_span({
1884
- className: styles_components_module.listItemTitle,
1885
- text: title
1886
- }),
1887
- description ? renderer_span({
1888
- className: styles_components_module.listItemDescription,
1889
- text: description
1890
- }) : null
1891
- ]
1892
- });
1893
- const actionsContainer = renderer_div({
1894
- className: styles_components_module.listItemActions,
1895
- children: actions
1896
- });
1897
- return renderer_div({
1898
- className: styles_components_module.listItem,
1899
- children: [
1900
- content,
1901
- actionsContainer
1902
- ]
2567
+ function createInput(options) {
2568
+ const { value, placeholder, ariaLabel, small = false, onInput } = options;
2569
+ const sizeClass = small ? styles_components_module.inputSmall : '';
2570
+ return input({
2571
+ className: `${styles_components_module.input} ${sizeClass}`.trim(),
2572
+ type: 'text',
2573
+ value,
2574
+ placeholder,
2575
+ ariaLabel,
2576
+ onInput: (event)=>{
2577
+ const target = event.target;
2578
+ onInput?.(target?.value ?? '');
2579
+ }
1903
2580
  });
1904
2581
  }
1905
2582
  function createSection(options) {
@@ -1982,6 +2659,18 @@ function createGridCard(options) {
1982
2659
  children
1983
2660
  });
1984
2661
  }
2662
+ function createDisconnectedState(message = 'Store not connected') {
2663
+ return renderer_div({
2664
+ className: styles_components_module.disconnectedState,
2665
+ style: {
2666
+ padding: '24px',
2667
+ textAlign: 'center',
2668
+ color: 'var(--c15t-text-muted)',
2669
+ fontSize: 'var(--c15t-devtools-font-size-sm)'
2670
+ },
2671
+ text: message
2672
+ });
2673
+ }
1985
2674
  const REFRESH_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1986
2675
  <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path>
1987
2676
  <path d="M21 3v5h-5"></path>
@@ -2009,19 +2698,11 @@ const TERMINAL_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 2
2009
2698
  <polyline points="4 17 10 11 4 5"></polyline>
2010
2699
  <line x1="12" y1="19" x2="20" y2="19"></line>
2011
2700
  </svg>`;
2012
- function actions_renderActionsPanel(container, options) {
2013
- const { getState, onResetConsents, onRefetchBanner, onShowBanner, onOpenPreferences, onCopyState } = options;
2701
+ function renderActionsPanel(container, options) {
2702
+ const { getState, onResetConsents, onRefetchBanner, onShowBanner, onOpenPreferences, onCopyState, onExportDebugBundle } = options;
2014
2703
  renderer_clearElement(container);
2015
2704
  const state = getState();
2016
- if (!state) return void container.appendChild(renderer_div({
2017
- style: {
2018
- padding: '24px',
2019
- textAlign: 'center',
2020
- color: 'var(--c15t-text-muted)',
2021
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2022
- },
2023
- text: 'Store not connected'
2024
- }));
2705
+ if (!state) return void container.appendChild(createDisconnectedState());
2025
2706
  const actionCards = [
2026
2707
  createActionCard({
2027
2708
  icon: actions_EYE_ICON,
@@ -2042,6 +2723,12 @@ function actions_renderActionsPanel(container, options) {
2042
2723
  icon: COPY_ICON,
2043
2724
  label: 'Copy State',
2044
2725
  onClick: onCopyState
2726
+ }),
2727
+ createActionCard({
2728
+ icon: REFRESH_ICON,
2729
+ label: 'Export Debug',
2730
+ onClick: ()=>onExportDebugBundle?.(),
2731
+ disabled: !onExportDebugBundle
2045
2732
  })
2046
2733
  ];
2047
2734
  const grid = createGrid({
@@ -2115,7 +2802,7 @@ function actions_renderActionsPanel(container, options) {
2115
2802
  container.appendChild(consoleSection);
2116
2803
  }
2117
2804
  function createActionCard(options) {
2118
- const { icon, label, onClick } = options;
2805
+ const { icon, label, onClick, disabled = false } = options;
2119
2806
  const card = renderer_div({
2120
2807
  className: styles_components_module.gridCard ?? '',
2121
2808
  style: {
@@ -2126,7 +2813,8 @@ function createActionCard(options) {
2126
2813
  gap: '6px',
2127
2814
  padding: '16px 8px',
2128
2815
  cursor: 'pointer',
2129
- transition: 'background-color var(--c15t-duration-fast) var(--c15t-easing)'
2816
+ transition: 'background-color var(--c15t-duration-fast) var(--c15t-easing)',
2817
+ opacity: disabled ? '0.55' : '1'
2130
2818
  },
2131
2819
  children: [
2132
2820
  createIconWrapper(icon, 20),
@@ -2141,13 +2829,15 @@ function createActionCard(options) {
2141
2829
  })
2142
2830
  ]
2143
2831
  });
2144
- card.addEventListener('click', onClick);
2145
- card.addEventListener('mouseenter', ()=>{
2146
- card.style.backgroundColor = 'var(--c15t-surface-hover)';
2147
- });
2148
- card.addEventListener('mouseleave', ()=>{
2149
- card.style.backgroundColor = '';
2150
- });
2832
+ if (!disabled) {
2833
+ card.addEventListener('click', onClick);
2834
+ card.addEventListener('mouseenter', ()=>{
2835
+ card.style.backgroundColor = 'var(--c15t-surface-hover)';
2836
+ });
2837
+ card.addEventListener('mouseleave', ()=>{
2838
+ card.style.backgroundColor = '';
2839
+ });
2840
+ }
2151
2841
  return card;
2152
2842
  }
2153
2843
  function createIconWrapper(icon, size) {
@@ -2168,19 +2858,12 @@ function createIconWrapper(icon, size) {
2168
2858
  function getNamespace(state) {
2169
2859
  return state.config?.meta?.namespace || 'c15tStore';
2170
2860
  }
2171
- function consents_renderConsentsPanel(container, options) {
2861
+ const consentSearchByContainer = new WeakMap();
2862
+ function renderConsentsPanel(container, options) {
2172
2863
  const { getState, onConsentChange, onSave, onAcceptAll, onRejectAll, onReset } = options;
2173
2864
  renderer_clearElement(container);
2174
2865
  const state = getState();
2175
- if (!state) return void container.appendChild(renderer_div({
2176
- style: {
2177
- padding: '24px',
2178
- textAlign: 'center',
2179
- color: 'var(--c15t-text-muted)',
2180
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2181
- },
2182
- text: 'Store not connected'
2183
- }));
2866
+ if (!state) return void container.appendChild(createDisconnectedState());
2184
2867
  const isIabMode = 'iab' === state.model;
2185
2868
  const savedConsents = state.consents || {};
2186
2869
  const selectedConsents = state.selectedConsents || {};
@@ -2194,15 +2877,40 @@ function consents_renderConsentsPanel(container, options) {
2194
2877
  ct.name,
2195
2878
  ct
2196
2879
  ]));
2880
+ const searchQuery = consentSearchByContainer.get(container) ?? '';
2197
2881
  const consentEntries = Object.entries(displayConsents);
2198
- if (0 === consentEntries.length) container.appendChild(renderer_div({
2882
+ const filteredConsentEntries = consentEntries.filter(([name])=>{
2883
+ if (!searchQuery) return true;
2884
+ const consentType = consentTypeMap.get(name);
2885
+ const displayName = consentType?.name || name;
2886
+ return `${name} ${displayName}`.toLowerCase().includes(searchQuery);
2887
+ });
2888
+ const showSearchInput = consentEntries.length > 4;
2889
+ if (showSearchInput) container.appendChild(renderer_div({
2890
+ style: {
2891
+ padding: '8px 0 10px'
2892
+ },
2893
+ children: [
2894
+ createInput({
2895
+ value: searchQuery,
2896
+ placeholder: 'Filter consents…',
2897
+ ariaLabel: 'Filter consents',
2898
+ small: true,
2899
+ onInput: (value)=>{
2900
+ consentSearchByContainer.set(container, value.trim().toLowerCase());
2901
+ renderConsentsPanel(container, options);
2902
+ }
2903
+ })
2904
+ ]
2905
+ }));
2906
+ if (0 === filteredConsentEntries.length) container.appendChild(renderer_div({
2199
2907
  style: {
2200
2908
  padding: '24px',
2201
2909
  textAlign: 'center',
2202
2910
  color: 'var(--c15t-devtools-text-muted)',
2203
2911
  fontSize: 'var(--c15t-devtools-font-size-sm)'
2204
2912
  },
2205
- text: 'No consents configured'
2913
+ text: 0 === consentEntries.length ? 'No consents configured' : 'No matching consents'
2206
2914
  }));
2207
2915
  else {
2208
2916
  if (isIabMode) {
@@ -2220,7 +2928,7 @@ function consents_renderConsentsPanel(container, options) {
2220
2928
  container.appendChild(iabNotice);
2221
2929
  }
2222
2930
  const gridCards = [];
2223
- for (const [name, value] of consentEntries){
2931
+ for (const [name, value] of filteredConsentEntries){
2224
2932
  const consentType = consentTypeMap.get(name);
2225
2933
  const isNecessary = 'necessary' === name;
2226
2934
  const displayName = consentType?.name || name;
@@ -2284,13 +2992,13 @@ function consents_renderConsentsPanel(container, options) {
2284
2992
  },
2285
2993
  children: [
2286
2994
  createButton({
2287
- text: 'All',
2995
+ text: 'Accept',
2288
2996
  variant: 'primary',
2289
2997
  small: true,
2290
2998
  onClick: onAcceptAll
2291
2999
  }),
2292
3000
  createButton({
2293
- text: 'None',
3001
+ text: 'Reject',
2294
3002
  variant: 'default',
2295
3003
  small: true,
2296
3004
  onClick: onRejectAll
@@ -2338,16 +3046,33 @@ function consents_renderConsentsPanel(container, options) {
2338
3046
  function formatConsentName(name) {
2339
3047
  return name.replace(/_/g, ' ').replace(/\b\w/g, (l)=>l.toUpperCase());
2340
3048
  }
2341
- function events_renderEventsPanel(container, options) {
3049
+ const panelStateByContainer = new WeakMap();
3050
+ function getPanelState(container) {
3051
+ const existing = panelStateByContainer.get(container);
3052
+ if (existing) return existing;
3053
+ const initialState = {
3054
+ activeFilter: 'all',
3055
+ selectedEventId: null,
3056
+ searchQuery: ''
3057
+ };
3058
+ panelStateByContainer.set(container, initialState);
3059
+ return initialState;
3060
+ }
3061
+ function renderEventsPanel(container, options) {
2342
3062
  const { getEvents, onClear } = options;
3063
+ const panelState = getPanelState(container);
2343
3064
  renderer_clearElement(container);
2344
- const events = getEvents();
3065
+ const allEvents = getEvents();
3066
+ const events = allEvents.filter((event)=>matchesFilter(event, panelState.activeFilter)).filter((event)=>matchesSearch(event, panelState.searchQuery));
3067
+ if (!events.some((event)=>event.id === panelState.selectedEventId)) panelState.selectedEventId = events[0]?.id ?? null;
3068
+ const selectedEvent = events.find((event)=>event.id === panelState.selectedEventId) ?? null;
2345
3069
  const header = renderer_div({
2346
3070
  style: {
2347
3071
  display: 'flex',
2348
3072
  alignItems: 'center',
2349
3073
  justifyContent: 'space-between',
2350
- padding: '12px 16px 8px'
3074
+ padding: '12px 16px 8px',
3075
+ gap: '8px'
2351
3076
  },
2352
3077
  children: [
2353
3078
  renderer_span({
@@ -2358,44 +3083,153 @@ function events_renderEventsPanel(container, options) {
2358
3083
  textTransform: 'uppercase',
2359
3084
  letterSpacing: '0.5px'
2360
3085
  },
2361
- text: `Events (${events.length})`
3086
+ text: `Events (${events.length}/${allEvents.length})`
2362
3087
  }),
2363
- createButton({
2364
- text: 'Clear',
2365
- small: true,
2366
- onClick: onClear
3088
+ renderer_div({
3089
+ style: {
3090
+ display: 'flex',
3091
+ gap: '6px'
3092
+ },
3093
+ children: [
3094
+ createButton({
3095
+ text: 'Export',
3096
+ small: true,
3097
+ onClick: ()=>exportEvents(allEvents)
3098
+ }),
3099
+ createButton({
3100
+ text: 'Clear',
3101
+ small: true,
3102
+ onClick: ()=>{
3103
+ onClear();
3104
+ panelState.selectedEventId = null;
3105
+ renderEventsPanel(container, options);
3106
+ }
3107
+ })
3108
+ ]
2367
3109
  })
2368
3110
  ]
2369
3111
  });
2370
3112
  container.appendChild(header);
3113
+ container.appendChild(renderer_div({
3114
+ style: {
3115
+ display: 'flex',
3116
+ flexWrap: 'wrap',
3117
+ gap: '6px',
3118
+ padding: '0 16px 8px'
3119
+ },
3120
+ children: EVENT_FILTERS.map((filter)=>createFilterButton(filter, filter === panelState.activeFilter, ()=>{
3121
+ panelState.activeFilter = filter;
3122
+ panelState.selectedEventId = null;
3123
+ renderEventsPanel(container, options);
3124
+ }))
3125
+ }));
3126
+ container.appendChild(renderer_div({
3127
+ style: {
3128
+ padding: '0 16px 8px'
3129
+ },
3130
+ children: [
3131
+ createInput({
3132
+ value: panelState.searchQuery,
3133
+ placeholder: 'Search events…',
3134
+ ariaLabel: 'Search events',
3135
+ small: true,
3136
+ onInput: (value)=>{
3137
+ panelState.searchQuery = value.trim().toLowerCase();
3138
+ panelState.selectedEventId = null;
3139
+ renderEventsPanel(container, options);
3140
+ }
3141
+ })
3142
+ ]
3143
+ }));
2371
3144
  const eventList = renderer_div({
2372
3145
  style: {
2373
3146
  display: 'flex',
2374
3147
  flexDirection: 'column',
2375
3148
  gap: '4px',
2376
3149
  padding: '0 12px 12px',
2377
- maxHeight: '400px',
3150
+ maxHeight: '300px',
2378
3151
  overflowY: 'auto'
2379
3152
  }
2380
3153
  });
2381
- if (0 === events.length) {
2382
- const emptyState = renderer_div({
2383
- style: {
2384
- padding: '32px 16px',
2385
- textAlign: 'center',
2386
- color: 'var(--c15t-text-muted)',
2387
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2388
- },
2389
- text: 'No events recorded yet'
2390
- });
2391
- eventList.appendChild(emptyState);
2392
- } else for (const event of events){
2393
- const eventItem = createEventItem(event);
2394
- eventList.appendChild(eventItem);
2395
- }
3154
+ if (0 === events.length) eventList.appendChild(renderer_div({
3155
+ style: {
3156
+ padding: '20px 16px',
3157
+ textAlign: 'center',
3158
+ color: 'var(--c15t-text-muted)',
3159
+ fontSize: 'var(--c15t-devtools-font-size-sm)'
3160
+ },
3161
+ text: 'No events match this filter'
3162
+ }));
3163
+ else for (const event of events)eventList.appendChild(createEventItem(event, event.id === panelState.selectedEventId, ()=>{
3164
+ panelState.selectedEventId = event.id;
3165
+ renderEventsPanel(container, options);
3166
+ }));
2396
3167
  container.appendChild(eventList);
3168
+ container.appendChild(createPayloadSection(selectedEvent));
3169
+ }
3170
+ const EVENT_FILTERS = [
3171
+ 'all',
3172
+ 'error',
3173
+ 'consent',
3174
+ 'network',
3175
+ 'iab'
3176
+ ];
3177
+ function createFilterButton(filter, active, onClick) {
3178
+ return createButton({
3179
+ text: filter.toUpperCase(),
3180
+ small: true,
3181
+ variant: active ? 'primary' : 'default',
3182
+ onClick
3183
+ });
3184
+ }
3185
+ function matchesFilter(event, filter) {
3186
+ if ('all' === filter) return true;
3187
+ if ('error' === filter) return 'error' === event.type;
3188
+ if ('consent' === filter) return 'consent_set' === event.type || 'consent_save' === event.type || 'consent_reset' === event.type;
3189
+ if ('network' === filter) return 'network' === event.type;
3190
+ return 'iab' === event.type;
3191
+ }
3192
+ function matchesSearch(event, query) {
3193
+ if (!query) return true;
3194
+ const haystack = `${event.type} ${event.message} ${JSON.stringify(event.data ?? {})}`;
3195
+ return haystack.toLowerCase().includes(query);
3196
+ }
3197
+ function createPayloadSection(event) {
3198
+ const payload = event?.data ? JSON.stringify(event.data, null, 2) : null;
3199
+ return renderer_div({
3200
+ style: {
3201
+ padding: '0 12px 12px'
3202
+ },
3203
+ children: [
3204
+ renderer_div({
3205
+ style: {
3206
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
3207
+ fontWeight: '600',
3208
+ color: 'var(--c15t-text-muted)',
3209
+ textTransform: 'uppercase',
3210
+ letterSpacing: '0.5px',
3211
+ marginBottom: '6px'
3212
+ },
3213
+ text: 'Payload'
3214
+ }),
3215
+ renderer_div({
3216
+ className: styles_components_module.gridCard ?? '',
3217
+ style: {
3218
+ padding: '8px',
3219
+ fontFamily: 'ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace',
3220
+ fontSize: '11px',
3221
+ color: 'var(--c15t-text-muted)',
3222
+ maxHeight: '140px',
3223
+ overflowY: 'auto',
3224
+ whiteSpace: 'pre-wrap',
3225
+ wordBreak: 'break-word'
3226
+ },
3227
+ text: payload || 'Select an event with payload data'
3228
+ })
3229
+ ]
3230
+ });
2397
3231
  }
2398
- function createEventItem(event) {
3232
+ function createEventItem(event, selected, onSelect) {
2399
3233
  const time = formatTime(event.timestamp);
2400
3234
  const icon = getEventIcon(event.type);
2401
3235
  const color = getEventColor(event.type);
@@ -2406,8 +3240,11 @@ function createEventItem(event) {
2406
3240
  alignItems: 'center',
2407
3241
  gap: '8px',
2408
3242
  padding: '6px 10px',
2409
- fontSize: 'var(--c15t-devtools-font-size-xs)'
3243
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
3244
+ cursor: 'pointer',
3245
+ borderColor: selected ? 'var(--c15t-devtools-badge-info, #3b82f6)' : 'var(--c15t-border)'
2410
3246
  },
3247
+ onClick: onSelect,
2411
3248
  children: [
2412
3249
  renderer_span({
2413
3250
  style: {
@@ -2436,6 +3273,21 @@ function createEventItem(event) {
2436
3273
  ]
2437
3274
  });
2438
3275
  }
3276
+ function exportEvents(events) {
3277
+ const json = JSON.stringify(events, null, 2);
3278
+ const blob = new Blob([
3279
+ json
3280
+ ], {
3281
+ type: 'application/json'
3282
+ });
3283
+ const url = URL.createObjectURL(blob);
3284
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
3285
+ const a = document.createElement('a');
3286
+ a.href = url;
3287
+ a.download = `c15t-events-${timestamp}.json`;
3288
+ a.click();
3289
+ URL.revokeObjectURL(url);
3290
+ }
2439
3291
  function formatTime(timestamp) {
2440
3292
  const date = new Date(timestamp);
2441
3293
  return date.toLocaleTimeString('en-US', {
@@ -2454,7 +3306,10 @@ function getEventIcon(type) {
2454
3306
  return '○';
2455
3307
  case 'error':
2456
3308
  return '✕';
2457
- case 'info':
3309
+ case 'network':
3310
+ return '◉';
3311
+ case 'iab':
3312
+ return '◆';
2458
3313
  default:
2459
3314
  return '○';
2460
3315
  }
@@ -2468,43 +3323,23 @@ function getEventColor(type) {
2468
3323
  return 'var(--c15t-devtools-badge-warning, #f59e0b)';
2469
3324
  case 'error':
2470
3325
  return 'var(--c15t-devtools-badge-error, #ef4444)';
2471
- case 'info':
3326
+ case 'network':
3327
+ return 'var(--c15t-devtools-badge-warning, #f59e0b)';
3328
+ case 'iab':
3329
+ return 'var(--c15t-devtools-badge-info, #3b82f6)';
2472
3330
  default:
2473
3331
  return 'var(--c15t-text-muted)';
2474
3332
  }
2475
3333
  }
2476
- function iab_renderIabPanel(container, options) {
2477
- const { getState, onReset } = options;
3334
+ const iabSearchByContainer = new WeakMap();
3335
+ function renderIabPanel(container, options) {
3336
+ const { getState, onSetPurposeConsent, onSetVendorConsent, onSetSpecialFeatureOptIn, onAcceptAll, onRejectAll, onSave, onReset } = options;
2478
3337
  renderer_clearElement(container);
2479
3338
  const state = getState();
2480
- if (!state) return void container.appendChild(renderer_div({
2481
- style: {
2482
- padding: '24px',
2483
- textAlign: 'center',
2484
- color: 'var(--c15t-text-muted)',
2485
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2486
- },
2487
- text: 'Store not connected'
2488
- }));
2489
- if ('iab' !== state.model) return void container.appendChild(renderer_div({
2490
- style: {
2491
- padding: '24px',
2492
- textAlign: 'center',
2493
- color: 'var(--c15t-text-muted)',
2494
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2495
- },
2496
- text: 'IAB TCF mode is not configured'
2497
- }));
3339
+ if (!state) return void container.appendChild(createDisconnectedState());
3340
+ if ('iab' !== state.model) return void container.appendChild(createDisconnectedState('IAB TCF mode is not configured'));
2498
3341
  const iabState = state.iab;
2499
- if (!iabState) return void container.appendChild(renderer_div({
2500
- style: {
2501
- padding: '24px',
2502
- textAlign: 'center',
2503
- color: 'var(--c15t-text-muted)',
2504
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2505
- },
2506
- text: 'IAB state not available'
2507
- }));
3342
+ if (!iabState) return void container.appendChild(createDisconnectedState('IAB state not available'));
2508
3343
  const tcString = iabState.tcString;
2509
3344
  const tcStringSection = createSection({
2510
3345
  title: 'TC String',
@@ -2536,9 +3371,30 @@ function iab_renderIabPanel(container, options) {
2536
3371
  });
2537
3372
  container.appendChild(tcStringSection);
2538
3373
  const gvl = iabState.gvl;
3374
+ const searchQuery = iabSearchByContainer.get(container) ?? '';
3375
+ container.appendChild(createSection({
3376
+ title: 'Filter',
3377
+ children: [
3378
+ createInput({
3379
+ value: searchQuery,
3380
+ placeholder: 'Filter purposes or vendors…',
3381
+ ariaLabel: 'Filter IAB purposes and vendors',
3382
+ small: true,
3383
+ onInput: (value)=>{
3384
+ iabSearchByContainer.set(container, value.trim().toLowerCase());
3385
+ renderIabPanel(container, options);
3386
+ }
3387
+ })
3388
+ ]
3389
+ }));
2539
3390
  const purposeConsents = iabState.purposeConsents || {};
2540
3391
  const purposes = gvl?.purposes || {};
2541
- const purposeEntries = Object.entries(purposeConsents);
3392
+ const purposeEntries = Object.entries(purposeConsents).filter(([purposeId])=>{
3393
+ if (!searchQuery) return true;
3394
+ const purposeInfo = purposes[purposeId];
3395
+ const purposeName = purposeInfo?.name || `Purpose ${purposeId}`;
3396
+ return `${purposeId} ${purposeName}`.toLowerCase().includes(searchQuery);
3397
+ });
2542
3398
  if (purposeEntries.length > 0) {
2543
3399
  const purposeList = renderer_div({
2544
3400
  style: {
@@ -2552,7 +3408,9 @@ function iab_renderIabPanel(container, options) {
2552
3408
  for (const [purposeId, consent] of purposeEntries){
2553
3409
  const purposeInfo = purposes[purposeId];
2554
3410
  const purposeName = purposeInfo?.name || `Purpose ${purposeId}`;
2555
- purposeList.appendChild(createPurposeRow(purposeId, purposeName, Boolean(consent)));
3411
+ purposeList.appendChild(createPurposeRow(purposeId, purposeName, Boolean(consent), (value)=>{
3412
+ onSetPurposeConsent(Number(purposeId), value);
3413
+ }));
2556
3414
  }
2557
3415
  const purposesSection = createSection({
2558
3416
  title: `Purposes (${purposeEntries.length})`,
@@ -2564,7 +3422,12 @@ function iab_renderIabPanel(container, options) {
2564
3422
  }
2565
3423
  const specialFeatureOptIns = iabState.specialFeatureOptIns || {};
2566
3424
  const specialFeatures = gvl?.specialFeatures || {};
2567
- const specialFeatureEntries = Object.entries(specialFeatureOptIns);
3425
+ const specialFeatureEntries = Object.entries(specialFeatureOptIns).filter(([featureId])=>{
3426
+ if (!searchQuery) return true;
3427
+ const featureInfo = specialFeatures[featureId];
3428
+ const featureName = featureInfo?.name || `Special Feature ${featureId}`;
3429
+ return `${featureId} ${featureName}`.toLowerCase().includes(searchQuery);
3430
+ });
2568
3431
  if (specialFeatureEntries.length > 0) {
2569
3432
  const specialFeatureList = renderer_div({
2570
3433
  style: {
@@ -2578,7 +3441,9 @@ function iab_renderIabPanel(container, options) {
2578
3441
  for (const [featureId, optIn] of specialFeatureEntries){
2579
3442
  const featureInfo = specialFeatures[featureId];
2580
3443
  const featureName = featureInfo?.name || `Special Feature ${featureId}`;
2581
- specialFeatureList.appendChild(createPurposeRow(featureId, featureName, Boolean(optIn)));
3444
+ specialFeatureList.appendChild(createPurposeRow(featureId, featureName, Boolean(optIn), (value)=>{
3445
+ onSetSpecialFeatureOptIn(Number(featureId), value);
3446
+ }, 'feature'));
2582
3447
  }
2583
3448
  const specialFeaturesSection = createSection({
2584
3449
  title: `Special Features (${specialFeatureEntries.length})`,
@@ -2590,7 +3455,12 @@ function iab_renderIabPanel(container, options) {
2590
3455
  }
2591
3456
  const vendorConsents = iabState.vendorConsents || {};
2592
3457
  const vendors = gvl?.vendors || {};
2593
- const vendorEntries = Object.entries(vendorConsents);
3458
+ const vendorEntries = Object.entries(vendorConsents).filter(([vendorId])=>{
3459
+ if (!searchQuery) return true;
3460
+ const vendorInfo = vendors[vendorId];
3461
+ const vendorName = vendorInfo?.name || `Vendor ${vendorId}`;
3462
+ return `${vendorId} ${vendorName}`.toLowerCase().includes(searchQuery);
3463
+ });
2594
3464
  const iabVendors = [];
2595
3465
  const customVendors = [];
2596
3466
  for (const [vendorId, consent] of vendorEntries){
@@ -2618,7 +3488,9 @@ function iab_renderIabPanel(container, options) {
2618
3488
  overflowY: 'auto'
2619
3489
  }
2620
3490
  });
2621
- for (const [vendorId, consent, vendorName] of iabVendors)vendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'iab'));
3491
+ for (const [vendorId, consent, vendorName] of iabVendors)vendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'iab', (value)=>{
3492
+ onSetVendorConsent(Number(vendorId), value);
3493
+ }));
2622
3494
  const vendorsSection = createSection({
2623
3495
  title: `IAB Vendors (${iabVendors.length})`,
2624
3496
  children: [
@@ -2637,7 +3509,9 @@ function iab_renderIabPanel(container, options) {
2637
3509
  overflowY: 'auto'
2638
3510
  }
2639
3511
  });
2640
- for (const [vendorId, consent, vendorName] of customVendors)customVendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'custom'));
3512
+ for (const [vendorId, consent, vendorName] of customVendors)customVendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'custom', (value)=>{
3513
+ onSetVendorConsent(vendorId, value);
3514
+ }));
2641
3515
  const customVendorsSection = createSection({
2642
3516
  title: `Custom Vendors (${customVendors.length})`,
2643
3517
  children: [
@@ -2659,15 +3533,40 @@ function iab_renderIabPanel(container, options) {
2659
3533
  style: {
2660
3534
  display: 'flex',
2661
3535
  alignItems: 'center',
2662
- justifyContent: 'flex-end',
3536
+ justifyContent: 'space-between',
2663
3537
  padding: '12px 16px',
2664
3538
  marginTop: 'auto',
2665
3539
  borderTop: '1px solid var(--c15t-border)',
2666
3540
  backgroundColor: 'var(--c15t-surface)'
2667
3541
  },
2668
3542
  children: [
3543
+ renderer_div({
3544
+ style: {
3545
+ display: 'flex',
3546
+ gap: '6px'
3547
+ },
3548
+ children: [
3549
+ createButton({
3550
+ text: 'Accept All',
3551
+ variant: 'primary',
3552
+ small: true,
3553
+ onClick: onAcceptAll
3554
+ }),
3555
+ createButton({
3556
+ text: 'Reject All',
3557
+ small: true,
3558
+ onClick: onRejectAll
3559
+ }),
3560
+ createButton({
3561
+ text: 'Save',
3562
+ variant: 'primary',
3563
+ small: true,
3564
+ onClick: onSave
3565
+ })
3566
+ ]
3567
+ }),
2669
3568
  createButton({
2670
- text: 'Reset All',
3569
+ text: 'Reset',
2671
3570
  variant: 'danger',
2672
3571
  small: true,
2673
3572
  onClick: onReset
@@ -2676,7 +3575,7 @@ function iab_renderIabPanel(container, options) {
2676
3575
  });
2677
3576
  container.appendChild(footer);
2678
3577
  }
2679
- function createPurposeRow(id, name, consent) {
3578
+ function createPurposeRow(id, name, consent, onChange, ariaKind = 'purpose') {
2680
3579
  return renderer_div({
2681
3580
  style: {
2682
3581
  display: 'flex',
@@ -2699,14 +3598,28 @@ function createPurposeRow(id, name, consent) {
2699
3598
  text: `${id}. ${name}`,
2700
3599
  title: name
2701
3600
  }),
2702
- createBadge({
2703
- text: consent ? '✓' : '✕',
2704
- variant: consent ? 'success' : 'error'
3601
+ renderer_div({
3602
+ style: {
3603
+ display: 'flex',
3604
+ alignItems: 'center',
3605
+ gap: '6px'
3606
+ },
3607
+ children: [
3608
+ createBadge({
3609
+ text: consent ? '✓' : '✕',
3610
+ variant: consent ? 'success' : 'error'
3611
+ }),
3612
+ createToggle({
3613
+ checked: consent,
3614
+ onChange,
3615
+ ariaLabel: `Toggle ${ariaKind} ${id}`
3616
+ })
3617
+ ]
2705
3618
  })
2706
3619
  ]
2707
3620
  });
2708
3621
  }
2709
- function createVendorRow(id, name, consent, type) {
3622
+ function createVendorRow(id, name, consent, type, onChange) {
2710
3623
  return renderer_div({
2711
3624
  style: {
2712
3625
  display: 'flex',
@@ -2753,27 +3666,24 @@ function createVendorRow(id, name, consent, type) {
2753
3666
  createBadge({
2754
3667
  text: consent ? '✓' : '✕',
2755
3668
  variant: consent ? 'success' : 'error'
3669
+ }),
3670
+ createToggle({
3671
+ checked: consent,
3672
+ onChange,
3673
+ ariaLabel: `Toggle vendor ${id}`
2756
3674
  })
2757
3675
  ]
2758
3676
  });
2759
3677
  }
2760
3678
  function truncateText(text, maxLength) {
2761
3679
  if (text.length <= maxLength) return text;
2762
- return text.slice(0, maxLength - 3) + '...';
3680
+ return `${text.slice(0, maxLength - 3)}...`;
2763
3681
  }
2764
- function location_renderLocationPanel(container, options) {
2765
- const { getState, onSetOverrides, onClearOverrides } = options;
3682
+ function renderLocationPanel(container, options) {
3683
+ const { getState, onApplyOverrides, onClearOverrides } = options;
2766
3684
  renderer_clearElement(container);
2767
3685
  const state = getState();
2768
- if (!state) return void container.appendChild(renderer_div({
2769
- style: {
2770
- padding: '24px',
2771
- textAlign: 'center',
2772
- color: 'var(--c15t-text-muted)',
2773
- fontSize: 'var(--c15t-devtools-font-size-sm)'
2774
- },
2775
- text: 'Store not connected'
2776
- }));
3686
+ if (!state) return void container.appendChild(createDisconnectedState());
2777
3687
  const locationInfo = state.locationInfo;
2778
3688
  const overrides = state.overrides;
2779
3689
  const translationConfig = state.translationConfig;
@@ -2783,145 +3693,230 @@ function location_renderLocationPanel(container, options) {
2783
3693
  createCompactInfoCard('Jurisdiction', locationInfo?.jurisdiction || '—'),
2784
3694
  createCompactInfoCard('Language', translationConfig?.defaultLanguage || '—')
2785
3695
  ];
3696
+ gridItems.push(createCompactInfoCard('GPC', getEffectiveGpcLabel(overrides?.gpc)));
2786
3697
  if (state.model) gridItems.push(createCompactInfoCard('Model', getModelLabel(state.model)));
2787
3698
  const locationGrid = createGrid({
2788
- columns: 2,
3699
+ columns: 3,
2789
3700
  children: gridItems
2790
3701
  });
2791
3702
  container.appendChild(locationGrid);
3703
+ const initialDraft = getDraftFromOverrides(overrides);
3704
+ let appliedOverrides = normalizeOverrideDraft(initialDraft);
3705
+ let isSubmitting = false;
3706
+ const countryField = createOverrideSelect({
3707
+ label: 'Country',
3708
+ selectOptions: COUNTRY_OPTIONS,
3709
+ value: initialDraft.country
3710
+ });
3711
+ const regionField = createOverrideInput({
3712
+ label: 'Region',
3713
+ placeholder: 'e.g., CA, NY, BE',
3714
+ value: initialDraft.region
3715
+ });
3716
+ const languageField = createOverrideInput({
3717
+ label: 'Language',
3718
+ placeholder: 'e.g., de, fr, en-US',
3719
+ value: initialDraft.language
3720
+ });
3721
+ const gpcField = createOverrideSelect({
3722
+ label: 'GPC',
3723
+ selectOptions: GPC_OPTIONS,
3724
+ value: initialDraft.gpc
3725
+ });
3726
+ const formStatus = renderer_span({
3727
+ className: styles_components_module.overrideStatus,
3728
+ text: 'In sync'
3729
+ });
3730
+ const applyButton = createButton({
3731
+ text: 'Apply',
3732
+ variant: 'primary',
3733
+ small: true,
3734
+ disabled: true,
3735
+ onClick: ()=>{
3736
+ applyDraft();
3737
+ }
3738
+ });
3739
+ const revertButton = createButton({
3740
+ text: 'Revert',
3741
+ small: true,
3742
+ disabled: true,
3743
+ onClick: ()=>{
3744
+ setDraftValues(getDraftFromOverrides(appliedOverrides));
3745
+ updateFormState();
3746
+ }
3747
+ });
3748
+ const clearButton = createButton({
3749
+ text: 'Clear',
3750
+ small: true,
3751
+ onClick: ()=>{
3752
+ clearDraftAndOverrides();
3753
+ }
3754
+ });
3755
+ const overrideFieldsGrid = renderer_div({
3756
+ style: {
3757
+ display: 'grid',
3758
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
3759
+ gap: '8px 10px'
3760
+ },
3761
+ children: [
3762
+ countryField.element,
3763
+ regionField.element,
3764
+ languageField.element,
3765
+ gpcField.element
3766
+ ]
3767
+ });
2792
3768
  const overrideSection = createSection({
2793
3769
  title: 'Override Settings',
2794
- actions: [
2795
- createButton({
2796
- text: 'Clear',
2797
- small: true,
2798
- onClick: onClearOverrides
2799
- })
2800
- ],
2801
3770
  children: [
2802
- createOverrideSelect({
2803
- label: 'Country',
2804
- selectOptions: COUNTRY_OPTIONS,
2805
- value: overrides?.country || '',
2806
- onChange: (value)=>onSetOverrides({
2807
- country: value || void 0
2808
- })
2809
- }),
2810
- createOverrideInput({
2811
- label: 'Region',
2812
- placeholder: 'e.g., CA, NY, BE',
2813
- value: overrides?.region || '',
2814
- onChange: (value)=>onSetOverrides({
2815
- region: value || void 0
2816
- })
3771
+ overrideFieldsGrid,
3772
+ renderer_span({
3773
+ className: styles_components_module.overrideHint,
3774
+ text: 'GPC override only affects opt-out or unregulated jurisdictions.'
2817
3775
  }),
2818
- createOverrideInput({
2819
- label: 'Language',
2820
- placeholder: 'e.g., de, fr, en',
2821
- value: overrides?.language || '',
2822
- onChange: (value)=>onSetOverrides({
2823
- language: value || void 0
2824
- })
3776
+ renderer_div({
3777
+ className: styles_components_module.overrideActions,
3778
+ children: [
3779
+ renderer_div({
3780
+ className: styles_components_module.overrideActionButtons,
3781
+ children: [
3782
+ revertButton,
3783
+ applyButton,
3784
+ clearButton
3785
+ ]
3786
+ }),
3787
+ formStatus
3788
+ ]
2825
3789
  })
2826
3790
  ]
2827
3791
  });
2828
3792
  container.appendChild(overrideSection);
2829
- const hasOverrides = overrides && (overrides.country || overrides.region || overrides.language);
2830
- if (hasOverrides) {
2831
- const overrideBanner = renderer_div({
2832
- style: {
2833
- padding: '8px 16px',
2834
- backgroundColor: 'var(--c15t-devtools-badge-info-bg)',
2835
- color: 'var(--c15t-devtools-badge-info)',
2836
- fontSize: 'var(--c15t-devtools-font-size-xs)',
2837
- borderTop: '1px solid var(--c15t-devtools-border)'
2838
- },
2839
- text: 'Overrides are active. This may affect consent behavior.'
3793
+ countryField.control.addEventListener('change', updateFormState);
3794
+ regionField.control.addEventListener('input', updateFormState);
3795
+ languageField.control.addEventListener('input', updateFormState);
3796
+ gpcField.control.addEventListener('change', updateFormState);
3797
+ updateFormState();
3798
+ async function applyDraft() {
3799
+ if (isSubmitting) return;
3800
+ const draftOverrides = getDraftOverrides();
3801
+ if (overridesEqual(draftOverrides, appliedOverrides)) return;
3802
+ isSubmitting = true;
3803
+ updateFormState();
3804
+ try {
3805
+ await onApplyOverrides(draftOverrides);
3806
+ appliedOverrides = draftOverrides;
3807
+ } finally{
3808
+ isSubmitting = false;
3809
+ updateFormState();
3810
+ }
3811
+ }
3812
+ async function clearDraftAndOverrides() {
3813
+ if (isSubmitting) return;
3814
+ isSubmitting = true;
3815
+ updateFormState();
3816
+ try {
3817
+ await onClearOverrides();
3818
+ appliedOverrides = {};
3819
+ setDraftValues(getDraftFromOverrides(void 0));
3820
+ } finally{
3821
+ isSubmitting = false;
3822
+ updateFormState();
3823
+ }
3824
+ }
3825
+ function getDraftOverrides() {
3826
+ return normalizeOverrideDraft({
3827
+ country: countryField.control.value,
3828
+ region: regionField.control.value,
3829
+ language: languageField.control.value,
3830
+ gpc: gpcField.control.value
2840
3831
  });
2841
- container.appendChild(overrideBanner);
3832
+ }
3833
+ function setDraftValues(draft) {
3834
+ countryField.control.value = draft.country;
3835
+ regionField.control.value = draft.region;
3836
+ languageField.control.value = draft.language;
3837
+ gpcField.control.value = draft.gpc;
3838
+ }
3839
+ function updateFormState() {
3840
+ const draftOverrides = getDraftOverrides();
3841
+ const hasDraftChanges = !overridesEqual(draftOverrides, appliedOverrides);
3842
+ applyButton.disabled = !hasDraftChanges || isSubmitting;
3843
+ revertButton.disabled = !hasDraftChanges || isSubmitting;
3844
+ clearButton.disabled = isSubmitting;
3845
+ formStatus.textContent = isSubmitting ? 'Applying...' : hasDraftChanges ? 'Unsaved changes' : hasOverridesValue(appliedOverrides) ? 'Overrides active' : 'No overrides';
3846
+ if (styles_components_module.overrideStatusDirty) formStatus.classList.toggle(styles_components_module.overrideStatusDirty, !isSubmitting && hasDraftChanges);
2842
3847
  }
2843
3848
  }
2844
3849
  function createOverrideInput(options) {
2845
- const { label, placeholder, value, onChange } = options;
2846
- let debounceTimer = null;
3850
+ const { label, placeholder, value } = options;
2847
3851
  const inputField = input({
2848
3852
  className: `${styles_components_module.input ?? ''} ${styles_components_module.inputSmall ?? ''}`.trim(),
2849
3853
  placeholder,
2850
- value,
2851
- onInput: (e)=>{
2852
- const target = e.target;
2853
- if (debounceTimer) clearTimeout(debounceTimer);
2854
- debounceTimer = setTimeout(()=>{
2855
- onChange(target.value);
2856
- }, 500);
2857
- }
2858
- });
2859
- return renderer_div({
2860
- style: {
2861
- display: 'flex',
2862
- alignItems: 'center',
2863
- justifyContent: 'space-between',
2864
- gap: '8px',
2865
- marginBottom: '8px'
2866
- },
2867
- children: [
2868
- renderer_div({
2869
- style: {
2870
- fontSize: 'var(--c15t-devtools-font-size-xs)',
2871
- color: 'var(--c15t-devtools-text-muted)',
2872
- minWidth: '60px'
2873
- },
2874
- text: label
2875
- }),
2876
- renderer_div({
2877
- style: {
2878
- flex: '1'
2879
- },
2880
- children: [
2881
- inputField
2882
- ]
2883
- })
2884
- ]
3854
+ value
2885
3855
  });
3856
+ return {
3857
+ element: renderer_div({
3858
+ className: styles_components_module.overrideField,
3859
+ children: [
3860
+ renderer_span({
3861
+ className: styles_components_module.overrideLabel,
3862
+ text: label
3863
+ }),
3864
+ inputField
3865
+ ]
3866
+ }),
3867
+ control: inputField
3868
+ };
2886
3869
  }
2887
3870
  function createOverrideSelect(options) {
2888
- const { label, selectOptions, value, onChange } = options;
3871
+ const { label, selectOptions, value } = options;
2889
3872
  const selectField = renderer_select({
2890
3873
  className: `${styles_components_module.input ?? ''} ${styles_components_module.inputSmall ?? ''}`.trim(),
2891
3874
  options: selectOptions,
2892
- selectedValue: value,
2893
- onChange: (e)=>{
2894
- const target = e.target;
2895
- onChange(target.value);
2896
- }
2897
- });
2898
- return renderer_div({
2899
- style: {
2900
- display: 'flex',
2901
- alignItems: 'center',
2902
- justifyContent: 'space-between',
2903
- gap: '8px',
2904
- marginBottom: '8px'
2905
- },
2906
- children: [
2907
- renderer_div({
2908
- style: {
2909
- fontSize: 'var(--c15t-devtools-font-size-xs)',
2910
- color: 'var(--c15t-devtools-text-muted)',
2911
- minWidth: '60px'
2912
- },
2913
- text: label
2914
- }),
2915
- renderer_div({
2916
- style: {
2917
- flex: '1'
2918
- },
2919
- children: [
2920
- selectField
2921
- ]
2922
- })
2923
- ]
3875
+ selectedValue: value
2924
3876
  });
3877
+ return {
3878
+ element: renderer_div({
3879
+ className: styles_components_module.overrideField,
3880
+ children: [
3881
+ renderer_span({
3882
+ className: styles_components_module.overrideLabel,
3883
+ text: label
3884
+ }),
3885
+ selectField
3886
+ ]
3887
+ }),
3888
+ control: selectField
3889
+ };
3890
+ }
3891
+ function getDraftFromOverrides(overrides) {
3892
+ return {
3893
+ country: overrides?.country ?? '',
3894
+ region: overrides?.region ?? '',
3895
+ language: overrides?.language ?? '',
3896
+ gpc: overrides?.gpc === true ? 'true' : overrides?.gpc === false ? 'false' : ''
3897
+ };
3898
+ }
3899
+ function normalizeOverrideDraft(draft) {
3900
+ return {
3901
+ country: normalizeAlphaCode(draft.country),
3902
+ region: normalizeAlphaCode(draft.region),
3903
+ language: normalizeLanguageCode(draft.language),
3904
+ gpc: 'true' === draft.gpc ? true : 'false' === draft.gpc ? false : void 0
3905
+ };
3906
+ }
3907
+ function normalizeAlphaCode(value) {
3908
+ const normalized = value.trim().toUpperCase();
3909
+ return normalized || void 0;
3910
+ }
3911
+ function normalizeLanguageCode(value) {
3912
+ const normalized = value.trim();
3913
+ return normalized || void 0;
3914
+ }
3915
+ function overridesEqual(a, b) {
3916
+ return a.country === b.country && a.region === b.region && a.language === b.language && a.gpc === b.gpc;
3917
+ }
3918
+ function hasOverridesValue(overrides) {
3919
+ return Boolean(overrides.country || overrides.region || overrides.language || void 0 !== overrides.gpc);
2925
3920
  }
2926
3921
  const COUNTRY_OPTIONS = [
2927
3922
  {
@@ -3045,6 +4040,32 @@ const COUNTRY_OPTIONS = [
3045
4040
  label: 'ZA - South Africa'
3046
4041
  }
3047
4042
  ];
4043
+ const GPC_OPTIONS = [
4044
+ {
4045
+ value: '',
4046
+ label: '-- Browser Default --'
4047
+ },
4048
+ {
4049
+ value: 'true',
4050
+ label: 'Force On (Simulated)'
4051
+ },
4052
+ {
4053
+ value: 'false',
4054
+ label: 'Force Off (Simulated)'
4055
+ }
4056
+ ];
4057
+ function getEffectiveGpcLabel(gpcOverride) {
4058
+ if (true === gpcOverride) return 'On (Override)';
4059
+ if (false === gpcOverride) return 'Off (Override)';
4060
+ if ('undefined' == typeof window || 'undefined' == typeof navigator) return 'Unknown';
4061
+ try {
4062
+ const nav = navigator;
4063
+ const value = nav.globalPrivacyControl;
4064
+ return true === value || '1' === value ? 'Active' : 'Inactive';
4065
+ } catch {
4066
+ return 'Unknown';
4067
+ }
4068
+ }
3048
4069
  function getModelLabel(model) {
3049
4070
  switch(model){
3050
4071
  case 'opt-in':
@@ -3061,9 +4082,11 @@ function createCompactInfoCard(label, value) {
3061
4082
  return renderer_div({
3062
4083
  className: styles_components_module.gridCard ?? '',
3063
4084
  style: {
4085
+ padding: '6px 8px',
4086
+ minHeight: 'auto',
3064
4087
  flexDirection: 'column',
3065
4088
  alignItems: 'flex-start',
3066
- gap: '2px'
4089
+ gap: '1px'
3067
4090
  },
3068
4091
  children: [
3069
4092
  renderer_span({
@@ -3088,34 +4111,38 @@ const dismissedResources = new Set();
3088
4111
  function scanDOM(state) {
3089
4112
  const results = [];
3090
4113
  const configuredScripts = state.scripts || [];
3091
- const managedDomains = new Map();
4114
+ const managedResources = [];
3092
4115
  for (const script of configuredScripts)if (script.src) try {
3093
4116
  const url = new URL(script.src, window.location.origin);
3094
- if (url.hostname !== window.location.hostname) managedDomains.set(url.hostname, script.id);
4117
+ if (url.hostname !== window.location.hostname) managedResources.push({
4118
+ scriptId: script.id,
4119
+ domain: url.hostname,
4120
+ pathPrefix: normalizePathname(url.pathname)
4121
+ });
3095
4122
  } catch {}
3096
4123
  const scriptElements = document.querySelectorAll("script[src]");
3097
4124
  for (const el of scriptElements){
3098
4125
  const src = el.getAttribute('src');
3099
4126
  if (!src) continue;
3100
- const resource = checkResource(src, "script", managedDomains);
4127
+ const resource = checkResource(src, "script", managedResources);
3101
4128
  if (resource) results.push(resource);
3102
4129
  }
3103
4130
  const iframeElements = document.querySelectorAll('iframe[src]');
3104
4131
  for (const el of iframeElements){
3105
4132
  const src = el.getAttribute('src');
3106
4133
  if (!src) continue;
3107
- const resource = checkResource(src, 'iframe', managedDomains);
4134
+ const resource = checkResource(src, 'iframe', managedResources);
3108
4135
  if (resource) results.push(resource);
3109
4136
  }
3110
4137
  return results;
3111
4138
  }
3112
- function checkResource(src, type, managedDomains) {
4139
+ function checkResource(src, type, managedResources) {
3113
4140
  try {
3114
4141
  const url = new URL(src, window.location.origin);
3115
4142
  const domain = url.hostname;
3116
4143
  if (domain === window.location.hostname) return null;
3117
4144
  if ('data:' === url.protocol || 'blob:' === url.protocol) return null;
3118
- const managedBy = managedDomains.get(domain);
4145
+ const managedBy = findManagedScriptId(url, managedResources);
3119
4146
  const isManaged = Boolean(managedBy);
3120
4147
  return {
3121
4148
  type,
@@ -3127,6 +4154,21 @@ function checkResource(src, type, managedDomains) {
3127
4154
  } catch {}
3128
4155
  return null;
3129
4156
  }
4157
+ function findManagedScriptId(url, managedResources) {
4158
+ const domain = url.hostname;
4159
+ const path = normalizePathname(url.pathname);
4160
+ let bestMatch = null;
4161
+ for (const matcher of managedResources)if (matcher.domain === domain) {
4162
+ if ('/' === matcher.pathPrefix || path.startsWith(matcher.pathPrefix)) {
4163
+ if (!bestMatch || matcher.pathPrefix.length > bestMatch.pathPrefix.length) bestMatch = matcher;
4164
+ }
4165
+ }
4166
+ return bestMatch?.scriptId;
4167
+ }
4168
+ function normalizePathname(pathname) {
4169
+ const trimmed = pathname.trim();
4170
+ return trimmed.length > 0 ? trimmed : '/';
4171
+ }
3130
4172
  function createDomScannerSection(state) {
3131
4173
  let resultsContainer = null;
3132
4174
  let lastScanResults = [];
@@ -3290,22 +4332,37 @@ const CODE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" f
3290
4332
  <polyline points="16 18 22 12 16 6"></polyline>
3291
4333
  <polyline points="8 6 2 12 8 18"></polyline>
3292
4334
  </svg>`;
3293
- function scripts_renderScriptsPanel(container, options) {
3294
- const { getState } = options;
4335
+ const scriptsSearchByContainer = new WeakMap();
4336
+ function renderScriptsPanel(container, options) {
4337
+ const { getState, getEvents } = options;
3295
4338
  renderer_clearElement(container);
3296
4339
  const state = getState();
3297
- if (!state) return void container.appendChild(renderer_div({
3298
- style: {
3299
- padding: '24px',
3300
- textAlign: 'center',
3301
- color: 'var(--c15t-text-muted)',
3302
- fontSize: 'var(--c15t-devtools-font-size-sm)'
3303
- },
3304
- text: 'Store not connected'
3305
- }));
4340
+ if (!state) return void container.appendChild(createDisconnectedState());
3306
4341
  const scripts = state.scripts || [];
3307
4342
  const loadedScripts = state.loadedScripts || {};
3308
4343
  const networkBlocker = state.networkBlocker;
4344
+ const events = getEvents?.() ?? [];
4345
+ const searchQuery = scriptsSearchByContainer.get(container) ?? '';
4346
+ const filteredScripts = scripts.filter((script)=>{
4347
+ if (!searchQuery) return true;
4348
+ const category = 'string' == typeof script.category ? script.category : JSON.stringify(script.category);
4349
+ return `${script.id} ${category}`.toLowerCase().includes(searchQuery);
4350
+ });
4351
+ if (scripts.length > 4) container.appendChild(createSection({
4352
+ title: 'Filter',
4353
+ children: [
4354
+ createInput({
4355
+ value: searchQuery,
4356
+ placeholder: "Filter scripts…",
4357
+ ariaLabel: "Filter scripts",
4358
+ small: true,
4359
+ onInput: (value)=>{
4360
+ scriptsSearchByContainer.set(container, value.trim().toLowerCase());
4361
+ renderScriptsPanel(container, options);
4362
+ }
4363
+ })
4364
+ ]
4365
+ }));
3309
4366
  if (0 === scripts.length) {
3310
4367
  const scriptsSection = createSection({
3311
4368
  title: 'Configured Scripts',
@@ -3322,10 +4379,19 @@ function scripts_renderScriptsPanel(container, options) {
3322
4379
  style: {
3323
4380
  display: 'flex',
3324
4381
  flexDirection: 'column',
3325
- gap: '4px'
4382
+ borderTop: '1px solid var(--c15t-border)',
4383
+ borderBottom: '1px solid var(--c15t-border)'
3326
4384
  }
3327
4385
  });
3328
- for (const script of scripts){
4386
+ if (0 === filteredScripts.length) scriptsList.appendChild(renderer_div({
4387
+ style: {
4388
+ padding: '10px 0',
4389
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
4390
+ color: 'var(--c15t-text-muted)'
4391
+ },
4392
+ text: "No matching scripts"
4393
+ }));
4394
+ for (const script of filteredScripts){
3329
4395
  const scriptId = script.id;
3330
4396
  const isLoaded = true === loadedScripts[scriptId];
3331
4397
  const category = script.category;
@@ -3349,17 +4415,64 @@ function scripts_renderScriptsPanel(container, options) {
3349
4415
  text: status.charAt(0).toUpperCase() + status.slice(1),
3350
4416
  variant: statusVariant
3351
4417
  });
3352
- const item = createListItem({
3353
- title: scriptId,
3354
- description: `Category: ${categoryDisplay}`,
3355
- actions: [
3356
- badge
4418
+ const row = renderer_div({
4419
+ style: {
4420
+ display: 'flex',
4421
+ alignItems: 'center',
4422
+ justifyContent: 'space-between',
4423
+ gap: '8px',
4424
+ padding: '8px 0',
4425
+ borderBottom: '1px solid var(--c15t-border)'
4426
+ },
4427
+ children: [
4428
+ renderer_div({
4429
+ style: {
4430
+ display: 'flex',
4431
+ flexDirection: 'column',
4432
+ gap: '2px',
4433
+ minWidth: '0',
4434
+ flex: '1'
4435
+ },
4436
+ children: [
4437
+ renderer_div({
4438
+ style: {
4439
+ fontSize: 'var(--c15t-font-size-sm)',
4440
+ fontWeight: '500',
4441
+ color: 'var(--c15t-text)',
4442
+ overflow: 'hidden',
4443
+ textOverflow: 'ellipsis',
4444
+ whiteSpace: 'nowrap'
4445
+ },
4446
+ text: scriptId
4447
+ }),
4448
+ renderer_div({
4449
+ style: {
4450
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
4451
+ color: 'var(--c15t-text-muted)',
4452
+ overflow: 'hidden',
4453
+ textOverflow: 'ellipsis',
4454
+ whiteSpace: 'nowrap'
4455
+ },
4456
+ text: `Category: ${categoryDisplay}`
4457
+ })
4458
+ ]
4459
+ }),
4460
+ renderer_div({
4461
+ style: {
4462
+ flexShrink: '0'
4463
+ },
4464
+ children: [
4465
+ badge
4466
+ ]
4467
+ })
3357
4468
  ]
3358
4469
  });
3359
- scriptsList.appendChild(item);
4470
+ scriptsList.appendChild(row);
3360
4471
  }
4472
+ const lastRow = scriptsList.lastElementChild;
4473
+ if (lastRow) lastRow.style.borderBottom = 'none';
3361
4474
  const scriptsSection = createSection({
3362
- title: `Configured Scripts (${scripts.length})`,
4475
+ title: `Configured Scripts (${filteredScripts.length}/${scripts.length})`,
3363
4476
  children: [
3364
4477
  scriptsList
3365
4478
  ]
@@ -3388,6 +4501,20 @@ function scripts_renderScriptsPanel(container, options) {
3388
4501
  ]
3389
4502
  });
3390
4503
  container.appendChild(networkSection);
4504
+ const blockedRequestEvents = events.filter((event)=>'network' === event.type);
4505
+ const networkEventsSection = createSection({
4506
+ title: `Blocked Requests (${blockedRequestEvents.length})`,
4507
+ children: 0 === blockedRequestEvents.length ? [
4508
+ renderer_div({
4509
+ style: {
4510
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
4511
+ color: 'var(--c15t-devtools-text-muted)'
4512
+ },
4513
+ text: 'No blocked network requests recorded in this session'
4514
+ })
4515
+ ] : createBlockedRequestContent(blockedRequestEvents)
4516
+ });
4517
+ container.appendChild(networkEventsSection);
3391
4518
  const loadedCount = Object.values(loadedScripts).filter(Boolean).length;
3392
4519
  const totalCount = scripts.length;
3393
4520
  const summarySection = createSection({
@@ -3413,12 +4540,80 @@ function scripts_renderScriptsPanel(container, options) {
3413
4540
  }
3414
4541
  function checkScriptConsent(state, category) {
3415
4542
  if (!category) return true;
4543
+ if ('function' == typeof state.has) try {
4544
+ return state.has(category);
4545
+ } catch {}
3416
4546
  if ('string' == typeof category) {
3417
4547
  const consents = state.consents || {};
3418
4548
  return true === consents[category];
3419
4549
  }
3420
4550
  return false;
3421
4551
  }
4552
+ function createBlockedRequestContent(events) {
4553
+ const stats = new Map();
4554
+ for (const event of events){
4555
+ const ruleId = getEventRuleId(event) ?? 'unknown';
4556
+ stats.set(ruleId, (stats.get(ruleId) ?? 0) + 1);
4557
+ }
4558
+ const statsList = renderer_div({
4559
+ style: {
4560
+ display: 'flex',
4561
+ flexDirection: 'column',
4562
+ gap: '4px',
4563
+ marginBottom: '8px'
4564
+ },
4565
+ children: [
4566
+ ...stats.entries()
4567
+ ].sort((a, b)=>b[1] - a[1]).map(([ruleId, count])=>createInfoRow({
4568
+ label: 'unknown' === ruleId ? 'Unknown Rule' : `Rule: ${ruleId}`,
4569
+ value: `${count}`
4570
+ }))
4571
+ });
4572
+ const latestEvents = events.slice(0, 5);
4573
+ const latestList = renderer_div({
4574
+ style: {
4575
+ display: 'flex',
4576
+ flexDirection: 'column',
4577
+ gap: '4px'
4578
+ },
4579
+ children: latestEvents.map((event)=>createInfoRow({
4580
+ label: `${formatEventTime(event.timestamp)} ${getEventMethod(event)}`,
4581
+ value: scripts_truncateText(getEventUrl(event), 38)
4582
+ }))
4583
+ });
4584
+ return [
4585
+ statsList,
4586
+ latestList
4587
+ ];
4588
+ }
4589
+ function getEventRuleId(event) {
4590
+ const data = event.data;
4591
+ const rule = data?.rule;
4592
+ const ruleId = rule?.id ?? data?.ruleId;
4593
+ return 'string' == typeof ruleId || 'number' == typeof ruleId ? String(ruleId) : void 0;
4594
+ }
4595
+ function getEventMethod(event) {
4596
+ const data = event.data;
4597
+ const method = data?.method;
4598
+ return 'string' == typeof method ? method.toUpperCase() : 'REQ';
4599
+ }
4600
+ function getEventUrl(event) {
4601
+ const data = event.data;
4602
+ const url = data?.url;
4603
+ return 'string' == typeof url ? url : event.message;
4604
+ }
4605
+ function formatEventTime(timestamp) {
4606
+ return new Date(timestamp).toLocaleTimeString('en-US', {
4607
+ hour12: false,
4608
+ hour: '2-digit',
4609
+ minute: '2-digit',
4610
+ second: '2-digit'
4611
+ });
4612
+ }
4613
+ function scripts_truncateText(text, maxLength) {
4614
+ if (text.length <= maxLength) return text;
4615
+ return `${text.slice(0, maxLength - 3)}...`;
4616
+ }
3422
4617
  const STORAGE_KEYS = {
3423
4618
  C15T: 'c15t',
3424
4619
  PENDING_SYNC: 'c15t:pending-consent-sync',
@@ -3444,7 +4639,7 @@ function clearAllLocalStorage() {
3444
4639
  localStorage.removeItem(STORAGE_KEYS.EUCONSENT);
3445
4640
  } catch {}
3446
4641
  }
3447
- async function reset_consents_resetAllConsents(store, stateManager) {
4642
+ async function resetAllConsents(store, stateManager) {
3448
4643
  const storeState = store.getState();
3449
4644
  storeState.resetConsents();
3450
4645
  clearAllCookies();
@@ -3455,7 +4650,222 @@ async function reset_consents_resetAllConsents(store, stateManager) {
3455
4650
  message: 'All consents reset (storage cleared)'
3456
4651
  });
3457
4652
  }
4653
+ function panel_renderer_createPanelRenderer(config) {
4654
+ const { storeConnector, stateManager, enableEventLogging = true, onPersistOverrides, onClearPersistedOverrides, onCopyState, onExportDebugBundle } = config;
4655
+ const getStoreState = ()=>storeConnector.getState();
4656
+ const logEvent = (type, message, data)=>{
4657
+ if (enableEventLogging) stateManager.addEvent({
4658
+ type,
4659
+ message,
4660
+ data
4661
+ });
4662
+ };
4663
+ const resetConsents = async ()=>{
4664
+ const store = storeConnector.getStore();
4665
+ if (store) await resetAllConsents(store, enableEventLogging ? stateManager : void 0);
4666
+ };
4667
+ const renderPanel = (container, tab)=>{
4668
+ switch(tab){
4669
+ case 'consents':
4670
+ renderConsentsPanel(container, {
4671
+ getState: getStoreState,
4672
+ onConsentChange: (name, value)=>{
4673
+ const store = storeConnector.getStore();
4674
+ if (store) {
4675
+ const consentName = String(name);
4676
+ store.getState().setSelectedConsent(consentName, value);
4677
+ logEvent('info', `${consentName} toggled to ${value} (not saved)`, {
4678
+ name: consentName,
4679
+ value
4680
+ });
4681
+ }
4682
+ },
4683
+ onSave: ()=>{
4684
+ const store = storeConnector.getStore();
4685
+ if (store) {
4686
+ store.getState().saveConsents('custom');
4687
+ logEvent('consent_save', 'Saved consent preferences');
4688
+ }
4689
+ },
4690
+ onAcceptAll: ()=>{
4691
+ const store = storeConnector.getStore();
4692
+ if (store) {
4693
+ store.getState().saveConsents('all');
4694
+ logEvent('consent_save', 'Accepted all consents');
4695
+ }
4696
+ },
4697
+ onRejectAll: ()=>{
4698
+ const store = storeConnector.getStore();
4699
+ if (store) {
4700
+ store.getState().saveConsents('necessary');
4701
+ logEvent('consent_save', 'Rejected all optional consents');
4702
+ }
4703
+ },
4704
+ onReset: resetConsents
4705
+ });
4706
+ break;
4707
+ case 'location':
4708
+ renderLocationPanel(container, {
4709
+ getState: getStoreState,
4710
+ onApplyOverrides: async (overrides)=>{
4711
+ const store = storeConnector.getStore();
4712
+ if (store) {
4713
+ await store.getState().setOverrides({
4714
+ country: overrides.country,
4715
+ region: overrides.region,
4716
+ language: overrides.language,
4717
+ gpc: overrides.gpc
4718
+ });
4719
+ logEvent('info', 'Overrides updated', {
4720
+ country: overrides.country,
4721
+ region: overrides.region,
4722
+ language: overrides.language,
4723
+ gpc: overrides.gpc
4724
+ });
4725
+ onPersistOverrides?.({
4726
+ country: overrides.country,
4727
+ region: overrides.region,
4728
+ language: overrides.language,
4729
+ gpc: overrides.gpc
4730
+ });
4731
+ }
4732
+ },
4733
+ onClearOverrides: async ()=>{
4734
+ const store = storeConnector.getStore();
4735
+ if (store) {
4736
+ await store.getState().setOverrides({
4737
+ country: void 0,
4738
+ region: void 0,
4739
+ language: void 0,
4740
+ gpc: void 0
4741
+ });
4742
+ logEvent('info', 'Overrides cleared');
4743
+ onClearPersistedOverrides?.();
4744
+ }
4745
+ }
4746
+ });
4747
+ break;
4748
+ case "scripts":
4749
+ renderScriptsPanel(container, {
4750
+ getState: getStoreState,
4751
+ getEvents: ()=>stateManager.getState().eventLog
4752
+ });
4753
+ break;
4754
+ case 'iab':
4755
+ renderIabPanel(container, {
4756
+ getState: getStoreState,
4757
+ onSetPurposeConsent: (purposeId, value)=>{
4758
+ const iab = storeConnector.getStore()?.getState().iab;
4759
+ if (!iab) return;
4760
+ iab.setPurposeConsent(purposeId, value);
4761
+ logEvent('iab', `IAB purpose ${purposeId} set to ${value}`);
4762
+ },
4763
+ onSetVendorConsent: (vendorId, value)=>{
4764
+ const iab = storeConnector.getStore()?.getState().iab;
4765
+ if (!iab) return;
4766
+ iab.setVendorConsent(vendorId, value);
4767
+ logEvent('iab', `IAB vendor ${vendorId} set to ${value}`);
4768
+ },
4769
+ onSetSpecialFeatureOptIn: (featureId, value)=>{
4770
+ const iab = storeConnector.getStore()?.getState().iab;
4771
+ if (!iab) return;
4772
+ iab.setSpecialFeatureOptIn(featureId, value);
4773
+ logEvent('iab', `IAB feature ${featureId} set to ${value}`);
4774
+ },
4775
+ onAcceptAll: ()=>{
4776
+ const iab = storeConnector.getStore()?.getState().iab;
4777
+ if (!iab) return;
4778
+ iab.acceptAll();
4779
+ logEvent('iab', 'IAB accept all selected');
4780
+ },
4781
+ onRejectAll: ()=>{
4782
+ const iab = storeConnector.getStore()?.getState().iab;
4783
+ if (!iab) return;
4784
+ iab.rejectAll();
4785
+ logEvent('iab', 'IAB reject all selected');
4786
+ },
4787
+ onSave: ()=>{
4788
+ const iab = storeConnector.getStore()?.getState().iab;
4789
+ if (!iab) return;
4790
+ iab.save().then(()=>logEvent('iab', 'IAB preferences saved')).catch((error)=>{
4791
+ logEvent('error', `Failed to save IAB preferences: ${String(error)}`);
4792
+ });
4793
+ },
4794
+ onReset: resetConsents
4795
+ });
4796
+ break;
4797
+ case 'events':
4798
+ renderEventsPanel(container, {
4799
+ getEvents: ()=>stateManager.getState().eventLog,
4800
+ onClear: ()=>{
4801
+ stateManager.clearEventLog();
4802
+ logEvent('info', 'Event log cleared');
4803
+ }
4804
+ });
4805
+ break;
4806
+ case 'actions':
4807
+ renderActionsPanel(container, {
4808
+ getState: getStoreState,
4809
+ onResetConsents: resetConsents,
4810
+ onRefetchBanner: async ()=>{
4811
+ const store = storeConnector.getStore();
4812
+ if (store) {
4813
+ await store.getState().initConsentManager();
4814
+ logEvent('info', 'Banner data refetched');
4815
+ }
4816
+ },
4817
+ onShowBanner: ()=>{
4818
+ const store = storeConnector.getStore();
4819
+ if (store) {
4820
+ store.getState().setActiveUI('banner', {
4821
+ force: true
4822
+ });
4823
+ logEvent('info', 'Banner shown');
4824
+ }
4825
+ },
4826
+ onOpenPreferences: ()=>{
4827
+ const store = storeConnector.getStore();
4828
+ if (store) {
4829
+ store.getState().setActiveUI('dialog');
4830
+ logEvent('info', 'Preferences dialog opened');
4831
+ }
4832
+ },
4833
+ onCopyState: ()=>{
4834
+ const state = getStoreState();
4835
+ if (state) if (onCopyState) {
4836
+ const result = onCopyState(state);
4837
+ if (result instanceof Promise) result.then((ok)=>{
4838
+ logEvent(ok ? 'info' : 'error', ok ? 'State copied to clipboard' : 'Failed to copy state');
4839
+ }).catch(()=>{
4840
+ logEvent('error', 'Failed to copy state');
4841
+ });
4842
+ else logEvent(result ? 'info' : 'error', result ? 'State copied to clipboard' : 'Failed to copy state');
4843
+ } else navigator.clipboard.writeText(JSON.stringify(state, null, 2)).then(()=>{
4844
+ logEvent('info', 'State copied to clipboard');
4845
+ }).catch(()=>{
4846
+ logEvent('error', 'Failed to copy state');
4847
+ });
4848
+ },
4849
+ onExportDebugBundle: onExportDebugBundle ? ()=>{
4850
+ try {
4851
+ onExportDebugBundle();
4852
+ logEvent('info', 'Debug bundle exported');
4853
+ } catch {
4854
+ logEvent('error', 'Failed to export debug bundle');
4855
+ }
4856
+ } : void 0
4857
+ });
4858
+ break;
4859
+ }
4860
+ };
4861
+ return {
4862
+ renderPanel,
4863
+ getStoreState,
4864
+ resetConsents
4865
+ };
4866
+ }
3458
4867
  const STORAGE_KEY = 'c15t-devtools-events';
4868
+ const ACTIVE_TAB_STORAGE_KEY = 'c15t-devtools-active-tab';
3459
4869
  function loadPersistedEvents() {
3460
4870
  if ('undefined' == typeof window) return [];
3461
4871
  try {
@@ -3470,11 +4880,29 @@ function persistEvents(events) {
3470
4880
  sessionStorage.setItem(STORAGE_KEY, JSON.stringify(events));
3471
4881
  } catch {}
3472
4882
  }
4883
+ function isDevToolsTab(value) {
4884
+ return 'consents' === value || 'location' === value || "scripts" === value || 'iab' === value || 'events' === value || 'actions' === value;
4885
+ }
4886
+ function loadPersistedActiveTab() {
4887
+ if ('undefined' == typeof window) return null;
4888
+ try {
4889
+ const stored = localStorage.getItem(ACTIVE_TAB_STORAGE_KEY);
4890
+ if (isDevToolsTab(stored)) return stored;
4891
+ } catch {}
4892
+ return null;
4893
+ }
4894
+ function persistActiveTab(tab) {
4895
+ if ('undefined' == typeof window) return;
4896
+ try {
4897
+ localStorage.setItem(ACTIVE_TAB_STORAGE_KEY, tab);
4898
+ } catch {}
4899
+ }
3473
4900
  function state_manager_createStateManager(initialState = {}) {
3474
4901
  const persistedEvents = loadPersistedEvents();
4902
+ const persistedActiveTab = loadPersistedActiveTab();
3475
4903
  let state = {
3476
4904
  isOpen: false,
3477
- activeTab: 'location',
4905
+ activeTab: persistedActiveTab ?? 'location',
3478
4906
  position: 'bottom-right',
3479
4907
  isConnected: false,
3480
4908
  eventLog: persistedEvents,
@@ -3515,6 +4943,7 @@ function state_manager_createStateManager(initialState = {}) {
3515
4943
  setState({
3516
4944
  activeTab: tab
3517
4945
  });
4946
+ persistActiveTab(tab);
3518
4947
  },
3519
4948
  setPosition: (position)=>{
3520
4949
  setState({
@@ -3556,12 +4985,67 @@ function store_connector_createStoreConnector(options = {}) {
3556
4985
  const { namespace = 'c15tStore', onConnect, onStateChange, onDisconnect } = options;
3557
4986
  let store = null;
3558
4987
  let unsubscribe = null;
3559
- let pollInterval = null;
4988
+ let reconnectTimeout = null;
4989
+ let reconnectAttempts = 0;
4990
+ let hasNotifiedDisconnect = false;
3560
4991
  const listeners = new Set();
4992
+ const diagnosticsListeners = new Set();
4993
+ let diagnostics = {
4994
+ namespace,
4995
+ reconnectAttempts: 0,
4996
+ nextRetryInMs: null,
4997
+ lastError: null,
4998
+ isPolling: false,
4999
+ disconnectNotified: false
5000
+ };
5001
+ const INITIAL_RETRY_DELAY_MS = 100;
5002
+ const MAX_RETRY_DELAY_MS = 2000;
5003
+ const DISCONNECT_NOTIFY_ATTEMPTS = 5;
5004
+ function updateDiagnostics(partial, notify = true) {
5005
+ diagnostics = {
5006
+ ...diagnostics,
5007
+ ...partial
5008
+ };
5009
+ if (!notify) return;
5010
+ for (const listener of diagnosticsListeners)listener(diagnostics);
5011
+ }
5012
+ function clearReconnectTimer() {
5013
+ if (reconnectTimeout) {
5014
+ clearTimeout(reconnectTimeout);
5015
+ reconnectTimeout = null;
5016
+ updateDiagnostics({
5017
+ isPolling: false,
5018
+ nextRetryInMs: null
5019
+ });
5020
+ }
5021
+ }
5022
+ function resetReconnectState() {
5023
+ reconnectAttempts = 0;
5024
+ hasNotifiedDisconnect = false;
5025
+ updateDiagnostics({
5026
+ reconnectAttempts: 0,
5027
+ nextRetryInMs: null,
5028
+ lastError: null,
5029
+ disconnectNotified: false
5030
+ });
5031
+ }
5032
+ function notifyDisconnectedOnce() {
5033
+ if (hasNotifiedDisconnect) return;
5034
+ hasNotifiedDisconnect = true;
5035
+ updateDiagnostics({
5036
+ disconnectNotified: true
5037
+ });
5038
+ onDisconnect?.();
5039
+ }
3561
5040
  function tryConnect() {
3562
5041
  if ('undefined' == typeof window) return false;
3563
5042
  const storeInstance = window[namespace];
3564
5043
  if (storeInstance && 'function' == typeof storeInstance.getState) {
5044
+ if (store === storeInstance && unsubscribe) return true;
5045
+ if (unsubscribe) {
5046
+ unsubscribe();
5047
+ unsubscribe = null;
5048
+ }
3565
5049
  store = storeInstance;
3566
5050
  unsubscribe = store.subscribe((state)=>{
3567
5051
  onStateChange?.(state);
@@ -3569,30 +5053,41 @@ function store_connector_createStoreConnector(options = {}) {
3569
5053
  });
3570
5054
  const currentState = store.getState();
3571
5055
  onConnect?.(currentState, store);
3572
- if (pollInterval) {
3573
- clearInterval(pollInterval);
3574
- pollInterval = null;
3575
- }
5056
+ clearReconnectTimer();
5057
+ resetReconnectState();
5058
+ updateDiagnostics({
5059
+ lastError: null
5060
+ });
3576
5061
  return true;
3577
5062
  }
5063
+ updateDiagnostics({
5064
+ lastError: `Store "${namespace}" not found on window`
5065
+ });
3578
5066
  return false;
3579
5067
  }
5068
+ function scheduleReconnect(immediate = false) {
5069
+ if (store || reconnectTimeout) return;
5070
+ const delay = immediate ? 0 : Math.min(INITIAL_RETRY_DELAY_MS * 2 ** Math.min(reconnectAttempts, 5), MAX_RETRY_DELAY_MS);
5071
+ updateDiagnostics({
5072
+ isPolling: true,
5073
+ nextRetryInMs: delay,
5074
+ reconnectAttempts
5075
+ });
5076
+ reconnectTimeout = setTimeout(()=>{
5077
+ reconnectTimeout = null;
5078
+ reconnectAttempts++;
5079
+ updateDiagnostics({
5080
+ reconnectAttempts,
5081
+ nextRetryInMs: null
5082
+ });
5083
+ if (tryConnect()) return;
5084
+ if (reconnectAttempts >= DISCONNECT_NOTIFY_ATTEMPTS) notifyDisconnectedOnce();
5085
+ scheduleReconnect();
5086
+ }, delay);
5087
+ }
3580
5088
  function startPolling() {
3581
- if (pollInterval) return;
3582
5089
  if (tryConnect()) return;
3583
- let attempts = 0;
3584
- const maxAttempts = 50;
3585
- pollInterval = setInterval(()=>{
3586
- attempts++;
3587
- if (tryConnect()) return;
3588
- if (attempts >= maxAttempts) {
3589
- if (pollInterval) {
3590
- clearInterval(pollInterval);
3591
- pollInterval = null;
3592
- }
3593
- onDisconnect?.();
3594
- }
3595
- }, 100);
5090
+ scheduleReconnect(true);
3596
5091
  }
3597
5092
  startPolling();
3598
5093
  return {
@@ -3606,17 +5101,155 @@ function store_connector_createStoreConnector(options = {}) {
3606
5101
  listeners.delete(listener);
3607
5102
  };
3608
5103
  },
5104
+ getDiagnostics: ()=>diagnostics,
5105
+ subscribeDiagnostics: (listener)=>{
5106
+ diagnosticsListeners.add(listener);
5107
+ listener(diagnostics);
5108
+ return ()=>{
5109
+ diagnosticsListeners.delete(listener);
5110
+ };
5111
+ },
5112
+ retryConnection: ()=>{
5113
+ if (store) return;
5114
+ resetReconnectState();
5115
+ scheduleReconnect(true);
5116
+ },
3609
5117
  destroy: ()=>{
3610
- if (pollInterval) {
3611
- clearInterval(pollInterval);
3612
- pollInterval = null;
3613
- }
5118
+ clearReconnectTimer();
3614
5119
  if (unsubscribe) {
3615
5120
  unsubscribe();
3616
5121
  unsubscribe = null;
3617
5122
  }
3618
5123
  store = null;
3619
5124
  listeners.clear();
5125
+ diagnosticsListeners.clear();
5126
+ }
5127
+ };
5128
+ }
5129
+ const REGISTRY_KEY = '__c15tDevToolsInstrumentationRegistry';
5130
+ let fallbackRegistry = null;
5131
+ function getRegistry() {
5132
+ if ('undefined' == typeof window) {
5133
+ if (!fallbackRegistry) fallbackRegistry = new Map();
5134
+ return fallbackRegistry;
5135
+ }
5136
+ const host = window;
5137
+ const existing = host[REGISTRY_KEY];
5138
+ if (existing) return existing;
5139
+ const registry = new Map();
5140
+ host[REGISTRY_KEY] = registry;
5141
+ return registry;
5142
+ }
5143
+ function getBlockedRequestMessage(payload) {
5144
+ const data = payload;
5145
+ const method = 'string' == typeof data?.method ? data.method.toUpperCase() : 'REQUEST';
5146
+ const url = 'string' == typeof data?.url ? data.url : 'unknown-url';
5147
+ return `Network blocked: ${method} ${url}`;
5148
+ }
5149
+ function emitEvent(entry, event) {
5150
+ for (const listener of entry.listeners)listener(event);
5151
+ }
5152
+ function ensureNetworkBlockerWrapped(entry) {
5153
+ const blocker = entry.store.getState().networkBlocker;
5154
+ if (!blocker) return;
5155
+ if (blocker.onRequestBlocked === entry.wrappedNetworkBlockedCallback) return;
5156
+ entry.originalNetworkBlockedCallback = blocker.onRequestBlocked;
5157
+ entry.wrappedNetworkBlockedCallback = (payload)=>{
5158
+ emitEvent(entry, {
5159
+ type: 'network',
5160
+ message: getBlockedRequestMessage(payload),
5161
+ data: payload
5162
+ });
5163
+ if ('function' == typeof entry.originalNetworkBlockedCallback) entry.originalNetworkBlockedCallback(payload);
5164
+ };
5165
+ entry.store.getState().setNetworkBlocker({
5166
+ ...blocker,
5167
+ onRequestBlocked: entry.wrappedNetworkBlockedCallback
5168
+ });
5169
+ }
5170
+ function restoreInstrumentation(entry) {
5171
+ entry.stopWatchingStore?.();
5172
+ entry.stopWatchingStore = null;
5173
+ const state = entry.store.getState();
5174
+ state.setCallback('onBannerFetched', entry.originalCallbacks.onBannerFetched);
5175
+ state.setCallback('onConsentSet', entry.originalCallbacks.onConsentSet);
5176
+ state.setCallback('onError', entry.originalCallbacks.onError);
5177
+ state.setCallback('onBeforeConsentRevocationReload', entry.originalCallbacks.onBeforeConsentRevocationReload);
5178
+ const blocker = state.networkBlocker;
5179
+ if (blocker && blocker.onRequestBlocked === entry.wrappedNetworkBlockedCallback) state.setNetworkBlocker({
5180
+ ...blocker,
5181
+ onRequestBlocked: entry.originalNetworkBlockedCallback
5182
+ });
5183
+ entry.wrappedNetworkBlockedCallback = null;
5184
+ }
5185
+ function createInstrumentationEntry(store) {
5186
+ const entry = {
5187
+ store,
5188
+ listeners: new Set(),
5189
+ originalCallbacks: {
5190
+ ...store.getState().callbacks
5191
+ },
5192
+ originalNetworkBlockedCallback: store.getState().networkBlocker?.onRequestBlocked,
5193
+ wrappedNetworkBlockedCallback: null,
5194
+ stopWatchingStore: null
5195
+ };
5196
+ store.getState().setCallback('onBannerFetched', (payload)=>{
5197
+ const jurisdiction = payload.jurisdiction;
5198
+ emitEvent(entry, {
5199
+ type: 'info',
5200
+ message: `Banner fetched: ${String(jurisdiction)}`,
5201
+ data: payload
5202
+ });
5203
+ if ('function' == typeof entry.originalCallbacks.onBannerFetched) entry.originalCallbacks.onBannerFetched(payload);
5204
+ });
5205
+ store.getState().setCallback('onConsentSet', (payload)=>{
5206
+ emitEvent(entry, {
5207
+ type: 'consent_set',
5208
+ message: 'Consent preferences updated',
5209
+ data: payload
5210
+ });
5211
+ if ('function' == typeof entry.originalCallbacks.onConsentSet) entry.originalCallbacks.onConsentSet(payload);
5212
+ });
5213
+ store.getState().setCallback('onError', (payload)=>{
5214
+ const errorMessage = payload.error;
5215
+ emitEvent(entry, {
5216
+ type: 'error',
5217
+ message: `Error: ${String(errorMessage)}`,
5218
+ data: payload
5219
+ });
5220
+ if ('function' == typeof entry.originalCallbacks.onError) entry.originalCallbacks.onError(payload);
5221
+ });
5222
+ store.getState().setCallback('onBeforeConsentRevocationReload', (payload)=>{
5223
+ emitEvent(entry, {
5224
+ type: 'info',
5225
+ message: 'Consent revocation - page will reload',
5226
+ data: payload
5227
+ });
5228
+ if ('function' == typeof entry.originalCallbacks.onBeforeConsentRevocationReload) entry.originalCallbacks.onBeforeConsentRevocationReload(payload);
5229
+ });
5230
+ ensureNetworkBlockerWrapped(entry);
5231
+ entry.stopWatchingStore = store.subscribe(()=>{
5232
+ ensureNetworkBlockerWrapped(entry);
5233
+ });
5234
+ return entry;
5235
+ }
5236
+ function store_instrumentation_registerStoreInstrumentation(options) {
5237
+ const { namespace, store, onEvent } = options;
5238
+ const registry = getRegistry();
5239
+ let entry = registry.get(namespace);
5240
+ if (!entry || entry.store !== store) {
5241
+ if (entry) restoreInstrumentation(entry);
5242
+ entry = createInstrumentationEntry(store);
5243
+ registry.set(namespace, entry);
5244
+ }
5245
+ entry.listeners.add(onEvent);
5246
+ return ()=>{
5247
+ const current = registry.get(namespace);
5248
+ if (!current) return;
5249
+ current.listeners.delete(onEvent);
5250
+ if (0 === current.listeners.size) {
5251
+ restoreInstrumentation(current);
5252
+ registry.delete(namespace);
3620
5253
  }
3621
5254
  };
3622
5255
  }
@@ -3629,15 +5262,88 @@ tokens_options.domAPI = styleDomAPI_default();
3629
5262
  tokens_options.insertStyleElement = insertStyleElement_default();
3630
5263
  injectStylesIntoStyleTag_default()(tokens.A, tokens_options);
3631
5264
  tokens.A && tokens.A.locals && tokens.A.locals;
5265
+ function normalizeOverridesForPersistence(overrides) {
5266
+ return {
5267
+ country: overrides?.country?.trim() || void 0,
5268
+ region: overrides?.region?.trim() || void 0,
5269
+ language: overrides?.language?.trim() || void 0,
5270
+ gpc: overrides?.gpc
5271
+ };
5272
+ }
5273
+ function persistedOverridesEqual(a, b) {
5274
+ return a.country === b.country && a.region === b.region && a.language === b.language && a.gpc === b.gpc;
5275
+ }
5276
+ function createStateCopy(state) {
5277
+ return {
5278
+ consents: state.consents,
5279
+ selectedConsents: state.selectedConsents,
5280
+ consentInfo: state.consentInfo,
5281
+ locationInfo: state.locationInfo,
5282
+ model: state.model,
5283
+ overrides: state.overrides,
5284
+ scripts: state.scripts?.map((script)=>({
5285
+ id: script.id
5286
+ })),
5287
+ loadedScripts: state.loadedScripts
5288
+ };
5289
+ }
3632
5290
  function createDevToolsPanel(options) {
3633
5291
  const { namespace = 'c15tStore' } = options;
5292
+ let detachInstrumentation = null;
3634
5293
  const stateManager = state_manager_createStateManager({
3635
5294
  isOpen: true
3636
5295
  });
3637
5296
  const storeConnector = store_connector_createStoreConnector({
3638
5297
  namespace,
3639
- onConnect: ()=>stateManager.setConnected(true),
3640
- onDisconnect: ()=>stateManager.setConnected(false)
5298
+ onConnect: (state, store)=>{
5299
+ detachInstrumentation?.();
5300
+ detachInstrumentation = store_instrumentation_registerStoreInstrumentation({
5301
+ namespace,
5302
+ store,
5303
+ onEvent: (event)=>stateManager.addEvent(event)
5304
+ });
5305
+ stateManager.setConnected(true);
5306
+ const persistedOverrides = override_storage_loadPersistedOverrides();
5307
+ if (persistedOverrides) {
5308
+ const currentOverrides = normalizeOverridesForPersistence(state.overrides);
5309
+ if (!persistedOverridesEqual(persistedOverrides, currentOverrides)) store.getState().setOverrides({
5310
+ country: persistedOverrides.country,
5311
+ region: persistedOverrides.region,
5312
+ language: persistedOverrides.language,
5313
+ gpc: persistedOverrides.gpc
5314
+ });
5315
+ }
5316
+ },
5317
+ onDisconnect: ()=>{
5318
+ stateManager.setConnected(false);
5319
+ detachInstrumentation?.();
5320
+ detachInstrumentation = null;
5321
+ }
5322
+ });
5323
+ const panelRenderer = panel_renderer_createPanelRenderer({
5324
+ storeConnector,
5325
+ stateManager,
5326
+ enableEventLogging: false,
5327
+ onPersistOverrides: override_storage_persistOverrides,
5328
+ onClearPersistedOverrides: override_storage_clearPersistedOverrides,
5329
+ onCopyState: async (state)=>{
5330
+ try {
5331
+ await navigator.clipboard.writeText(JSON.stringify(createStateCopy(state), null, 2));
5332
+ return true;
5333
+ } catch {
5334
+ return false;
5335
+ }
5336
+ },
5337
+ onExportDebugBundle: ()=>{
5338
+ const bundle = debug_bundle_createDebugBundle({
5339
+ namespace,
5340
+ devToolsState: stateManager.getState(),
5341
+ connection: storeConnector.getDiagnostics(),
5342
+ recentEvents: stateManager.getState().eventLog.slice(0, 100),
5343
+ storeState: debug_bundle_sanitizeStoreState(storeConnector.getState())
5344
+ });
5345
+ debug_bundle_downloadDebugBundle(bundle);
5346
+ }
3641
5347
  });
3642
5348
  const container = renderer_div({
3643
5349
  style: {
@@ -3658,108 +5364,42 @@ function createDevToolsPanel(options) {
3658
5364
  }
3659
5365
  });
3660
5366
  function renderActivePanel() {
3661
- const state = stateManager.getState();
3662
- const getStoreState = ()=>storeConnector.getState();
3663
- switch(state.activeTab){
3664
- case 'consents':
3665
- consents_renderConsentsPanel(contentArea, {
3666
- getState: getStoreState,
3667
- onConsentChange: (name, value)=>{
3668
- storeConnector.getStore()?.getState().setSelectedConsent(name, value);
3669
- },
3670
- onSave: ()=>{
3671
- storeConnector.getStore()?.getState().saveConsents('custom');
3672
- },
3673
- onAcceptAll: ()=>{
3674
- storeConnector.getStore()?.getState().saveConsents('all');
3675
- },
3676
- onRejectAll: ()=>{
3677
- storeConnector.getStore()?.getState().saveConsents('necessary');
3678
- },
3679
- onReset: async ()=>{
3680
- const store = storeConnector.getStore();
3681
- if (store) await reset_consents_resetAllConsents(store);
3682
- }
3683
- });
3684
- break;
3685
- case 'location':
3686
- location_renderLocationPanel(contentArea, {
3687
- getState: getStoreState,
3688
- onSetOverrides: async (overrides)=>{
3689
- const store = storeConnector.getStore();
3690
- if (store) {
3691
- const current = store.getState().overrides || {};
3692
- await store.getState().setOverrides({
3693
- ...current,
3694
- ...overrides
3695
- });
3696
- }
3697
- },
3698
- onClearOverrides: async ()=>{
3699
- await storeConnector.getStore()?.getState().setOverrides(void 0);
3700
- }
3701
- });
3702
- break;
3703
- case "scripts":
3704
- scripts_renderScriptsPanel(contentArea, {
3705
- getState: getStoreState
3706
- });
3707
- break;
3708
- case 'iab':
3709
- iab_renderIabPanel(contentArea, {
3710
- getState: getStoreState,
3711
- onReset: async ()=>{
3712
- const store = storeConnector.getStore();
3713
- if (store) await reset_consents_resetAllConsents(store);
3714
- }
3715
- });
3716
- break;
3717
- case 'events':
3718
- events_renderEventsPanel(contentArea, {
3719
- getEvents: ()=>stateManager.getState().eventLog,
3720
- onClear: ()=>{
3721
- stateManager.clearEventLog();
3722
- }
3723
- });
3724
- break;
3725
- case 'actions':
3726
- actions_renderActionsPanel(contentArea, {
3727
- getState: getStoreState,
3728
- onResetConsents: async ()=>{
3729
- const store = storeConnector.getStore();
3730
- if (store) await reset_consents_resetAllConsents(store);
3731
- },
3732
- onRefetchBanner: async ()=>{
3733
- await storeConnector.getStore()?.getState().initConsentManager();
3734
- },
3735
- onShowBanner: ()=>{
3736
- storeConnector.getStore()?.getState().setActiveUI('banner', {
3737
- force: true
3738
- });
3739
- },
3740
- onOpenPreferences: ()=>{
3741
- storeConnector.getStore()?.getState().setActiveUI('dialog');
3742
- },
3743
- onCopyState: ()=>{
3744
- const state = storeConnector.getState();
3745
- if (state) navigator.clipboard.writeText(JSON.stringify(state, null, 2));
3746
- }
3747
- });
3748
- break;
5367
+ const activeTab = syncTabs();
5368
+ panelRenderer.renderPanel(contentArea, activeTab);
5369
+ }
5370
+ let tabsInstance = null;
5371
+ let iabDisabled = true;
5372
+ function getDisabledTabs() {
5373
+ const disabledTabs = [];
5374
+ const storeState = storeConnector.getState();
5375
+ if (!storeState || 'iab' !== storeState.model) disabledTabs.push('iab');
5376
+ return disabledTabs;
5377
+ }
5378
+ function syncTabs() {
5379
+ const disabledTabs = getDisabledTabs();
5380
+ const nextIabDisabled = disabledTabs.includes('iab');
5381
+ let activeTab = stateManager.getState().activeTab;
5382
+ if (disabledTabs.includes(activeTab)) {
5383
+ activeTab = 'consents';
5384
+ stateManager.setActiveTab(activeTab);
3749
5385
  }
5386
+ if (tabsInstance && iabDisabled === nextIabDisabled) tabsInstance.setActiveTab(activeTab);
5387
+ else {
5388
+ tabsInstance?.destroy();
5389
+ tabsInstance = tabs_createTabs({
5390
+ activeTab,
5391
+ onTabChange: (tab)=>{
5392
+ stateManager.setActiveTab(tab);
5393
+ renderActivePanel();
5394
+ },
5395
+ disabledTabs
5396
+ });
5397
+ iabDisabled = nextIabDisabled;
5398
+ if (!tabsInstance.element.parentElement) container.appendChild(tabsInstance.element);
5399
+ }
5400
+ return activeTab;
3750
5401
  }
3751
- const storeState = storeConnector.getState();
3752
- const disabledTabs = [];
3753
- if (!storeState || 'iab' !== storeState.model) disabledTabs.push('iab');
3754
- const tabsInstance = tabs_createTabs({
3755
- activeTab: stateManager.getState().activeTab,
3756
- onTabChange: (tab)=>{
3757
- stateManager.setActiveTab(tab);
3758
- renderActivePanel();
3759
- },
3760
- disabledTabs
3761
- });
3762
- container.appendChild(tabsInstance.element);
5402
+ syncTabs();
3763
5403
  container.appendChild(contentArea);
3764
5404
  renderActivePanel();
3765
5405
  const unsubscribe = storeConnector.subscribe(()=>{
@@ -3768,8 +5408,10 @@ function createDevToolsPanel(options) {
3768
5408
  return {
3769
5409
  element: container,
3770
5410
  destroy: ()=>{
5411
+ detachInstrumentation?.();
5412
+ detachInstrumentation = null;
3771
5413
  unsubscribe();
3772
- tabsInstance.destroy();
5414
+ tabsInstance?.destroy();
3773
5415
  storeConnector.destroy();
3774
5416
  stateManager.destroy();
3775
5417
  }