@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/dist/react.js CHANGED
@@ -2187,6 +2187,27 @@ panel_module_options.domAPI = styleDomAPI_default();
2187
2187
  panel_module_options.insertStyleElement = insertStyleElement_default();
2188
2188
  injectStylesIntoStyleTag_default()(panel_module.A, panel_module_options);
2189
2189
  const styles_panel_module = panel_module.A && panel_module.A.locals ? panel_module.A.locals : void 0;
2190
+ function formatInitSource(source, detail) {
2191
+ const label = (()=>{
2192
+ switch(source){
2193
+ case 'ssr':
2194
+ return 'SSR Prefetch';
2195
+ case 'backend':
2196
+ return 'Backend';
2197
+ case 'backend-cache-hit':
2198
+ return 'Backend (Cache Hit)';
2199
+ case 'offline-fallback':
2200
+ return 'Offline Fallback';
2201
+ case 'offline-mode':
2202
+ return 'Offline Mode';
2203
+ case 'custom':
2204
+ return 'Custom Client';
2205
+ default:
2206
+ return '—';
2207
+ }
2208
+ })();
2209
+ return detail ? `${label} [${detail}]` : label;
2210
+ }
2190
2211
  const PREFERENCE_TRIGGER_SELECTORS = '[data-c15t-trigger], [aria-label*="privacy settings" i], [aria-label*="preference" i]';
2191
2212
  const PREVIOUS_DISPLAY_ATTR = 'data-c15t-devtools-prev-display';
2192
2213
  function detectPreferenceTrigger() {
@@ -2223,7 +2244,7 @@ function getPreferenceCenterOpener(namespace = 'c15tStore') {
2223
2244
  }
2224
2245
  return null;
2225
2246
  }
2226
- const version = '2.0.0-rc.4';
2247
+ const version = '2.0.0-rc.5';
2227
2248
  const DEVTOOLS_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 446 445" aria-label="c15t">
2228
2249
  <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"/>
2229
2250
  </svg>`;
@@ -2607,6 +2628,7 @@ function createPanel(options) {
2607
2628
  const storeState = storeConnector.getState();
2608
2629
  const isLoading = storeState?.isLoadingConsentInfo ?? false;
2609
2630
  const diagnostics = storeConnector.getDiagnostics();
2631
+ const initSource = formatInitSource(storeState?.initDataSource ?? null, storeState?.initDataSourceDetail ?? null);
2610
2632
  const statusChildren = [
2611
2633
  renderer_span({
2612
2634
  className: `${styles_panel_module.statusDot} ${isConnected ? styles_panel_module.statusConnected : styles_panel_module.statusDisconnected}`
@@ -2623,6 +2645,10 @@ function createPanel(options) {
2623
2645
  className: styles_panel_module.footerMeta,
2624
2646
  text: `· ${diagnostics.namespace} · retry ${diagnostics.reconnectAttempts} · next ${formatRetryDelay(diagnostics.nextRetryInMs)}`
2625
2647
  }));
2648
+ if (isConnected) statusChildren.push(renderer_span({
2649
+ className: styles_panel_module.footerMeta,
2650
+ text: `· Init: ${initSource}`
2651
+ }));
2626
2652
  footerElement.appendChild(renderer_div({
2627
2653
  className: styles_panel_module.footerStatus,
2628
2654
  children: statusChildren
@@ -2790,6 +2816,11 @@ const LOCATION_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 2
2790
2816
  <line x1="2" y1="12" x2="22" y2="12"></line>
2791
2817
  <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>
2792
2818
  </svg>`;
2819
+ 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">
2820
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
2821
+ <path d="M9 12h6"></path>
2822
+ <path d="M12 9v6"></path>
2823
+ </svg>`;
2793
2824
  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">
2794
2825
  <polyline points="16 18 22 12 16 6"></polyline>
2795
2826
  <polyline points="8 6 2 12 8 18"></polyline>
@@ -2817,6 +2848,11 @@ const TABS = [
2817
2848
  label: 'Location',
2818
2849
  icon: LOCATION_ICON
2819
2850
  },
2851
+ {
2852
+ id: 'policy',
2853
+ label: 'Policy',
2854
+ icon: POLICY_ICON
2855
+ },
2820
2856
  {
2821
2857
  id: 'consents',
2822
2858
  label: 'Consents',
@@ -2977,6 +3013,7 @@ function tabs_createTabs(options) {
2977
3013
  const forcedOverflowTab = showOverflowSecondTabInStrip ? preferredSecondTab : overflowSecondTab;
2978
3014
  const layoutTabIds = [
2979
3015
  'location',
3016
+ 'policy',
2980
3017
  stripSecondTab,
2981
3018
  "scripts",
2982
3019
  'actions',
@@ -3641,7 +3678,6 @@ function createIconWrapper(icon, size) {
3641
3678
  function getNamespace(state) {
3642
3679
  return state.config?.meta?.namespace || 'c15tStore';
3643
3680
  }
3644
- const consentSearchByContainer = new WeakMap();
3645
3681
  function renderConsentsPanel(container, options) {
3646
3682
  const { getState, onConsentChange, onSave, onAcceptAll, onRejectAll, onReset } = options;
3647
3683
  clearElement(container);
@@ -3660,40 +3696,15 @@ function renderConsentsPanel(container, options) {
3660
3696
  ct.name,
3661
3697
  ct
3662
3698
  ]));
3663
- const searchQuery = consentSearchByContainer.get(container) ?? '';
3664
3699
  const consentEntries = Object.entries(displayConsents);
3665
- const filteredConsentEntries = consentEntries.filter(([name])=>{
3666
- if (!searchQuery) return true;
3667
- const consentType = consentTypeMap.get(name);
3668
- const displayName = consentType?.name || name;
3669
- return `${name} ${displayName}`.toLowerCase().includes(searchQuery);
3670
- });
3671
- const showSearchInput = consentEntries.length > 4;
3672
- if (showSearchInput) container.appendChild(renderer_div({
3673
- style: {
3674
- padding: '8px 0 10px'
3675
- },
3676
- children: [
3677
- createInput({
3678
- value: searchQuery,
3679
- placeholder: 'Filter consents…',
3680
- ariaLabel: 'Filter consents',
3681
- small: true,
3682
- onInput: (value)=>{
3683
- consentSearchByContainer.set(container, value.trim().toLowerCase());
3684
- renderConsentsPanel(container, options);
3685
- }
3686
- })
3687
- ]
3688
- }));
3689
- if (0 === filteredConsentEntries.length) container.appendChild(renderer_div({
3700
+ if (0 === consentEntries.length) container.appendChild(renderer_div({
3690
3701
  style: {
3691
3702
  padding: '24px',
3692
3703
  textAlign: 'center',
3693
3704
  color: 'var(--c15t-devtools-text-muted)',
3694
3705
  fontSize: 'var(--c15t-devtools-font-size-sm)'
3695
3706
  },
3696
- text: 0 === consentEntries.length ? 'No consents configured' : 'No matching consents'
3707
+ text: 'No consents configured'
3697
3708
  }));
3698
3709
  else {
3699
3710
  if (isIabMode) {
@@ -3711,7 +3722,7 @@ function renderConsentsPanel(container, options) {
3711
3722
  container.appendChild(iabNotice);
3712
3723
  }
3713
3724
  const gridCards = [];
3714
- for (const [name, value] of filteredConsentEntries){
3725
+ for (const [name, value] of consentEntries){
3715
3726
  const consentType = consentTypeMap.get(name);
3716
3727
  const isNecessary = 'necessary' === name;
3717
3728
  const displayName = consentType?.name || name;
@@ -4470,11 +4481,15 @@ function renderLocationPanel(container, options) {
4470
4481
  const locationInfo = state.locationInfo;
4471
4482
  const overrides = state.overrides;
4472
4483
  const translationConfig = state.translationConfig;
4484
+ const initData = state.lastBannerFetchData;
4485
+ const activePolicy = initData?.policy;
4486
+ const policyDecision = initData?.policyDecision;
4487
+ const initSource = formatInitSource(state.initDataSource, state.initDataSourceDetail);
4473
4488
  const gridItems = [
4474
4489
  createCompactInfoCard('Country', locationInfo?.countryCode || '—'),
4475
4490
  createCompactInfoCard('Region', locationInfo?.regionCode || '—'),
4476
- createCompactInfoCard('Jurisdiction', locationInfo?.jurisdiction || '—'),
4477
- createCompactInfoCard('Language', translationConfig?.defaultLanguage || '—')
4491
+ createCompactInfoCard('Language', translationConfig?.defaultLanguage || '—'),
4492
+ createCompactInfoCard('Init Source', initSource)
4478
4493
  ];
4479
4494
  gridItems.push(createCompactInfoCard('GPC', getEffectiveGpcLabel(overrides?.gpc)));
4480
4495
  if (state.model) gridItems.push(createCompactInfoCard('Model', getModelLabel(state.model)));
@@ -4482,7 +4497,6 @@ function renderLocationPanel(container, options) {
4482
4497
  columns: 3,
4483
4498
  children: gridItems
4484
4499
  });
4485
- container.appendChild(locationGrid);
4486
4500
  const initialDraft = getDraftFromOverrides(overrides);
4487
4501
  let appliedOverrides = normalizeOverrideDraft(initialDraft);
4488
4502
  let isSubmitting = false;
@@ -4573,6 +4587,12 @@ function renderLocationPanel(container, options) {
4573
4587
  ]
4574
4588
  });
4575
4589
  container.appendChild(overrideSection);
4590
+ container.appendChild(locationGrid);
4591
+ container.appendChild(createActivePolicySummarySection({
4592
+ policy: activePolicy,
4593
+ policyDecision,
4594
+ policySnapshotToken: initData?.policySnapshotToken
4595
+ }));
4576
4596
  countryField.control.addEventListener('change', updateFormState);
4577
4597
  regionField.control.addEventListener('input', updateFormState);
4578
4598
  languageField.control.addEventListener('input', updateFormState);
@@ -4861,15 +4881,53 @@ function getModelLabel(model) {
4861
4881
  return 'None';
4862
4882
  }
4863
4883
  }
4884
+ function createActivePolicySummarySection(options) {
4885
+ const { policy, policyDecision, policySnapshotToken } = options;
4886
+ if (!policy && !policyDecision) return createSection({
4887
+ title: 'Active Policy',
4888
+ children: [
4889
+ renderer_div({
4890
+ style: {
4891
+ padding: '10px 12px',
4892
+ fontSize: 'var(--c15t-devtools-font-size-sm)',
4893
+ color: 'var(--c15t-text-muted)'
4894
+ },
4895
+ text: 'No active policy matched.'
4896
+ })
4897
+ ]
4898
+ });
4899
+ const cards = [
4900
+ createCompactInfoCard('Policy ID', policy?.id ?? policyDecision?.policyId ?? '—'),
4901
+ createCompactInfoCard('Matched By', policyDecision?.matchedBy ?? '—'),
4902
+ createCompactInfoCard('Snapshot Token', policySnapshotToken ? 'present' : 'missing')
4903
+ ];
4904
+ return createSection({
4905
+ title: 'Active Policy',
4906
+ children: [
4907
+ renderer_div({
4908
+ style: {
4909
+ display: 'grid',
4910
+ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
4911
+ gap: 'var(--c15t-space-sm, 0.5rem)'
4912
+ },
4913
+ children: cards
4914
+ }),
4915
+ renderer_span({
4916
+ className: styles_components_module.overrideHint,
4917
+ text: 'Open the Policy tab for full policy-pack diagnostics.'
4918
+ })
4919
+ ]
4920
+ });
4921
+ }
4864
4922
  function createCompactInfoCard(label, value) {
4865
4923
  return renderer_div({
4866
4924
  className: styles_components_module.gridCard ?? '',
4867
4925
  style: {
4868
- padding: '6px 8px',
4926
+ padding: '8px 10px',
4869
4927
  minHeight: 'auto',
4870
4928
  flexDirection: 'column',
4871
4929
  alignItems: 'flex-start',
4872
- gap: '1px'
4930
+ gap: '2px'
4873
4931
  },
4874
4932
  children: [
4875
4933
  renderer_span({
@@ -4890,6 +4948,253 @@ function createCompactInfoCard(label, value) {
4890
4948
  ]
4891
4949
  });
4892
4950
  }
4951
+ function renderPolicyPanel(container, options) {
4952
+ const { getState } = options;
4953
+ clearElement(container);
4954
+ const state = getState();
4955
+ if (!state) return void container.appendChild(createDisconnectedState());
4956
+ const initData = state.lastBannerFetchData;
4957
+ const activePolicy = initData?.policy;
4958
+ const policyDecision = initData?.policyDecision;
4959
+ const initSource = formatInitSource(state.initDataSource, state.initDataSourceDetail);
4960
+ container.appendChild(createMatchTraceSection({
4961
+ policyDecision,
4962
+ policyId: activePolicy?.id ?? policyDecision?.policyId
4963
+ }));
4964
+ if (!activePolicy && !policyDecision) return void container.appendChild(createSection({
4965
+ title: 'Policy',
4966
+ children: [
4967
+ renderer_div({
4968
+ style: {
4969
+ padding: '10px 12px',
4970
+ fontSize: 'var(--c15t-devtools-font-size-sm)',
4971
+ color: 'var(--c15t-text-muted)'
4972
+ },
4973
+ text: 'No active policy matched for this request.'
4974
+ }),
4975
+ createHint(`Init Source: ${initSource}`)
4976
+ ]
4977
+ }));
4978
+ container.appendChild(createSection({
4979
+ title: 'Policy',
4980
+ children: [
4981
+ policy_createGrid(3, [
4982
+ createCard('ID', activePolicy?.id ?? policyDecision?.policyId ?? '—'),
4983
+ createCard('Model', policy_getModelLabel(activePolicy?.model)),
4984
+ createCard('Scope', getScopeModeLabel(activePolicy?.consent?.scopeMode ?? state.policyScopeMode)),
4985
+ createCard('Categories', formatList(state.policyCategories ?? activePolicy?.consent?.categories)),
4986
+ createCard('Preselected', formatList(activePolicy?.consent?.preselectedCategories)),
4987
+ createCard('Expiry', 'number' == typeof activePolicy?.consent?.expiryDays ? `${activePolicy.consent.expiryDays}d` : '—')
4988
+ ]),
4989
+ createHint(`${initSource} · ${formatFingerprint(policyDecision?.fingerprint)}`)
4990
+ ]
4991
+ }));
4992
+ const uiMode = activePolicy?.ui?.mode;
4993
+ if (uiMode && 'none' !== uiMode) {
4994
+ const bannerCards = buildSurfaceCards('Banner', activePolicy?.ui?.banner, state.policyBanner);
4995
+ const dialogCards = buildSurfaceCards('Dialog', activePolicy?.ui?.dialog, state.policyDialog);
4996
+ if (bannerCards.length > 0 || dialogCards.length > 0) container.appendChild(createSection({
4997
+ title: `UI · ${uiMode}`,
4998
+ children: [
4999
+ policy_createGrid(3, [
5000
+ ...bannerCards,
5001
+ ...dialogCards
5002
+ ])
5003
+ ]
5004
+ }));
5005
+ }
5006
+ const proofLabel = formatProofSummary(activePolicy?.proof);
5007
+ const snapshotLabel = initData?.policySnapshotToken ? 'present' : 'missing';
5008
+ container.appendChild(createSection({
5009
+ title: 'Proof & Snapshot',
5010
+ children: [
5011
+ policy_createGrid(3, [
5012
+ createCard('Proof', proofLabel),
5013
+ createCard('Snapshot', snapshotLabel),
5014
+ createCard('I18n', activePolicy?.i18n?.messageProfile ?? activePolicy?.i18n?.language ?? '—')
5015
+ ])
5016
+ ]
5017
+ }));
5018
+ }
5019
+ function buildSurfaceCards(prefix, policySurface, storeSurface) {
5020
+ const policyLayout = Array.isArray(policySurface?.layout) && 0 === policySurface.layout.length ? null : policySurface?.layout ?? null;
5021
+ const storeLayout = Array.isArray(storeSurface.layout) && 0 === storeSurface.layout.length ? null : storeSurface.layout ?? null;
5022
+ const actions = formatList(policySurface?.allowedActions ?? storeSurface.allowedActions);
5023
+ const primary = policySurface?.primaryAction ?? storeSurface.primaryAction ?? null;
5024
+ const layout = policyLayout ?? storeLayout;
5025
+ const direction = policySurface?.direction ?? storeSurface.direction ?? null;
5026
+ const profile = policySurface?.uiProfile ?? storeSurface.uiProfile ?? null;
5027
+ const scrollLock = policySurface?.scrollLock ?? storeSurface.scrollLock ?? null;
5028
+ if ('—' === actions && !primary && !layout && !direction && !profile && null === scrollLock) return [];
5029
+ const cards = [
5030
+ createCard(`${prefix} Actions`, actions)
5031
+ ];
5032
+ if (primary) cards.push(createCard(`${prefix} Primary`, primary));
5033
+ if (layout) cards.push(createCard(`${prefix} Layout`, Array.isArray(layout) ? layout.map((group)=>Array.isArray(group) ? `[${group.join(', ')}]` : group).join(' / ') : layout));
5034
+ if (direction) cards.push(createCard(`${prefix} Direction`, direction));
5035
+ if (profile) cards.push(createCard(`${prefix} Profile`, profile));
5036
+ if (null !== scrollLock) cards.push(createCard(`${prefix} Scroll Lock`, scrollLock ? 'on' : 'off'));
5037
+ return cards;
5038
+ }
5039
+ function createMatchTraceSection(options) {
5040
+ const { policyDecision, policyId } = options;
5041
+ const entries = buildTraceEntries(policyDecision, policyId);
5042
+ return createSection({
5043
+ title: 'Match Trace',
5044
+ children: [
5045
+ renderer_div({
5046
+ style: {
5047
+ display: 'grid',
5048
+ gridTemplateColumns: '1fr',
5049
+ gap: '4px'
5050
+ },
5051
+ children: entries.map((entry)=>renderer_div({
5052
+ className: styles_components_module.gridCard ?? '',
5053
+ style: {
5054
+ padding: '6px 10px',
5055
+ display: 'flex',
5056
+ alignItems: 'center',
5057
+ justifyContent: 'space-between',
5058
+ gap: '10px'
5059
+ },
5060
+ children: [
5061
+ renderer_span({
5062
+ style: {
5063
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
5064
+ color: 'var(--c15t-text-muted)',
5065
+ fontFamily: 'ui-monospace, monospace'
5066
+ },
5067
+ text: entry.step
5068
+ }),
5069
+ renderer_span({
5070
+ style: {
5071
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
5072
+ fontFamily: 'ui-monospace, monospace'
5073
+ },
5074
+ text: entry.result
5075
+ })
5076
+ ]
5077
+ }))
5078
+ }),
5079
+ createHint('region → country → default · fallback on geo failure · Simulate via Location tab')
5080
+ ]
5081
+ });
5082
+ }
5083
+ function buildTraceEntries(decision, policyId) {
5084
+ if (!decision) return [
5085
+ {
5086
+ step: 'decision metadata',
5087
+ result: 'UNAVAILABLE'
5088
+ }
5089
+ ];
5090
+ const country = decision.country ?? 'n/a';
5091
+ const regionKey = decision.country && decision.region ? `${decision.country}-${decision.region}` : 'n/a';
5092
+ const resolved = policyId ?? decision.policyId ?? 'unknown';
5093
+ const matched = decision.matchedBy;
5094
+ return [
5095
+ {
5096
+ step: `region(${regionKey})`,
5097
+ result: 'region' === matched ? `MATCH → ${resolved}` : 'MISS'
5098
+ },
5099
+ {
5100
+ step: `country(${country})`,
5101
+ result: 'country' === matched ? `MATCH → ${resolved}` : 'region' === matched ? 'SKIPPED' : 'MISS'
5102
+ },
5103
+ {
5104
+ step: 'fallback(geo-fail)',
5105
+ result: 'fallback' === matched ? `MATCH → ${resolved}` : 'SKIPPED'
5106
+ },
5107
+ {
5108
+ step: 'default(catch-all)',
5109
+ result: 'default' === matched ? `MATCH → ${resolved}` : 'SKIPPED'
5110
+ }
5111
+ ];
5112
+ }
5113
+ function policy_getModelLabel(model) {
5114
+ switch(model){
5115
+ case 'opt-in':
5116
+ return 'Opt-In';
5117
+ case 'opt-out':
5118
+ return 'Opt-Out';
5119
+ case 'iab':
5120
+ return 'IAB TCF';
5121
+ default:
5122
+ return 'None';
5123
+ }
5124
+ }
5125
+ function getScopeModeLabel(mode) {
5126
+ switch(mode){
5127
+ case 'strict':
5128
+ return 'Strict';
5129
+ case 'permissive':
5130
+ return 'Permissive';
5131
+ default:
5132
+ return '—';
5133
+ }
5134
+ }
5135
+ function formatList(items) {
5136
+ if (!items || 0 === items.length) return '—';
5137
+ if (items.includes('*')) return '* (all)';
5138
+ return items.join(', ');
5139
+ }
5140
+ function formatProofSummary(proof) {
5141
+ if (!proof) return '—';
5142
+ const parts = [];
5143
+ if (proof.storeIp) parts.push('IP');
5144
+ if (proof.storeUserAgent) parts.push('UA');
5145
+ if (proof.storeLanguage) parts.push('Lang');
5146
+ return parts.length > 0 ? parts.join(', ') : 'none';
5147
+ }
5148
+ function formatFingerprint(fingerprint) {
5149
+ if (!fingerprint) return 'no fingerprint';
5150
+ if (fingerprint.length <= 12) return fingerprint;
5151
+ return `${fingerprint.slice(0, 8)}…${fingerprint.slice(-4)}`;
5152
+ }
5153
+ function createCard(label, value) {
5154
+ return renderer_div({
5155
+ className: styles_components_module.gridCard ?? '',
5156
+ style: {
5157
+ padding: '8px 10px',
5158
+ minHeight: 'auto',
5159
+ flexDirection: 'column',
5160
+ alignItems: 'flex-start',
5161
+ gap: '2px'
5162
+ },
5163
+ children: [
5164
+ renderer_span({
5165
+ style: {
5166
+ fontSize: 'var(--c15t-devtools-font-size-xs)',
5167
+ color: 'var(--c15t-text-muted)'
5168
+ },
5169
+ text: label
5170
+ }),
5171
+ renderer_span({
5172
+ style: {
5173
+ fontSize: 'var(--c15t-font-size-sm)',
5174
+ fontWeight: '500',
5175
+ fontFamily: 'ui-monospace, monospace'
5176
+ },
5177
+ text: value
5178
+ })
5179
+ ]
5180
+ });
5181
+ }
5182
+ function policy_createGrid(columns, children) {
5183
+ return renderer_div({
5184
+ style: {
5185
+ display: 'grid',
5186
+ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
5187
+ gap: 'var(--c15t-space-sm, 0.5rem)'
5188
+ },
5189
+ children
5190
+ });
5191
+ }
5192
+ function createHint(text) {
5193
+ return renderer_span({
5194
+ className: styles_components_module.overrideHint,
5195
+ text
5196
+ });
5197
+ }
4893
5198
  const dismissedResources = new Set();
4894
5199
  function scanDOM(state) {
4895
5200
  const results = [];
@@ -5528,6 +5833,11 @@ function panel_renderer_createPanelRenderer(config) {
5528
5833
  }
5529
5834
  });
5530
5835
  break;
5836
+ case 'policy':
5837
+ renderPolicyPanel(container, {
5838
+ getState: getStoreState
5839
+ });
5840
+ break;
5531
5841
  case "scripts":
5532
5842
  renderScriptsPanel(container, {
5533
5843
  getState: getStoreState,
@@ -5664,7 +5974,7 @@ function persistEvents(events) {
5664
5974
  } catch {}
5665
5975
  }
5666
5976
  function isDevToolsTab(value) {
5667
- return 'consents' === value || 'location' === value || "scripts" === value || 'iab' === value || 'events' === value || 'actions' === value;
5977
+ return 'consents' === value || 'location' === value || 'policy' === value || "scripts" === value || 'iab' === value || 'events' === value || 'actions' === value;
5668
5978
  }
5669
5979
  function loadPersistedActiveTab() {
5670
5980
  if ('undefined' == typeof window) return null;