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

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 (75) hide show
  1. package/README.md +12 -1
  2. package/dist/index.cjs +346 -36
  3. package/dist/index.js +346 -36
  4. package/dist/react.cjs +346 -36
  5. package/dist/react.js +346 -36
  6. package/dist/tanstack.cjs +340 -35
  7. package/dist/tanstack.js +340 -35
  8. package/{dist → dist-types}/components/dropdown-menu.d.ts +0 -1
  9. package/{dist → dist-types}/components/index.d.ts +0 -1
  10. package/{dist → dist-types}/components/panel.d.ts +0 -1
  11. package/{dist → dist-types}/components/tabs.d.ts +0 -1
  12. package/{dist → dist-types}/components/ui.d.ts +0 -1
  13. package/{dist → dist-types}/core/debug-bundle.d.ts +1 -2
  14. package/{dist → dist-types}/core/devtools.d.ts +0 -1
  15. package/{dist → dist-types}/core/draggable.d.ts +0 -1
  16. package/{dist → dist-types}/core/index.d.ts +0 -1
  17. package/{dist → dist-types}/core/override-storage.d.ts +1 -2
  18. package/{dist → dist-types}/core/panel-renderer.d.ts +1 -2
  19. package/{dist → dist-types}/core/renderer.d.ts +0 -1
  20. package/{dist → dist-types}/core/reset-consents.d.ts +1 -2
  21. package/{dist → dist-types}/core/state-manager.d.ts +1 -2
  22. package/{dist → dist-types}/core/store-connector.d.ts +1 -2
  23. package/{dist → dist-types}/core/store-instrumentation.d.ts +1 -2
  24. package/{dist → dist-types}/index.d.ts +0 -1
  25. package/{dist → dist-types}/panels/actions.d.ts +1 -2
  26. package/{dist → dist-types}/panels/consents.d.ts +1 -2
  27. package/{dist → dist-types}/panels/dom-scanner.d.ts +1 -2
  28. package/{dist → dist-types}/panels/events.d.ts +0 -1
  29. package/{dist → dist-types}/panels/iab.d.ts +1 -2
  30. package/{dist → dist-types}/panels/index.d.ts +1 -1
  31. package/{dist → dist-types}/panels/location.d.ts +1 -2
  32. package/dist-types/panels/policy.d.ts +12 -0
  33. package/{dist → dist-types}/panels/scripts.d.ts +1 -2
  34. package/{dist → dist-types}/react.d.ts +0 -1
  35. package/{dist → dist-types}/styles/index.d.ts +0 -1
  36. package/{dist → dist-types}/tanstack.d.ts +0 -1
  37. package/{dist → dist-types}/utils/index.d.ts +1 -1
  38. package/dist-types/utils/init-source.d.ts +2 -0
  39. package/{dist → dist-types}/utils/preference-trigger.d.ts +0 -1
  40. package/dist-types/version.d.ts +1 -0
  41. package/package.json +32 -29
  42. package/CHANGELOG.md +0 -163
  43. package/dist/components/dropdown-menu.d.ts.map +0 -1
  44. package/dist/components/index.d.ts.map +0 -1
  45. package/dist/components/panel.d.ts.map +0 -1
  46. package/dist/components/tabs.d.ts.map +0 -1
  47. package/dist/components/ui.d.ts.map +0 -1
  48. package/dist/core/debug-bundle.d.ts.map +0 -1
  49. package/dist/core/devtools.d.ts.map +0 -1
  50. package/dist/core/draggable.d.ts.map +0 -1
  51. package/dist/core/index.d.ts.map +0 -1
  52. package/dist/core/override-storage.d.ts.map +0 -1
  53. package/dist/core/panel-renderer.d.ts.map +0 -1
  54. package/dist/core/renderer.d.ts.map +0 -1
  55. package/dist/core/reset-consents.d.ts.map +0 -1
  56. package/dist/core/state-manager.d.ts.map +0 -1
  57. package/dist/core/store-connector.d.ts.map +0 -1
  58. package/dist/core/store-instrumentation.d.ts.map +0 -1
  59. package/dist/index.d.ts.map +0 -1
  60. package/dist/panels/actions.d.ts.map +0 -1
  61. package/dist/panels/consents.d.ts.map +0 -1
  62. package/dist/panels/dom-scanner.d.ts.map +0 -1
  63. package/dist/panels/events.d.ts.map +0 -1
  64. package/dist/panels/iab.d.ts.map +0 -1
  65. package/dist/panels/index.d.ts.map +0 -1
  66. package/dist/panels/location.d.ts.map +0 -1
  67. package/dist/panels/scripts.d.ts.map +0 -1
  68. package/dist/react.d.ts.map +0 -1
  69. package/dist/styles/index.d.ts.map +0 -1
  70. package/dist/tanstack.d.ts.map +0 -1
  71. package/dist/utils/index.d.ts.map +0 -1
  72. package/dist/utils/preference-trigger.d.ts.map +0 -1
  73. package/dist/version.d.ts +0 -2
  74. package/dist/version.d.ts.map +0 -1
  75. package/tsconfig.json +0 -20
package/README.md CHANGED
@@ -5,7 +5,8 @@ Developer tools for debugging and inspecting c15t consent management state.
5
5
  ## Features
6
6
 
7
7
  - **Consents Panel**: View and toggle consent states in real-time
8
- - **Location Panel**: Inspect detected location and set overrides
8
+ - **Location Panel**: Inspect location and apply geo/language overrides
9
+ - **Policy Panel**: Inspect detailed runtime policy-pack decision data
9
10
  - **Scripts Panel**: Monitor script loading status
10
11
  - **Actions Panel**: Quick actions for testing consent flows
11
12
  - **Framework Agnostic**: Pure JavaScript core with React wrapper
@@ -119,10 +120,20 @@ window.__c15tDevTools.getState();
119
120
  ### Location
120
121
 
121
122
  - Detected country, region, and jurisdiction
123
+ - Compact active policy summary (policy ID + matcher + snapshot status)
122
124
  - Set country, region, and language overrides
123
125
  - View active consent model with description
124
126
  - Clear all overrides
125
127
 
128
+ ### Policy
129
+
130
+ - Runtime policy decision details from `/init`
131
+ - Policy ID, match strategy, and fingerprint
132
+ - Consent model, scope mode, purpose scope
133
+ - UI constraints (mode, allowed actions, primary action)
134
+ - i18n profile, expiry, and proof-capture summary
135
+ - Snapshot token presence indicator
136
+
126
137
  ### Scripts
127
138
 
128
139
  - List configured scripts with consent requirements
package/dist/index.cjs CHANGED
@@ -2206,6 +2206,27 @@ var __webpack_exports__ = {};
2206
2206
  panel_module_options.insertStyleElement = insertStyleElement_default();
2207
2207
  injectStylesIntoStyleTag_default()(panel_module.A, panel_module_options);
2208
2208
  const styles_panel_module = panel_module.A && panel_module.A.locals ? panel_module.A.locals : void 0;
2209
+ function formatInitSource(source, detail) {
2210
+ const label = (()=>{
2211
+ switch(source){
2212
+ case 'ssr':
2213
+ return 'SSR Prefetch';
2214
+ case 'backend':
2215
+ return 'Backend';
2216
+ case 'backend-cache-hit':
2217
+ return 'Backend (Cache Hit)';
2218
+ case 'offline-fallback':
2219
+ return 'Offline Fallback';
2220
+ case 'offline-mode':
2221
+ return 'Offline Mode';
2222
+ case 'custom':
2223
+ return 'Custom Client';
2224
+ default:
2225
+ return '—';
2226
+ }
2227
+ })();
2228
+ return detail ? `${label} [${detail}]` : label;
2229
+ }
2209
2230
  const PREFERENCE_TRIGGER_SELECTORS = '[data-c15t-trigger], [aria-label*="privacy settings" i], [aria-label*="preference" i]';
2210
2231
  const PREVIOUS_DISPLAY_ATTR = 'data-c15t-devtools-prev-display';
2211
2232
  function detectPreferenceTrigger() {
@@ -2242,7 +2263,7 @@ var __webpack_exports__ = {};
2242
2263
  }
2243
2264
  return null;
2244
2265
  }
2245
- const version = '2.0.0-rc.4';
2266
+ const version = '2.0.0-rc.5';
2246
2267
  const DEVTOOLS_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 446 445" aria-label="c15t">
2247
2268
  <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"/>
2248
2269
  </svg>`;
@@ -2626,6 +2647,7 @@ var __webpack_exports__ = {};
2626
2647
  const storeState = storeConnector.getState();
2627
2648
  const isLoading = storeState?.isLoadingConsentInfo ?? false;
2628
2649
  const diagnostics = storeConnector.getDiagnostics();
2650
+ const initSource = formatInitSource(storeState?.initDataSource ?? null, storeState?.initDataSourceDetail ?? null);
2629
2651
  const statusChildren = [
2630
2652
  renderer_span({
2631
2653
  className: `${styles_panel_module.statusDot} ${isConnected ? styles_panel_module.statusConnected : styles_panel_module.statusDisconnected}`
@@ -2642,6 +2664,10 @@ var __webpack_exports__ = {};
2642
2664
  className: styles_panel_module.footerMeta,
2643
2665
  text: `· ${diagnostics.namespace} · retry ${diagnostics.reconnectAttempts} · next ${formatRetryDelay(diagnostics.nextRetryInMs)}`
2644
2666
  }));
2667
+ if (isConnected) statusChildren.push(renderer_span({
2668
+ className: styles_panel_module.footerMeta,
2669
+ text: `· Init: ${initSource}`
2670
+ }));
2645
2671
  footerElement.appendChild(renderer_div({
2646
2672
  className: styles_panel_module.footerStatus,
2647
2673
  children: statusChildren
@@ -2808,6 +2834,11 @@ var __webpack_exports__ = {};
2808
2834
  <circle cx="12" cy="12" r="10"></circle>
2809
2835
  <line x1="2" y1="12" x2="22" y2="12"></line>
2810
2836
  <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
2837
+ </svg>`;
2838
+ const POLICY_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">
2839
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
2840
+ <path d="M9 12h6"></path>
2841
+ <path d="M12 9v6"></path>
2811
2842
  </svg>`;
2812
2843
  const SCRIPTS_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">
2813
2844
  <polyline points="16 18 22 12 16 6"></polyline>
@@ -2836,6 +2867,11 @@ var __webpack_exports__ = {};
2836
2867
  label: 'Location',
2837
2868
  icon: LOCATION_ICON
2838
2869
  },
2870
+ {
2871
+ id: 'policy',
2872
+ label: 'Policy',
2873
+ icon: POLICY_ICON
2874
+ },
2839
2875
  {
2840
2876
  id: 'consents',
2841
2877
  label: 'Consents',
@@ -2996,6 +3032,7 @@ var __webpack_exports__ = {};
2996
3032
  const forcedOverflowTab = showOverflowSecondTabInStrip ? preferredSecondTab : overflowSecondTab;
2997
3033
  const layoutTabIds = [
2998
3034
  'location',
3035
+ 'policy',
2999
3036
  stripSecondTab,
3000
3037
  "scripts",
3001
3038
  'actions',
@@ -3660,7 +3697,6 @@ var __webpack_exports__ = {};
3660
3697
  function getNamespace(state) {
3661
3698
  return state.config?.meta?.namespace || 'c15tStore';
3662
3699
  }
3663
- const consentSearchByContainer = new WeakMap();
3664
3700
  function renderConsentsPanel(container, options) {
3665
3701
  const { getState, onConsentChange, onSave, onAcceptAll, onRejectAll, onReset } = options;
3666
3702
  clearElement(container);
@@ -3679,40 +3715,15 @@ var __webpack_exports__ = {};
3679
3715
  ct.name,
3680
3716
  ct
3681
3717
  ]));
3682
- const searchQuery = consentSearchByContainer.get(container) ?? '';
3683
3718
  const consentEntries = Object.entries(displayConsents);
3684
- const filteredConsentEntries = consentEntries.filter(([name])=>{
3685
- if (!searchQuery) return true;
3686
- const consentType = consentTypeMap.get(name);
3687
- const displayName = consentType?.name || name;
3688
- return `${name} ${displayName}`.toLowerCase().includes(searchQuery);
3689
- });
3690
- const showSearchInput = consentEntries.length > 4;
3691
- if (showSearchInput) container.appendChild(renderer_div({
3692
- style: {
3693
- padding: '8px 0 10px'
3694
- },
3695
- children: [
3696
- createInput({
3697
- value: searchQuery,
3698
- placeholder: 'Filter consents…',
3699
- ariaLabel: 'Filter consents',
3700
- small: true,
3701
- onInput: (value)=>{
3702
- consentSearchByContainer.set(container, value.trim().toLowerCase());
3703
- renderConsentsPanel(container, options);
3704
- }
3705
- })
3706
- ]
3707
- }));
3708
- if (0 === filteredConsentEntries.length) container.appendChild(renderer_div({
3719
+ if (0 === consentEntries.length) container.appendChild(renderer_div({
3709
3720
  style: {
3710
3721
  padding: '24px',
3711
3722
  textAlign: 'center',
3712
3723
  color: 'var(--c15t-devtools-text-muted)',
3713
3724
  fontSize: 'var(--c15t-devtools-font-size-sm)'
3714
3725
  },
3715
- text: 0 === consentEntries.length ? 'No consents configured' : 'No matching consents'
3726
+ text: 'No consents configured'
3716
3727
  }));
3717
3728
  else {
3718
3729
  if (isIabMode) {
@@ -3730,7 +3741,7 @@ var __webpack_exports__ = {};
3730
3741
  container.appendChild(iabNotice);
3731
3742
  }
3732
3743
  const gridCards = [];
3733
- for (const [name, value] of filteredConsentEntries){
3744
+ for (const [name, value] of consentEntries){
3734
3745
  const consentType = consentTypeMap.get(name);
3735
3746
  const isNecessary = 'necessary' === name;
3736
3747
  const displayName = consentType?.name || name;
@@ -4489,11 +4500,15 @@ var __webpack_exports__ = {};
4489
4500
  const locationInfo = state.locationInfo;
4490
4501
  const overrides = state.overrides;
4491
4502
  const translationConfig = state.translationConfig;
4503
+ const initData = state.lastBannerFetchData;
4504
+ const activePolicy = initData?.policy;
4505
+ const policyDecision = initData?.policyDecision;
4506
+ const initSource = formatInitSource(state.initDataSource, state.initDataSourceDetail);
4492
4507
  const gridItems = [
4493
4508
  createCompactInfoCard('Country', locationInfo?.countryCode || '—'),
4494
4509
  createCompactInfoCard('Region', locationInfo?.regionCode || '—'),
4495
- createCompactInfoCard('Jurisdiction', locationInfo?.jurisdiction || '—'),
4496
- createCompactInfoCard('Language', translationConfig?.defaultLanguage || '—')
4510
+ createCompactInfoCard('Language', translationConfig?.defaultLanguage || '—'),
4511
+ createCompactInfoCard('Init Source', initSource)
4497
4512
  ];
4498
4513
  gridItems.push(createCompactInfoCard('GPC', getEffectiveGpcLabel(overrides?.gpc)));
4499
4514
  if (state.model) gridItems.push(createCompactInfoCard('Model', getModelLabel(state.model)));
@@ -4501,7 +4516,6 @@ var __webpack_exports__ = {};
4501
4516
  columns: 3,
4502
4517
  children: gridItems
4503
4518
  });
4504
- container.appendChild(locationGrid);
4505
4519
  const initialDraft = getDraftFromOverrides(overrides);
4506
4520
  let appliedOverrides = normalizeOverrideDraft(initialDraft);
4507
4521
  let isSubmitting = false;
@@ -4592,6 +4606,12 @@ var __webpack_exports__ = {};
4592
4606
  ]
4593
4607
  });
4594
4608
  container.appendChild(overrideSection);
4609
+ container.appendChild(locationGrid);
4610
+ container.appendChild(createActivePolicySummarySection({
4611
+ policy: activePolicy,
4612
+ policyDecision,
4613
+ policySnapshotToken: initData?.policySnapshotToken
4614
+ }));
4595
4615
  countryField.control.addEventListener('change', updateFormState);
4596
4616
  regionField.control.addEventListener('input', updateFormState);
4597
4617
  languageField.control.addEventListener('input', updateFormState);
@@ -4880,15 +4900,53 @@ var __webpack_exports__ = {};
4880
4900
  return 'None';
4881
4901
  }
4882
4902
  }
4903
+ function createActivePolicySummarySection(options) {
4904
+ const { policy, policyDecision, policySnapshotToken } = options;
4905
+ if (!policy && !policyDecision) return createSection({
4906
+ title: 'Active Policy',
4907
+ children: [
4908
+ renderer_div({
4909
+ style: {
4910
+ padding: '10px 12px',
4911
+ fontSize: 'var(--c15t-devtools-font-size-sm)',
4912
+ color: 'var(--c15t-text-muted)'
4913
+ },
4914
+ text: 'No active policy matched.'
4915
+ })
4916
+ ]
4917
+ });
4918
+ const cards = [
4919
+ createCompactInfoCard('Policy ID', policy?.id ?? policyDecision?.policyId ?? '—'),
4920
+ createCompactInfoCard('Matched By', policyDecision?.matchedBy ?? '—'),
4921
+ createCompactInfoCard('Snapshot Token', policySnapshotToken ? 'present' : 'missing')
4922
+ ];
4923
+ return createSection({
4924
+ title: 'Active Policy',
4925
+ children: [
4926
+ renderer_div({
4927
+ style: {
4928
+ display: 'grid',
4929
+ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
4930
+ gap: 'var(--c15t-space-sm, 0.5rem)'
4931
+ },
4932
+ children: cards
4933
+ }),
4934
+ renderer_span({
4935
+ className: styles_components_module.overrideHint,
4936
+ text: 'Open the Policy tab for full policy-pack diagnostics.'
4937
+ })
4938
+ ]
4939
+ });
4940
+ }
4883
4941
  function createCompactInfoCard(label, value) {
4884
4942
  return renderer_div({
4885
4943
  className: styles_components_module.gridCard ?? '',
4886
4944
  style: {
4887
- padding: '6px 8px',
4945
+ padding: '8px 10px',
4888
4946
  minHeight: 'auto',
4889
4947
  flexDirection: 'column',
4890
4948
  alignItems: 'flex-start',
4891
- gap: '1px'
4949
+ gap: '2px'
4892
4950
  },
4893
4951
  children: [
4894
4952
  renderer_span({
@@ -4909,6 +4967,253 @@ var __webpack_exports__ = {};
4909
4967
  ]
4910
4968
  });
4911
4969
  }
4970
+ function renderPolicyPanel(container, options) {
4971
+ const { getState } = options;
4972
+ clearElement(container);
4973
+ const state = getState();
4974
+ if (!state) return void container.appendChild(createDisconnectedState());
4975
+ const initData = state.lastBannerFetchData;
4976
+ const activePolicy = initData?.policy;
4977
+ const policyDecision = initData?.policyDecision;
4978
+ const initSource = formatInitSource(state.initDataSource, state.initDataSourceDetail);
4979
+ container.appendChild(createMatchTraceSection({
4980
+ policyDecision,
4981
+ policyId: activePolicy?.id ?? policyDecision?.policyId
4982
+ }));
4983
+ if (!activePolicy && !policyDecision) return void container.appendChild(createSection({
4984
+ title: 'Policy',
4985
+ children: [
4986
+ renderer_div({
4987
+ style: {
4988
+ padding: '10px 12px',
4989
+ fontSize: 'var(--c15t-devtools-font-size-sm)',
4990
+ color: 'var(--c15t-text-muted)'
4991
+ },
4992
+ text: 'No active policy matched for this request.'
4993
+ }),
4994
+ createHint(`Init Source: ${initSource}`)
4995
+ ]
4996
+ }));
4997
+ container.appendChild(createSection({
4998
+ title: 'Policy',
4999
+ children: [
5000
+ policy_createGrid(3, [
5001
+ createCard('ID', activePolicy?.id ?? policyDecision?.policyId ?? '—'),
5002
+ createCard('Model', policy_getModelLabel(activePolicy?.model)),
5003
+ createCard('Scope', getScopeModeLabel(activePolicy?.consent?.scopeMode ?? state.policyScopeMode)),
5004
+ createCard('Categories', formatList(state.policyCategories ?? activePolicy?.consent?.categories)),
5005
+ createCard('Preselected', formatList(activePolicy?.consent?.preselectedCategories)),
5006
+ createCard('Expiry', 'number' == typeof activePolicy?.consent?.expiryDays ? `${activePolicy.consent.expiryDays}d` : '—')
5007
+ ]),
5008
+ createHint(`${initSource} · ${formatFingerprint(policyDecision?.fingerprint)}`)
5009
+ ]
5010
+ }));
5011
+ const uiMode = activePolicy?.ui?.mode;
5012
+ if (uiMode && 'none' !== uiMode) {
5013
+ const bannerCards = buildSurfaceCards('Banner', activePolicy?.ui?.banner, state.policyBanner);
5014
+ const dialogCards = buildSurfaceCards('Dialog', activePolicy?.ui?.dialog, state.policyDialog);
5015
+ if (bannerCards.length > 0 || dialogCards.length > 0) container.appendChild(createSection({
5016
+ title: `UI · ${uiMode}`,
5017
+ children: [
5018
+ policy_createGrid(3, [
5019
+ ...bannerCards,
5020
+ ...dialogCards
5021
+ ])
5022
+ ]
5023
+ }));
5024
+ }
5025
+ const proofLabel = formatProofSummary(activePolicy?.proof);
5026
+ const snapshotLabel = initData?.policySnapshotToken ? 'present' : 'missing';
5027
+ container.appendChild(createSection({
5028
+ title: 'Proof & Snapshot',
5029
+ children: [
5030
+ policy_createGrid(3, [
5031
+ createCard('Proof', proofLabel),
5032
+ createCard('Snapshot', snapshotLabel),
5033
+ createCard('I18n', activePolicy?.i18n?.messageProfile ?? activePolicy?.i18n?.language ?? '—')
5034
+ ])
5035
+ ]
5036
+ }));
5037
+ }
5038
+ function buildSurfaceCards(prefix, policySurface, storeSurface) {
5039
+ const policyLayout = Array.isArray(policySurface?.layout) && 0 === policySurface.layout.length ? null : policySurface?.layout ?? null;
5040
+ const storeLayout = Array.isArray(storeSurface.layout) && 0 === storeSurface.layout.length ? null : storeSurface.layout ?? null;
5041
+ const actions = formatList(policySurface?.allowedActions ?? storeSurface.allowedActions);
5042
+ const primary = policySurface?.primaryAction ?? storeSurface.primaryAction ?? null;
5043
+ const layout = policyLayout ?? storeLayout;
5044
+ const direction = policySurface?.direction ?? storeSurface.direction ?? null;
5045
+ const profile = policySurface?.uiProfile ?? storeSurface.uiProfile ?? null;
5046
+ const scrollLock = policySurface?.scrollLock ?? storeSurface.scrollLock ?? null;
5047
+ if ('—' === actions && !primary && !layout && !direction && !profile && null === scrollLock) return [];
5048
+ const cards = [
5049
+ createCard(`${prefix} Actions`, actions)
5050
+ ];
5051
+ if (primary) cards.push(createCard(`${prefix} Primary`, primary));
5052
+ if (layout) cards.push(createCard(`${prefix} Layout`, Array.isArray(layout) ? layout.map((group)=>Array.isArray(group) ? `[${group.join(', ')}]` : group).join(' / ') : layout));
5053
+ if (direction) cards.push(createCard(`${prefix} Direction`, direction));
5054
+ if (profile) cards.push(createCard(`${prefix} Profile`, profile));
5055
+ if (null !== scrollLock) cards.push(createCard(`${prefix} Scroll Lock`, scrollLock ? 'on' : 'off'));
5056
+ return cards;
5057
+ }
5058
+ function createMatchTraceSection(options) {
5059
+ const { policyDecision, policyId } = options;
5060
+ const entries = buildTraceEntries(policyDecision, policyId);
5061
+ return createSection({
5062
+ title: 'Match Trace',
5063
+ children: [
5064
+ renderer_div({
5065
+ style: {
5066
+ display: 'grid',
5067
+ gridTemplateColumns: '1fr',
5068
+ gap: '4px'
5069
+ },
5070
+ children: entries.map((entry)=>renderer_div({
5071
+ className: styles_components_module.gridCard ?? '',
5072
+ style: {
5073
+ padding: '6px 10px',
5074
+ display: 'flex',
5075
+ alignItems: 'center',
5076
+ justifyContent: 'space-between',
5077
+ gap: '10px'
5078
+ },
5079
+ children: [
5080
+ renderer_span({
5081
+ style: {
5082
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
5083
+ color: 'var(--c15t-text-muted)',
5084
+ fontFamily: 'ui-monospace, monospace'
5085
+ },
5086
+ text: entry.step
5087
+ }),
5088
+ renderer_span({
5089
+ style: {
5090
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
5091
+ fontFamily: 'ui-monospace, monospace'
5092
+ },
5093
+ text: entry.result
5094
+ })
5095
+ ]
5096
+ }))
5097
+ }),
5098
+ createHint('region → country → default · fallback on geo failure · Simulate via Location tab')
5099
+ ]
5100
+ });
5101
+ }
5102
+ function buildTraceEntries(decision, policyId) {
5103
+ if (!decision) return [
5104
+ {
5105
+ step: 'decision metadata',
5106
+ result: 'UNAVAILABLE'
5107
+ }
5108
+ ];
5109
+ const country = decision.country ?? 'n/a';
5110
+ const regionKey = decision.country && decision.region ? `${decision.country}-${decision.region}` : 'n/a';
5111
+ const resolved = policyId ?? decision.policyId ?? 'unknown';
5112
+ const matched = decision.matchedBy;
5113
+ return [
5114
+ {
5115
+ step: `region(${regionKey})`,
5116
+ result: 'region' === matched ? `MATCH → ${resolved}` : 'MISS'
5117
+ },
5118
+ {
5119
+ step: `country(${country})`,
5120
+ result: 'country' === matched ? `MATCH → ${resolved}` : 'region' === matched ? 'SKIPPED' : 'MISS'
5121
+ },
5122
+ {
5123
+ step: 'fallback(geo-fail)',
5124
+ result: 'fallback' === matched ? `MATCH → ${resolved}` : 'SKIPPED'
5125
+ },
5126
+ {
5127
+ step: 'default(catch-all)',
5128
+ result: 'default' === matched ? `MATCH → ${resolved}` : 'SKIPPED'
5129
+ }
5130
+ ];
5131
+ }
5132
+ function policy_getModelLabel(model) {
5133
+ switch(model){
5134
+ case 'opt-in':
5135
+ return 'Opt-In';
5136
+ case 'opt-out':
5137
+ return 'Opt-Out';
5138
+ case 'iab':
5139
+ return 'IAB TCF';
5140
+ default:
5141
+ return 'None';
5142
+ }
5143
+ }
5144
+ function getScopeModeLabel(mode) {
5145
+ switch(mode){
5146
+ case 'strict':
5147
+ return 'Strict';
5148
+ case 'permissive':
5149
+ return 'Permissive';
5150
+ default:
5151
+ return '—';
5152
+ }
5153
+ }
5154
+ function formatList(items) {
5155
+ if (!items || 0 === items.length) return '—';
5156
+ if (items.includes('*')) return '* (all)';
5157
+ return items.join(', ');
5158
+ }
5159
+ function formatProofSummary(proof) {
5160
+ if (!proof) return '—';
5161
+ const parts = [];
5162
+ if (proof.storeIp) parts.push('IP');
5163
+ if (proof.storeUserAgent) parts.push('UA');
5164
+ if (proof.storeLanguage) parts.push('Lang');
5165
+ return parts.length > 0 ? parts.join(', ') : 'none';
5166
+ }
5167
+ function formatFingerprint(fingerprint) {
5168
+ if (!fingerprint) return 'no fingerprint';
5169
+ if (fingerprint.length <= 12) return fingerprint;
5170
+ return `${fingerprint.slice(0, 8)}…${fingerprint.slice(-4)}`;
5171
+ }
5172
+ function createCard(label, value) {
5173
+ return renderer_div({
5174
+ className: styles_components_module.gridCard ?? '',
5175
+ style: {
5176
+ padding: '8px 10px',
5177
+ minHeight: 'auto',
5178
+ flexDirection: 'column',
5179
+ alignItems: 'flex-start',
5180
+ gap: '2px'
5181
+ },
5182
+ children: [
5183
+ renderer_span({
5184
+ style: {
5185
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
5186
+ color: 'var(--c15t-text-muted)'
5187
+ },
5188
+ text: label
5189
+ }),
5190
+ renderer_span({
5191
+ style: {
5192
+ fontSize: 'var(--c15t-font-size-sm)',
5193
+ fontWeight: '500',
5194
+ fontFamily: 'ui-monospace, monospace'
5195
+ },
5196
+ text: value
5197
+ })
5198
+ ]
5199
+ });
5200
+ }
5201
+ function policy_createGrid(columns, children) {
5202
+ return renderer_div({
5203
+ style: {
5204
+ display: 'grid',
5205
+ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
5206
+ gap: 'var(--c15t-space-sm, 0.5rem)'
5207
+ },
5208
+ children
5209
+ });
5210
+ }
5211
+ function createHint(text) {
5212
+ return renderer_span({
5213
+ className: styles_components_module.overrideHint,
5214
+ text
5215
+ });
5216
+ }
4912
5217
  const dismissedResources = new Set();
4913
5218
  function scanDOM(state) {
4914
5219
  const results = [];
@@ -5547,6 +5852,11 @@ var __webpack_exports__ = {};
5547
5852
  }
5548
5853
  });
5549
5854
  break;
5855
+ case 'policy':
5856
+ renderPolicyPanel(container, {
5857
+ getState: getStoreState
5858
+ });
5859
+ break;
5550
5860
  case "scripts":
5551
5861
  renderScriptsPanel(container, {
5552
5862
  getState: getStoreState,
@@ -5683,7 +5993,7 @@ var __webpack_exports__ = {};
5683
5993
  } catch {}
5684
5994
  }
5685
5995
  function isDevToolsTab(value) {
5686
- return 'consents' === value || 'location' === value || "scripts" === value || 'iab' === value || 'events' === value || 'actions' === value;
5996
+ return 'consents' === value || 'location' === value || 'policy' === value || "scripts" === value || 'iab' === value || 'events' === value || 'actions' === value;
5687
5997
  }
5688
5998
  function loadPersistedActiveTab() {
5689
5999
  if ('undefined' == typeof window) return null;