weifuwu 0.69.0 → 0.71.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.
Files changed (38) hide show
  1. package/README.md +8 -8
  2. package/dist/client/diff.d.ts +7 -6
  3. package/dist/client/index.js +5 -5
  4. package/dist/client/registry.d.ts +17 -2
  5. package/dist/client/render.d.ts +12 -5
  6. package/dist/client/router.d.ts +3 -2
  7. package/dist/client/types.d.ts +41 -18
  8. package/dist/client/ui.d.ts +7 -1
  9. package/dist/client/vnode.d.ts +12 -5
  10. package/dist/components/ApprovalCard/ApprovalCard.d.ts +2 -0
  11. package/dist/components/Badge/Badge.d.ts +6 -0
  12. package/dist/components/Card/Card.d.ts +2 -0
  13. package/dist/components/Cascader/Cascader.d.ts +6 -2
  14. package/dist/components/CodeBlock/CodeBlock.d.ts +2 -2
  15. package/dist/components/CodeBlock/highlight.d.ts +15 -0
  16. package/dist/components/Drawer/Drawer.d.ts +2 -0
  17. package/dist/components/EmptyState/EmptyState.d.ts +3 -2
  18. package/dist/components/Icon/Icon.d.ts +1 -1
  19. package/dist/components/InputNumber/InputNumber.d.ts +2 -2
  20. package/dist/components/Layout/Layout.d.ts +2 -0
  21. package/dist/components/Markdown/parser.d.ts +11 -5
  22. package/dist/components/Menu/Menu.d.ts +1 -1
  23. package/dist/components/ProgressBar/ProgressBar.d.ts +5 -0
  24. package/dist/components/Rate/Rate.d.ts +2 -0
  25. package/dist/components/Sparkline/Sparkline.d.ts +2 -0
  26. package/dist/components/StatCard/StatCard.d.ts +5 -3
  27. package/dist/components/Timeline/Timeline.d.ts +5 -4
  28. package/dist/components/Transfer/Transfer.d.ts +6 -2
  29. package/dist/components/Tree/Tree.d.ts +3 -1
  30. package/dist/components/VirtualTable/VirtualTable.d.ts +9 -1
  31. package/dist/components/index.d.ts +1 -1
  32. package/dist/components/index.js +14 -14
  33. package/dist/components/style.css +874 -331
  34. package/dist/index.js +8 -1
  35. package/dist/layout/weifuwu-layout.css +193 -25
  36. package/docs/components.md +2 -2
  37. package/docs/layout.md +37 -9
  38. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4787,11 +4787,18 @@ function extractParams(path, match) {
4787
4787
  const m = path.match(match.re);
4788
4788
  if (m) {
4789
4789
  for (let i = 0; i < match.keys.length; i++) {
4790
- params[match.keys[i]] = decodeURIComponent(m[i + 1]);
4790
+ params[match.keys[i]] = safeDecode(m[i + 1]);
4791
4791
  }
4792
4792
  }
4793
4793
  return params;
4794
4794
  }
4795
+ function safeDecode(s) {
4796
+ try {
4797
+ return decodeURIComponent(s);
4798
+ } catch {
4799
+ return s;
4800
+ }
4801
+ }
4795
4802
 
4796
4803
  // src/ui/ssr-page.ts
4797
4804
  var DEFAULT_TEMPLATE = (p) => `<!DOCTYPE html>
@@ -46,6 +46,7 @@
46
46
  --wf-dark-bg-hover: rgba(255, 255, 255, 0.06); /* 暗色 hover 反白 */
47
47
  --wf-dark-surface-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
48
48
  --wf-dark-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
49
+ --wf-dark-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.3);
49
50
  --wf-dark-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
50
51
  --wf-dark-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
51
52
  --wf-dark-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4);
@@ -82,6 +83,7 @@
82
83
 
83
84
  /* ── 阴影 ── */
84
85
  --wf-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
86
+ --wf-shadow-inset: inset 0 1px 2px rgba(0,0,0,0.04); /* 内阴影:凹槽/轨道深度(Switch/ThemeSwitch) */
85
87
  --wf-shadow: 0 1px 3px rgba(0,0,0,0.08);
86
88
  --wf-shadow-md: 0 4px 12px rgba(0,0,0,0.1);
87
89
  --wf-shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
@@ -222,6 +224,7 @@
222
224
 
223
225
  /* ── 浮层遮罩(Modal/Drawer overlay,暗色经 --wf-dark-overlay 映射) ── */
224
226
  --wf-overlay: rgba(0, 0, 0, 0.4);
227
+ --wf-overlay-blur: 4px; /* 遮罩毛玻璃(弹层现代质感,不支持 backdrop-filter 的环境自动降级为纯色) */
225
228
 
226
229
  /* ── 表单控件 ── */
227
230
  --wf-accent-color: var(--wf-color-primary);
@@ -307,6 +310,7 @@
307
310
  --wf-shadow: var(--wf-dark-shadow);
308
311
  --wf-shadow-md: var(--wf-dark-shadow-md);
309
312
  --wf-shadow-lg: var(--wf-dark-shadow-lg);
313
+ --wf-shadow-inset: var(--wf-dark-shadow-inset);
310
314
 
311
315
  --wf-color-primary-text: var(--wf-dark-brand-500);
312
316
  --wf-color-success-text: var(--wf-dark-green-500);
@@ -357,6 +361,7 @@
357
361
  --wf-shadow: var(--wf-dark-shadow);
358
362
  --wf-shadow-md: var(--wf-dark-shadow-md);
359
363
  --wf-shadow-lg: var(--wf-dark-shadow-lg);
364
+ --wf-shadow-inset: var(--wf-dark-shadow-inset);
360
365
 
361
366
  --wf-color-primary-text: var(--wf-dark-brand-500);
362
367
  --wf-color-success-text: var(--wf-dark-green-500);
@@ -377,6 +382,36 @@
377
382
  box-sizing: border-box;
378
383
  }
379
384
 
385
+ /* 选区色:primary 调(文字反白,与品牌一致) */
386
+ ::selection {
387
+ background: var(--wf-color-primary-bg);
388
+ color: var(--wf-color-primary-text);
389
+ }
390
+
391
+ /* 主题滚动条(全局——所有滚动区一致;Firefox thin + WebKit 样式) */
392
+ * {
393
+ scrollbar-width: thin;
394
+ scrollbar-color: var(--wf-color-border-dark) transparent;
395
+ }
396
+ ::-webkit-scrollbar {
397
+ width: 8px;
398
+ height: 8px;
399
+ }
400
+ ::-webkit-scrollbar-track {
401
+ background: transparent;
402
+ }
403
+ ::-webkit-scrollbar-thumb {
404
+ background: var(--wf-color-border-dark);
405
+ border-radius: var(--wf-radius-sm);
406
+ border: 2px solid transparent;
407
+ background-clip: padding-box;
408
+ }
409
+ ::-webkit-scrollbar-thumb:hover {
410
+ background: var(--wf-color-text-secondary);
411
+ background-clip: padding-box;
412
+ border: 2px solid transparent;
413
+ }
414
+
380
415
  :root {
381
416
  /* 原生控件/滚动条跟随主题;暗色激活时在 _dark.css 中切为 dark */
382
417
  color-scheme: light;
@@ -447,11 +482,20 @@ button {
447
482
  padding: var(--wf-control-pad-y) var(--wf-space-md);
448
483
  min-height: var(--wf-control-height);
449
484
  cursor: pointer;
485
+ transition: border-color var(--wf-dur-fast) var(--wf-ease-out),
486
+ color var(--wf-dur-fast) var(--wf-ease-out),
487
+ box-shadow var(--wf-dur-fast) var(--wf-ease-out),
488
+ background-color var(--wf-dur-fast) var(--wf-ease-out),
489
+ filter var(--wf-dur-fast) var(--wf-ease-out);
450
490
  }
451
491
  button:hover {
452
492
  border-color: var(--wf-color-primary);
453
493
  color: var(--wf-color-primary-text);
454
494
  }
495
+ button:active:not(:disabled),
496
+ [role="button"]:active {
497
+ filter: brightness(0.95); /* 按压反馈:不偏移布局,适用任意宽度(block/inline) */
498
+ }
455
499
 
456
500
  /* 键盘焦点:button 与 [tabindex] 统一 focus ring(仅键盘触发,鼠标点击不闪) */
457
501
  button:focus-visible,
@@ -460,6 +504,15 @@ button:focus-visible,
460
504
  box-shadow: var(--wf-focus-ring);
461
505
  }
462
506
 
507
+ [role="button"] {
508
+ cursor: pointer;
509
+ transition: background-color var(--wf-dur-fast) var(--wf-ease-out),
510
+ border-color var(--wf-dur-fast) var(--wf-ease-out),
511
+ box-shadow var(--wf-dur-fast) var(--wf-ease-out),
512
+ color var(--wf-dur-fast) var(--wf-ease-out),
513
+ filter var(--wf-dur-fast) var(--wf-ease-out);
514
+ }
515
+
463
516
  /* 触屏设备(coarse pointer):交互目标提升到 44px(Apple HIG / WCAG 2.5.8) */
464
517
  @media (pointer: coarse) {
465
518
  button,
@@ -571,10 +624,13 @@ body.wf-dragging {
571
624
  }
572
625
 
573
626
  @layer layout {
574
- /* stack — 纵向堆叠 */
627
+ /* stack — 纵向堆叠(L3:gap 默认值 :where() 零优先级——wf-gap-* 等覆盖工具
628
+ 靠优先级获胜,不再依赖 import 顺序) */
575
629
  .wf-stack {
576
630
  display: flex;
577
631
  flex-direction: column;
632
+ }
633
+ :where(.wf-stack) {
578
634
  gap: var(--wf-gap, var(--wf-gap-md));
579
635
  }
580
636
  @media (min-width: 640px) { .wf-stack\@sm { flex-direction: row; align-items: var(--wf-align, center); } }
@@ -585,6 +641,8 @@ body.wf-dragging {
585
641
  .wf-stack-reverse {
586
642
  display: flex;
587
643
  flex-direction: column-reverse;
644
+ }
645
+ :where(.wf-stack-reverse) {
588
646
  gap: var(--wf-gap, var(--wf-gap-md));
589
647
  }
590
648
  @media (min-width: 640px) { .wf-stack-reverse\@sm { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
@@ -593,10 +651,13 @@ body.wf-dragging {
593
651
  }
594
652
 
595
653
  @layer layout {
596
- /* row — 横向排列 */
654
+ /* row — 横向排列(L3:gap/align 默认值 :where() 零优先级——wf-gap-*、wf-top 等
655
+ 覆盖工具靠优先级获胜,不再依赖 import 顺序) */
597
656
  .wf-row {
598
657
  display: flex;
599
658
  flex-wrap: wrap;
659
+ }
660
+ :where(.wf-row) {
600
661
  gap: var(--wf-gap, var(--wf-gap-md));
601
662
  align-items: var(--wf-align, center);
602
663
  }
@@ -609,6 +670,8 @@ body.wf-dragging {
609
670
  display: flex;
610
671
  flex-direction: row-reverse;
611
672
  flex-wrap: wrap;
673
+ }
674
+ :where(.wf-row-reverse) {
612
675
  gap: var(--wf-gap, var(--wf-gap-md));
613
676
  align-items: var(--wf-align, center);
614
677
  }
@@ -618,10 +681,12 @@ body.wf-dragging {
618
681
  }
619
682
 
620
683
  @layer layout {
621
- /* split — 两端展开 */
684
+ /* split — 两端展开(L3:align/gap 默认值 :where() 零优先级) */
622
685
  .wf-split {
623
686
  display: flex;
624
687
  justify-content: space-between;
688
+ }
689
+ :where(.wf-split) {
625
690
  align-items: var(--wf-align, center);
626
691
  gap: var(--wf-gap, var(--wf-gap-md));
627
692
  }
@@ -641,57 +706,84 @@ body.wf-dragging {
641
706
  }
642
707
 
643
708
  @layer layout {
644
- /* right — 靠右对齐 */
709
+ /* between — 两端对齐(行内垂直居中;L3:align/gap 默认值 :where() 零优先级) */
710
+ .wf-between {
711
+ display: flex;
712
+ justify-content: space-between;
713
+ }
714
+ :where(.wf-between) {
715
+ align-items: var(--wf-align, center);
716
+ gap: var(--wf-gap, var(--wf-gap-md));
717
+ }
718
+ @media (min-width: 640px) { .wf-between\@sm { display: flex; justify-content: space-between; align-items: var(--wf-align, center); gap: var(--wf-gap, var(--wf-gap-md)); } }
719
+ @media (min-width: 768px) { .wf-between\@md { display: flex; justify-content: space-between; align-items: var(--wf-align, center); gap: var(--wf-gap, var(--wf-gap-md)); } }
720
+ @media (min-width: 1024px) { .wf-between\@lg { display: flex; justify-content: space-between; align-items: var(--wf-align, center); gap: var(--wf-gap, var(--wf-gap-md)); } }
721
+ }
722
+
723
+ @layer layout {
724
+ /* right — 靠右对齐(L3:align/gap 默认值 :where() 零优先级) */
645
725
  .wf-right {
646
726
  display: flex;
647
727
  justify-content: flex-end;
728
+ }
729
+ :where(.wf-right) {
648
730
  align-items: var(--wf-align, center);
649
731
  gap: var(--wf-gap, var(--wf-gap-md));
650
732
  }
651
733
  }
652
734
 
653
735
  @layer layout {
654
- /* top — 顶部对齐 */
736
+ /* top — 顶部对齐(L3:gap 默认值 :where() 零优先级;align-items 是身份属性) */
655
737
  .wf-top {
656
738
  display: flex;
657
739
  align-items: flex-start;
740
+ }
741
+ :where(.wf-top) {
658
742
  gap: var(--wf-gap, var(--wf-gap-md));
659
743
  }
660
744
  }
661
745
 
662
746
  @layer layout {
663
- /* bottom — 底部对齐 */
747
+ /* bottom — 底部对齐(L3:gap 默认值 :where() 零优先级;align-items 是身份属性) */
664
748
  .wf-bottom {
665
749
  display: flex;
666
750
  align-items: flex-end;
751
+ }
752
+ :where(.wf-bottom) {
667
753
  gap: var(--wf-gap, var(--wf-gap-md));
668
754
  }
669
755
  }
670
756
 
671
757
  @layer layout {
672
- /* stretch — 拉伸对齐 */
758
+ /* stretch — 拉伸对齐(L3:gap 默认值 :where() 零优先级;align-items 是身份属性) */
673
759
  .wf-stretch {
674
760
  display: flex;
675
761
  align-items: stretch;
762
+ }
763
+ :where(.wf-stretch) {
676
764
  gap: var(--wf-gap, var(--wf-gap-md));
677
765
  }
678
766
  }
679
767
 
680
768
  @layer layout {
681
- /* around — 均匀环绕分布 */
769
+ /* around — 均匀环绕分布(L3:align/gap 默认值 :where() 零优先级) */
682
770
  .wf-around {
683
771
  display: flex;
684
772
  justify-content: space-around;
773
+ }
774
+ :where(.wf-around) {
685
775
  align-items: var(--wf-align, center);
686
776
  gap: var(--wf-gap, var(--wf-gap-md));
687
777
  }
688
778
  }
689
779
 
690
780
  @layer layout {
691
- /* evenly — 均匀分布 */
781
+ /* evenly — 均匀分布(L3:align/gap 默认值 :where() 零优先级) */
692
782
  .wf-evenly {
693
783
  display: flex;
694
784
  justify-content: space-evenly;
785
+ }
786
+ :where(.wf-evenly) {
695
787
  align-items: var(--wf-align, center);
696
788
  gap: var(--wf-gap, var(--wf-gap-md));
697
789
  }
@@ -707,10 +799,18 @@ body.wf-dragging {
707
799
  }
708
800
 
709
801
  @layer layout {
710
- /* fixed — 固定不伸缩 */
802
+ /* flex-none — 固定不伸缩(推荐名;wf-fixed 为兼容别名——易与 position:fixed 混淆,
803
+ position:fixed 请用 wf-pin) */
804
+ .wf-flex-none,
711
805
  .wf-fixed {
712
806
  flex: none;
713
807
  }
808
+
809
+ /* pin — 固定定位(角标/浮层锚点——position: fixed,坐标由 inline 参数控制) */
810
+ .wf-pin {
811
+ position: fixed;
812
+ z-index: var(--wf-z, var(--wf-cover-z));
813
+ }
714
814
  }
715
815
 
716
816
  @layer layout {
@@ -862,19 +962,24 @@ body.wf-dragging {
862
962
  }
863
963
 
864
964
  @layer layout {
865
- /* grid — 二维网格 */
965
+ /* grid — 二维网格(L3:gap 默认值 :where() 零优先级) */
866
966
  .wf-grid {
867
967
  display: grid;
868
968
  grid-template-columns: var(--wf-cols, repeat(auto-fill, minmax(280px, 1fr)));
969
+ }
970
+ :where(.wf-grid) {
869
971
  gap: var(--wf-gap, var(--wf-gap-lg));
870
972
  }
871
973
  }
872
974
 
873
975
  @layer layout {
874
- /* cluster — 换行簇 */
976
+ /* cluster — 换行簇(L3:justify/align/gap 默认值 :where() 零优先级——
977
+ wf-right 等分布工具叠加时靠优先级获胜) */
875
978
  .wf-cluster {
876
979
  display: flex;
877
980
  flex-wrap: wrap;
981
+ }
982
+ :where(.wf-cluster) {
878
983
  gap: var(--wf-gap, var(--wf-gap-sm));
879
984
  justify-content: var(--wf-justify, center);
880
985
  align-items: var(--wf-align, center);
@@ -882,7 +987,7 @@ body.wf-dragging {
882
987
  }
883
988
 
884
989
  @layer layout {
885
- /* scroll — 可滚动容器 */
990
+ /* scroll — 可滚动容器(全局滚动条样式见 _base.css;此类仅标记语义 + overflow) */
886
991
  .wf-scroll {
887
992
  overflow: auto;
888
993
  }
@@ -920,7 +1025,7 @@ body.wf-dragging {
920
1025
  }
921
1026
  }
922
1027
 
923
- @layer layout {
1028
+ @layer utilities {
924
1029
  /* block — 块级显示 */
925
1030
  .wf-block {
926
1031
  display: block;
@@ -944,9 +1049,25 @@ body.wf-dragging {
944
1049
  }
945
1050
  }
946
1051
 
1052
+ @layer utilities {
1053
+ /* flex — 弹性盒 display 工具(补全 display 族:block/inline/inline-block/contents/hidden/flex)
1054
+ * 核心场景:响应式显隐的 flex 容器——`wf-hidden wf-flex@lg`(窄隐宽显且保持 flex 布局)。
1055
+ * 不可用 wf-block@lg 替代:block 会覆盖 wf-stack 的 display:flex,
1056
+ * 致 flex-direction/gap/mt-auto 全部静默失效(layouts-demo 侧栏 mt-auto 实测踩坑)。 */
1057
+ .wf-flex {
1058
+ display: flex;
1059
+ }
1060
+ @media (min-width: 640px) { .wf-flex\@sm { display: flex; } }
1061
+ @media (min-width: 768px) { .wf-flex\@md { display: flex; } }
1062
+ @media (min-width: 1024px) { .wf-flex\@lg { display: flex; } }
1063
+ }
1064
+
947
1065
  @layer layout {
948
1066
  /* container — 内容宽度约束居中 */
1067
+ /* width: 100% 必须:margin: 0 auto 在 flex 父中阻止 stretch(宽度=内容)
1068
+ ——landing 特性区 404px 而非撑满 1100 容器的根因 */
949
1069
  .wf-container {
1070
+ width: 100%;
950
1071
  max-width: var(--wf-max, 1200px);
951
1072
  margin: 0 auto;
952
1073
  padding: 0 var(--wf-gap, var(--wf-gap-lg));
@@ -969,12 +1090,15 @@ body.wf-dragging {
969
1090
  }
970
1091
 
971
1092
  @layer layout {
972
- /* nowrap — 不换行 */
1093
+ /* nowrap — 不换行(只管 flex-wrap——不定义 align-items,避免与
1094
+ wf-stretch 等对齐工具叠加冲突:双份 CSS 下 nowrap 后置曾覆盖 stretch;
1095
+ L3:gap 默认值 :where() 零优先级) */
973
1096
  .wf-nowrap {
974
1097
  display: flex;
975
1098
  flex-wrap: nowrap;
1099
+ }
1100
+ :where(.wf-nowrap) {
976
1101
  gap: var(--wf-gap, var(--wf-gap-md));
977
- align-items: var(--wf-align, center);
978
1102
  }
979
1103
  }
980
1104
 
@@ -1016,6 +1140,20 @@ body.wf-dragging {
1016
1140
  .wf-py-xl { padding-top: var(--wf-space-xl); padding-bottom: var(--wf-space-xl); }
1017
1141
  .wf-py-2xl { padding-top: var(--wf-space-2xl); padding-bottom: var(--wf-space-2xl); }
1018
1142
 
1143
+ /* ── padding: 单侧顶/底(幽灵类防线抓出 wf-pt-md/wf-pb-md 误用——补全家族) ── */
1144
+ .wf-pt-xs { padding-top: var(--wf-space-xs); }
1145
+ .wf-pt-sm { padding-top: var(--wf-space-sm); }
1146
+ .wf-pt-md { padding-top: var(--wf-space-md); }
1147
+ .wf-pt-lg { padding-top: var(--wf-space-lg); }
1148
+ .wf-pt-xl { padding-top: var(--wf-space-xl); }
1149
+ .wf-pt-2xl { padding-top: var(--wf-space-2xl); }
1150
+ .wf-pb-xs { padding-bottom: var(--wf-space-xs); }
1151
+ .wf-pb-sm { padding-bottom: var(--wf-space-sm); }
1152
+ .wf-pb-md { padding-bottom: var(--wf-space-md); }
1153
+ .wf-pb-lg { padding-bottom: var(--wf-space-lg); }
1154
+ .wf-pb-xl { padding-bottom: var(--wf-space-xl); }
1155
+ .wf-pb-2xl { padding-bottom: var(--wf-space-2xl); }
1156
+
1019
1157
  /* ── margin:重置 ── */
1020
1158
  .wf-m-0 { margin: 0; }
1021
1159
  .wf-p-0 { padding: 0; }
@@ -1045,20 +1183,33 @@ body.wf-dragging {
1045
1183
  /* ── margin: auto(居中) ── */
1046
1184
  .wf-mx-auto { margin-left: auto; margin-right: auto; }
1047
1185
  .wf-my-auto { margin-top: auto; margin-bottom: auto; }
1048
-
1049
- /* ── gap:为 flex/grid 原语设置间距变量 ── */
1050
- .wf-gap-none { --wf-gap: 0; }
1051
- .wf-gap-xs { --wf-gap: var(--wf-gap-xs); }
1052
- .wf-gap-sm { --wf-gap: var(--wf-gap-sm); }
1053
- .wf-gap-md { --wf-gap: var(--wf-gap-md); }
1054
- .wf-gap-lg { --wf-gap: var(--wf-gap-lg); }
1055
- .wf-gap-xl { --wf-gap: var(--wf-gap-xl); }
1056
- .wf-gap-2xl { --wf-gap: var(--wf-gap-2xl); }
1186
+ .wf-mt-auto { margin-top: auto; } /* flex 列推底(侧栏 footer——曾写不存在类静默失效) */
1187
+ .wf-mb-auto { margin-bottom: auto; }
1188
+
1189
+ /* ── gap:直接设置 gap 属性(不设 --wf-gap 变量——变量会继承污染子孙:
1190
+ 壳 root wf-gap-none 曾让 KPI grid 的 gap: var(--wf-gap) = 0 卡无间距) ── */
1191
+ .wf-gap-none { gap: 0; }
1192
+ .wf-gap-xs { gap: var(--wf-gap-xs); }
1193
+ .wf-gap-sm { gap: var(--wf-gap-sm); }
1194
+ .wf-gap-md { gap: var(--wf-gap-md); }
1195
+ .wf-gap-lg { gap: var(--wf-gap-lg); }
1196
+ .wf-gap-xl { gap: var(--wf-gap-xl); }
1197
+ .wf-gap-2xl { gap: var(--wf-gap-2xl); }
1057
1198
 
1058
1199
  /* ── 尺寸 ── */
1059
1200
  .wf-w-full { width: 100%; }
1060
1201
  .wf-h-full { height: 100%; }
1061
1202
  .wf-w-auto { width: auto; }
1203
+
1204
+ /* ── 固定宽度阶梯(对齐 token 阶梯——卡片/面板/容器宽不再内联) ── */
1205
+ .wf-w-xs { width: var(--wf-w-xs, 320px); }
1206
+ .wf-w-sm { width: var(--wf-w-sm, 480px); }
1207
+ .wf-w-md { width: var(--wf-w-md, 640px); }
1208
+ .wf-w-lg { width: var(--wf-w-lg, 768px); }
1209
+ .wf-w-xl { width: var(--wf-w-xl, 1024px); }
1210
+ .wf-w-max { max-width: 100%; }
1211
+ .wf-w-md-auto { width: 100%; }
1212
+ @media (min-width: 768px) { .wf-w-md-auto { width: var(--wf-w-md, 640px); } }
1062
1213
  }
1063
1214
 
1064
1215
  @layer layout {
@@ -1177,6 +1328,23 @@ body.wf-dragging {
1177
1328
  display: inline-flex;
1178
1329
  }
1179
1330
 
1331
+ /* ── 折叠态:图标-only 导航(配合 --wf-sidebar-width 收窄侧栏) ── */
1332
+ .wf-nav--collapsed .wf-nav-item {
1333
+ justify-content: center;
1334
+ gap: 0;
1335
+ padding: var(--wf-space-sm);
1336
+ }
1337
+ .wf-nav--collapsed .wf-nav-label {
1338
+ display: none;
1339
+ }
1340
+ .wf-nav--collapsed .wf-nav-group {
1341
+ text-align: center;
1342
+ padding-left: 0;
1343
+ padding-right: 0;
1344
+ overflow: hidden;
1345
+ white-space: nowrap;
1346
+ }
1347
+
1180
1348
  /* ── 主内容区 ── */
1181
1349
  .wf-main {
1182
1350
  padding: var(--wf-space-lg);
@@ -2,14 +2,14 @@
2
2
 
3
3
  > 本页为 weifuwu 官方文档拆分页 · [返回 README](../README.md)
4
4
 
5
- 92 个 HTML 原语组件。每个是 `(_init, ctx) => (props) => VNode`(两阶段组件,与前端框架同一模型),引用 `--wf-*` CSS 变量做主题。另含 `confirm()` / `toast()` 命令式中间件。
5
+ 109 个 HTML 原语组件。每个是 `(_init, ctx) => (props) => VNode`(两阶段组件,与前端框架同一模型),引用 `--wf-*` CSS 变量做主题。另含 `confirm()` / `toast()` 命令式中间件。
6
6
 
7
7
  > **组件速查(weifuwu 组件 ↔ antd / Element Plus / shadcn-ui 对应 + 迁移示例)**:见 [`docs/components-map.md`](components-map.md)——从其他组件库迁来的开发者按功能直接找对应组件。
8
8
  > **自定义组件开发**:见 [docs/custom-components.md](custom-components.md)——usePopup/useControlled/对话框/AI 组件/类型纪律逐步指南。
9
9
 
10
10
  ```ts
11
11
  import { Button, Input, Table, Modal, Toast } from 'weifuwu/components'
12
- import 'weifuwu/components/style.css' // 包含 Token + 67 布局原语 + 组件样式,一次性引入
12
+ import 'weifuwu/components/style.css' // 包含 Token + 57 布局原语 + 136 工具类 + 组件样式,一次性引入
13
13
  ```
14
14
 
15
15
  ### 使用示例
package/docs/layout.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 本页为 weifuwu 官方文档拆分页 · [返回 README](../README.md)
4
4
 
5
- 纯 CSS 布局原语 + 工具类 + 143 个主题 Token。不绑定任何 JS 框架。
5
+ 纯 CSS 57 个布局原语 + 136 个工具类 + 167 个主题 Token。不绑定任何 JS 框架。
6
6
 
7
7
  > **学习路径与命名规范**:见 [`design/style-guide.md`](../design/style-guide.md)——统一语法 `wf-<域>-<名>`、三档学习(组件 → 10 核心原语 → 完整速查)、场景速查、变量定制。
8
8
 
@@ -25,7 +25,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
25
25
 
26
26
  也支持相对路径:`ctx.ui.css('./src/style.css')`。
27
27
 
28
- ## 70 个布局原语
28
+ ## 57 个布局原语
29
29
 
30
30
  | 类别 | 原语 | 效果 |
31
31
  |------|------|------|
@@ -44,11 +44,12 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
44
44
  | | `wf-bottom` | align-items: flex-end |
45
45
  | | `wf-stretch` | align-items: stretch |
46
46
  | **弹性** | `wf-fill` | flex: 1 + min-width: 0 |
47
- | | `wf-fixed` | flex: none |
47
+ | | `wf-flex-none` | flex: none(`wf-fixed` 为兼容别名——易与 position:fixed 混淆,推荐新名) |
48
48
  | | `wf-auto` | flex: auto |
49
49
  | | `wf-shrink` | min-width/height: 0 |
50
50
  | **Z轴** | `wf-cover` | position: fixed + inset: 0 |
51
- | | `wf-pop` | position: absolute |
51
+ | | `wf-pin` | position: fixed(角标/浮层锚点,坐标由 inline 控制——相对视口) |
52
+ | | `wf-pop` | position: absolute(容器内角标:配合 `wf-layer`/`wf-anchor` 父级) |
52
53
  | | `wf-anchor` | position: relative |
53
54
  | | `wf-layer` | position: relative + z-index |
54
55
  | | `wf-sticky` | position: sticky |
@@ -60,17 +61,24 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
60
61
  | | `wf-scroll` | overflow: auto |
61
62
  | | `wf-clip` | overflow: hidden |
62
63
  | **显隐** | `wf-hidden` `wf-hidden@sm/md/lg` | display: none |
64
+ | | `wf-flex` `wf-flex@sm/md/lg` | display: flex(响应式显隐的 flex 容器恢复用——见「组合规则」) |
63
65
  | | `wf-block` `wf-block@sm/md/lg` | display: block |
64
66
  | | `wf-inline` | display: inline |
65
67
  | | `wf-inline-block` | display: inline-block |
66
68
  | | `wf-contents` | display: contents |
67
- | **间距** | `wf-p-*` / `wf-px-*` / `wf-py-*`(xs~2xl) | padding:全/水平/垂直,引用 `--wf-space-*` |
69
+ | | `wf-dim` | opacity 0.7(运行中/历史态淡化) |
70
+ | **间距** | `wf-p-*` / `wf-px-*` / `wf-py-*`(xs~2xl,另有 `wf-p-0`) | padding:全/水平/垂直,引用 `--wf-space-*` |
71
+ | | `wf-m-0` | margin: 0 |
68
72
  | | `wf-mt-*` / `wf-mb-*` / `wf-my-*`(xs~2xl) | margin:top/bottom/垂直 |
69
- | | `wf-mx-auto` / `wf-my-auto` | margin: auto 居中 |
73
+ | | `wf-pt-*` / `wf-pb-*`(xs~2xl) | padding:单侧顶/底(分隔条场景) |
74
+ | | `wf-mx-auto` / `wf-my-auto` / `wf-mt-auto` / `wf-mb-auto` | margin: auto 居中 / flex 列推底 |
70
75
  | | `wf-gap-*`(xs~2xl) | 为 flex/grid 原语设置 `--wf-gap` |
71
76
  | **尺寸** | `wf-w-full` / `wf-h-full` / `wf-w-auto` | 宽/高 100%、auto |
77
+ | | `wf-w-xs…xl` / `wf-w-max` / `wf-w-md-auto` | 固定宽阶(`--wf-space-*` 派生)/ max-content / md 起 auto |
72
78
  | **边框** | `wf-border` / `wf-border-t/b/l/r` | 1px 边框(`--wf-border-width` + `--wf-color-border`) |
73
- | **面工具** | `wf-bg-secondary/tertiary/brand/success/warning/error/info` | 语义背景色(`--wf-color-*-bg`) |
79
+ | **面工具** | `wf-bg-secondary/tertiary/brand/success/warning/error/info`(`wf-bg-primary` 为 brand 别名) | 语义背景色(`--wf-color-*-bg`) |
80
+ | | `wf-elevate` | 抬升阴影(`--wf-shadow-*`) |
81
+ | | `wf-panel-in` | 内凹面板(次级背景 + 内边距) |
74
82
  | | `wf-pill` | 胶囊圆角(999px,状态徽章/标签/色块) |
75
83
  | | `wf-rounded-sm` `wf-rounded` `wf-rounded-md` `wf-rounded-lg` | 圆角工具(`--wf-radius-*`) |
76
84
  | **气泡** | `wf-bubble` / `wf-bubble--own` / `wf-bubble--ai` | 聊天气泡(pre-wrap + 折行内建) |
@@ -84,13 +92,33 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
84
92
  | | `wf-main` | 主内容区(padding + min-width: 0) |
85
93
  | | `wf-text-*` 排版工具 | 见下文「排版工具」 |
86
94
 
95
+ ### 组合规则(冲突防线)
96
+
97
+ 原语分两类:**布局身份类**(display/position/flex-direction——stack/row/grid/nav/center/cover/pop…)
98
+ 与**叠加工具类**(gap/对齐/显隐——叠加在身份类上)。规则:
99
+
100
+ 1. **同一元素只带一个布局身份类**——两个身份类设置同一属性不同值时 import 顺序定胜负,
101
+ 静默失效(事故例:`wf-nav wf-row` → nav 的 column 胜,横向失效;`wf-grid wf-stack` → stack 胜,网格失效)。
102
+ `style-audit` 静态扫描 class 组合,命中即红。
103
+ 2. **对齐工具可叠加**:`wf-top/right/bottom/stretch/between/around/evenly` 专门用于覆盖
104
+ `wf-row` 等的默认对齐(后导入获胜,组合合法)。`wf-nowrap` 专门关 `wf-row` 的 wrap。
105
+ 3. **`wf-center` 是 column 双轴居中**(不是 justify-center)——要“行内水平居中”用 `wf-cluster`,
106
+ 不要 `wf-row wf-center`(center 的 column 方向会静默覆盖 row)。
107
+ 4. **响应式显隐用 `wf-flex@bp` 恢复**:`wf-hidden wf-flex@lg` = 窄隐宽显(flex 容器)。
108
+ 不可用 `wf-block@lg`——block 会覆盖 `wf-stack` 的 display:flex,gap/mt-auto 静默失效。
109
+ 5. **容器内角标用 `wf-pop`**(父级 `wf-layer`/`wf-anchor`);`wf-pin` 是相对视口的 fixed,
110
+ 嵌进带壳页面会覆盖壳。
111
+
112
+ 完整冲突矩阵由 `node scripts/layout-inventory.mjs --json` 生成(属性指纹自动推导)。
113
+
87
114
  ### 排版工具(`wf-text-*`)
88
115
 
89
116
  | 工具 | 效果 |
90
117
  |------|------|
91
118
  | `wf-text-left/center/right` | text-align |
92
119
  | `wf-text-xs…5xl` | 字号(`--wf-font-size-*`) |
93
- | `wf-text-secondary/tertiary/disabled/brand` | 中性色阶 |
120
+ | `wf-text-secondary/tertiary/disabled/brand`(`wf-text-primary` 为 brand 别名) | 中性色阶 |
121
+ | `wf-nums` | tabular-nums(数值防宽度抖动) |
94
122
  | `wf-text-success/warning/error/info` | 语义色文本(`--wf-color-*`) |
95
123
  | `wf-text-medium/semibold/bold` | 字重 |
96
124
  | `wf-tracking-normal/wide/wider` | letter-spacing |
@@ -101,7 +129,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
101
129
  | `wf-truncate` | 单行省略(ellipsis) |
102
130
  | `wf-line-clamp-2/3` | 多行截断 |
103
131
 
104
- ## 143 个主题 Token
132
+ ## 167 个主题 Token
105
133
 
106
134
  **双层结构**:原始层(Primitive,色值只定义一次,品牌/暗色调校改这里)+ 语义层(Semantic,组件消费)。
107
135
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weifuwu",
3
3
  "type": "module",
4
- "version": "0.69.0",
4
+ "version": "0.71.0",
5
5
  "description": "AI SaaS framework — (req, ctx) => Response",
6
6
  "exports": {
7
7
  ".": {