@svadmin/ui 0.78.2 → 0.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.css CHANGED
@@ -9551,6 +9551,191 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
9551
9551
  max-width: 32rem;
9552
9552
  }
9553
9553
 
9554
+ /* --- Auditable content primitives --- */
9555
+ .svadmin-filter-bottom-sheet[data-slot="sheet"] {
9556
+ inset-block-start: auto;
9557
+ inset-block-end: 0;
9558
+ inset-inline: 0;
9559
+ width: 100%;
9560
+ max-width: none;
9561
+ height: auto;
9562
+ max-height: 85dvh;
9563
+ overflow-y: auto;
9564
+ padding: 1rem 1rem max(1rem, env(safe-area-inset-bottom));
9565
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
9566
+ }
9567
+ .svadmin-filter-bottom-sheet .svadmin-sheet-close { min-width: 44px; min-height: 44px; }
9568
+ .svadmin-filter-bottom-sheet [data-slot="sheet-content"] { display: grid; gap: 1rem; }
9569
+ .layout-clean-flat [data-svadmin-audit-section],
9570
+ .layout-clean-flat [data-svadmin-chart-container],
9571
+ .layout-clean-flat [data-svadmin-evidence-card],
9572
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9573
+ min-width: 0;
9574
+ }
9575
+
9576
+ .layout-clean-flat [data-svadmin-audit-section] {
9577
+ position: relative;
9578
+ border-block: 1px solid var(--svadmin-border, var(--border));
9579
+ background: var(--svadmin-surface, var(--card));
9580
+ }
9581
+
9582
+ .layout-clean-flat [data-svadmin-audit-section]::before,
9583
+ .layout-clean-flat [data-svadmin-evidence-card]::before {
9584
+ position: absolute;
9585
+ inset-block: 0;
9586
+ inset-inline-start: 0;
9587
+ width: 4px;
9588
+ background: var(--svadmin-accent-color, var(--primary));
9589
+ content: "";
9590
+ }
9591
+
9592
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="info"],
9593
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="info"],
9594
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="info"] {
9595
+ --svadmin-accent-color: var(--info);
9596
+ }
9597
+
9598
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="success"],
9599
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="success"],
9600
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="success"] {
9601
+ --svadmin-accent-color: var(--success);
9602
+ }
9603
+
9604
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="warning"],
9605
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="warning"],
9606
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="warning"] {
9607
+ --svadmin-accent-color: var(--warning);
9608
+ }
9609
+
9610
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="danger"],
9611
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="danger"],
9612
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="danger"] {
9613
+ --svadmin-accent-color: var(--destructive);
9614
+ }
9615
+
9616
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="neutral"],
9617
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="neutral"],
9618
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="neutral"] {
9619
+ --svadmin-accent-color: var(--muted-foreground);
9620
+ }
9621
+
9622
+ .svadmin-audit-section__header,
9623
+ .svadmin-chart-container__header,
9624
+ .svadmin-evidence-card__header {
9625
+ display: flex;
9626
+ align-items: flex-start;
9627
+ justify-content: space-between;
9628
+ gap: 1rem;
9629
+ }
9630
+
9631
+ .svadmin-audit-section__header {
9632
+ padding: 1rem 1.25rem;
9633
+ border-block-end: 1px solid var(--svadmin-border, var(--border));
9634
+ }
9635
+
9636
+ .svadmin-audit-section__heading,
9637
+ .svadmin-chart-container__header > div {
9638
+ min-width: 0;
9639
+ }
9640
+
9641
+ .svadmin-audit-section__eyebrow {
9642
+ margin: 0 0 0.25rem;
9643
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9644
+ font-size: 0.6875rem;
9645
+ font-weight: 600;
9646
+ letter-spacing: 0;
9647
+ }
9648
+
9649
+ .svadmin-audit-section__header h2,
9650
+ .svadmin-chart-container__header h3,
9651
+ .svadmin-evidence-card__header h3 {
9652
+ margin: 0;
9653
+ color: var(--svadmin-text, var(--foreground));
9654
+ font-size: 0.9375rem;
9655
+ font-weight: 600;
9656
+ line-height: 1.3;
9657
+ }
9658
+
9659
+ .svadmin-audit-section__description,
9660
+ .svadmin-chart-container__header p,
9661
+ .svadmin-evidence-card__description,
9662
+ .svadmin-evidence-card__source {
9663
+ margin: 0.25rem 0 0;
9664
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9665
+ font-size: 0.75rem;
9666
+ line-height: 1.45;
9667
+ }
9668
+
9669
+ .svadmin-audit-section__body {
9670
+ min-width: 0;
9671
+ }
9672
+
9673
+ .svadmin-audit-section__header,
9674
+ .svadmin-audit-section__body,
9675
+ .svadmin-evidence-card__header,
9676
+ .svadmin-evidence-card__description,
9677
+ .svadmin-evidence-card__source {
9678
+ overflow-wrap: anywhere;
9679
+ }
9680
+ .svadmin-audit-section__actions { flex-shrink: 0; max-width: 100%; }
9681
+ .svadmin-audit-notice {
9682
+ display: flex;
9683
+ flex-wrap: wrap;
9684
+ align-items: center;
9685
+ justify-content: space-between;
9686
+ gap: 0.75rem;
9687
+ padding: 0.75rem 1rem;
9688
+ border-inline-start: 4px solid var(--warning);
9689
+ background: var(--muted);
9690
+ font-size: 0.8125rem;
9691
+ }
9692
+ .svadmin-audit-notice[data-state="readonly"] { border-inline-start-color: var(--muted-foreground); }
9693
+ .svadmin-status-pill[data-slot="badge"] { border-radius: 999px; white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
9694
+ .layout-clean-flat [data-svadmin-coverage-matrix]:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
9695
+ .layout-clean-flat [data-svadmin-coverage-matrix] table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
9696
+ .layout-clean-flat [data-svadmin-coverage-matrix] caption { padding: 0.75rem; text-align: start; font-weight: 600; }
9697
+ .layout-clean-flat [data-svadmin-coverage-matrix] :is(th, td) { padding: 0.75rem; text-align: start; min-width: 9rem; overflow-wrap: anywhere; border-bottom: 1px solid var(--border); }
9698
+ .layout-clean-flat [data-svadmin-coverage-matrix] thead { color: var(--muted-foreground); background: var(--muted); }
9699
+ .layout-clean-flat [data-svadmin-coverage-matrix] tbody th { border-inline-start: 4px solid var(--svadmin-accent-color, var(--border)); }
9700
+ .svadmin-evidence-card__header { flex-wrap: wrap; }
9701
+ @media (max-width: 767px) {
9702
+ .svadmin-audit-section__header { flex-wrap: wrap; }
9703
+ .svadmin-audit-section__actions a { display: inline-flex; align-items: center; min-height: 44px; }
9704
+ }
9705
+
9706
+ .svadmin-chart-container__body {
9707
+ min-width: 0;
9708
+ padding: 1rem;
9709
+ }
9710
+
9711
+ .layout-clean-flat [data-svadmin-evidence-card] {
9712
+ position: relative;
9713
+ min-width: 0;
9714
+ border: 1px solid var(--svadmin-border, var(--border));
9715
+ background: var(--svadmin-surface, var(--card));
9716
+ padding: 1rem 1rem 1rem 1.25rem;
9717
+ }
9718
+
9719
+ .svadmin-evidence-card__body {
9720
+ margin-top: 0.75rem;
9721
+ color: var(--svadmin-text, var(--foreground));
9722
+ font-size: 0.8125rem;
9723
+ line-height: 1.5;
9724
+ }
9725
+
9726
+ .svadmin-evidence-card__source {
9727
+ margin-top: 1rem;
9728
+ color: var(--primary);
9729
+ font-weight: 600;
9730
+ }
9731
+
9732
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9733
+ overflow-x: auto;
9734
+ border: 1px solid var(--svadmin-border, var(--border));
9735
+ background: var(--svadmin-surface, var(--card));
9736
+ }
9737
+
9738
+
9554
9739
  @media (min-width: 768px) {
9555
9740
  .layout-clean-flat [data-svadmin-form-settings] [data-svadmin-field] {
9556
9741
  display: grid;
@@ -10108,7 +10293,8 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
10108
10293
  }
10109
10294
 
10110
10295
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active::before {
10111
- content: none;
10296
+ width: 4px;
10297
+ content: "";
10112
10298
  }
10113
10299
 
10114
10300
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active svg {
@@ -9551,6 +9551,191 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
9551
9551
  max-width: 32rem;
9552
9552
  }
9553
9553
 
9554
+ /* --- Auditable content primitives --- */
9555
+ .svadmin-filter-bottom-sheet[data-slot="sheet"] {
9556
+ inset-block-start: auto;
9557
+ inset-block-end: 0;
9558
+ inset-inline: 0;
9559
+ width: 100%;
9560
+ max-width: none;
9561
+ height: auto;
9562
+ max-height: 85dvh;
9563
+ overflow-y: auto;
9564
+ padding: 1rem 1rem max(1rem, env(safe-area-inset-bottom));
9565
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
9566
+ }
9567
+ .svadmin-filter-bottom-sheet .svadmin-sheet-close { min-width: 44px; min-height: 44px; }
9568
+ .svadmin-filter-bottom-sheet [data-slot="sheet-content"] { display: grid; gap: 1rem; }
9569
+ .layout-clean-flat [data-svadmin-audit-section],
9570
+ .layout-clean-flat [data-svadmin-chart-container],
9571
+ .layout-clean-flat [data-svadmin-evidence-card],
9572
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9573
+ min-width: 0;
9574
+ }
9575
+
9576
+ .layout-clean-flat [data-svadmin-audit-section] {
9577
+ position: relative;
9578
+ border-block: 1px solid var(--svadmin-border, var(--border));
9579
+ background: var(--svadmin-surface, var(--card));
9580
+ }
9581
+
9582
+ .layout-clean-flat [data-svadmin-audit-section]::before,
9583
+ .layout-clean-flat [data-svadmin-evidence-card]::before {
9584
+ position: absolute;
9585
+ inset-block: 0;
9586
+ inset-inline-start: 0;
9587
+ width: 4px;
9588
+ background: var(--svadmin-accent-color, var(--primary));
9589
+ content: "";
9590
+ }
9591
+
9592
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="info"],
9593
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="info"],
9594
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="info"] {
9595
+ --svadmin-accent-color: var(--info);
9596
+ }
9597
+
9598
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="success"],
9599
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="success"],
9600
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="success"] {
9601
+ --svadmin-accent-color: var(--success);
9602
+ }
9603
+
9604
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="warning"],
9605
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="warning"],
9606
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="warning"] {
9607
+ --svadmin-accent-color: var(--warning);
9608
+ }
9609
+
9610
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="danger"],
9611
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="danger"],
9612
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="danger"] {
9613
+ --svadmin-accent-color: var(--destructive);
9614
+ }
9615
+
9616
+ .layout-clean-flat [data-svadmin-audit-section][data-accent="neutral"],
9617
+ .layout-clean-flat [data-svadmin-coverage-matrix] [data-accent="neutral"],
9618
+ .layout-clean-flat [data-svadmin-evidence-card][data-accent="neutral"] {
9619
+ --svadmin-accent-color: var(--muted-foreground);
9620
+ }
9621
+
9622
+ .svadmin-audit-section__header,
9623
+ .svadmin-chart-container__header,
9624
+ .svadmin-evidence-card__header {
9625
+ display: flex;
9626
+ align-items: flex-start;
9627
+ justify-content: space-between;
9628
+ gap: 1rem;
9629
+ }
9630
+
9631
+ .svadmin-audit-section__header {
9632
+ padding: 1rem 1.25rem;
9633
+ border-block-end: 1px solid var(--svadmin-border, var(--border));
9634
+ }
9635
+
9636
+ .svadmin-audit-section__heading,
9637
+ .svadmin-chart-container__header > div {
9638
+ min-width: 0;
9639
+ }
9640
+
9641
+ .svadmin-audit-section__eyebrow {
9642
+ margin: 0 0 0.25rem;
9643
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9644
+ font-size: 0.6875rem;
9645
+ font-weight: 600;
9646
+ letter-spacing: 0;
9647
+ }
9648
+
9649
+ .svadmin-audit-section__header h2,
9650
+ .svadmin-chart-container__header h3,
9651
+ .svadmin-evidence-card__header h3 {
9652
+ margin: 0;
9653
+ color: var(--svadmin-text, var(--foreground));
9654
+ font-size: 0.9375rem;
9655
+ font-weight: 600;
9656
+ line-height: 1.3;
9657
+ }
9658
+
9659
+ .svadmin-audit-section__description,
9660
+ .svadmin-chart-container__header p,
9661
+ .svadmin-evidence-card__description,
9662
+ .svadmin-evidence-card__source {
9663
+ margin: 0.25rem 0 0;
9664
+ color: var(--svadmin-text-muted, var(--muted-foreground));
9665
+ font-size: 0.75rem;
9666
+ line-height: 1.45;
9667
+ }
9668
+
9669
+ .svadmin-audit-section__body {
9670
+ min-width: 0;
9671
+ }
9672
+
9673
+ .svadmin-audit-section__header,
9674
+ .svadmin-audit-section__body,
9675
+ .svadmin-evidence-card__header,
9676
+ .svadmin-evidence-card__description,
9677
+ .svadmin-evidence-card__source {
9678
+ overflow-wrap: anywhere;
9679
+ }
9680
+ .svadmin-audit-section__actions { flex-shrink: 0; max-width: 100%; }
9681
+ .svadmin-audit-notice {
9682
+ display: flex;
9683
+ flex-wrap: wrap;
9684
+ align-items: center;
9685
+ justify-content: space-between;
9686
+ gap: 0.75rem;
9687
+ padding: 0.75rem 1rem;
9688
+ border-inline-start: 4px solid var(--warning);
9689
+ background: var(--muted);
9690
+ font-size: 0.8125rem;
9691
+ }
9692
+ .svadmin-audit-notice[data-state="readonly"] { border-inline-start-color: var(--muted-foreground); }
9693
+ .svadmin-status-pill[data-slot="badge"] { border-radius: 999px; white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
9694
+ .layout-clean-flat [data-svadmin-coverage-matrix]:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
9695
+ .layout-clean-flat [data-svadmin-coverage-matrix] table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
9696
+ .layout-clean-flat [data-svadmin-coverage-matrix] caption { padding: 0.75rem; text-align: start; font-weight: 600; }
9697
+ .layout-clean-flat [data-svadmin-coverage-matrix] :is(th, td) { padding: 0.75rem; text-align: start; min-width: 9rem; overflow-wrap: anywhere; border-bottom: 1px solid var(--border); }
9698
+ .layout-clean-flat [data-svadmin-coverage-matrix] thead { color: var(--muted-foreground); background: var(--muted); }
9699
+ .layout-clean-flat [data-svadmin-coverage-matrix] tbody th { border-inline-start: 4px solid var(--svadmin-accent-color, var(--border)); }
9700
+ .svadmin-evidence-card__header { flex-wrap: wrap; }
9701
+ @media (max-width: 767px) {
9702
+ .svadmin-audit-section__header { flex-wrap: wrap; }
9703
+ .svadmin-audit-section__actions a { display: inline-flex; align-items: center; min-height: 44px; }
9704
+ }
9705
+
9706
+ .svadmin-chart-container__body {
9707
+ min-width: 0;
9708
+ padding: 1rem;
9709
+ }
9710
+
9711
+ .layout-clean-flat [data-svadmin-evidence-card] {
9712
+ position: relative;
9713
+ min-width: 0;
9714
+ border: 1px solid var(--svadmin-border, var(--border));
9715
+ background: var(--svadmin-surface, var(--card));
9716
+ padding: 1rem 1rem 1rem 1.25rem;
9717
+ }
9718
+
9719
+ .svadmin-evidence-card__body {
9720
+ margin-top: 0.75rem;
9721
+ color: var(--svadmin-text, var(--foreground));
9722
+ font-size: 0.8125rem;
9723
+ line-height: 1.5;
9724
+ }
9725
+
9726
+ .svadmin-evidence-card__source {
9727
+ margin-top: 1rem;
9728
+ color: var(--primary);
9729
+ font-weight: 600;
9730
+ }
9731
+
9732
+ .layout-clean-flat [data-svadmin-coverage-matrix] {
9733
+ overflow-x: auto;
9734
+ border: 1px solid var(--svadmin-border, var(--border));
9735
+ background: var(--svadmin-surface, var(--card));
9736
+ }
9737
+
9738
+
9554
9739
  @media (min-width: 768px) {
9555
9740
  .layout-clean-flat [data-svadmin-form-settings] [data-svadmin-field] {
9556
9741
  display: grid;
@@ -10108,7 +10293,8 @@ details.svadmin-collapsible:not([open]) > :not(summary) {
10108
10293
  }
10109
10294
 
10110
10295
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active::before {
10111
- content: none;
10296
+ width: 4px;
10297
+ content: "";
10112
10298
  }
10113
10299
 
10114
10300
  .layout-clean-flat[data-theme="stripe"] .sidebar-menu-item-active svg {
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
3
  import { connectPanelChannel, type PanelChannel } from 'devframe/in-page-channel';
4
+ import { DEVTOOLS_CHANNEL } from '@vibeunion/devtools-devframe';
4
5
  import {
5
- SVADMIN_DEVFRAME_CHANNEL,
6
+ type DevtoolsCacheMutation,
6
7
  type DevtoolsQuerySelector,
7
8
  type SvadminDevframeProtocol,
8
9
  type SvadminDevtoolsSnapshot,
@@ -22,11 +23,14 @@
22
23
  ...(selectedOperation ? { operation: selectedOperation } : {}),
23
24
  });
24
25
 
25
- async function run(action: (selector?: DevtoolsQuerySelector) => Promise<void> | void): Promise<void> {
26
+ async function run(action: DevtoolsCacheMutation, selector?: DevtoolsQuerySelector): Promise<void> {
26
27
  if (!channel || !snapshot) return;
27
28
  busy = true;
28
29
  try {
29
- await action(Object.keys(selector).length > 0 ? selector : undefined);
30
+ await channel.call('cacheAction', {
31
+ action,
32
+ ...(selector && Object.keys(selector).length > 0 ? { selector } : {}),
33
+ });
30
34
  snapshot = await channel.call('getSnapshot');
31
35
  } finally {
32
36
  busy = false;
@@ -35,7 +39,7 @@
35
39
 
36
40
  onMount(() => {
37
41
  const nextChannel = connectPanelChannel<SvadminDevframeProtocol>({
38
- name: SVADMIN_DEVFRAME_CHANNEL,
42
+ name: DEVTOOLS_CHANNEL,
39
43
  functions: {},
40
44
  });
41
45
  channel = nextChannel;
@@ -109,13 +113,13 @@
109
113
  </ul>
110
114
  {/if}
111
115
  <div>
112
- <button type="button" disabled={busy} onclick={() => run((selector) => channel?.call('cacheInvalidate', selector))}>Invalidate</button>
113
- <button type="button" disabled={busy} onclick={() => run((selector) => channel?.call('cacheRefetch', selector))}>Refetch</button>
114
- <button type="button" disabled={busy} onclick={() => run((selector) => channel?.call('cacheCancel', selector))}>Cancel</button>
115
- <button type="button" disabled={busy} onclick={() => run((selector) => channel?.call('cacheReset', selector))}>Reset</button>
116
- <button type="button" disabled={busy} onclick={() => run((selector) => channel?.call('cacheRemove', selector))}>Remove</button>
117
- <button type="button" disabled={busy} onclick={() => run(() => channel?.call('cacheClear'))}>Clear cache</button>
118
- <button type="button" disabled={busy} onclick={() => run(() => channel?.call('clearMutations'))}>Clear mutations</button>
116
+ <button type="button" disabled={busy} onclick={() => run('invalidate', selector)}>Invalidate</button>
117
+ <button type="button" disabled={busy} onclick={() => run('refetch', selector)}>Refetch</button>
118
+ <button type="button" disabled={busy} onclick={() => run('cancel', selector)}>Cancel</button>
119
+ <button type="button" disabled={busy} onclick={() => run('reset', selector)}>Reset</button>
120
+ <button type="button" disabled={busy} onclick={() => run('remove', selector)}>Remove</button>
121
+ <button type="button" disabled={busy} onclick={() => run('clear')}>Clear cache</button>
122
+ <button type="button" disabled={busy} onclick={() => run('clearMutations')}>Clear mutations</button>
119
123
  </div>
120
124
  {:else}
121
125
  <p>Waiting for an svadmin page.</p>
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { definedOptions } from '@svadmin/core/options';
4
+ import { useTranslation } from '@svadmin/core/i18n';
5
+ import DataState from './DataState.svelte';
6
+ import { Button } from '../ui/button/index.js';
7
+ import { RefreshCw } from '@lucide/svelte';
8
+ import type { AuditDataState } from './audit-state.js';
9
+
10
+ let { state = 'ready', message, retry, children }: {
11
+ state?: AuditDataState;
12
+ message?: string | undefined;
13
+ retry?: (() => void) | undefined;
14
+ children?: Snippet | undefined;
15
+ } = $props();
16
+ const i18n = useTranslation();
17
+ </script>
18
+
19
+ {#if state === 'loading' || state === 'empty' || state === 'error' || state === 'forbidden'}
20
+ <DataState {state} {...definedOptions({ title: message, retry: state === 'error' ? retry : undefined })} />
21
+ {:else}
22
+ {#if state === 'partial' || state === 'readonly'}
23
+ <div class="svadmin-audit-notice" data-state={state} role="status">
24
+ <span>{message ?? (state === 'partial'
25
+ ? (i18n.locale === 'zh-CN' ? '部分数据暂不可用' : 'Some data is unavailable')
26
+ : (i18n.locale === 'zh-CN' ? '只读' : 'Read only'))}</span>
27
+ {#if state === 'partial' && retry}
28
+ <Button variant="outline" size="sm" onclick={retry}><RefreshCw size={16} aria-hidden="true" />{i18n.t('common.retry')}</Button>
29
+ {/if}
30
+ </div>
31
+ {/if}
32
+ {@render children?.()}
33
+ {/if}
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ type $$ComponentProps = {
4
+ state?: AuditDataState;
5
+ message?: string | undefined;
6
+ retry?: (() => void) | undefined;
7
+ children?: Snippet | undefined;
8
+ };
9
+ declare const AuditContent: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type AuditContent = ReturnType<typeof AuditContent>;
11
+ export default AuditContent;
@@ -0,0 +1,46 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import AuditContent from './AuditContent.svelte';
5
+ import type { AuditDataState } from './audit-state.js';
6
+
7
+ export type AuditAccent = 'primary' | 'info' | 'success' | 'warning' | 'danger' | 'neutral';
8
+
9
+ interface Props {
10
+ title: string;
11
+ description?: string | undefined;
12
+ eyebrow?: string;
13
+ accent?: AuditAccent;
14
+ actions?: Snippet | undefined;
15
+ children?: Snippet;
16
+ class?: string;
17
+ state?: AuditDataState;
18
+ message?: string | undefined;
19
+ retry?: (() => void) | undefined;
20
+ }
21
+
22
+ let {
23
+ title,
24
+ description,
25
+ eyebrow,
26
+ accent = 'primary',
27
+ actions,
28
+ children,
29
+ class: className = '',
30
+ state = 'ready',
31
+ message,
32
+ retry,
33
+ }: Props = $props();
34
+ </script>
35
+
36
+ <section aria-label={title} class={cn('svadmin-audit-section', className)} data-svadmin-audit-section data-accent={accent} data-state={state}>
37
+ <header class="svadmin-audit-section__header">
38
+ <div class="svadmin-audit-section__heading">
39
+ {#if eyebrow}<p class="svadmin-audit-section__eyebrow">{eyebrow}</p>{/if}
40
+ <h2>{title}</h2>
41
+ {#if description}<p class="svadmin-audit-section__description">{description}</p>{/if}
42
+ </div>
43
+ {#if actions && state !== 'forbidden'}<div class="svadmin-audit-section__actions">{@render actions()}</div>{/if}
44
+ </header>
45
+ <div class="svadmin-audit-section__body"><AuditContent {state} {message} {retry} {children} /></div>
46
+ </section>
@@ -0,0 +1,18 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ export type AuditAccent = 'primary' | 'info' | 'success' | 'warning' | 'danger' | 'neutral';
4
+ interface Props {
5
+ title: string;
6
+ description?: string | undefined;
7
+ eyebrow?: string;
8
+ accent?: AuditAccent;
9
+ actions?: Snippet | undefined;
10
+ children?: Snippet;
11
+ class?: string;
12
+ state?: AuditDataState;
13
+ message?: string | undefined;
14
+ retry?: (() => void) | undefined;
15
+ }
16
+ declare const AuditSection: import("svelte").Component<Props, {}, "">;
17
+ type AuditSection = ReturnType<typeof AuditSection>;
18
+ export default AuditSection;
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import AuditSection from './AuditSection.svelte';
5
+ import type { AuditDataState } from './audit-state.js';
6
+
7
+ interface Props {
8
+ title: string;
9
+ description?: string;
10
+ actions?: Snippet;
11
+ children?: Snippet;
12
+ class?: string;
13
+ state?: AuditDataState;
14
+ message?: string | undefined;
15
+ retry?: (() => void) | undefined;
16
+ }
17
+
18
+ let {
19
+ title,
20
+ description,
21
+ actions,
22
+ children,
23
+ class: className = '',
24
+ state = 'ready',
25
+ message,
26
+ retry,
27
+ }: Props = $props();
28
+ </script>
29
+
30
+ <AuditSection {title} {description} {actions} {state} {message} {retry} accent="info" class={cn('svadmin-chart-container', className)}>
31
+ <div class="svadmin-chart-container__body">{@render children?.()}</div>
32
+ </AuditSection>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ interface Props {
4
+ title: string;
5
+ description?: string;
6
+ actions?: Snippet;
7
+ children?: Snippet;
8
+ class?: string;
9
+ state?: AuditDataState;
10
+ message?: string | undefined;
11
+ retry?: (() => void) | undefined;
12
+ }
13
+ declare const ChartContainer: import("svelte").Component<Props, {}, "">;
14
+ type ChartContainer = ReturnType<typeof ChartContainer>;
15
+ export default ChartContainer;
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils.js';
3
+ import type { AuditAccent } from './AuditSection.svelte';
4
+ import AuditContent from './AuditContent.svelte';
5
+ import type { AuditDataState } from './audit-state.js';
6
+
7
+ export interface CoverageMatrixColumn {
8
+ key: string;
9
+ label: string;
10
+ }
11
+
12
+ export interface CoverageMatrixRow {
13
+ id: string;
14
+ title: string;
15
+ cells: Readonly<Record<string, string | undefined>>;
16
+ accent?: AuditAccent;
17
+ }
18
+
19
+ interface Props {
20
+ columns: readonly CoverageMatrixColumn[];
21
+ rows: readonly CoverageMatrixRow[];
22
+ class?: string;
23
+ caption: string;
24
+ rowLabel?: string;
25
+ state?: AuditDataState;
26
+ message?: string | undefined;
27
+ retry?: (() => void) | undefined;
28
+ }
29
+
30
+ let { columns, rows, caption, rowLabel = '', state = 'ready', message, retry, class: className = '' }: Props = $props();
31
+ </script>
32
+
33
+ <AuditContent state={state === 'ready' && rows.length === 0 ? 'empty' : state} {message} {retry}>
34
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex (命名滚动区域需要键盘访问。) -->
35
+ <div class={cn('svadmin-coverage-matrix', className)} data-svadmin-coverage-matrix role="region" aria-label={caption} tabindex="0">
36
+ <table>
37
+ <caption>{caption}</caption>
38
+ <thead><tr><th scope="col">{rowLabel}</th>{#each columns as column (column.key)}<th scope="col">{column.label}</th>{/each}</tr></thead>
39
+ <tbody>
40
+ {#each rows as row (row.id)}
41
+ <tr data-accent={row.accent ?? 'neutral'}>
42
+ <th scope="row">{row.title}</th>
43
+ {#each columns as column (column.key)}<td>{row.cells[column.key] ?? '—'}</td>{/each}
44
+ </tr>
45
+ {/each}
46
+ </tbody>
47
+ </table>
48
+ </div>
49
+ </AuditContent>
@@ -0,0 +1,25 @@
1
+ import type { AuditAccent } from './AuditSection.svelte';
2
+ import type { AuditDataState } from './audit-state.js';
3
+ export interface CoverageMatrixColumn {
4
+ key: string;
5
+ label: string;
6
+ }
7
+ export interface CoverageMatrixRow {
8
+ id: string;
9
+ title: string;
10
+ cells: Readonly<Record<string, string | undefined>>;
11
+ accent?: AuditAccent;
12
+ }
13
+ interface Props {
14
+ columns: readonly CoverageMatrixColumn[];
15
+ rows: readonly CoverageMatrixRow[];
16
+ class?: string;
17
+ caption: string;
18
+ rowLabel?: string;
19
+ state?: AuditDataState;
20
+ message?: string | undefined;
21
+ retry?: (() => void) | undefined;
22
+ }
23
+ declare const CoverageMatrix: import("svelte").Component<Props, {}, "">;
24
+ type CoverageMatrix = ReturnType<typeof CoverageMatrix>;
25
+ export default CoverageMatrix;
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../utils.js';
4
+ import StatusPill, { type StatusPillTone } from './StatusPill.svelte';
5
+
6
+ interface Props {
7
+ title: string;
8
+ status: string;
9
+ statusTone?: StatusPillTone;
10
+ description?: string;
11
+ source?: string;
12
+ children?: Snippet;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ title,
18
+ status,
19
+ statusTone = 'neutral',
20
+ description,
21
+ source,
22
+ children,
23
+ class: className = '',
24
+ }: Props = $props();
25
+ </script>
26
+
27
+ <article aria-label={title} class={cn('svadmin-evidence-card', className)} data-svadmin-evidence-card data-accent={statusTone}>
28
+ <div class="svadmin-evidence-card__header">
29
+ <h3>{title}</h3>
30
+ <StatusPill label={status} tone={statusTone} />
31
+ </div>
32
+ {#if description}<p class="svadmin-evidence-card__description">{description}</p>{/if}
33
+ {#if children}<div class="svadmin-evidence-card__body">{@render children()}</div>{/if}
34
+ {#if source}<p class="svadmin-evidence-card__source">{source}</p>{/if}
35
+ </article>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type StatusPillTone } from './StatusPill.svelte';
3
+ interface Props {
4
+ title: string;
5
+ status: string;
6
+ statusTone?: StatusPillTone;
7
+ description?: string;
8
+ source?: string;
9
+ children?: Snippet;
10
+ class?: string;
11
+ }
12
+ declare const EvidenceCard: import("svelte").Component<Props, {}, "">;
13
+ type EvidenceCard = ReturnType<typeof EvidenceCard>;
14
+ export default EvidenceCard;
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
- import type { Snippet } from 'svelte';
2
+ import { onMount, type Snippet } from 'svelte';
3
+ import { useTranslation } from '@svadmin/core/i18n';
4
+ import * as Sheet from '../ui/sheet/index.js';
3
5
  import { Search, X, SlidersHorizontal, ChevronDown, ChevronUp } from '@lucide/svelte';
4
6
  import { Button } from '../ui/button/index.js';
5
7
  import { Input } from '../ui/input/index.js';
@@ -40,6 +42,15 @@
40
42
 
41
43
  const isCompact = $derived(density === 'compact');
42
44
  const advancedPanelId = `svadmin-filter-toolbar-advanced-${uid}`;
45
+ const i18n = useTranslation();
46
+ let mobile = $state(false);
47
+ onMount(() => {
48
+ const media = window.matchMedia('(max-width: 767px)');
49
+ const update = () => { mobile = media.matches; };
50
+ update();
51
+ media.addEventListener('change', update);
52
+ return () => media.removeEventListener('change', update);
53
+ });
43
54
  </script>
44
55
 
45
56
  <div class={cn('svadmin-filter-toolbar', className)} data-density={density} data-svadmin-filter-toolbar data-advanced-open={advanced && advancedOpen ? 'true' : 'false'}>
@@ -67,12 +78,12 @@
67
78
  {/if}
68
79
  </div>
69
80
  {/if}
70
- {#if filters}
81
+ {#if filters && !mobile}
71
82
  <div class="svadmin-filter-toolbar__filters">
72
83
  {@render filters()}
73
84
  </div>
74
85
  {/if}
75
- {#if advanced}
86
+ {#if advanced || (mobile && filters)}
76
87
  <Button
77
88
  variant={advancedOpen ? 'secondary' : 'outline'}
78
89
  size={isCompact ? 'sm' : 'default'}
@@ -101,7 +112,16 @@
101
112
  </div>
102
113
  {/if}
103
114
  </div>
104
- {#if advanced && advancedOpen}
115
+ {#if mobile && (advanced || filters)}
116
+ <Sheet.Root bind:open={advancedOpen} class="svadmin-filter-bottom-sheet" role="dialog" aria-modal="true"
117
+ aria-label={advancedLabel} id={advancedPanelId} closeLabel={i18n.t('common.close')}>
118
+ <Sheet.Header><Sheet.Title>{advancedLabel}</Sheet.Title></Sheet.Header>
119
+ <Sheet.Content>
120
+ {@render filters?.()}
121
+ {@render advanced?.()}
122
+ </Sheet.Content>
123
+ </Sheet.Root>
124
+ {:else if advanced && advancedOpen}
105
125
  <div id={advancedPanelId} role="region" aria-label={advancedLabel} class="svadmin-filter-toolbar-advanced" aria-hidden="false">
106
126
  {@render advanced()}
107
127
  </div>
@@ -1,4 +1,4 @@
1
- import type { Snippet } from 'svelte';
1
+ import { type Snippet } from 'svelte';
2
2
  interface Props {
3
3
  query?: string;
4
4
  placeholder?: string;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils.js';
3
+ import StatusBadge from './StatusBadge.svelte';
4
+
5
+ export type StatusPillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';
6
+
7
+ interface Props {
8
+ label: string;
9
+ tone?: StatusPillTone;
10
+ class?: string;
11
+ }
12
+
13
+ let { label, tone = 'neutral', class: className = '' }: Props = $props();
14
+ </script>
15
+
16
+ <StatusBadge status={tone} {label} class={cn('svadmin-status-pill', className)} />
@@ -0,0 +1,9 @@
1
+ export type StatusPillTone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';
2
+ interface Props {
3
+ label: string;
4
+ tone?: StatusPillTone;
5
+ class?: string;
6
+ }
7
+ declare const StatusPill: import("svelte").Component<Props, {}, "">;
8
+ type StatusPill = ReturnType<typeof StatusPill>;
9
+ export default StatusPill;
@@ -0,0 +1 @@
1
+ export type AuditDataState = 'ready' | 'loading' | 'empty' | 'partial' | 'error' | 'forbidden' | 'readonly';
@@ -0,0 +1 @@
1
+ export {};
@@ -48,5 +48,14 @@ export { default as FeedbackNotice } from './FeedbackNotice.svelte';
48
48
  export type { FeedbackNoticeTone, FeedbackNoticePriority } from './FeedbackNotice.svelte';
49
49
  export { default as AuditTimeline } from './AuditTimeline.svelte';
50
50
  export type { TimelineItem } from './AuditTimeline.svelte';
51
+ export { default as AuditSection } from './AuditSection.svelte';
52
+ export type { AuditAccent } from './AuditSection.svelte';
53
+ export type { AuditDataState } from './audit-state.js';
54
+ export { default as ChartContainer } from './ChartContainer.svelte';
55
+ export { default as CoverageMatrix } from './CoverageMatrix.svelte';
56
+ export type { CoverageMatrixColumn, CoverageMatrixRow } from './CoverageMatrix.svelte';
57
+ export { default as EvidenceCard } from './EvidenceCard.svelte';
58
+ export { default as StatusPill } from './StatusPill.svelte';
59
+ export type { StatusPillTone } from './StatusPill.svelte';
51
60
  export { default as MediaThumbnail } from './MediaThumbnail.svelte';
52
61
  export type { MediaThumbnailSize, MediaThumbnailFit } from './MediaThumbnail.svelte';
@@ -33,4 +33,9 @@ export { default as TwoFactorStepper } from './TwoFactorStepper.svelte';
33
33
  export { default as SystemErrorState } from './SystemErrorState.svelte';
34
34
  export { default as FeedbackNotice } from './FeedbackNotice.svelte';
35
35
  export { default as AuditTimeline } from './AuditTimeline.svelte';
36
+ export { default as AuditSection } from './AuditSection.svelte';
37
+ export { default as ChartContainer } from './ChartContainer.svelte';
38
+ export { default as CoverageMatrix } from './CoverageMatrix.svelte';
39
+ export { default as EvidenceCard } from './EvidenceCard.svelte';
40
+ export { default as StatusPill } from './StatusPill.svelte';
36
41
  export { default as MediaThumbnail } from './MediaThumbnail.svelte';
@@ -1,22 +1,7 @@
1
- import type { DevtoolsQueryDiagnostic, SvadminDevtoolsSnapshot } from '@svadmin/devtools-contract';
2
- export declare const SVADMIN_DEVFRAME_CHANNEL = "svadmin:devtools";
1
+ import { type DevtoolsPageBridge } from '@vibeunion/devtools-devframe';
2
+ import type { DevtoolsCacheActionRequest, DevtoolsCacheActionResult, DevtoolsQueryDiagnostic, SvadminDevtoolsSnapshot } from '@svadmin/devtools-contract';
3
3
  export type { DevtoolsCacheDiagnostics, DevtoolsProviderDiagnostic, DevtoolsQueryDiagnostic, SvadminDevtoolsSnapshot, } from '@svadmin/devtools-contract';
4
- export type SvadminDevtoolsBridge = {
5
- getSnapshot: () => SvadminDevtoolsSnapshot | null;
6
- subscribe: (listener: (snapshot: SvadminDevtoolsSnapshot) => void) => () => void;
7
- cache: {
8
- cancel: (selector?: DevtoolsQuerySelector) => Promise<void>;
9
- invalidate: (selector?: DevtoolsQuerySelector) => Promise<void>;
10
- refetch: (selector?: DevtoolsQuerySelector) => Promise<void>;
11
- remove: (selector?: DevtoolsQuerySelector) => void;
12
- reset: (selector?: DevtoolsQuerySelector) => Promise<void>;
13
- clear: () => void;
14
- clearMutations: () => void;
15
- };
16
- devframe: {
17
- channelName: typeof SVADMIN_DEVFRAME_CHANNEL;
18
- };
19
- };
4
+ export type { DevtoolsCacheActionRequest, DevtoolsCacheActionResult, DevtoolsCacheMutation, } from '@svadmin/devtools-contract';
20
5
  export type DevtoolsQuerySelector = Partial<Pick<DevtoolsQueryDiagnostic, 'provider' | 'resource' | 'operation'>>;
21
6
  type QueryClientLike = {
22
7
  getQueryCache: () => {
@@ -43,19 +28,19 @@ type QueryClientLike = {
43
28
  }) => Promise<void>;
44
29
  getMutationCache: () => {
45
30
  clear: () => void;
31
+ getAll?: () => readonly unknown[];
46
32
  };
47
33
  };
34
+ /**
35
+ * Page-script protocol as seen by a panel. It mirrors the shared DevTools wire
36
+ * (`getSnapshot` + `cacheAction`); the shared bridge installs the same shape on
37
+ * `window.__VIBEUNION_DEVTOOLS__`.
38
+ */
48
39
  export type SvadminDevframeProtocol = {
49
40
  functions: {
50
41
  pageScript: {
51
42
  getSnapshot: () => SvadminDevtoolsSnapshot | null;
52
- cacheCancel: (selector?: DevtoolsQuerySelector) => Promise<void>;
53
- cacheInvalidate: (selector?: DevtoolsQuerySelector) => Promise<void>;
54
- cacheRefetch: (selector?: DevtoolsQuerySelector) => Promise<void>;
55
- cacheRemove: (selector?: DevtoolsQuerySelector) => void;
56
- cacheReset: (selector?: DevtoolsQuerySelector) => Promise<void>;
57
- cacheClear: () => void;
58
- clearMutations: () => void;
43
+ cacheAction: (request: DevtoolsCacheActionRequest) => Promise<DevtoolsCacheActionResult>;
59
44
  };
60
45
  };
61
46
  };
@@ -72,9 +57,10 @@ export declare function createSvadminDevtoolsCacheActions(): {
72
57
  clear: () => void;
73
58
  clearMutations: () => void | undefined;
74
59
  };
75
- export declare function installSvadminDevtoolsBridge(): void;
76
- declare global {
77
- interface Window {
78
- __SVADMIN_DEVTOOLS__?: SvadminDevtoolsBridge;
79
- }
80
- }
60
+ /**
61
+ * Executes a shared-protocol cache action against the attached query client and
62
+ * reports how many entries matched. Only the operations svadmin implements are
63
+ * accepted; mutation-variant operations the panel never advertises throw.
64
+ */
65
+ export declare function runSvadminDevtoolsCacheAction(request: DevtoolsCacheActionRequest): Promise<DevtoolsCacheActionResult>;
66
+ export declare function installSvadminDevtoolsBridge(): DevtoolsPageBridge | null;
@@ -1,11 +1,9 @@
1
1
  import { parseQueryKey } from '@svadmin/core';
2
- import { createPageScriptChannel } from 'devframe/in-page-channel';
2
+ import { installDevtoolsPageBridge, } from '@vibeunion/devtools-devframe';
3
3
  import { DEV } from 'esm-env';
4
- export const SVADMIN_DEVFRAME_CHANNEL = 'svadmin:devtools';
5
4
  const listeners = new Set();
6
5
  let snapshot = null;
7
6
  let queryClient = null;
8
- let devframeChannel = null;
9
7
  export function publishSvadminDevtoolsSnapshot(next) {
10
8
  snapshot = next;
11
9
  for (const listener of listeners)
@@ -58,35 +56,53 @@ export function createSvadminDevtoolsCacheActions() {
58
56
  clearMutations: () => queryClient?.getMutationCache().clear(),
59
57
  };
60
58
  }
61
- export function installSvadminDevtoolsBridge() {
62
- if (typeof window === 'undefined' || !DEV)
63
- return;
64
- if (!devframeChannel) {
65
- const cache = createSvadminDevtoolsCacheActions();
66
- devframeChannel = createPageScriptChannel({
67
- name: SVADMIN_DEVFRAME_CHANNEL,
68
- functions: {
69
- getSnapshot: { type: 'query', handler: getSvadminDevtoolsSnapshot },
70
- cacheCancel: { type: 'action', handler: cache.cancel },
71
- cacheInvalidate: { type: 'action', handler: cache.invalidate },
72
- cacheRefetch: { type: 'action', handler: cache.refetch },
73
- cacheRemove: { type: 'action', handler: cache.remove },
74
- cacheReset: { type: 'action', handler: cache.reset },
75
- cacheClear: { type: 'action', handler: cache.clear },
76
- clearMutations: { type: 'action', handler: cache.clearMutations },
77
- },
78
- });
59
+ /**
60
+ * Executes a shared-protocol cache action against the attached query client and
61
+ * reports how many entries matched. Only the operations svadmin implements are
62
+ * accepted; mutation-variant operations the panel never advertises throw.
63
+ */
64
+ export async function runSvadminDevtoolsCacheAction(request) {
65
+ const { action, selector } = request;
66
+ const actions = createSvadminDevtoolsCacheActions();
67
+ let matched;
68
+ switch (action) {
69
+ case 'cancel':
70
+ matched = matchingQueryKeys(selector).length;
71
+ await actions.cancel(selector);
72
+ break;
73
+ case 'invalidate':
74
+ matched = matchingQueryKeys(selector).length;
75
+ await actions.invalidate(selector);
76
+ break;
77
+ case 'refetch':
78
+ matched = matchingQueryKeys(selector).length;
79
+ await actions.refetch(selector);
80
+ break;
81
+ case 'remove':
82
+ matched = matchingQueryKeys(selector).length;
83
+ actions.remove(selector);
84
+ break;
85
+ case 'reset':
86
+ matched = matchingQueryKeys(selector).length;
87
+ await actions.reset(selector);
88
+ break;
89
+ case 'clear':
90
+ matched = matchingQueryKeys().length;
91
+ actions.clear();
92
+ break;
93
+ case 'clearMutations':
94
+ matched = queryClient?.getMutationCache().getAll?.().length ?? 0;
95
+ actions.clearMutations();
96
+ break;
97
+ default:
98
+ throw new Error(`Unsupported svadmin cache action: ${String(action)}`);
79
99
  }
80
- const bridge = {
100
+ return { action, matched, changed: matched };
101
+ }
102
+ export function installSvadminDevtoolsBridge() {
103
+ return installDevtoolsPageBridge({
104
+ development: DEV,
81
105
  getSnapshot: getSvadminDevtoolsSnapshot,
82
- subscribe: subscribeSvadminDevtools,
83
- cache: createSvadminDevtoolsCacheActions(),
84
- devframe: { channelName: SVADMIN_DEVFRAME_CHANNEL },
85
- };
86
- Object.defineProperty(window, '__SVADMIN_DEVTOOLS__', {
87
- configurable: true,
88
- enumerable: false,
89
- value: bridge,
90
- writable: false,
106
+ cacheAction: runSvadminDevtoolsCacheAction,
91
107
  });
92
108
  }
package/dist/index.d.ts CHANGED
@@ -48,8 +48,9 @@ export { default as Can } from './components/CanAccess.svelte';
48
48
  export { default as UndoableNotification } from './components/UndoableNotification.svelte';
49
49
  export { default as DevTools } from './components/DevTools.svelte';
50
50
  export { default as DevframePanel } from './components/DevframePanel.svelte';
51
- export { attachSvadminDevtoolsQueryClient, createSvadminDevtoolsCacheActions, getSvadminDevtoolsSnapshot, installSvadminDevtoolsBridge, publishSvadminDevtoolsSnapshot, subscribeSvadminDevtools, SVADMIN_DEVFRAME_CHANNEL, } from './devtools-bridge.js';
52
- export type { DevtoolsCacheDiagnostics, DevtoolsProviderDiagnostic, DevtoolsQueryDiagnostic, SvadminDevtoolsBridge, SvadminDevtoolsSnapshot, DevtoolsQuerySelector, SvadminDevframeProtocol, } from './devtools-bridge.js';
51
+ export { attachSvadminDevtoolsQueryClient, createSvadminDevtoolsCacheActions, getSvadminDevtoolsSnapshot, installSvadminDevtoolsBridge, publishSvadminDevtoolsSnapshot, runSvadminDevtoolsCacheAction, subscribeSvadminDevtools, } from './devtools-bridge.js';
52
+ export type { DevtoolsCacheDiagnostics, DevtoolsProviderDiagnostic, DevtoolsQueryDiagnostic, SvadminDevtoolsSnapshot, DevtoolsQuerySelector, SvadminDevframeProtocol, } from './devtools-bridge.js';
53
+ export type { DevtoolsPageBridge } from '@vibeunion/devtools-devframe';
53
54
  export { default as Authenticated } from './components/Authenticated.svelte';
54
55
  export { default as UpdatePasswordPage } from './components/UpdatePasswordPage.svelte';
55
56
  export { default as ProfilePage } from './components/ProfilePage.svelte';
@@ -330,6 +331,15 @@ export type { RowActionItem } from './components/RowActions.svelte';
330
331
  export { default as DetailDrawer } from './components/DetailDrawer.svelte';
331
332
  export { default as AuditTimeline } from './components/content/AuditTimeline.svelte';
332
333
  export type { TimelineItem } from './components/content/AuditTimeline.svelte';
334
+ export { default as AuditSection } from './components/content/AuditSection.svelte';
335
+ export type { AuditAccent } from './components/content/AuditSection.svelte';
336
+ export type { AuditDataState } from './components/content/audit-state.js';
337
+ export { default as ChartContainer } from './components/content/ChartContainer.svelte';
338
+ export { default as CoverageMatrix } from './components/content/CoverageMatrix.svelte';
339
+ export type { CoverageMatrixColumn, CoverageMatrixRow } from './components/content/CoverageMatrix.svelte';
340
+ export { default as EvidenceCard } from './components/content/EvidenceCard.svelte';
341
+ export { default as StatusPill } from './components/content/StatusPill.svelte';
342
+ export type { StatusPillTone } from './components/content/StatusPill.svelte';
333
343
  export type { TagTone } from './components/fields/TagField.svelte';
334
344
  export type { BooleanTone } from './components/fields/BooleanField.svelte';
335
345
  export type { DateFieldFormat } from './components/fields/DateField.svelte';
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ export { default as Can } from './components/CanAccess.svelte';
42
42
  export { default as UndoableNotification } from './components/UndoableNotification.svelte';
43
43
  export { default as DevTools } from './components/DevTools.svelte';
44
44
  export { default as DevframePanel } from './components/DevframePanel.svelte';
45
- export { attachSvadminDevtoolsQueryClient, createSvadminDevtoolsCacheActions, getSvadminDevtoolsSnapshot, installSvadminDevtoolsBridge, publishSvadminDevtoolsSnapshot, subscribeSvadminDevtools, SVADMIN_DEVFRAME_CHANNEL, } from './devtools-bridge.js';
45
+ export { attachSvadminDevtoolsQueryClient, createSvadminDevtoolsCacheActions, getSvadminDevtoolsSnapshot, installSvadminDevtoolsBridge, publishSvadminDevtoolsSnapshot, runSvadminDevtoolsCacheAction, subscribeSvadminDevtools, } from './devtools-bridge.js';
46
46
  export { default as Authenticated } from './components/Authenticated.svelte';
47
47
  export { default as UpdatePasswordPage } from './components/UpdatePasswordPage.svelte';
48
48
  export { default as ProfilePage } from './components/ProfilePage.svelte';
@@ -279,6 +279,11 @@ export { referenceDemoData } from './reference-data.js';
279
279
  export { default as RowActions } from './components/RowActions.svelte';
280
280
  export { default as DetailDrawer } from './components/DetailDrawer.svelte';
281
281
  export { default as AuditTimeline } from './components/content/AuditTimeline.svelte';
282
+ export { default as AuditSection } from './components/content/AuditSection.svelte';
283
+ export { default as ChartContainer } from './components/content/ChartContainer.svelte';
284
+ export { default as CoverageMatrix } from './components/content/CoverageMatrix.svelte';
285
+ export { default as EvidenceCard } from './components/content/EvidenceCard.svelte';
286
+ export { default as StatusPill } from './components/content/StatusPill.svelte';
282
287
  export { default as FilterDropdown } from './components/FilterDropdown.svelte';
283
288
  export { default as BatchActionBar } from './components/BatchActionBar.svelte';
284
289
  export { default as MediaThumbnail } from './components/content/MediaThumbnail.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/ui",
3
- "version": "0.78.2",
3
+ "version": "0.80.0",
4
4
  "description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -139,7 +139,8 @@
139
139
  }
140
140
  },
141
141
  "dependencies": {
142
- "@svadmin/devtools-contract": "^0.2.0",
142
+ "@svadmin/devtools-contract": "^0.3.0",
143
+ "@vibeunion/devtools-devframe": "^0.1.0",
143
144
  "@sinclair/typebox": "^0.34.52",
144
145
  "@floating-ui/dom": "^1.8.0",
145
146
  "esm-env": "^1.2.2",