@svadmin/ui 0.86.0 → 0.88.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +3 -1
  2. package/dist/app.css +188 -5
  3. package/dist/app.theme.css +188 -5
  4. package/dist/component-registry.d.ts +1 -1
  5. package/dist/component-registry.js +2 -2
  6. package/dist/components/ApprovalActionCard.svelte +54 -31
  7. package/dist/components/ApprovalCenter.svelte +73 -31
  8. package/dist/components/AuthPageShell.svelte +13 -2
  9. package/dist/components/AutoTable.svelte +13 -3
  10. package/dist/components/CreatePage.svelte +22 -15
  11. package/dist/components/CreatePage.svelte.d.ts +4 -0
  12. package/dist/components/EditPage.svelte +23 -16
  13. package/dist/components/EditPage.svelte.d.ts +4 -0
  14. package/dist/components/ListPage.svelte +20 -14
  15. package/dist/components/ListPage.svelte.d.ts +2 -0
  16. package/dist/components/LoginPage.svelte +27 -11
  17. package/dist/components/LoginPage.svelte.d.ts +4 -0
  18. package/dist/components/NotificationsSettings.svelte +16 -8
  19. package/dist/components/PagePattern.svelte +2 -2
  20. package/dist/components/PagePattern.svelte.d.ts +1 -1
  21. package/dist/components/PasswordInput.svelte +36 -17
  22. package/dist/components/PasswordInput.svelte.d.ts +3 -0
  23. package/dist/components/ShowPage.svelte +20 -21
  24. package/dist/components/account/MemberDirectory.svelte +17 -4
  25. package/dist/components/account/SecurityLogPage.svelte +8 -1
  26. package/dist/components/content/AuditTimeline.svelte +32 -4
  27. package/dist/components/content/AuditTimeline.svelte.d.ts +9 -0
  28. package/dist/components/content/DataState.svelte +5 -4
  29. package/dist/components/content/DataState.svelte.d.ts +1 -1
  30. package/dist/components/content/FilterToolbar.svelte +7 -1
  31. package/dist/components/content/WorkspaceLayout.svelte +3 -1
  32. package/dist/components/content/WorkspaceLayout.svelte.d.ts +1 -0
  33. package/dist/components/network/TeamCrewTablePage.svelte +8 -1
  34. package/dist/components/network/UserCardsNFTPage.svelte +3 -1
  35. package/dist/components/profile/ProjectsGrid.svelte +1 -1
  36. package/dist/components/profile/TeamsShowcase.svelte +1 -1
  37. package/dist/design-contract.d.ts +1 -1
  38. package/dist/design-contract.js +1 -1
  39. package/dist/page-chrome.css +3 -3
  40. package/dist/recipes.d.ts +102 -69
  41. package/dist/recipes.js +40 -2
  42. package/package.json +1 -1
package/README.md CHANGED
@@ -57,7 +57,9 @@ Use the business page layer as the default customer API:
57
57
  `DataState` for the recurring work patterns inside those pages.
58
58
 
59
59
  `PagePattern` has a shared `state` contract (`ready`, `loading`, `empty`,
60
- `error`, `forbidden`) and hides page content while a non-ready state is active.
60
+ `filtered-empty`, `error`, `forbidden`) and hides page content while a non-ready
61
+ state is active. Use `filtered-empty` when active filters match no records; keep
62
+ it distinct from an initially empty resource.
61
63
  This keeps shadcn-svelte as an implementation source while customers receive a
62
64
  stable, Ant Design-like product workflow. Prefer these APIs over composing
63
65
  primitive `Button`, `Card`, `Dialog`, and `Table` components directly in every
package/dist/app.css CHANGED
@@ -5532,7 +5532,6 @@
5532
5532
  --font-weight-medium: 500;
5533
5533
  --font-weight-semibold: 600;
5534
5534
  --radius-md: calc(var(--radius) - 2px);
5535
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
5536
5535
  --drop-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.15);
5537
5536
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
5538
5537
  --default-transition-duration: 150ms;
@@ -5763,6 +5762,9 @@
5763
5762
  .gap-8 {
5764
5763
  gap: calc(var(--spacing) * 8);
5765
5764
  }
5765
+ .gap-\[var\(--svadmin-space-1\)\] {
5766
+ gap: var(--svadmin-space-1);
5767
+ }
5766
5768
  .gap-\[var\(--svadmin-space-2\)\] {
5767
5769
  gap: var(--svadmin-space-2);
5768
5770
  }
@@ -5802,11 +5804,21 @@
5802
5804
  margin-block-start: calc(var(--svadmin-space-1) * var(--tw-space-y-reverse));
5803
5805
  margin-block-end: calc(var(--svadmin-space-1) * calc(1 - var(--tw-space-y-reverse)));
5804
5806
  }
5807
+ :where(.space-y-\[var\(--svadmin-space-2\)\] > :not(:last-child)) {
5808
+ --tw-space-y-reverse: 0;
5809
+ margin-block-start: calc(var(--svadmin-space-2) * var(--tw-space-y-reverse));
5810
+ margin-block-end: calc(var(--svadmin-space-2) * calc(1 - var(--tw-space-y-reverse)));
5811
+ }
5805
5812
  :where(.space-y-\[var\(--svadmin-space-3\)\] > :not(:last-child)) {
5806
5813
  --tw-space-y-reverse: 0;
5807
5814
  margin-block-start: calc(var(--svadmin-space-3) * var(--tw-space-y-reverse));
5808
5815
  margin-block-end: calc(var(--svadmin-space-3) * calc(1 - var(--tw-space-y-reverse)));
5809
5816
  }
5817
+ :where(.space-y-\[var\(--svadmin-space-4\)\] > :not(:last-child)) {
5818
+ --tw-space-y-reverse: 0;
5819
+ margin-block-start: calc(var(--svadmin-space-4) * var(--tw-space-y-reverse));
5820
+ margin-block-end: calc(var(--svadmin-space-4) * calc(1 - var(--tw-space-y-reverse)));
5821
+ }
5810
5822
  :where(.space-y-\[var\(--svadmin-space-5\)\] > :not(:last-child)) {
5811
5823
  --tw-space-y-reverse: 0;
5812
5824
  margin-block-start: calc(var(--svadmin-space-5) * var(--tw-space-y-reverse));
@@ -5902,6 +5914,9 @@
5902
5914
  .p-0 {
5903
5915
  padding: 0px;
5904
5916
  }
5917
+ .p-3 {
5918
+ padding: calc(var(--spacing) * 3);
5919
+ }
5905
5920
  .p-4 {
5906
5921
  padding: calc(var(--spacing) * 4);
5907
5922
  }
@@ -5914,6 +5929,9 @@
5914
5929
  .px-4 {
5915
5930
  padding-inline: calc(var(--spacing) * 4);
5916
5931
  }
5932
+ .px-\[var\(--svadmin-space-3\)\] {
5933
+ padding-inline: var(--svadmin-space-3);
5934
+ }
5917
5935
  .px-\[var\(--svadmin-space-4\)\] {
5918
5936
  padding-inline: var(--svadmin-space-4);
5919
5937
  }
@@ -6111,12 +6129,18 @@
6111
6129
  .\[--svadmin-metric-state-border-width\:3px\] {
6112
6130
  --svadmin-metric-state-border-width: 3px;
6113
6131
  }
6132
+ .\[--svadmin-metric-state-height\:4\.5rem\] {
6133
+ --svadmin-metric-state-height: 4.5rem;
6134
+ }
6114
6135
  .\[--svadmin-metric-state-height\:6rem\] {
6115
6136
  --svadmin-metric-state-height: 6rem;
6116
6137
  }
6117
6138
  .\[--svadmin-metric-state-padding\:1\.25rem\] {
6118
6139
  --svadmin-metric-state-padding: 1.25rem;
6119
6140
  }
6141
+ .\[--svadmin-metric-state-padding\:var\(--svadmin-space-3\)\] {
6142
+ --svadmin-metric-state-padding: var(--svadmin-space-3);
6143
+ }
6120
6144
  .\[--svadmin-status-color\:var\(--destructive\)\] {
6121
6145
  --svadmin-status-color: var(--destructive);
6122
6146
  }
@@ -6135,6 +6159,9 @@
6135
6159
  .\[--svadmin-table-state-height\:var\(--svadmin-table-state-min-h\)\] {
6136
6160
  --svadmin-table-state-height: var(--svadmin-table-state-min-h);
6137
6161
  }
6162
+ .\[--svadmin-table-state-height\:var\(--svadmin-table-state-min-h-compact\)\] {
6163
+ --svadmin-table-state-height: var(--svadmin-table-state-min-h-compact);
6164
+ }
6138
6165
  .\[writing-mode\:vertical-rl\] {
6139
6166
  writing-mode: vertical-rl;
6140
6167
  }
@@ -6214,18 +6241,30 @@
6214
6241
  .\[\&_\[data-command-group-heading\]\]\:uppercase [data-command-group-heading] {
6215
6242
  text-transform: uppercase;
6216
6243
  }
6244
+ .\[\&\[data-slot\=table-cell\]\]\:py-\[var\(--svadmin-space-1\)\][data-slot=table-cell] {
6245
+ padding-block: var(--svadmin-space-1);
6246
+ }
6217
6247
  .\[\&\[data-slot\=table-cell\]\]\:py-\[var\(--svadmin-space-2\)\][data-slot=table-cell] {
6218
6248
  padding-block: var(--svadmin-space-2);
6219
6249
  }
6220
6250
  .\[\&\[data-slot\=table-cell\]\]\:text-\[length\:var\(--svadmin-text-body\)\][data-slot=table-cell] {
6221
6251
  font-size: var(--svadmin-text-body);
6222
6252
  }
6253
+ .\[\&\[data-slot\=table-cell\]\]\:text-\[length\:var\(--svadmin-text-caption\)\][data-slot=table-cell] {
6254
+ font-size: var(--svadmin-text-caption);
6255
+ }
6256
+ .\[\&\[data-slot\=table-head\]\]\:py-\[var\(--svadmin-space-1\)\][data-slot=table-head] {
6257
+ padding-block: var(--svadmin-space-1);
6258
+ }
6223
6259
  .\[\&\[data-slot\=table-head\]\]\:py-\[var\(--svadmin-space-2\)\][data-slot=table-head] {
6224
6260
  padding-block: var(--svadmin-space-2);
6225
6261
  }
6226
6262
  .\[\&\[data-slot\=table-head\]\]\:text-\[length\:var\(--svadmin-text-body\)\][data-slot=table-head] {
6227
6263
  font-size: var(--svadmin-text-body);
6228
6264
  }
6265
+ .\[\&\[data-slot\=table-head\]\]\:text-\[length\:var\(--svadmin-text-caption\)\][data-slot=table-head] {
6266
+ font-size: var(--svadmin-text-caption);
6267
+ }
6229
6268
  }
6230
6269
  @property --tw-rotate-x {
6231
6270
  syntax: "*";
@@ -6571,7 +6610,7 @@
6571
6610
  transform: translateY(-50%);
6572
6611
  pointer-events: none;
6573
6612
  }
6574
- .svadmin-filter-toolbar .svadmin-filter-toolbar__input {
6613
+ .svadmin-filter-toolbar .svadmin-filter-toolbar__input[data-slot="input"] {
6575
6614
  height: var(--svadmin-filter-control-height);
6576
6615
  padding-inline: calc(var(--svadmin-space-5) + var(--svadmin-space-3));
6577
6616
  font-size: var(--svadmin-filter-control-font);
@@ -6579,9 +6618,9 @@
6579
6618
  }
6580
6619
  .svadmin-filter-toolbar .svadmin-filter-toolbar__clear {
6581
6620
  position: absolute;
6582
- top: 50%;
6621
+ inset-block: 0;
6583
6622
  inset-inline-end: var(--svadmin-space-1);
6584
- transform: translateY(-50%);
6623
+ margin-block: auto;
6585
6624
  }
6586
6625
  .svadmin-filter-toolbar .svadmin-filter-toolbar__toggle {
6587
6626
  flex-shrink: 0;
@@ -7037,6 +7076,7 @@
7037
7076
  --svadmin-row-h: 3rem; /* 48px */
7038
7077
  --svadmin-table-head-h: 2.75rem; /* 44px */
7039
7078
  --svadmin-table-state-min-h: 8rem; /* 128px */
7079
+ --svadmin-table-state-min-h-compact: 6rem; /* 96px */
7040
7080
  --svadmin-space-1: 0.25rem; /* 4px */
7041
7081
  --svadmin-space-2: 0.5rem; /* 8px */
7042
7082
  --svadmin-space-3: 0.75rem; /* 12px */
@@ -10449,6 +10489,28 @@ details[data-svadmin-filter][open] > :not(summary),
10449
10489
  .svadmin-stats-card__footer { margin-top: 0.5rem; }
10450
10490
 
10451
10491
  /* Auth form compatibility for existing consumers. */
10492
+ [data-svadmin-auth-page] {
10493
+ overflow-wrap: anywhere;
10494
+ }
10495
+
10496
+ [data-svadmin-login-form] :is(input, button) {
10497
+ scroll-margin-block: 0.5rem;
10498
+ }
10499
+
10500
+ [data-svadmin-auth-aside],
10501
+ [data-svadmin-auth-main],
10502
+ [data-svadmin-auth-main] > div,
10503
+ [data-svadmin-auth-card],
10504
+ [data-svadmin-auth-card] form,
10505
+ [data-svadmin-auth-brand],
10506
+ [data-svadmin-auth-brand] > :last-child {
10507
+ min-width: 0;
10508
+ }
10509
+
10510
+ [data-svadmin-auth-brand] > :not(:last-child) {
10511
+ flex-shrink: 0;
10512
+ }
10513
+
10452
10514
  .layout-clean-flat [data-svadmin-auth-page] > main > div {
10453
10515
  display: flex;
10454
10516
  width: min(100%, 28.75rem);
@@ -10538,7 +10600,8 @@ details[data-svadmin-filter][open] > :not(summary),
10538
10600
  gap: 0.5rem;
10539
10601
  }
10540
10602
 
10541
- .layout-clean-flat [data-svadmin-auth-page] form > div > div:first-child {
10603
+ /* 标签操作行保持横排,嵌套密码控件仍使用自己的纵向布局。 */
10604
+ .layout-clean-flat [data-svadmin-auth-page] form > div > div:first-child:not(.svadmin-password-input):not([data-svadmin-password-input]) {
10542
10605
  display: flex;
10543
10606
  align-items: center;
10544
10607
  justify-content: space-between;
@@ -10551,6 +10614,28 @@ details[data-svadmin-filter][open] > :not(summary),
10551
10614
  font-weight: 600;
10552
10615
  }
10553
10616
 
10617
+ .svadmin-auth-input-field {
10618
+ position: relative;
10619
+ display: block;
10620
+ min-width: 0;
10621
+ }
10622
+
10623
+ .svadmin-auth-input-field .svadmin-auth-input-control.svadmin-input {
10624
+ padding-inline-start: 2.25rem;
10625
+ }
10626
+
10627
+ .svadmin-auth-input-icon {
10628
+ position: absolute;
10629
+ top: 50%;
10630
+ inset-inline-start: 0.75rem;
10631
+ z-index: 1;
10632
+ width: 1rem;
10633
+ height: 1rem;
10634
+ transform: translateY(-50%);
10635
+ color: var(--muted-foreground);
10636
+ pointer-events: none;
10637
+ }
10638
+
10554
10639
  .layout-clean-flat [data-svadmin-auth-page] form input {
10555
10640
  width: 100%;
10556
10641
  min-height: 2.75rem;
@@ -10591,6 +10676,104 @@ details[data-svadmin-filter][open] > :not(summary),
10591
10676
  }
10592
10677
  }
10593
10678
 
10679
+ /* Password field anatomy. These stable classes do not depend on host Tailwind,
10680
+ * and the logical padding keeps both the lock and reveal control clear of text
10681
+ * in empty, filled, and plain-text states. */
10682
+ .svadmin-password-input {
10683
+ display: grid;
10684
+ min-width: 0;
10685
+ gap: 0.5rem;
10686
+ }
10687
+
10688
+ .svadmin-password-input__label {
10689
+ color: var(--foreground);
10690
+ font-size: 0.875rem;
10691
+ font-weight: 600;
10692
+ }
10693
+
10694
+ .svadmin-password-input__field {
10695
+ position: relative;
10696
+ display: block;
10697
+ min-width: 0;
10698
+ }
10699
+
10700
+ .svadmin-password-input__field .svadmin-password-input__control.svadmin-input {
10701
+ padding-inline-start: 2.25rem;
10702
+ padding-inline-end: 2.5rem;
10703
+ }
10704
+
10705
+ .svadmin-password-input__icon,
10706
+ .svadmin-password-input__toggle {
10707
+ position: absolute;
10708
+ top: 50%;
10709
+ z-index: 1;
10710
+ translate: 0 -50%;
10711
+ }
10712
+
10713
+ .svadmin-password-input__icon {
10714
+ inset-inline-start: 0.75rem;
10715
+ width: 1rem;
10716
+ height: 1rem;
10717
+ color: var(--muted-foreground);
10718
+ pointer-events: none;
10719
+ }
10720
+
10721
+ .svadmin-password-input .svadmin-password-input__toggle {
10722
+ inset-inline-end: 0.25rem;
10723
+ width: 1.75rem;
10724
+ height: 1.75rem;
10725
+ min-height: 0;
10726
+ padding: 0;
10727
+ }
10728
+
10729
+ .svadmin-password-input__glyph {
10730
+ width: 1rem;
10731
+ height: 1rem;
10732
+ }
10733
+
10734
+ .svadmin-password-input__strength {
10735
+ display: flex;
10736
+ min-width: 0;
10737
+ align-items: center;
10738
+ gap: 0.5rem;
10739
+ }
10740
+
10741
+ .svadmin-password-input__strength-track {
10742
+ flex: 1 1 auto;
10743
+ min-width: 0;
10744
+ height: 0.375rem;
10745
+ overflow: hidden;
10746
+ border-radius: 999px;
10747
+ background: var(--muted);
10748
+ }
10749
+
10750
+ .svadmin-password-input__strength-fill {
10751
+ height: 100%;
10752
+ border-radius: inherit;
10753
+ background: var(--destructive);
10754
+ transition: width var(--svadmin-motion-standard) var(--svadmin-motion-easing);
10755
+ }
10756
+
10757
+ .svadmin-password-input__strength-fill[data-tone="warning"] {
10758
+ background: var(--warning);
10759
+ }
10760
+
10761
+ .svadmin-password-input__strength-fill[data-tone="info"] {
10762
+ background: var(--info);
10763
+ }
10764
+
10765
+ .svadmin-password-input__strength-fill[data-tone="success"] {
10766
+ background: var(--success);
10767
+ }
10768
+
10769
+ .svadmin-password-input__strength-label {
10770
+ flex: 0 0 auto;
10771
+ color: var(--muted-foreground);
10772
+ font-size: 0.75rem;
10773
+ line-height: 1.25;
10774
+ white-space: nowrap;
10775
+ }
10776
+
10594
10777
  /* --- Stripe-inspired visual refinement ---------------------
10595
10778
  * The existing clean-flat preset owns the shell geometry. This layer keeps
10596
10779
  * that contract intact while giving the example a single neutral canvas,
@@ -5532,7 +5532,6 @@
5532
5532
  --font-weight-medium: 500;
5533
5533
  --font-weight-semibold: 600;
5534
5534
  --radius-md: calc(var(--radius) - 2px);
5535
- --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
5536
5535
  --drop-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.15);
5537
5536
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
5538
5537
  --default-transition-duration: 150ms;
@@ -5763,6 +5762,9 @@
5763
5762
  .gap-8 {
5764
5763
  gap: calc(var(--spacing) * 8);
5765
5764
  }
5765
+ .gap-\[var\(--svadmin-space-1\)\] {
5766
+ gap: var(--svadmin-space-1);
5767
+ }
5766
5768
  .gap-\[var\(--svadmin-space-2\)\] {
5767
5769
  gap: var(--svadmin-space-2);
5768
5770
  }
@@ -5802,11 +5804,21 @@
5802
5804
  margin-block-start: calc(var(--svadmin-space-1) * var(--tw-space-y-reverse));
5803
5805
  margin-block-end: calc(var(--svadmin-space-1) * calc(1 - var(--tw-space-y-reverse)));
5804
5806
  }
5807
+ :where(.space-y-\[var\(--svadmin-space-2\)\] > :not(:last-child)) {
5808
+ --tw-space-y-reverse: 0;
5809
+ margin-block-start: calc(var(--svadmin-space-2) * var(--tw-space-y-reverse));
5810
+ margin-block-end: calc(var(--svadmin-space-2) * calc(1 - var(--tw-space-y-reverse)));
5811
+ }
5805
5812
  :where(.space-y-\[var\(--svadmin-space-3\)\] > :not(:last-child)) {
5806
5813
  --tw-space-y-reverse: 0;
5807
5814
  margin-block-start: calc(var(--svadmin-space-3) * var(--tw-space-y-reverse));
5808
5815
  margin-block-end: calc(var(--svadmin-space-3) * calc(1 - var(--tw-space-y-reverse)));
5809
5816
  }
5817
+ :where(.space-y-\[var\(--svadmin-space-4\)\] > :not(:last-child)) {
5818
+ --tw-space-y-reverse: 0;
5819
+ margin-block-start: calc(var(--svadmin-space-4) * var(--tw-space-y-reverse));
5820
+ margin-block-end: calc(var(--svadmin-space-4) * calc(1 - var(--tw-space-y-reverse)));
5821
+ }
5810
5822
  :where(.space-y-\[var\(--svadmin-space-5\)\] > :not(:last-child)) {
5811
5823
  --tw-space-y-reverse: 0;
5812
5824
  margin-block-start: calc(var(--svadmin-space-5) * var(--tw-space-y-reverse));
@@ -5902,6 +5914,9 @@
5902
5914
  .p-0 {
5903
5915
  padding: 0px;
5904
5916
  }
5917
+ .p-3 {
5918
+ padding: calc(var(--spacing) * 3);
5919
+ }
5905
5920
  .p-4 {
5906
5921
  padding: calc(var(--spacing) * 4);
5907
5922
  }
@@ -5914,6 +5929,9 @@
5914
5929
  .px-4 {
5915
5930
  padding-inline: calc(var(--spacing) * 4);
5916
5931
  }
5932
+ .px-\[var\(--svadmin-space-3\)\] {
5933
+ padding-inline: var(--svadmin-space-3);
5934
+ }
5917
5935
  .px-\[var\(--svadmin-space-4\)\] {
5918
5936
  padding-inline: var(--svadmin-space-4);
5919
5937
  }
@@ -6111,12 +6129,18 @@
6111
6129
  .\[--svadmin-metric-state-border-width\:3px\] {
6112
6130
  --svadmin-metric-state-border-width: 3px;
6113
6131
  }
6132
+ .\[--svadmin-metric-state-height\:4\.5rem\] {
6133
+ --svadmin-metric-state-height: 4.5rem;
6134
+ }
6114
6135
  .\[--svadmin-metric-state-height\:6rem\] {
6115
6136
  --svadmin-metric-state-height: 6rem;
6116
6137
  }
6117
6138
  .\[--svadmin-metric-state-padding\:1\.25rem\] {
6118
6139
  --svadmin-metric-state-padding: 1.25rem;
6119
6140
  }
6141
+ .\[--svadmin-metric-state-padding\:var\(--svadmin-space-3\)\] {
6142
+ --svadmin-metric-state-padding: var(--svadmin-space-3);
6143
+ }
6120
6144
  .\[--svadmin-status-color\:var\(--destructive\)\] {
6121
6145
  --svadmin-status-color: var(--destructive);
6122
6146
  }
@@ -6135,6 +6159,9 @@
6135
6159
  .\[--svadmin-table-state-height\:var\(--svadmin-table-state-min-h\)\] {
6136
6160
  --svadmin-table-state-height: var(--svadmin-table-state-min-h);
6137
6161
  }
6162
+ .\[--svadmin-table-state-height\:var\(--svadmin-table-state-min-h-compact\)\] {
6163
+ --svadmin-table-state-height: var(--svadmin-table-state-min-h-compact);
6164
+ }
6138
6165
  .\[writing-mode\:vertical-rl\] {
6139
6166
  writing-mode: vertical-rl;
6140
6167
  }
@@ -6214,18 +6241,30 @@
6214
6241
  .\[\&_\[data-command-group-heading\]\]\:uppercase [data-command-group-heading] {
6215
6242
  text-transform: uppercase;
6216
6243
  }
6244
+ .\[\&\[data-slot\=table-cell\]\]\:py-\[var\(--svadmin-space-1\)\][data-slot=table-cell] {
6245
+ padding-block: var(--svadmin-space-1);
6246
+ }
6217
6247
  .\[\&\[data-slot\=table-cell\]\]\:py-\[var\(--svadmin-space-2\)\][data-slot=table-cell] {
6218
6248
  padding-block: var(--svadmin-space-2);
6219
6249
  }
6220
6250
  .\[\&\[data-slot\=table-cell\]\]\:text-\[length\:var\(--svadmin-text-body\)\][data-slot=table-cell] {
6221
6251
  font-size: var(--svadmin-text-body);
6222
6252
  }
6253
+ .\[\&\[data-slot\=table-cell\]\]\:text-\[length\:var\(--svadmin-text-caption\)\][data-slot=table-cell] {
6254
+ font-size: var(--svadmin-text-caption);
6255
+ }
6256
+ .\[\&\[data-slot\=table-head\]\]\:py-\[var\(--svadmin-space-1\)\][data-slot=table-head] {
6257
+ padding-block: var(--svadmin-space-1);
6258
+ }
6223
6259
  .\[\&\[data-slot\=table-head\]\]\:py-\[var\(--svadmin-space-2\)\][data-slot=table-head] {
6224
6260
  padding-block: var(--svadmin-space-2);
6225
6261
  }
6226
6262
  .\[\&\[data-slot\=table-head\]\]\:text-\[length\:var\(--svadmin-text-body\)\][data-slot=table-head] {
6227
6263
  font-size: var(--svadmin-text-body);
6228
6264
  }
6265
+ .\[\&\[data-slot\=table-head\]\]\:text-\[length\:var\(--svadmin-text-caption\)\][data-slot=table-head] {
6266
+ font-size: var(--svadmin-text-caption);
6267
+ }
6229
6268
  }
6230
6269
  @property --tw-rotate-x {
6231
6270
  syntax: "*";
@@ -6571,7 +6610,7 @@
6571
6610
  transform: translateY(-50%);
6572
6611
  pointer-events: none;
6573
6612
  }
6574
- .svadmin-filter-toolbar .svadmin-filter-toolbar__input {
6613
+ .svadmin-filter-toolbar .svadmin-filter-toolbar__input[data-slot="input"] {
6575
6614
  height: var(--svadmin-filter-control-height);
6576
6615
  padding-inline: calc(var(--svadmin-space-5) + var(--svadmin-space-3));
6577
6616
  font-size: var(--svadmin-filter-control-font);
@@ -6579,9 +6618,9 @@
6579
6618
  }
6580
6619
  .svadmin-filter-toolbar .svadmin-filter-toolbar__clear {
6581
6620
  position: absolute;
6582
- top: 50%;
6621
+ inset-block: 0;
6583
6622
  inset-inline-end: var(--svadmin-space-1);
6584
- transform: translateY(-50%);
6623
+ margin-block: auto;
6585
6624
  }
6586
6625
  .svadmin-filter-toolbar .svadmin-filter-toolbar__toggle {
6587
6626
  flex-shrink: 0;
@@ -7037,6 +7076,7 @@
7037
7076
  --svadmin-row-h: 3rem; /* 48px */
7038
7077
  --svadmin-table-head-h: 2.75rem; /* 44px */
7039
7078
  --svadmin-table-state-min-h: 8rem; /* 128px */
7079
+ --svadmin-table-state-min-h-compact: 6rem; /* 96px */
7040
7080
  --svadmin-space-1: 0.25rem; /* 4px */
7041
7081
  --svadmin-space-2: 0.5rem; /* 8px */
7042
7082
  --svadmin-space-3: 0.75rem; /* 12px */
@@ -10449,6 +10489,28 @@ details[data-svadmin-filter][open] > :not(summary),
10449
10489
  .svadmin-stats-card__footer { margin-top: 0.5rem; }
10450
10490
 
10451
10491
  /* Auth form compatibility for existing consumers. */
10492
+ [data-svadmin-auth-page] {
10493
+ overflow-wrap: anywhere;
10494
+ }
10495
+
10496
+ [data-svadmin-login-form] :is(input, button) {
10497
+ scroll-margin-block: 0.5rem;
10498
+ }
10499
+
10500
+ [data-svadmin-auth-aside],
10501
+ [data-svadmin-auth-main],
10502
+ [data-svadmin-auth-main] > div,
10503
+ [data-svadmin-auth-card],
10504
+ [data-svadmin-auth-card] form,
10505
+ [data-svadmin-auth-brand],
10506
+ [data-svadmin-auth-brand] > :last-child {
10507
+ min-width: 0;
10508
+ }
10509
+
10510
+ [data-svadmin-auth-brand] > :not(:last-child) {
10511
+ flex-shrink: 0;
10512
+ }
10513
+
10452
10514
  .layout-clean-flat [data-svadmin-auth-page] > main > div {
10453
10515
  display: flex;
10454
10516
  width: min(100%, 28.75rem);
@@ -10538,7 +10600,8 @@ details[data-svadmin-filter][open] > :not(summary),
10538
10600
  gap: 0.5rem;
10539
10601
  }
10540
10602
 
10541
- .layout-clean-flat [data-svadmin-auth-page] form > div > div:first-child {
10603
+ /* 标签操作行保持横排,嵌套密码控件仍使用自己的纵向布局。 */
10604
+ .layout-clean-flat [data-svadmin-auth-page] form > div > div:first-child:not(.svadmin-password-input):not([data-svadmin-password-input]) {
10542
10605
  display: flex;
10543
10606
  align-items: center;
10544
10607
  justify-content: space-between;
@@ -10551,6 +10614,28 @@ details[data-svadmin-filter][open] > :not(summary),
10551
10614
  font-weight: 600;
10552
10615
  }
10553
10616
 
10617
+ .svadmin-auth-input-field {
10618
+ position: relative;
10619
+ display: block;
10620
+ min-width: 0;
10621
+ }
10622
+
10623
+ .svadmin-auth-input-field .svadmin-auth-input-control.svadmin-input {
10624
+ padding-inline-start: 2.25rem;
10625
+ }
10626
+
10627
+ .svadmin-auth-input-icon {
10628
+ position: absolute;
10629
+ top: 50%;
10630
+ inset-inline-start: 0.75rem;
10631
+ z-index: 1;
10632
+ width: 1rem;
10633
+ height: 1rem;
10634
+ transform: translateY(-50%);
10635
+ color: var(--muted-foreground);
10636
+ pointer-events: none;
10637
+ }
10638
+
10554
10639
  .layout-clean-flat [data-svadmin-auth-page] form input {
10555
10640
  width: 100%;
10556
10641
  min-height: 2.75rem;
@@ -10591,6 +10676,104 @@ details[data-svadmin-filter][open] > :not(summary),
10591
10676
  }
10592
10677
  }
10593
10678
 
10679
+ /* Password field anatomy. These stable classes do not depend on host Tailwind,
10680
+ * and the logical padding keeps both the lock and reveal control clear of text
10681
+ * in empty, filled, and plain-text states. */
10682
+ .svadmin-password-input {
10683
+ display: grid;
10684
+ min-width: 0;
10685
+ gap: 0.5rem;
10686
+ }
10687
+
10688
+ .svadmin-password-input__label {
10689
+ color: var(--foreground);
10690
+ font-size: 0.875rem;
10691
+ font-weight: 600;
10692
+ }
10693
+
10694
+ .svadmin-password-input__field {
10695
+ position: relative;
10696
+ display: block;
10697
+ min-width: 0;
10698
+ }
10699
+
10700
+ .svadmin-password-input__field .svadmin-password-input__control.svadmin-input {
10701
+ padding-inline-start: 2.25rem;
10702
+ padding-inline-end: 2.5rem;
10703
+ }
10704
+
10705
+ .svadmin-password-input__icon,
10706
+ .svadmin-password-input__toggle {
10707
+ position: absolute;
10708
+ top: 50%;
10709
+ z-index: 1;
10710
+ translate: 0 -50%;
10711
+ }
10712
+
10713
+ .svadmin-password-input__icon {
10714
+ inset-inline-start: 0.75rem;
10715
+ width: 1rem;
10716
+ height: 1rem;
10717
+ color: var(--muted-foreground);
10718
+ pointer-events: none;
10719
+ }
10720
+
10721
+ .svadmin-password-input .svadmin-password-input__toggle {
10722
+ inset-inline-end: 0.25rem;
10723
+ width: 1.75rem;
10724
+ height: 1.75rem;
10725
+ min-height: 0;
10726
+ padding: 0;
10727
+ }
10728
+
10729
+ .svadmin-password-input__glyph {
10730
+ width: 1rem;
10731
+ height: 1rem;
10732
+ }
10733
+
10734
+ .svadmin-password-input__strength {
10735
+ display: flex;
10736
+ min-width: 0;
10737
+ align-items: center;
10738
+ gap: 0.5rem;
10739
+ }
10740
+
10741
+ .svadmin-password-input__strength-track {
10742
+ flex: 1 1 auto;
10743
+ min-width: 0;
10744
+ height: 0.375rem;
10745
+ overflow: hidden;
10746
+ border-radius: 999px;
10747
+ background: var(--muted);
10748
+ }
10749
+
10750
+ .svadmin-password-input__strength-fill {
10751
+ height: 100%;
10752
+ border-radius: inherit;
10753
+ background: var(--destructive);
10754
+ transition: width var(--svadmin-motion-standard) var(--svadmin-motion-easing);
10755
+ }
10756
+
10757
+ .svadmin-password-input__strength-fill[data-tone="warning"] {
10758
+ background: var(--warning);
10759
+ }
10760
+
10761
+ .svadmin-password-input__strength-fill[data-tone="info"] {
10762
+ background: var(--info);
10763
+ }
10764
+
10765
+ .svadmin-password-input__strength-fill[data-tone="success"] {
10766
+ background: var(--success);
10767
+ }
10768
+
10769
+ .svadmin-password-input__strength-label {
10770
+ flex: 0 0 auto;
10771
+ color: var(--muted-foreground);
10772
+ font-size: 0.75rem;
10773
+ line-height: 1.25;
10774
+ white-space: nowrap;
10775
+ }
10776
+
10594
10777
  /* --- Stripe-inspired visual refinement ---------------------
10595
10778
  * The existing clean-flat preset owns the shell geometry. This layer keeps
10596
10779
  * that contract intact while giving the example a single neutral canvas,
@@ -11528,7 +11528,7 @@ export declare function createComponentRegistryManifest(): {
11528
11528
  readonly density: readonly ["comfortable", "compact"];
11529
11529
  };
11530
11530
  readonly pageFamilies: readonly ["list", "form", "detail", "workspace", "dashboard"];
11531
- readonly pageStates: readonly ["ready", "loading", "empty", "error", "forbidden"];
11531
+ readonly pageStates: readonly ["ready", "loading", "empty", "filtered-empty", "error", "forbidden"];
11532
11532
  readonly composition: {
11533
11533
  readonly maxPrimaryActions: 1;
11534
11534
  readonly secondaryActions: "overflow";
@@ -24,7 +24,7 @@ const registryEntries = [
24
24
  pageId: { kind: "string" },
25
25
  width: variant("contentPageRecipe", "width"),
26
26
  density: { kind: "enum", values: ["comfortable", "compact"], default: "comfortable", description: "Deprecated compatibility prop; use the shared baseline." },
27
- state: { kind: "enum", values: ["ready", "loading", "empty", "error", "forbidden"], default: "ready" },
27
+ state: { kind: "enum", values: ["ready", "loading", "empty", "filtered-empty", "error", "forbidden"], default: "ready" },
28
28
  stateTitle: { kind: "string" },
29
29
  stateDescription: { kind: "string" },
30
30
  retry: { kind: "callback", description: "Use an error-state retry callback." },
@@ -40,7 +40,7 @@ const registryEntries = [
40
40
  stateAction: { kind: "snippet" },
41
41
  },
42
42
  tokens: semanticTokens,
43
- states: ["ready", "loading", "empty", "error", "forbidden"],
43
+ states: ["ready", "loading", "empty", "filtered-empty", "error", "forbidden"],
44
44
  },
45
45
  {
46
46
  id: "content-page-shell",