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

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 (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/__tests__/core/override-storage.test.d.ts +2 -0
  3. package/dist/__tests__/core/override-storage.test.d.ts.map +1 -0
  4. package/dist/__tests__/core/store-connector.test.d.ts +2 -0
  5. package/dist/__tests__/core/store-connector.test.d.ts.map +1 -0
  6. package/dist/__tests__/panels/events.test.d.ts +2 -0
  7. package/dist/__tests__/panels/events.test.d.ts.map +1 -0
  8. package/dist/__tests__/panels/iab.test.d.ts +2 -0
  9. package/dist/__tests__/panels/iab.test.d.ts.map +1 -0
  10. package/dist/__tests__/panels/scripts.test.d.ts +2 -0
  11. package/dist/__tests__/panels/scripts.test.d.ts.map +1 -0
  12. package/dist/__tests__/utils/preference-trigger.test.d.ts +2 -0
  13. package/dist/__tests__/utils/preference-trigger.test.d.ts.map +1 -0
  14. package/dist/components/panel.d.ts +1 -0
  15. package/dist/components/panel.d.ts.map +1 -1
  16. package/dist/core/devtools.d.ts.map +1 -1
  17. package/dist/core/override-storage.d.ts +7 -0
  18. package/dist/core/override-storage.d.ts.map +1 -0
  19. package/dist/core/panel-renderer.d.ts.map +1 -1
  20. package/dist/core/state-manager.d.ts +1 -1
  21. package/dist/core/state-manager.d.ts.map +1 -1
  22. package/dist/core/store-connector.d.ts +4 -0
  23. package/dist/core/store-connector.d.ts.map +1 -1
  24. package/dist/index.cjs +1092 -244
  25. package/dist/index.js +1092 -244
  26. package/dist/panels/dom-scanner.d.ts.map +1 -1
  27. package/dist/panels/events.d.ts.map +1 -1
  28. package/dist/panels/iab.d.ts +6 -0
  29. package/dist/panels/iab.d.ts.map +1 -1
  30. package/dist/panels/location.d.ts +9 -6
  31. package/dist/panels/location.d.ts.map +1 -1
  32. package/dist/panels/scripts.d.ts +2 -0
  33. package/dist/panels/scripts.d.ts.map +1 -1
  34. package/dist/react.cjs +1015 -237
  35. package/dist/react.js +1015 -237
  36. package/dist/tanstack.cjs +810 -201
  37. package/dist/tanstack.js +810 -201
  38. package/dist/utils/preference-trigger.d.ts +2 -2
  39. package/dist/utils/preference-trigger.d.ts.map +1 -1
  40. package/dist/version.d.ts +2 -0
  41. package/dist/version.d.ts.map +1 -0
  42. package/package.json +10 -8
package/dist/index.cjs CHANGED
@@ -345,22 +345,26 @@ var __webpack_modules__ = {
345
345
  justify-content: center;
346
346
  align-items: center;
347
347
  gap: var(--c15t-space-xs, .25rem);
348
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-sm, .5rem);
349
348
  border: 1px solid var(--c15t-border, #e3e3e3);
350
349
  border-radius: var(--c15t-radius-md, .5rem);
351
350
  background-color: var(--c15t-surface, #fff);
351
+ min-height: 30px;
352
352
  color: var(--c15t-text, #171717);
353
353
  font-family: inherit;
354
- font-size: var(--c15t-devtools-font-size-xs, .75rem);
354
+ font-size: 12px;
355
355
  font-weight: var(--c15t-font-weight-medium, 500);
356
356
  cursor: pointer;
357
- 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));
357
+ 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));
358
+ padding: 5px 10px;
359
+ line-height: 1;
358
360
  display: inline-flex;
361
+ box-shadow: 0 1px 1px #0000000a;
359
362
  }
360
363
 
361
364
  .btn-evRVlh:hover {
362
365
  background-color: var(--c15t-surface-hover, #f7f7f7);
363
366
  border-color: var(--c15t-border-hover, #c9c9c9);
367
+ box-shadow: 0 2px 6px #00000014;
364
368
  }
365
369
 
366
370
  .btn-evRVlh:focus-visible {
@@ -368,9 +372,14 @@ var __webpack_modules__ = {
368
372
  outline-offset: 1px;
369
373
  }
370
374
 
375
+ .btn-evRVlh:active {
376
+ box-shadow: 0 1px 2px #00000014;
377
+ }
378
+
371
379
  .btn-evRVlh:disabled {
372
380
  opacity: .5;
373
381
  cursor: not-allowed;
382
+ box-shadow: none;
374
383
  }
375
384
 
376
385
  .btnPrimary-dA6nqY {
@@ -396,8 +405,10 @@ var __webpack_modules__ = {
396
405
  }
397
406
 
398
407
  .btnSmall-TjXoqZ {
399
- padding: 2px var(--c15t-space-xs, .25rem);
400
- font-size: 10px;
408
+ border-radius: var(--c15t-radius-sm, .375rem);
409
+ min-height: 26px;
410
+ padding: 3px 8px;
411
+ font-size: 11px;
401
412
  }
402
413
 
403
414
  .btnIcon-fiYQAh {
@@ -549,6 +560,50 @@ var __webpack_modules__ = {
549
560
  letter-spacing: .5px;
550
561
  }
551
562
 
563
+ .overrideField-keNdpJ {
564
+ flex-direction: column;
565
+ gap: 3px;
566
+ margin-bottom: 0;
567
+ display: flex;
568
+ }
569
+
570
+ .overrideLabel-ApMoTw {
571
+ color: var(--c15t-text-muted, #737373);
572
+ font-size: 11px;
573
+ font-weight: 600;
574
+ }
575
+
576
+ .overrideHint-yCfwGt {
577
+ color: var(--c15t-devtools-text-muted, #737373);
578
+ margin-top: 6px;
579
+ font-size: 11px;
580
+ }
581
+
582
+ .overrideActions-imdcn7 {
583
+ border-top: 1px dashed var(--c15t-border, #e3e3e3);
584
+ justify-content: space-between;
585
+ align-items: center;
586
+ gap: 8px;
587
+ margin-top: 8px;
588
+ padding-top: 8px;
589
+ display: flex;
590
+ }
591
+
592
+ .overrideActionButtons-gYOx1e {
593
+ flex-wrap: wrap;
594
+ gap: 6px;
595
+ display: flex;
596
+ }
597
+
598
+ .overrideStatus-sty_qS {
599
+ color: var(--c15t-text-muted, #737373);
600
+ font-size: 11px;
601
+ }
602
+
603
+ .overrideStatusDirty-OUdDMw {
604
+ color: var(--c15t-devtools-badge-warning, #f59f0a);
605
+ }
606
+
552
607
  .infoRow-RlB_0h {
553
608
  padding: var(--c15t-space-xs, .25rem) 0;
554
609
  justify-content: space-between;
@@ -641,6 +696,13 @@ var __webpack_modules__ = {
641
696
  section: "section-a197cB",
642
697
  sectionHeader: "sectionHeader-Xcljcw",
643
698
  sectionTitle: "sectionTitle-RUiFld",
699
+ overrideField: "overrideField-keNdpJ",
700
+ overrideLabel: "overrideLabel-ApMoTw",
701
+ overrideHint: "overrideHint-yCfwGt",
702
+ overrideActions: "overrideActions-imdcn7",
703
+ overrideActionButtons: "overrideActionButtons-gYOx1e",
704
+ overrideStatus: "overrideStatus-sty_qS",
705
+ overrideStatusDirty: "overrideStatusDirty-OUdDMw",
644
706
  infoRow: "infoRow-RlB_0h",
645
707
  infoLabel: "infoLabel-_pbK33",
646
708
  infoValue: "infoValue-flMl_e",
@@ -1123,16 +1185,23 @@ var __webpack_modules__ = {
1123
1185
  ___CSS_LOADER_EXPORT___.push([
1124
1186
  module.id,
1125
1187
  `.tabList-IyuiBE {
1188
+ align-items: center;
1126
1189
  gap: var(--c15t-space-xs, .25rem);
1127
- padding: var(--c15t-space-sm, .5rem) var(--c15t-space-md, 1rem);
1190
+ padding: var(--c15t-space-sm, .5rem) var(--c15t-space-sm, .5rem);
1128
1191
  border-bottom: 1px solid var(--c15t-border, #e3e3e3);
1129
1192
  background-color: var(--c15t-surface, #fff);
1130
1193
  scrollbar-width: none;
1131
1194
  -ms-overflow-style: none;
1195
+ scroll-padding-inline-end: var(--c15t-space-sm, .5rem);
1132
1196
  display: flex;
1133
1197
  overflow-x: auto;
1134
1198
  }
1135
1199
 
1200
+ .tabList-IyuiBE:after {
1201
+ content: "";
1202
+ flex: 0 0 var(--c15t-space-sm, .5rem);
1203
+ }
1204
+
1136
1205
  .tabList-IyuiBE::-webkit-scrollbar {
1137
1206
  display: none;
1138
1207
  }
@@ -1140,17 +1209,17 @@ var __webpack_modules__ = {
1140
1209
  .tab-yfDEqg {
1141
1210
  align-items: center;
1142
1211
  gap: var(--c15t-space-xs, .25rem);
1143
- padding: var(--c15t-space-xs, .25rem) var(--c15t-space-sm, .5rem);
1144
1212
  border-radius: var(--c15t-radius-md, .5rem);
1145
1213
  color: var(--c15t-text-muted, #737373);
1146
1214
  font-family: inherit;
1147
- font-size: var(--c15t-devtools-font-size-xs, .75rem);
1215
+ font-size: 11px;
1148
1216
  font-weight: var(--c15t-font-weight-medium, 500);
1149
1217
  cursor: pointer;
1150
1218
  white-space: nowrap;
1151
1219
  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));
1152
1220
  background-color: #0000;
1153
1221
  border: none;
1222
+ padding: 3px 7px;
1154
1223
  display: flex;
1155
1224
  }
1156
1225
 
@@ -1885,9 +1954,9 @@ var __webpack_exports__ = {};
1885
1954
  const elements = getPreferenceTriggerElements();
1886
1955
  for (const el of elements)el.style.display = visible ? '' : 'none';
1887
1956
  }
1888
- function getPreferenceCenterOpener() {
1957
+ function getPreferenceCenterOpener(namespace = 'c15tStore') {
1889
1958
  const win = window;
1890
- const store = win.c15tStore;
1959
+ const store = win[namespace];
1891
1960
  if (store && 'function' == typeof store.getState) {
1892
1961
  const state = store.getState();
1893
1962
  if ('function' == typeof state.setActiveUI) return ()=>{
@@ -1896,6 +1965,7 @@ var __webpack_exports__ = {};
1896
1965
  }
1897
1966
  return null;
1898
1967
  }
1968
+ const version = '2.0.0-rc.3';
1899
1969
  const DEVTOOLS_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 446 445" aria-label="c15t">
1900
1970
  <path fill="currentColor" d="M223.178.313c39.064 0 70.732 31.668 70.732 70.732-.001 39.064-31.668 70.731-70.732 70.731-12.181 0-23.642-3.079-33.649-8.502l-55.689 55.689a70.267 70.267 0 0 1 5.574 13.441h167.531c8.695-29.217 35.762-50.523 67.804-50.523 39.064 0 70.731 31.668 70.731 70.732s-31.668 70.732-70.731 70.732c-32.042 0-59.108-21.306-67.803-50.523H139.413a70.417 70.417 0 0 1-7.888 17.396l54.046 54.046c10.893-6.851 23.786-10.815 37.605-10.815 39.064 0 70.732 31.669 70.732 70.733 0 39.064-31.668 70.731-70.732 70.731s-70.732-31.667-70.732-70.731c0-10.518 2.296-20.499 6.414-29.471l-57.78-57.78c-8.972 4.117-18.952 6.414-29.47 6.414-39.063 0-70.731-31.668-70.732-70.732 0-39.064 31.669-70.732 70.733-70.732 12.18 0 23.642 3.079 33.649 8.502l55.688-55.688c-5.423-10.007-8.502-21.469-8.502-33.65 0-39.064 31.668-70.733 70.732-70.733Zm0 343.555c-16.742 0-30.314 13.572-30.314 30.314 0 16.741 13.572 30.313 30.314 30.313s30.314-13.572 30.314-30.313c0-16.742-13.572-30.314-30.314-30.314ZM71.611 192.299c-16.742 0-30.315 13.572-30.315 30.314s13.573 30.314 30.315 30.314c16.741 0 30.313-13.572 30.313-30.314 0-16.741-13.572-30.314-30.313-30.314Zm303.138 0c-16.729 0-30.294 13.551-30.315 30.275l.001.039-.001.038c.021 16.725 13.586 30.276 30.315 30.276 16.741 0 30.313-13.572 30.313-30.314 0-16.741-13.572-30.314-30.313-30.314ZM223.178 40.73c-16.742 0-30.314 13.573-30.314 30.315s13.573 30.313 30.314 30.313c16.742 0 30.313-13.572 30.314-30.313 0-16.742-13.572-30.314-30.314-30.315Z"/>
1901
1971
  </svg>`;
@@ -2099,7 +2169,7 @@ var __webpack_exports__ = {};
2099
2169
  }
2100
2170
  }
2101
2171
  function createPanel(options) {
2102
- const { stateManager, storeConnector, onRenderContent, enableUnifiedMode = true } = options;
2172
+ const { stateManager, storeConnector, onRenderContent, namespace = 'c15tStore', enableUnifiedMode = true } = options;
2103
2173
  let removePortal = null;
2104
2174
  let isAnimatingOut = false;
2105
2175
  let draggable = null;
@@ -2128,7 +2198,7 @@ var __webpack_exports__ = {};
2128
2198
  return;
2129
2199
  }
2130
2200
  hasPreferenceTrigger = detectPreferenceTrigger();
2131
- const preferenceCenterOpener = getPreferenceCenterOpener();
2201
+ const preferenceCenterOpener = getPreferenceCenterOpener(namespace);
2132
2202
  useUnifiedMode = hasPreferenceTrigger && null !== preferenceCenterOpener;
2133
2203
  if (useUnifiedMode && !dropdownMenu) {
2134
2204
  dropdownMenu = createDropdownMenu({
@@ -2148,7 +2218,7 @@ var __webpack_exports__ = {};
2148
2218
  description: 'Open privacy settings',
2149
2219
  icon: PREFERENCES_ICON,
2150
2220
  onClick: ()=>{
2151
- const opener = getPreferenceCenterOpener();
2221
+ const opener = getPreferenceCenterOpener(namespace);
2152
2222
  if (opener) opener();
2153
2223
  }
2154
2224
  },
@@ -2204,6 +2274,7 @@ var __webpack_exports__ = {};
2204
2274
  let panelElement = null;
2205
2275
  let backdropElement = null;
2206
2276
  let contentContainer = null;
2277
+ let footerElement = null;
2207
2278
  function createPanelElement() {
2208
2279
  const corner = draggable?.getCorner() ?? stateManager.getState().position;
2209
2280
  const positionClass = getPositionClass(corner);
@@ -2255,33 +2326,53 @@ var __webpack_exports__ = {};
2255
2326
  contentContainer = renderer_div({
2256
2327
  className: styles_panel_module.content
2257
2328
  });
2258
- const isConnected = storeConnector.isConnected();
2259
- const footer = renderer_div({
2260
- className: styles_panel_module.footer,
2261
- children: [
2262
- renderer_div({
2263
- className: styles_panel_module.footerStatus,
2264
- children: [
2265
- span({
2266
- className: `${styles_panel_module.statusDot} ${isConnected ? styles_panel_module.statusConnected : styles_panel_module.statusDisconnected}`
2267
- }),
2268
- span({
2269
- text: isConnected ? 'Connected' : 'Disconnected'
2270
- })
2271
- ]
2272
- }),
2273
- span({
2274
- text: 'v1.8.3'
2275
- })
2276
- ]
2329
+ footerElement = renderer_div({
2330
+ className: styles_panel_module.footer
2277
2331
  });
2332
+ updateFooter();
2278
2333
  panel.appendChild(header);
2279
2334
  panel.appendChild(contentContainer);
2280
- panel.appendChild(footer);
2281
- if (isConnected) onRenderContent(contentContainer);
2335
+ panel.appendChild(footerElement);
2336
+ if (storeConnector.isConnected()) onRenderContent(contentContainer);
2282
2337
  else renderErrorState(contentContainer);
2283
2338
  return panel;
2284
2339
  }
2340
+ function updateFooter() {
2341
+ if (!footerElement) return;
2342
+ clearElement(footerElement);
2343
+ const isConnected = storeConnector.isConnected();
2344
+ const storeState = storeConnector.getState();
2345
+ const isLoading = storeState?.isLoadingConsentInfo ?? false;
2346
+ const statusChildren = [
2347
+ span({
2348
+ className: `${styles_panel_module.statusDot} ${isConnected ? styles_panel_module.statusConnected : styles_panel_module.statusDisconnected}`
2349
+ }),
2350
+ span({
2351
+ text: isConnected ? 'Connected' : 'Disconnected'
2352
+ })
2353
+ ];
2354
+ if (isLoading) statusChildren.push(span({
2355
+ style: {
2356
+ marginLeft: '4px',
2357
+ opacity: '0.7'
2358
+ },
2359
+ text: '\u00b7 Fetching /init\u2026'
2360
+ }));
2361
+ footerElement.appendChild(renderer_div({
2362
+ className: styles_panel_module.footerStatus,
2363
+ children: statusChildren
2364
+ }));
2365
+ if (!isConnected) footerElement.appendChild(renderer_button({
2366
+ className: styles_panel_module.closeButton,
2367
+ text: 'Retry',
2368
+ onClick: ()=>{
2369
+ storeConnector.retryConnection();
2370
+ }
2371
+ }));
2372
+ footerElement.appendChild(span({
2373
+ text: `v${version}`
2374
+ }));
2375
+ }
2285
2376
  function renderErrorState(container) {
2286
2377
  clearElement(container);
2287
2378
  const errorState = renderer_div({
@@ -2304,6 +2395,13 @@ var __webpack_exports__ = {};
2304
2395
  renderer_div({
2305
2396
  className: styles_panel_module.errorMessage,
2306
2397
  text: 'c15t consent manager is not initialized. Make sure you have set up the ConsentManagerProvider in your app.'
2398
+ }),
2399
+ renderer_button({
2400
+ className: styles_panel_module.closeButton,
2401
+ text: 'Retry Connection',
2402
+ onClick: ()=>{
2403
+ storeConnector.retryConnection();
2404
+ }
2307
2405
  })
2308
2406
  ]
2309
2407
  });
@@ -2339,6 +2437,7 @@ var __webpack_exports__ = {};
2339
2437
  panelElement = null;
2340
2438
  }
2341
2439
  contentContainer = null;
2440
+ footerElement = null;
2342
2441
  isAnimatingOut = false;
2343
2442
  floatingButton.style.display = '';
2344
2443
  stateManager.setOpen(false);
@@ -2357,6 +2456,7 @@ var __webpack_exports__ = {};
2357
2456
  update();
2358
2457
  });
2359
2458
  const unsubscribeStore = storeConnector.subscribe(()=>{
2459
+ updateFooter();
2360
2460
  if (contentContainer) if (storeConnector.isConnected()) onRenderContent(contentContainer);
2361
2461
  else renderErrorState(contentContainer);
2362
2462
  });
@@ -3012,13 +3112,13 @@ var __webpack_exports__ = {};
3012
3112
  },
3013
3113
  children: [
3014
3114
  createButton({
3015
- text: 'All',
3115
+ text: 'Accept',
3016
3116
  variant: 'primary',
3017
3117
  small: true,
3018
3118
  onClick: onAcceptAll
3019
3119
  }),
3020
3120
  createButton({
3021
- text: 'None',
3121
+ text: 'Reject',
3022
3122
  variant: 'default',
3023
3123
  small: true,
3024
3124
  onClick: onRejectAll
@@ -3066,16 +3166,22 @@ var __webpack_exports__ = {};
3066
3166
  function formatConsentName(name) {
3067
3167
  return name.replace(/_/g, ' ').replace(/\b\w/g, (l)=>l.toUpperCase());
3068
3168
  }
3169
+ let activeFilter = 'all';
3170
+ let selectedEventId = null;
3069
3171
  function renderEventsPanel(container, options) {
3070
3172
  const { getEvents, onClear } = options;
3071
3173
  clearElement(container);
3072
- const events = getEvents();
3174
+ const allEvents = getEvents();
3175
+ const events = allEvents.filter((event)=>matchesFilter(event, activeFilter));
3176
+ if (!events.some((event)=>event.id === selectedEventId)) selectedEventId = events[0]?.id ?? null;
3177
+ const selectedEvent = events.find((event)=>event.id === selectedEventId) ?? null;
3073
3178
  const header = renderer_div({
3074
3179
  style: {
3075
3180
  display: 'flex',
3076
3181
  alignItems: 'center',
3077
3182
  justifyContent: 'space-between',
3078
- padding: '12px 16px 8px'
3183
+ padding: '12px 16px 8px',
3184
+ gap: '8px'
3079
3185
  },
3080
3186
  children: [
3081
3187
  span({
@@ -3086,44 +3192,130 @@ var __webpack_exports__ = {};
3086
3192
  textTransform: 'uppercase',
3087
3193
  letterSpacing: '0.5px'
3088
3194
  },
3089
- text: `Events (${events.length})`
3195
+ text: `Events (${events.length}/${allEvents.length})`
3090
3196
  }),
3091
- createButton({
3092
- text: 'Clear',
3093
- small: true,
3094
- onClick: onClear
3197
+ renderer_div({
3198
+ style: {
3199
+ display: 'flex',
3200
+ gap: '6px'
3201
+ },
3202
+ children: [
3203
+ createButton({
3204
+ text: 'Export',
3205
+ small: true,
3206
+ onClick: ()=>exportEvents(allEvents)
3207
+ }),
3208
+ createButton({
3209
+ text: 'Clear',
3210
+ small: true,
3211
+ onClick: ()=>{
3212
+ onClear();
3213
+ selectedEventId = null;
3214
+ renderEventsPanel(container, options);
3215
+ }
3216
+ })
3217
+ ]
3095
3218
  })
3096
3219
  ]
3097
3220
  });
3098
3221
  container.appendChild(header);
3222
+ container.appendChild(renderer_div({
3223
+ style: {
3224
+ display: 'flex',
3225
+ flexWrap: 'wrap',
3226
+ gap: '6px',
3227
+ padding: '0 16px 8px'
3228
+ },
3229
+ children: EVENT_FILTERS.map((filter)=>createFilterButton(filter, filter === activeFilter, ()=>{
3230
+ activeFilter = filter;
3231
+ selectedEventId = null;
3232
+ renderEventsPanel(container, options);
3233
+ }))
3234
+ }));
3099
3235
  const eventList = renderer_div({
3100
3236
  style: {
3101
3237
  display: 'flex',
3102
3238
  flexDirection: 'column',
3103
3239
  gap: '4px',
3104
3240
  padding: '0 12px 12px',
3105
- maxHeight: '400px',
3241
+ maxHeight: '300px',
3106
3242
  overflowY: 'auto'
3107
3243
  }
3108
3244
  });
3109
- if (0 === events.length) {
3110
- const emptyState = renderer_div({
3111
- style: {
3112
- padding: '32px 16px',
3113
- textAlign: 'center',
3114
- color: 'var(--c15t-text-muted)',
3115
- fontSize: 'var(--c15t-devtools-font-size-sm)'
3116
- },
3117
- text: 'No events recorded yet'
3118
- });
3119
- eventList.appendChild(emptyState);
3120
- } else for (const event of events){
3121
- const eventItem = createEventItem(event);
3122
- eventList.appendChild(eventItem);
3123
- }
3245
+ if (0 === events.length) eventList.appendChild(renderer_div({
3246
+ style: {
3247
+ padding: '20px 16px',
3248
+ textAlign: 'center',
3249
+ color: 'var(--c15t-text-muted)',
3250
+ fontSize: 'var(--c15t-devtools-font-size-sm)'
3251
+ },
3252
+ text: 'No events match this filter'
3253
+ }));
3254
+ else for (const event of events)eventList.appendChild(createEventItem(event, event.id === selectedEventId, ()=>{
3255
+ selectedEventId = event.id;
3256
+ renderEventsPanel(container, options);
3257
+ }));
3124
3258
  container.appendChild(eventList);
3259
+ container.appendChild(createPayloadSection(selectedEvent));
3260
+ }
3261
+ const EVENT_FILTERS = [
3262
+ 'all',
3263
+ 'error',
3264
+ 'consent',
3265
+ 'network',
3266
+ 'iab'
3267
+ ];
3268
+ function createFilterButton(filter, active, onClick) {
3269
+ return createButton({
3270
+ text: filter.toUpperCase(),
3271
+ small: true,
3272
+ variant: active ? 'primary' : 'default',
3273
+ onClick
3274
+ });
3275
+ }
3276
+ function matchesFilter(event, filter) {
3277
+ if ('all' === filter) return true;
3278
+ if ('error' === filter) return 'error' === event.type;
3279
+ if ('consent' === filter) return 'consent_set' === event.type || 'consent_save' === event.type || 'consent_reset' === event.type;
3280
+ if ('network' === filter) return 'network' === event.type;
3281
+ return 'iab' === event.type;
3282
+ }
3283
+ function createPayloadSection(event) {
3284
+ const payload = event?.data ? JSON.stringify(event.data, null, 2) : null;
3285
+ return renderer_div({
3286
+ style: {
3287
+ padding: '0 12px 12px'
3288
+ },
3289
+ children: [
3290
+ renderer_div({
3291
+ style: {
3292
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
3293
+ fontWeight: '600',
3294
+ color: 'var(--c15t-text-muted)',
3295
+ textTransform: 'uppercase',
3296
+ letterSpacing: '0.5px',
3297
+ marginBottom: '6px'
3298
+ },
3299
+ text: 'Payload'
3300
+ }),
3301
+ renderer_div({
3302
+ className: styles_components_module.gridCard ?? '',
3303
+ style: {
3304
+ padding: '8px',
3305
+ fontFamily: 'ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace',
3306
+ fontSize: '11px',
3307
+ color: 'var(--c15t-text-muted)',
3308
+ maxHeight: '140px',
3309
+ overflowY: 'auto',
3310
+ whiteSpace: 'pre-wrap',
3311
+ wordBreak: 'break-word'
3312
+ },
3313
+ text: payload || 'Select an event with payload data'
3314
+ })
3315
+ ]
3316
+ });
3125
3317
  }
3126
- function createEventItem(event) {
3318
+ function createEventItem(event, selected, onSelect) {
3127
3319
  const time = formatTime(event.timestamp);
3128
3320
  const icon = getEventIcon(event.type);
3129
3321
  const color = getEventColor(event.type);
@@ -3134,8 +3326,11 @@ var __webpack_exports__ = {};
3134
3326
  alignItems: 'center',
3135
3327
  gap: '8px',
3136
3328
  padding: '6px 10px',
3137
- fontSize: 'var(--c15t-devtools-font-size-xs)'
3329
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
3330
+ cursor: 'pointer',
3331
+ borderColor: selected ? 'var(--c15t-devtools-badge-info, #3b82f6)' : 'var(--c15t-border)'
3138
3332
  },
3333
+ onClick: onSelect,
3139
3334
  children: [
3140
3335
  span({
3141
3336
  style: {
@@ -3164,6 +3359,21 @@ var __webpack_exports__ = {};
3164
3359
  ]
3165
3360
  });
3166
3361
  }
3362
+ function exportEvents(events) {
3363
+ const json = JSON.stringify(events, null, 2);
3364
+ const blob = new Blob([
3365
+ json
3366
+ ], {
3367
+ type: 'application/json'
3368
+ });
3369
+ const url = URL.createObjectURL(blob);
3370
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
3371
+ const a = document.createElement('a');
3372
+ a.href = url;
3373
+ a.download = `c15t-events-${timestamp}.json`;
3374
+ a.click();
3375
+ URL.revokeObjectURL(url);
3376
+ }
3167
3377
  function formatTime(timestamp) {
3168
3378
  const date = new Date(timestamp);
3169
3379
  return date.toLocaleTimeString('en-US', {
@@ -3182,7 +3392,10 @@ var __webpack_exports__ = {};
3182
3392
  return '○';
3183
3393
  case 'error':
3184
3394
  return '✕';
3185
- case 'info':
3395
+ case 'network':
3396
+ return '◉';
3397
+ case 'iab':
3398
+ return '◆';
3186
3399
  default:
3187
3400
  return '○';
3188
3401
  }
@@ -3196,13 +3409,16 @@ var __webpack_exports__ = {};
3196
3409
  return 'var(--c15t-devtools-badge-warning, #f59e0b)';
3197
3410
  case 'error':
3198
3411
  return 'var(--c15t-devtools-badge-error, #ef4444)';
3199
- case 'info':
3412
+ case 'network':
3413
+ return 'var(--c15t-devtools-badge-warning, #f59e0b)';
3414
+ case 'iab':
3415
+ return 'var(--c15t-devtools-badge-info, #3b82f6)';
3200
3416
  default:
3201
3417
  return 'var(--c15t-text-muted)';
3202
3418
  }
3203
3419
  }
3204
3420
  function renderIabPanel(container, options) {
3205
- const { getState, onReset } = options;
3421
+ const { getState, onSetPurposeConsent, onSetVendorConsent, onSetSpecialFeatureOptIn, onAcceptAll, onRejectAll, onSave, onReset } = options;
3206
3422
  clearElement(container);
3207
3423
  const state = getState();
3208
3424
  if (!state) return void container.appendChild(renderer_div({
@@ -3280,7 +3496,9 @@ var __webpack_exports__ = {};
3280
3496
  for (const [purposeId, consent] of purposeEntries){
3281
3497
  const purposeInfo = purposes[purposeId];
3282
3498
  const purposeName = purposeInfo?.name || `Purpose ${purposeId}`;
3283
- purposeList.appendChild(createPurposeRow(purposeId, purposeName, Boolean(consent)));
3499
+ purposeList.appendChild(createPurposeRow(purposeId, purposeName, Boolean(consent), (value)=>{
3500
+ onSetPurposeConsent(Number(purposeId), value);
3501
+ }));
3284
3502
  }
3285
3503
  const purposesSection = createSection({
3286
3504
  title: `Purposes (${purposeEntries.length})`,
@@ -3306,7 +3524,9 @@ var __webpack_exports__ = {};
3306
3524
  for (const [featureId, optIn] of specialFeatureEntries){
3307
3525
  const featureInfo = specialFeatures[featureId];
3308
3526
  const featureName = featureInfo?.name || `Special Feature ${featureId}`;
3309
- specialFeatureList.appendChild(createPurposeRow(featureId, featureName, Boolean(optIn)));
3527
+ specialFeatureList.appendChild(createPurposeRow(featureId, featureName, Boolean(optIn), (value)=>{
3528
+ onSetSpecialFeatureOptIn(Number(featureId), value);
3529
+ }, 'feature'));
3310
3530
  }
3311
3531
  const specialFeaturesSection = createSection({
3312
3532
  title: `Special Features (${specialFeatureEntries.length})`,
@@ -3346,7 +3566,9 @@ var __webpack_exports__ = {};
3346
3566
  overflowY: 'auto'
3347
3567
  }
3348
3568
  });
3349
- for (const [vendorId, consent, vendorName] of iabVendors)vendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'iab'));
3569
+ for (const [vendorId, consent, vendorName] of iabVendors)vendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'iab', (value)=>{
3570
+ onSetVendorConsent(Number(vendorId), value);
3571
+ }));
3350
3572
  const vendorsSection = createSection({
3351
3573
  title: `IAB Vendors (${iabVendors.length})`,
3352
3574
  children: [
@@ -3365,7 +3587,9 @@ var __webpack_exports__ = {};
3365
3587
  overflowY: 'auto'
3366
3588
  }
3367
3589
  });
3368
- for (const [vendorId, consent, vendorName] of customVendors)customVendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'custom'));
3590
+ for (const [vendorId, consent, vendorName] of customVendors)customVendorList.appendChild(createVendorRow(vendorId, vendorName, consent, 'custom', (value)=>{
3591
+ onSetVendorConsent(vendorId, value);
3592
+ }));
3369
3593
  const customVendorsSection = createSection({
3370
3594
  title: `Custom Vendors (${customVendors.length})`,
3371
3595
  children: [
@@ -3387,15 +3611,40 @@ var __webpack_exports__ = {};
3387
3611
  style: {
3388
3612
  display: 'flex',
3389
3613
  alignItems: 'center',
3390
- justifyContent: 'flex-end',
3614
+ justifyContent: 'space-between',
3391
3615
  padding: '12px 16px',
3392
3616
  marginTop: 'auto',
3393
3617
  borderTop: '1px solid var(--c15t-border)',
3394
3618
  backgroundColor: 'var(--c15t-surface)'
3395
3619
  },
3396
3620
  children: [
3621
+ renderer_div({
3622
+ style: {
3623
+ display: 'flex',
3624
+ gap: '6px'
3625
+ },
3626
+ children: [
3627
+ createButton({
3628
+ text: 'Accept All',
3629
+ variant: 'primary',
3630
+ small: true,
3631
+ onClick: onAcceptAll
3632
+ }),
3633
+ createButton({
3634
+ text: 'Reject All',
3635
+ small: true,
3636
+ onClick: onRejectAll
3637
+ }),
3638
+ createButton({
3639
+ text: 'Save',
3640
+ variant: 'primary',
3641
+ small: true,
3642
+ onClick: onSave
3643
+ })
3644
+ ]
3645
+ }),
3397
3646
  createButton({
3398
- text: 'Reset All',
3647
+ text: 'Reset',
3399
3648
  variant: 'danger',
3400
3649
  small: true,
3401
3650
  onClick: onReset
@@ -3404,7 +3653,7 @@ var __webpack_exports__ = {};
3404
3653
  });
3405
3654
  container.appendChild(footer);
3406
3655
  }
3407
- function createPurposeRow(id, name, consent) {
3656
+ function createPurposeRow(id, name, consent, onChange, ariaKind = 'purpose') {
3408
3657
  return renderer_div({
3409
3658
  style: {
3410
3659
  display: 'flex',
@@ -3427,14 +3676,28 @@ var __webpack_exports__ = {};
3427
3676
  text: `${id}. ${name}`,
3428
3677
  title: name
3429
3678
  }),
3430
- createBadge({
3431
- text: consent ? '✓' : '✕',
3432
- variant: consent ? 'success' : 'error'
3679
+ renderer_div({
3680
+ style: {
3681
+ display: 'flex',
3682
+ alignItems: 'center',
3683
+ gap: '6px'
3684
+ },
3685
+ children: [
3686
+ createBadge({
3687
+ text: consent ? '✓' : '✕',
3688
+ variant: consent ? 'success' : 'error'
3689
+ }),
3690
+ createToggle({
3691
+ checked: consent,
3692
+ onChange,
3693
+ ariaLabel: `Toggle ${ariaKind} ${id}`
3694
+ })
3695
+ ]
3433
3696
  })
3434
3697
  ]
3435
3698
  });
3436
3699
  }
3437
- function createVendorRow(id, name, consent, type) {
3700
+ function createVendorRow(id, name, consent, type, onChange) {
3438
3701
  return renderer_div({
3439
3702
  style: {
3440
3703
  display: 'flex',
@@ -3481,16 +3744,21 @@ var __webpack_exports__ = {};
3481
3744
  createBadge({
3482
3745
  text: consent ? '✓' : '✕',
3483
3746
  variant: consent ? 'success' : 'error'
3747
+ }),
3748
+ createToggle({
3749
+ checked: consent,
3750
+ onChange,
3751
+ ariaLabel: `Toggle vendor ${id}`
3484
3752
  })
3485
3753
  ]
3486
3754
  });
3487
3755
  }
3488
3756
  function truncateText(text, maxLength) {
3489
3757
  if (text.length <= maxLength) return text;
3490
- return text.slice(0, maxLength - 3) + '...';
3758
+ return `${text.slice(0, maxLength - 3)}...`;
3491
3759
  }
3492
3760
  function renderLocationPanel(container, options) {
3493
- const { getState, onSetOverrides, onClearOverrides } = options;
3761
+ const { getState, onApplyOverrides, onClearOverrides } = options;
3494
3762
  clearElement(container);
3495
3763
  const state = getState();
3496
3764
  if (!state) return void container.appendChild(renderer_div({
@@ -3511,145 +3779,230 @@ var __webpack_exports__ = {};
3511
3779
  createCompactInfoCard('Jurisdiction', locationInfo?.jurisdiction || '—'),
3512
3780
  createCompactInfoCard('Language', translationConfig?.defaultLanguage || '—')
3513
3781
  ];
3782
+ gridItems.push(createCompactInfoCard('GPC', getEffectiveGpcLabel(overrides?.gpc)));
3514
3783
  if (state.model) gridItems.push(createCompactInfoCard('Model', getModelLabel(state.model)));
3515
3784
  const locationGrid = createGrid({
3516
- columns: 2,
3785
+ columns: 3,
3517
3786
  children: gridItems
3518
3787
  });
3519
3788
  container.appendChild(locationGrid);
3789
+ const initialDraft = getDraftFromOverrides(overrides);
3790
+ let appliedOverrides = normalizeOverrideDraft(initialDraft);
3791
+ let isSubmitting = false;
3792
+ const countryField = createOverrideSelect({
3793
+ label: 'Country',
3794
+ selectOptions: COUNTRY_OPTIONS,
3795
+ value: initialDraft.country
3796
+ });
3797
+ const regionField = createOverrideInput({
3798
+ label: 'Region',
3799
+ placeholder: 'e.g., CA, NY, BE',
3800
+ value: initialDraft.region
3801
+ });
3802
+ const languageField = createOverrideInput({
3803
+ label: 'Language',
3804
+ placeholder: 'e.g., de, fr, en-US',
3805
+ value: initialDraft.language
3806
+ });
3807
+ const gpcField = createOverrideSelect({
3808
+ label: 'GPC',
3809
+ selectOptions: GPC_OPTIONS,
3810
+ value: initialDraft.gpc
3811
+ });
3812
+ const formStatus = span({
3813
+ className: styles_components_module.overrideStatus,
3814
+ text: 'In sync'
3815
+ });
3816
+ const applyButton = createButton({
3817
+ text: 'Apply',
3818
+ variant: 'primary',
3819
+ small: true,
3820
+ disabled: true,
3821
+ onClick: ()=>{
3822
+ applyDraft();
3823
+ }
3824
+ });
3825
+ const revertButton = createButton({
3826
+ text: 'Revert',
3827
+ small: true,
3828
+ disabled: true,
3829
+ onClick: ()=>{
3830
+ setDraftValues(getDraftFromOverrides(appliedOverrides));
3831
+ updateFormState();
3832
+ }
3833
+ });
3834
+ const clearButton = createButton({
3835
+ text: 'Clear',
3836
+ small: true,
3837
+ onClick: ()=>{
3838
+ clearDraftAndOverrides();
3839
+ }
3840
+ });
3841
+ const overrideFieldsGrid = renderer_div({
3842
+ style: {
3843
+ display: 'grid',
3844
+ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
3845
+ gap: '8px 10px'
3846
+ },
3847
+ children: [
3848
+ countryField.element,
3849
+ regionField.element,
3850
+ languageField.element,
3851
+ gpcField.element
3852
+ ]
3853
+ });
3520
3854
  const overrideSection = createSection({
3521
3855
  title: 'Override Settings',
3522
- actions: [
3523
- createButton({
3524
- text: 'Clear',
3525
- small: true,
3526
- onClick: onClearOverrides
3527
- })
3528
- ],
3529
3856
  children: [
3530
- createOverrideSelect({
3531
- label: 'Country',
3532
- selectOptions: COUNTRY_OPTIONS,
3533
- value: overrides?.country || '',
3534
- onChange: (value)=>onSetOverrides({
3535
- country: value || void 0
3536
- })
3537
- }),
3538
- createOverrideInput({
3539
- label: 'Region',
3540
- placeholder: 'e.g., CA, NY, BE',
3541
- value: overrides?.region || '',
3542
- onChange: (value)=>onSetOverrides({
3543
- region: value || void 0
3544
- })
3857
+ overrideFieldsGrid,
3858
+ span({
3859
+ className: styles_components_module.overrideHint,
3860
+ text: 'GPC override only affects opt-out or unregulated jurisdictions.'
3545
3861
  }),
3546
- createOverrideInput({
3547
- label: 'Language',
3548
- placeholder: 'e.g., de, fr, en',
3549
- value: overrides?.language || '',
3550
- onChange: (value)=>onSetOverrides({
3551
- language: value || void 0
3552
- })
3862
+ renderer_div({
3863
+ className: styles_components_module.overrideActions,
3864
+ children: [
3865
+ renderer_div({
3866
+ className: styles_components_module.overrideActionButtons,
3867
+ children: [
3868
+ revertButton,
3869
+ applyButton,
3870
+ clearButton
3871
+ ]
3872
+ }),
3873
+ formStatus
3874
+ ]
3553
3875
  })
3554
3876
  ]
3555
3877
  });
3556
3878
  container.appendChild(overrideSection);
3557
- const hasOverrides = overrides && (overrides.country || overrides.region || overrides.language);
3558
- if (hasOverrides) {
3559
- const overrideBanner = renderer_div({
3560
- style: {
3561
- padding: '8px 16px',
3562
- backgroundColor: 'var(--c15t-devtools-badge-info-bg)',
3563
- color: 'var(--c15t-devtools-badge-info)',
3564
- fontSize: 'var(--c15t-devtools-font-size-xs)',
3565
- borderTop: '1px solid var(--c15t-devtools-border)'
3566
- },
3567
- text: 'Overrides are active. This may affect consent behavior.'
3879
+ countryField.control.addEventListener('change', updateFormState);
3880
+ regionField.control.addEventListener('input', updateFormState);
3881
+ languageField.control.addEventListener('input', updateFormState);
3882
+ gpcField.control.addEventListener('change', updateFormState);
3883
+ updateFormState();
3884
+ async function applyDraft() {
3885
+ if (isSubmitting) return;
3886
+ const draftOverrides = getDraftOverrides();
3887
+ if (overridesEqual(draftOverrides, appliedOverrides)) return;
3888
+ isSubmitting = true;
3889
+ updateFormState();
3890
+ try {
3891
+ await onApplyOverrides(draftOverrides);
3892
+ appliedOverrides = draftOverrides;
3893
+ } finally{
3894
+ isSubmitting = false;
3895
+ updateFormState();
3896
+ }
3897
+ }
3898
+ async function clearDraftAndOverrides() {
3899
+ if (isSubmitting) return;
3900
+ isSubmitting = true;
3901
+ updateFormState();
3902
+ try {
3903
+ await onClearOverrides();
3904
+ appliedOverrides = {};
3905
+ setDraftValues(getDraftFromOverrides(void 0));
3906
+ } finally{
3907
+ isSubmitting = false;
3908
+ updateFormState();
3909
+ }
3910
+ }
3911
+ function getDraftOverrides() {
3912
+ return normalizeOverrideDraft({
3913
+ country: countryField.control.value,
3914
+ region: regionField.control.value,
3915
+ language: languageField.control.value,
3916
+ gpc: gpcField.control.value
3568
3917
  });
3569
- container.appendChild(overrideBanner);
3918
+ }
3919
+ function setDraftValues(draft) {
3920
+ countryField.control.value = draft.country;
3921
+ regionField.control.value = draft.region;
3922
+ languageField.control.value = draft.language;
3923
+ gpcField.control.value = draft.gpc;
3924
+ }
3925
+ function updateFormState() {
3926
+ const draftOverrides = getDraftOverrides();
3927
+ const hasDraftChanges = !overridesEqual(draftOverrides, appliedOverrides);
3928
+ applyButton.disabled = !hasDraftChanges || isSubmitting;
3929
+ revertButton.disabled = !hasDraftChanges || isSubmitting;
3930
+ clearButton.disabled = isSubmitting;
3931
+ formStatus.textContent = isSubmitting ? 'Applying...' : hasDraftChanges ? 'Unsaved changes' : hasOverridesValue(appliedOverrides) ? 'Overrides active' : 'No overrides';
3932
+ if (styles_components_module.overrideStatusDirty) formStatus.classList.toggle(styles_components_module.overrideStatusDirty, !isSubmitting && hasDraftChanges);
3570
3933
  }
3571
3934
  }
3572
3935
  function createOverrideInput(options) {
3573
- const { label, placeholder, value, onChange } = options;
3574
- let debounceTimer = null;
3936
+ const { label, placeholder, value } = options;
3575
3937
  const inputField = input({
3576
3938
  className: `${styles_components_module.input ?? ''} ${styles_components_module.inputSmall ?? ''}`.trim(),
3577
3939
  placeholder,
3578
- value,
3579
- onInput: (e)=>{
3580
- const target = e.target;
3581
- if (debounceTimer) clearTimeout(debounceTimer);
3582
- debounceTimer = setTimeout(()=>{
3583
- onChange(target.value);
3584
- }, 500);
3585
- }
3586
- });
3587
- return renderer_div({
3588
- style: {
3589
- display: 'flex',
3590
- alignItems: 'center',
3591
- justifyContent: 'space-between',
3592
- gap: '8px',
3593
- marginBottom: '8px'
3594
- },
3595
- children: [
3596
- renderer_div({
3597
- style: {
3598
- fontSize: 'var(--c15t-devtools-font-size-xs)',
3599
- color: 'var(--c15t-devtools-text-muted)',
3600
- minWidth: '60px'
3601
- },
3602
- text: label
3603
- }),
3604
- renderer_div({
3605
- style: {
3606
- flex: '1'
3607
- },
3608
- children: [
3609
- inputField
3610
- ]
3611
- })
3612
- ]
3940
+ value
3613
3941
  });
3942
+ return {
3943
+ element: renderer_div({
3944
+ className: styles_components_module.overrideField,
3945
+ children: [
3946
+ span({
3947
+ className: styles_components_module.overrideLabel,
3948
+ text: label
3949
+ }),
3950
+ inputField
3951
+ ]
3952
+ }),
3953
+ control: inputField
3954
+ };
3614
3955
  }
3615
3956
  function createOverrideSelect(options) {
3616
- const { label, selectOptions, value, onChange } = options;
3957
+ const { label, selectOptions, value } = options;
3617
3958
  const selectField = renderer_select({
3618
3959
  className: `${styles_components_module.input ?? ''} ${styles_components_module.inputSmall ?? ''}`.trim(),
3619
3960
  options: selectOptions,
3620
- selectedValue: value,
3621
- onChange: (e)=>{
3622
- const target = e.target;
3623
- onChange(target.value);
3624
- }
3625
- });
3626
- return renderer_div({
3627
- style: {
3628
- display: 'flex',
3629
- alignItems: 'center',
3630
- justifyContent: 'space-between',
3631
- gap: '8px',
3632
- marginBottom: '8px'
3633
- },
3634
- children: [
3635
- renderer_div({
3636
- style: {
3637
- fontSize: 'var(--c15t-devtools-font-size-xs)',
3638
- color: 'var(--c15t-devtools-text-muted)',
3639
- minWidth: '60px'
3640
- },
3641
- text: label
3642
- }),
3643
- renderer_div({
3644
- style: {
3645
- flex: '1'
3646
- },
3647
- children: [
3648
- selectField
3649
- ]
3650
- })
3651
- ]
3961
+ selectedValue: value
3652
3962
  });
3963
+ return {
3964
+ element: renderer_div({
3965
+ className: styles_components_module.overrideField,
3966
+ children: [
3967
+ span({
3968
+ className: styles_components_module.overrideLabel,
3969
+ text: label
3970
+ }),
3971
+ selectField
3972
+ ]
3973
+ }),
3974
+ control: selectField
3975
+ };
3976
+ }
3977
+ function getDraftFromOverrides(overrides) {
3978
+ return {
3979
+ country: overrides?.country ?? '',
3980
+ region: overrides?.region ?? '',
3981
+ language: overrides?.language ?? '',
3982
+ gpc: overrides?.gpc === true ? 'true' : overrides?.gpc === false ? 'false' : ''
3983
+ };
3984
+ }
3985
+ function normalizeOverrideDraft(draft) {
3986
+ return {
3987
+ country: normalizeAlphaCode(draft.country),
3988
+ region: normalizeAlphaCode(draft.region),
3989
+ language: normalizeLanguageCode(draft.language),
3990
+ gpc: 'true' === draft.gpc ? true : 'false' === draft.gpc ? false : void 0
3991
+ };
3992
+ }
3993
+ function normalizeAlphaCode(value) {
3994
+ const normalized = value.trim().toUpperCase();
3995
+ return normalized || void 0;
3996
+ }
3997
+ function normalizeLanguageCode(value) {
3998
+ const normalized = value.trim();
3999
+ return normalized || void 0;
4000
+ }
4001
+ function overridesEqual(a, b) {
4002
+ return a.country === b.country && a.region === b.region && a.language === b.language && a.gpc === b.gpc;
4003
+ }
4004
+ function hasOverridesValue(overrides) {
4005
+ return Boolean(overrides.country || overrides.region || overrides.language || void 0 !== overrides.gpc);
3653
4006
  }
3654
4007
  const COUNTRY_OPTIONS = [
3655
4008
  {
@@ -3773,6 +4126,32 @@ var __webpack_exports__ = {};
3773
4126
  label: 'ZA - South Africa'
3774
4127
  }
3775
4128
  ];
4129
+ const GPC_OPTIONS = [
4130
+ {
4131
+ value: '',
4132
+ label: '-- Browser Default --'
4133
+ },
4134
+ {
4135
+ value: 'true',
4136
+ label: 'Force On (Simulated)'
4137
+ },
4138
+ {
4139
+ value: 'false',
4140
+ label: 'Force Off (Simulated)'
4141
+ }
4142
+ ];
4143
+ function getEffectiveGpcLabel(gpcOverride) {
4144
+ if (true === gpcOverride) return 'On (Override)';
4145
+ if (false === gpcOverride) return 'Off (Override)';
4146
+ if ('undefined' == typeof window || 'undefined' == typeof navigator) return 'Unknown';
4147
+ try {
4148
+ const nav = navigator;
4149
+ const value = nav.globalPrivacyControl;
4150
+ return true === value || '1' === value ? 'Active' : 'Inactive';
4151
+ } catch {
4152
+ return 'Unknown';
4153
+ }
4154
+ }
3776
4155
  function getModelLabel(model) {
3777
4156
  switch(model){
3778
4157
  case 'opt-in':
@@ -3789,9 +4168,11 @@ var __webpack_exports__ = {};
3789
4168
  return renderer_div({
3790
4169
  className: styles_components_module.gridCard ?? '',
3791
4170
  style: {
4171
+ padding: '6px 8px',
4172
+ minHeight: 'auto',
3792
4173
  flexDirection: 'column',
3793
4174
  alignItems: 'flex-start',
3794
- gap: '2px'
4175
+ gap: '1px'
3795
4176
  },
3796
4177
  children: [
3797
4178
  span({
@@ -3816,34 +4197,38 @@ var __webpack_exports__ = {};
3816
4197
  function scanDOM(state) {
3817
4198
  const results = [];
3818
4199
  const configuredScripts = state.scripts || [];
3819
- const managedDomains = new Map();
4200
+ const managedResources = [];
3820
4201
  for (const script of configuredScripts)if (script.src) try {
3821
4202
  const url = new URL(script.src, window.location.origin);
3822
- if (url.hostname !== window.location.hostname) managedDomains.set(url.hostname, script.id);
4203
+ if (url.hostname !== window.location.hostname) managedResources.push({
4204
+ scriptId: script.id,
4205
+ domain: url.hostname,
4206
+ pathPrefix: normalizePathname(url.pathname)
4207
+ });
3823
4208
  } catch {}
3824
4209
  const scriptElements = document.querySelectorAll("script[src]");
3825
4210
  for (const el of scriptElements){
3826
4211
  const src = el.getAttribute('src');
3827
4212
  if (!src) continue;
3828
- const resource = checkResource(src, "script", managedDomains);
4213
+ const resource = checkResource(src, "script", managedResources);
3829
4214
  if (resource) results.push(resource);
3830
4215
  }
3831
4216
  const iframeElements = document.querySelectorAll('iframe[src]');
3832
4217
  for (const el of iframeElements){
3833
4218
  const src = el.getAttribute('src');
3834
4219
  if (!src) continue;
3835
- const resource = checkResource(src, 'iframe', managedDomains);
4220
+ const resource = checkResource(src, 'iframe', managedResources);
3836
4221
  if (resource) results.push(resource);
3837
4222
  }
3838
4223
  return results;
3839
4224
  }
3840
- function checkResource(src, type, managedDomains) {
4225
+ function checkResource(src, type, managedResources) {
3841
4226
  try {
3842
4227
  const url = new URL(src, window.location.origin);
3843
4228
  const domain = url.hostname;
3844
4229
  if (domain === window.location.hostname) return null;
3845
4230
  if ('data:' === url.protocol || 'blob:' === url.protocol) return null;
3846
- const managedBy = managedDomains.get(domain);
4231
+ const managedBy = findManagedScriptId(url, managedResources);
3847
4232
  const isManaged = Boolean(managedBy);
3848
4233
  return {
3849
4234
  type,
@@ -3855,6 +4240,21 @@ var __webpack_exports__ = {};
3855
4240
  } catch {}
3856
4241
  return null;
3857
4242
  }
4243
+ function findManagedScriptId(url, managedResources) {
4244
+ const domain = url.hostname;
4245
+ const path = normalizePathname(url.pathname);
4246
+ let bestMatch = null;
4247
+ for (const matcher of managedResources)if (matcher.domain === domain) {
4248
+ if ('/' === matcher.pathPrefix || path.startsWith(matcher.pathPrefix)) {
4249
+ if (!bestMatch || matcher.pathPrefix.length > bestMatch.pathPrefix.length) bestMatch = matcher;
4250
+ }
4251
+ }
4252
+ return bestMatch?.scriptId;
4253
+ }
4254
+ function normalizePathname(pathname) {
4255
+ const trimmed = pathname.trim();
4256
+ return trimmed.length > 0 ? trimmed : '/';
4257
+ }
3858
4258
  function createDomScannerSection(state) {
3859
4259
  let resultsContainer = null;
3860
4260
  let lastScanResults = [];
@@ -4019,7 +4419,7 @@ var __webpack_exports__ = {};
4019
4419
  <polyline points="8 6 2 12 8 18"></polyline>
4020
4420
  </svg>`;
4021
4421
  function renderScriptsPanel(container, options) {
4022
- const { getState } = options;
4422
+ const { getState, getEvents } = options;
4023
4423
  clearElement(container);
4024
4424
  const state = getState();
4025
4425
  if (!state) return void container.appendChild(renderer_div({
@@ -4034,6 +4434,7 @@ var __webpack_exports__ = {};
4034
4434
  const scripts = state.scripts || [];
4035
4435
  const loadedScripts = state.loadedScripts || {};
4036
4436
  const networkBlocker = state.networkBlocker;
4437
+ const events = getEvents?.() ?? [];
4037
4438
  if (0 === scripts.length) {
4038
4439
  const scriptsSection = createSection({
4039
4440
  title: 'Configured Scripts',
@@ -4116,6 +4517,20 @@ var __webpack_exports__ = {};
4116
4517
  ]
4117
4518
  });
4118
4519
  container.appendChild(networkSection);
4520
+ const blockedRequestEvents = events.filter((event)=>'network' === event.type);
4521
+ const networkEventsSection = createSection({
4522
+ title: `Blocked Requests (${blockedRequestEvents.length})`,
4523
+ children: 0 === blockedRequestEvents.length ? [
4524
+ renderer_div({
4525
+ style: {
4526
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
4527
+ color: 'var(--c15t-devtools-text-muted)'
4528
+ },
4529
+ text: 'No blocked network requests recorded in this session'
4530
+ })
4531
+ ] : createBlockedRequestContent(blockedRequestEvents)
4532
+ });
4533
+ container.appendChild(networkEventsSection);
4119
4534
  const loadedCount = Object.values(loadedScripts).filter(Boolean).length;
4120
4535
  const totalCount = scripts.length;
4121
4536
  const summarySection = createSection({
@@ -4141,12 +4556,127 @@ var __webpack_exports__ = {};
4141
4556
  }
4142
4557
  function checkScriptConsent(state, category) {
4143
4558
  if (!category) return true;
4559
+ if ('function' == typeof state.has) try {
4560
+ return state.has(category);
4561
+ } catch {}
4144
4562
  if ('string' == typeof category) {
4145
4563
  const consents = state.consents || {};
4146
4564
  return true === consents[category];
4147
4565
  }
4148
4566
  return false;
4149
4567
  }
4568
+ function createBlockedRequestContent(events) {
4569
+ const stats = new Map();
4570
+ for (const event of events){
4571
+ const ruleId = getEventRuleId(event) ?? 'unknown';
4572
+ stats.set(ruleId, (stats.get(ruleId) ?? 0) + 1);
4573
+ }
4574
+ const statsList = renderer_div({
4575
+ style: {
4576
+ display: 'flex',
4577
+ flexDirection: 'column',
4578
+ gap: '4px',
4579
+ marginBottom: '8px'
4580
+ },
4581
+ children: [
4582
+ ...stats.entries()
4583
+ ].sort((a, b)=>b[1] - a[1]).map(([ruleId, count])=>createInfoRow({
4584
+ label: 'unknown' === ruleId ? 'Unknown Rule' : `Rule: ${ruleId}`,
4585
+ value: `${count}`
4586
+ }))
4587
+ });
4588
+ const latestEvents = events.slice(0, 5);
4589
+ const latestList = renderer_div({
4590
+ style: {
4591
+ display: 'flex',
4592
+ flexDirection: 'column',
4593
+ gap: '4px'
4594
+ },
4595
+ children: latestEvents.map((event)=>createInfoRow({
4596
+ label: `${formatEventTime(event.timestamp)} ${getEventMethod(event)}`,
4597
+ value: scripts_truncateText(getEventUrl(event), 38)
4598
+ }))
4599
+ });
4600
+ return [
4601
+ statsList,
4602
+ latestList
4603
+ ];
4604
+ }
4605
+ function getEventRuleId(event) {
4606
+ const data = event.data;
4607
+ const rule = data?.rule;
4608
+ const ruleId = rule?.id ?? data?.ruleId;
4609
+ return 'string' == typeof ruleId || 'number' == typeof ruleId ? String(ruleId) : void 0;
4610
+ }
4611
+ function getEventMethod(event) {
4612
+ const data = event.data;
4613
+ const method = data?.method;
4614
+ return 'string' == typeof method ? method.toUpperCase() : 'REQ';
4615
+ }
4616
+ function getEventUrl(event) {
4617
+ const data = event.data;
4618
+ const url = data?.url;
4619
+ return 'string' == typeof url ? url : event.message;
4620
+ }
4621
+ function formatEventTime(timestamp) {
4622
+ return new Date(timestamp).toLocaleTimeString('en-US', {
4623
+ hour12: false,
4624
+ hour: '2-digit',
4625
+ minute: '2-digit',
4626
+ second: '2-digit'
4627
+ });
4628
+ }
4629
+ function scripts_truncateText(text, maxLength) {
4630
+ if (text.length <= maxLength) return text;
4631
+ return `${text.slice(0, maxLength - 3)}...`;
4632
+ }
4633
+ const DEVTOOLS_OVERRIDES_STORAGE_KEY = 'c15t-devtools-overrides';
4634
+ function normalizeStringValue(value) {
4635
+ if ('string' != typeof value) return;
4636
+ const normalized = value.trim();
4637
+ return normalized.length > 0 ? normalized : void 0;
4638
+ }
4639
+ function normalizeBooleanValue(value) {
4640
+ return 'boolean' == typeof value ? value : void 0;
4641
+ }
4642
+ function normalizeOverrides(value) {
4643
+ if (!value || 'object' != typeof value) return null;
4644
+ const source = value;
4645
+ const overrides = {
4646
+ country: normalizeStringValue(source.country),
4647
+ region: normalizeStringValue(source.region),
4648
+ language: normalizeStringValue(source.language),
4649
+ gpc: normalizeBooleanValue(source.gpc)
4650
+ };
4651
+ return hasPersistedOverrides(overrides) ? overrides : null;
4652
+ }
4653
+ function hasPersistedOverrides(overrides) {
4654
+ return Boolean(overrides.country || overrides.region || overrides.language || void 0 !== overrides.gpc);
4655
+ }
4656
+ function loadPersistedOverrides(storageKey = DEVTOOLS_OVERRIDES_STORAGE_KEY) {
4657
+ if ('undefined' == typeof window) return null;
4658
+ try {
4659
+ const stored = localStorage.getItem(storageKey);
4660
+ if (!stored) return null;
4661
+ const parsed = JSON.parse(stored);
4662
+ return normalizeOverrides(parsed);
4663
+ } catch {
4664
+ return null;
4665
+ }
4666
+ }
4667
+ function persistOverrides(overrides, storageKey = DEVTOOLS_OVERRIDES_STORAGE_KEY) {
4668
+ if ('undefined' == typeof window) return;
4669
+ try {
4670
+ if (!hasPersistedOverrides(overrides)) return void localStorage.removeItem(storageKey);
4671
+ localStorage.setItem(storageKey, JSON.stringify(overrides));
4672
+ } catch {}
4673
+ }
4674
+ function clearPersistedOverrides(storageKey = DEVTOOLS_OVERRIDES_STORAGE_KEY) {
4675
+ if ('undefined' == typeof window) return;
4676
+ try {
4677
+ localStorage.removeItem(storageKey);
4678
+ } catch {}
4679
+ }
4150
4680
  const STORAGE_KEYS = {
4151
4681
  C15T: 'c15t',
4152
4682
  PENDING_SYNC: 'c15t:pending-consent-sync',
@@ -4284,12 +4814,37 @@ var __webpack_exports__ = {};
4284
4814
  const { namespace = 'c15tStore', onConnect, onStateChange, onDisconnect } = options;
4285
4815
  let store = null;
4286
4816
  let unsubscribe = null;
4287
- let pollInterval = null;
4817
+ let reconnectTimeout = null;
4818
+ let reconnectAttempts = 0;
4819
+ let hasNotifiedDisconnect = false;
4288
4820
  const listeners = new Set();
4821
+ const INITIAL_RETRY_DELAY_MS = 100;
4822
+ const MAX_RETRY_DELAY_MS = 2000;
4823
+ const DISCONNECT_NOTIFY_ATTEMPTS = 5;
4824
+ function clearReconnectTimer() {
4825
+ if (reconnectTimeout) {
4826
+ clearTimeout(reconnectTimeout);
4827
+ reconnectTimeout = null;
4828
+ }
4829
+ }
4830
+ function resetReconnectState() {
4831
+ reconnectAttempts = 0;
4832
+ hasNotifiedDisconnect = false;
4833
+ }
4834
+ function notifyDisconnectedOnce() {
4835
+ if (hasNotifiedDisconnect) return;
4836
+ hasNotifiedDisconnect = true;
4837
+ onDisconnect?.();
4838
+ }
4289
4839
  function tryConnect() {
4290
4840
  if ('undefined' == typeof window) return false;
4291
4841
  const storeInstance = window[namespace];
4292
4842
  if (storeInstance && 'function' == typeof storeInstance.getState) {
4843
+ if (store === storeInstance && unsubscribe) return true;
4844
+ if (unsubscribe) {
4845
+ unsubscribe();
4846
+ unsubscribe = null;
4847
+ }
4293
4848
  store = storeInstance;
4294
4849
  unsubscribe = store.subscribe((state)=>{
4295
4850
  onStateChange?.(state);
@@ -4297,30 +4852,26 @@ var __webpack_exports__ = {};
4297
4852
  });
4298
4853
  const currentState = store.getState();
4299
4854
  onConnect?.(currentState, store);
4300
- if (pollInterval) {
4301
- clearInterval(pollInterval);
4302
- pollInterval = null;
4303
- }
4855
+ clearReconnectTimer();
4856
+ resetReconnectState();
4304
4857
  return true;
4305
4858
  }
4306
4859
  return false;
4307
4860
  }
4861
+ function scheduleReconnect(immediate = false) {
4862
+ if (store || reconnectTimeout) return;
4863
+ const delay = immediate ? 0 : Math.min(INITIAL_RETRY_DELAY_MS * 2 ** Math.min(reconnectAttempts, 5), MAX_RETRY_DELAY_MS);
4864
+ reconnectTimeout = setTimeout(()=>{
4865
+ reconnectTimeout = null;
4866
+ reconnectAttempts++;
4867
+ if (tryConnect()) return;
4868
+ if (reconnectAttempts >= DISCONNECT_NOTIFY_ATTEMPTS) notifyDisconnectedOnce();
4869
+ scheduleReconnect();
4870
+ }, delay);
4871
+ }
4308
4872
  function startPolling() {
4309
- if (pollInterval) return;
4310
4873
  if (tryConnect()) return;
4311
- let attempts = 0;
4312
- const maxAttempts = 50;
4313
- pollInterval = setInterval(()=>{
4314
- attempts++;
4315
- if (tryConnect()) return;
4316
- if (attempts >= maxAttempts) {
4317
- if (pollInterval) {
4318
- clearInterval(pollInterval);
4319
- pollInterval = null;
4320
- }
4321
- onDisconnect?.();
4322
- }
4323
- }, 100);
4874
+ scheduleReconnect(true);
4324
4875
  }
4325
4876
  startPolling();
4326
4877
  return {
@@ -4334,11 +4885,13 @@ var __webpack_exports__ = {};
4334
4885
  listeners.delete(listener);
4335
4886
  };
4336
4887
  },
4888
+ retryConnection: ()=>{
4889
+ if (store) return;
4890
+ resetReconnectState();
4891
+ scheduleReconnect(true);
4892
+ },
4337
4893
  destroy: ()=>{
4338
- if (pollInterval) {
4339
- clearInterval(pollInterval);
4340
- pollInterval = null;
4341
- }
4894
+ clearReconnectTimer();
4342
4895
  if (unsubscribe) {
4343
4896
  unsubscribe();
4344
4897
  unsubscribe = null;
@@ -4366,6 +4919,86 @@ var __webpack_exports__ = {};
4366
4919
  tokens_options.insertStyleElement = insertStyleElement_default();
4367
4920
  injectStylesIntoStyleTag_default()(tokens.A, tokens_options);
4368
4921
  tokens.A && tokens.A.locals && tokens.A.locals;
4922
+ const PANEL_HEIGHT_TRANSITION = 'height var(--c15t-duration-normal, 200ms) var(--c15t-easing, cubic-bezier(0.4, 0, 0.2, 1))';
4923
+ const PANEL_HEIGHT_TRANSITION_MS = 200;
4924
+ const PANEL_HEIGHT_TRANSITION_BUFFER_MS = 80;
4925
+ function normalizeOverridesForPersistence(overrides) {
4926
+ return {
4927
+ country: overrides?.country?.trim() || void 0,
4928
+ region: overrides?.region?.trim() || void 0,
4929
+ language: overrides?.language?.trim() || void 0,
4930
+ gpc: overrides?.gpc
4931
+ };
4932
+ }
4933
+ function persistedOverridesEqual(a, b) {
4934
+ return a.country === b.country && a.region === b.region && a.language === b.language && a.gpc === b.gpc;
4935
+ }
4936
+ function getBlockedRequestMessage(payload) {
4937
+ const data = payload;
4938
+ const method = 'string' == typeof data?.method ? data.method.toUpperCase() : 'REQUEST';
4939
+ const url = 'string' == typeof data?.url ? data.url : 'unknown-url';
4940
+ return `Network blocked: ${method} ${url}`;
4941
+ }
4942
+ function prefersReducedMotion() {
4943
+ return 'undefined' != typeof window && 'function' == typeof window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
4944
+ }
4945
+ function createPanelHeightAnimator() {
4946
+ let activePanel = null;
4947
+ let frameId = null;
4948
+ let timeoutId = null;
4949
+ let removeTransitionListener = null;
4950
+ function clearAnimationState() {
4951
+ if (null !== frameId) {
4952
+ window.cancelAnimationFrame(frameId);
4953
+ frameId = null;
4954
+ }
4955
+ if (null !== timeoutId) {
4956
+ clearTimeout(timeoutId);
4957
+ timeoutId = null;
4958
+ }
4959
+ if (removeTransitionListener) {
4960
+ removeTransitionListener();
4961
+ removeTransitionListener = null;
4962
+ }
4963
+ if (activePanel) {
4964
+ activePanel.style.height = '';
4965
+ activePanel.style.transition = '';
4966
+ activePanel.style.willChange = '';
4967
+ activePanel = null;
4968
+ }
4969
+ }
4970
+ function animate(panel, previousHeight) {
4971
+ if (!Number.isFinite(previousHeight) || prefersReducedMotion()) return;
4972
+ const nextHeight = panel.getBoundingClientRect().height;
4973
+ if (!Number.isFinite(nextHeight) || Math.abs(nextHeight - previousHeight) < 1) return;
4974
+ clearAnimationState();
4975
+ activePanel = panel;
4976
+ panel.style.height = `${previousHeight}px`;
4977
+ panel.style.willChange = 'height';
4978
+ panel.getBoundingClientRect();
4979
+ const handleTransitionEnd = (event)=>{
4980
+ const transitionEvent = event;
4981
+ if ('string' == typeof transitionEvent.propertyName && transitionEvent.propertyName && 'height' !== transitionEvent.propertyName) return;
4982
+ clearAnimationState();
4983
+ };
4984
+ panel.addEventListener('transitionend', handleTransitionEnd);
4985
+ removeTransitionListener = ()=>{
4986
+ panel.removeEventListener('transitionend', handleTransitionEnd);
4987
+ };
4988
+ frameId = window.requestAnimationFrame(()=>{
4989
+ frameId = null;
4990
+ panel.style.transition = PANEL_HEIGHT_TRANSITION;
4991
+ panel.style.height = `${nextHeight}px`;
4992
+ });
4993
+ timeoutId = setTimeout(()=>{
4994
+ clearAnimationState();
4995
+ }, PANEL_HEIGHT_TRANSITION_MS + PANEL_HEIGHT_TRANSITION_BUFFER_MS);
4996
+ }
4997
+ return {
4998
+ animate,
4999
+ destroy: clearAnimationState
5000
+ };
5001
+ }
4369
5002
  function createDevTools(options = {}) {
4370
5003
  const { namespace = 'c15tStore', position = 'bottom-right', defaultOpen = false } = options;
4371
5004
  const stateManager = createStateManager({
@@ -4373,6 +5006,8 @@ var __webpack_exports__ = {};
4373
5006
  isOpen: defaultOpen
4374
5007
  });
4375
5008
  let originalCallbacks = {};
5009
+ let originalNetworkBlockedCallback;
5010
+ let hasWrappedNetworkBlockerCallback = false;
4376
5011
  const storeConnector = createStoreConnector({
4377
5012
  namespace,
4378
5013
  onConnect: (state, store)=>{
@@ -4416,6 +5051,48 @@ var __webpack_exports__ = {};
4416
5051
  });
4417
5052
  if ('function' == typeof originalCallbacks.onBeforeConsentRevocationReload) originalCallbacks.onBeforeConsentRevocationReload(payload);
4418
5053
  });
5054
+ const currentNetworkBlocker = store.getState().networkBlocker;
5055
+ if (currentNetworkBlocker && !hasWrappedNetworkBlockerCallback) {
5056
+ originalNetworkBlockedCallback = currentNetworkBlocker.onRequestBlocked;
5057
+ hasWrappedNetworkBlockerCallback = true;
5058
+ store.getState().setNetworkBlocker({
5059
+ ...currentNetworkBlocker,
5060
+ onRequestBlocked: (payload)=>{
5061
+ stateManager.addEvent({
5062
+ type: 'network',
5063
+ message: getBlockedRequestMessage(payload),
5064
+ data: payload
5065
+ });
5066
+ if ('function' == typeof originalNetworkBlockedCallback) originalNetworkBlockedCallback(payload);
5067
+ }
5068
+ });
5069
+ }
5070
+ const persistedOverrides = loadPersistedOverrides();
5071
+ if (persistedOverrides) {
5072
+ const currentOverrides = normalizeOverridesForPersistence(store.getState().overrides);
5073
+ if (!persistedOverridesEqual(persistedOverrides, currentOverrides)) store.getState().setOverrides({
5074
+ country: persistedOverrides.country,
5075
+ region: persistedOverrides.region,
5076
+ language: persistedOverrides.language,
5077
+ gpc: persistedOverrides.gpc
5078
+ }).then(()=>{
5079
+ stateManager.addEvent({
5080
+ type: 'info',
5081
+ message: 'Applied persisted devtools overrides',
5082
+ data: {
5083
+ country: persistedOverrides.country,
5084
+ region: persistedOverrides.region,
5085
+ language: persistedOverrides.language,
5086
+ gpc: persistedOverrides.gpc
5087
+ }
5088
+ });
5089
+ }).catch(()=>{
5090
+ stateManager.addEvent({
5091
+ type: 'error',
5092
+ message: 'Failed to apply persisted devtools overrides'
5093
+ });
5094
+ });
5095
+ }
4419
5096
  },
4420
5097
  onDisconnect: ()=>{
4421
5098
  stateManager.setConnected(false);
@@ -4427,14 +5104,18 @@ var __webpack_exports__ = {};
4427
5104
  onStateChange: ()=>{}
4428
5105
  });
4429
5106
  let tabsInstance = null;
5107
+ const panelHeightAnimator = createPanelHeightAnimator();
4430
5108
  const panelInstance = createPanel({
4431
5109
  stateManager,
4432
5110
  storeConnector,
5111
+ namespace,
4433
5112
  onRenderContent: (container)=>{
4434
5113
  renderContent(container, stateManager, storeConnector);
4435
5114
  }
4436
5115
  });
4437
5116
  function renderContent(container, stateManager, storeConnector) {
5117
+ const panel = container.parentElement;
5118
+ const previousPanelHeight = panel?.getBoundingClientRect().height ?? 0;
4438
5119
  clearElement(container);
4439
5120
  const storeState = storeConnector.getState();
4440
5121
  const disabledTabs = [];
@@ -4516,38 +5197,46 @@ var __webpack_exports__ = {};
4516
5197
  case 'location':
4517
5198
  renderLocationPanel(panelContent, {
4518
5199
  getState: getStoreState,
4519
- onSetOverrides: async (overrides)=>{
5200
+ onApplyOverrides: async (overrides)=>{
4520
5201
  const store = storeConnector.getStore();
4521
5202
  if (store) {
4522
- const currentOverrides = store.getState().overrides || {};
4523
5203
  await store.getState().setOverrides({
4524
- ...currentOverrides,
4525
- ...overrides
5204
+ country: overrides.country,
5205
+ region: overrides.region,
5206
+ language: overrides.language,
5207
+ gpc: overrides.gpc
4526
5208
  });
4527
- stateManager.addEvent({
4528
- type: 'info',
4529
- message: 'Overrides updated',
4530
- data: overrides
5209
+ persistOverrides({
5210
+ country: overrides.country,
5211
+ region: overrides.region,
5212
+ language: overrides.language,
5213
+ gpc: overrides.gpc
4531
5214
  });
4532
- await store.getState().initConsentManager();
4533
5215
  stateManager.addEvent({
4534
5216
  type: 'info',
4535
- message: 'Consent manager re-initialized with new overrides'
5217
+ message: 'Overrides updated',
5218
+ data: {
5219
+ country: overrides.country,
5220
+ region: overrides.region,
5221
+ language: overrides.language,
5222
+ gpc: overrides.gpc
5223
+ }
4536
5224
  });
4537
5225
  }
4538
5226
  },
4539
5227
  onClearOverrides: async ()=>{
4540
5228
  const store = storeConnector.getStore();
4541
5229
  if (store) {
4542
- await store.getState().setOverrides(void 0);
4543
- stateManager.addEvent({
4544
- type: 'info',
4545
- message: 'Overrides cleared'
5230
+ await store.getState().setOverrides({
5231
+ country: void 0,
5232
+ region: void 0,
5233
+ language: void 0,
5234
+ gpc: void 0
4546
5235
  });
4547
- await store.getState().initConsentManager();
5236
+ clearPersistedOverrides();
4548
5237
  stateManager.addEvent({
4549
5238
  type: 'info',
4550
- message: 'Consent manager re-initialized'
5239
+ message: 'Overrides cleared'
4551
5240
  });
4552
5241
  }
4553
5242
  }
@@ -4555,12 +5244,85 @@ var __webpack_exports__ = {};
4555
5244
  break;
4556
5245
  case "scripts":
4557
5246
  renderScriptsPanel(panelContent, {
4558
- getState: getStoreState
5247
+ getState: getStoreState,
5248
+ getEvents: ()=>stateManager.getState().eventLog
4559
5249
  });
4560
5250
  break;
4561
5251
  case 'iab':
4562
5252
  renderIabPanel(panelContent, {
4563
5253
  getState: getStoreState,
5254
+ onSetPurposeConsent: (purposeId, value)=>{
5255
+ const iab = storeConnector.getStore()?.getState().iab;
5256
+ if (!iab) return;
5257
+ iab.setPurposeConsent(purposeId, value);
5258
+ stateManager.addEvent({
5259
+ type: 'iab',
5260
+ message: `IAB purpose ${purposeId} set to ${value}`,
5261
+ data: {
5262
+ purposeId,
5263
+ value
5264
+ }
5265
+ });
5266
+ },
5267
+ onSetVendorConsent: (vendorId, value)=>{
5268
+ const iab = storeConnector.getStore()?.getState().iab;
5269
+ if (!iab) return;
5270
+ iab.setVendorConsent(vendorId, value);
5271
+ stateManager.addEvent({
5272
+ type: 'iab',
5273
+ message: `IAB vendor ${vendorId} set to ${value}`,
5274
+ data: {
5275
+ vendorId,
5276
+ value
5277
+ }
5278
+ });
5279
+ },
5280
+ onSetSpecialFeatureOptIn: (featureId, value)=>{
5281
+ const iab = storeConnector.getStore()?.getState().iab;
5282
+ if (!iab) return;
5283
+ iab.setSpecialFeatureOptIn(featureId, value);
5284
+ stateManager.addEvent({
5285
+ type: 'iab',
5286
+ message: `IAB feature ${featureId} set to ${value}`,
5287
+ data: {
5288
+ featureId,
5289
+ value
5290
+ }
5291
+ });
5292
+ },
5293
+ onAcceptAll: ()=>{
5294
+ const iab = storeConnector.getStore()?.getState().iab;
5295
+ if (!iab) return;
5296
+ iab.acceptAll();
5297
+ stateManager.addEvent({
5298
+ type: 'iab',
5299
+ message: 'IAB accept all selected'
5300
+ });
5301
+ },
5302
+ onRejectAll: ()=>{
5303
+ const iab = storeConnector.getStore()?.getState().iab;
5304
+ if (!iab) return;
5305
+ iab.rejectAll();
5306
+ stateManager.addEvent({
5307
+ type: 'iab',
5308
+ message: 'IAB reject all selected'
5309
+ });
5310
+ },
5311
+ onSave: ()=>{
5312
+ const iab = storeConnector.getStore()?.getState().iab;
5313
+ if (!iab) return;
5314
+ iab.save().then(()=>{
5315
+ stateManager.addEvent({
5316
+ type: 'iab',
5317
+ message: 'IAB preferences saved'
5318
+ });
5319
+ }).catch((error)=>{
5320
+ stateManager.addEvent({
5321
+ type: 'error',
5322
+ message: `Failed to save IAB preferences: ${String(error)}`
5323
+ });
5324
+ });
5325
+ },
4564
5326
  onReset: async ()=>{
4565
5327
  const store = storeConnector.getStore();
4566
5328
  if (store) await resetAllConsents(store, stateManager);
@@ -4648,6 +5410,7 @@ var __webpack_exports__ = {};
4648
5410
  });
4649
5411
  break;
4650
5412
  }
5413
+ if (panel) panelHeightAnimator.animate(panel, previousPanelHeight);
4651
5414
  }
4652
5415
  storeConnector.subscribe(()=>{
4653
5416
  panelInstance.update();
@@ -4665,6 +5428,21 @@ var __webpack_exports__ = {};
4665
5428
  };
4666
5429
  },
4667
5430
  destroy: ()=>{
5431
+ const store = storeConnector.getStore();
5432
+ if (store) {
5433
+ store.getState().setCallback('onBannerFetched', originalCallbacks.onBannerFetched);
5434
+ store.getState().setCallback('onConsentSet', originalCallbacks.onConsentSet);
5435
+ store.getState().setCallback('onError', originalCallbacks.onError);
5436
+ store.getState().setCallback('onBeforeConsentRevocationReload', originalCallbacks.onBeforeConsentRevocationReload);
5437
+ if (hasWrappedNetworkBlockerCallback) {
5438
+ const currentNetworkBlocker = store.getState().networkBlocker;
5439
+ if (currentNetworkBlocker) store.getState().setNetworkBlocker({
5440
+ ...currentNetworkBlocker,
5441
+ onRequestBlocked: originalNetworkBlockedCallback
5442
+ });
5443
+ }
5444
+ }
5445
+ panelHeightAnimator.destroy();
4668
5446
  tabsInstance?.destroy();
4669
5447
  panelInstance.destroy();
4670
5448
  storeConnector.destroy();
@@ -4677,12 +5455,42 @@ var __webpack_exports__ = {};
4677
5455
  }
4678
5456
  function createDevToolsPanel(options) {
4679
5457
  const { namespace = 'c15tStore' } = options;
5458
+ let originalEmbeddedNetworkBlockedCallback;
5459
+ let hasWrappedEmbeddedNetworkBlocker = false;
4680
5460
  const stateManager = createStateManager({
4681
5461
  isOpen: true
4682
5462
  });
4683
5463
  const storeConnector = createStoreConnector({
4684
5464
  namespace,
4685
- onConnect: ()=>stateManager.setConnected(true),
5465
+ onConnect: (state, store)=>{
5466
+ stateManager.setConnected(true);
5467
+ const currentNetworkBlocker = state.networkBlocker;
5468
+ if (currentNetworkBlocker && !hasWrappedEmbeddedNetworkBlocker) {
5469
+ originalEmbeddedNetworkBlockedCallback = currentNetworkBlocker.onRequestBlocked;
5470
+ hasWrappedEmbeddedNetworkBlocker = true;
5471
+ store.getState().setNetworkBlocker({
5472
+ ...currentNetworkBlocker,
5473
+ onRequestBlocked: (payload)=>{
5474
+ stateManager.addEvent({
5475
+ type: 'network',
5476
+ message: getBlockedRequestMessage(payload),
5477
+ data: payload
5478
+ });
5479
+ if ('function' == typeof originalEmbeddedNetworkBlockedCallback) originalEmbeddedNetworkBlockedCallback(payload);
5480
+ }
5481
+ });
5482
+ }
5483
+ const persistedOverrides = loadPersistedOverrides();
5484
+ if (persistedOverrides) {
5485
+ const currentOverrides = normalizeOverridesForPersistence(state.overrides);
5486
+ if (!persistedOverridesEqual(persistedOverrides, currentOverrides)) store.getState().setOverrides({
5487
+ country: persistedOverrides.country,
5488
+ region: persistedOverrides.region,
5489
+ language: persistedOverrides.language,
5490
+ gpc: persistedOverrides.gpc
5491
+ });
5492
+ }
5493
+ },
4686
5494
  onDisconnect: ()=>stateManager.setConnected(false)
4687
5495
  });
4688
5496
  const container = renderer_div({
@@ -4731,29 +5539,61 @@ var __webpack_exports__ = {};
4731
5539
  case 'location':
4732
5540
  renderLocationPanel(contentArea, {
4733
5541
  getState: getStoreState,
4734
- onSetOverrides: async (overrides)=>{
5542
+ onApplyOverrides: async (overrides)=>{
4735
5543
  const store = storeConnector.getStore();
4736
5544
  if (store) {
4737
- const current = store.getState().overrides || {};
4738
5545
  await store.getState().setOverrides({
4739
- ...current,
4740
- ...overrides
5546
+ country: overrides.country,
5547
+ region: overrides.region,
5548
+ language: overrides.language,
5549
+ gpc: overrides.gpc
5550
+ });
5551
+ persistOverrides({
5552
+ country: overrides.country,
5553
+ region: overrides.region,
5554
+ language: overrides.language,
5555
+ gpc: overrides.gpc
4741
5556
  });
4742
5557
  }
4743
5558
  },
4744
5559
  onClearOverrides: async ()=>{
4745
- await storeConnector.getStore()?.getState().setOverrides(void 0);
5560
+ await storeConnector.getStore()?.getState().setOverrides({
5561
+ country: void 0,
5562
+ region: void 0,
5563
+ language: void 0,
5564
+ gpc: void 0
5565
+ });
5566
+ clearPersistedOverrides();
4746
5567
  }
4747
5568
  });
4748
5569
  break;
4749
5570
  case "scripts":
4750
5571
  renderScriptsPanel(contentArea, {
4751
- getState: getStoreState
5572
+ getState: getStoreState,
5573
+ getEvents: ()=>stateManager.getState().eventLog
4752
5574
  });
4753
5575
  break;
4754
5576
  case 'iab':
4755
5577
  renderIabPanel(contentArea, {
4756
5578
  getState: getStoreState,
5579
+ onSetPurposeConsent: (purposeId, value)=>{
5580
+ storeConnector.getStore()?.getState().iab?.setPurposeConsent(purposeId, value);
5581
+ },
5582
+ onSetVendorConsent: (vendorId, value)=>{
5583
+ storeConnector.getStore()?.getState().iab?.setVendorConsent(vendorId, value);
5584
+ },
5585
+ onSetSpecialFeatureOptIn: (featureId, value)=>{
5586
+ storeConnector.getStore()?.getState().iab?.setSpecialFeatureOptIn(featureId, value);
5587
+ },
5588
+ onAcceptAll: ()=>{
5589
+ storeConnector.getStore()?.getState().iab?.acceptAll();
5590
+ },
5591
+ onRejectAll: ()=>{
5592
+ storeConnector.getStore()?.getState().iab?.rejectAll();
5593
+ },
5594
+ onSave: ()=>{
5595
+ storeConnector.getStore()?.getState().iab?.save();
5596
+ },
4757
5597
  onReset: async ()=>{
4758
5598
  const store = storeConnector.getStore();
4759
5599
  if (store) await resetAllConsents(store);
@@ -4814,6 +5654,14 @@ var __webpack_exports__ = {};
4814
5654
  return {
4815
5655
  element: container,
4816
5656
  destroy: ()=>{
5657
+ const store = storeConnector.getStore();
5658
+ if (store && hasWrappedEmbeddedNetworkBlocker) {
5659
+ const currentNetworkBlocker = store.getState().networkBlocker;
5660
+ if (currentNetworkBlocker) store.getState().setNetworkBlocker({
5661
+ ...currentNetworkBlocker,
5662
+ onRequestBlocked: originalEmbeddedNetworkBlockedCallback
5663
+ });
5664
+ }
4817
5665
  unsubscribe();
4818
5666
  tabsInstance.destroy();
4819
5667
  storeConnector.destroy();