@tfdesign/b-end 1.0.4

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 (176) hide show
  1. package/AI_READ_FIRST.md +131 -0
  2. package/LICENSE +21 -0
  3. package/README.md +353 -0
  4. package/package.json +67 -0
  5. package/scripts/check-tfds-contract.mjs +334 -0
  6. package/scripts/check-tfds-integration.mjs +263 -0
  7. package/scripts/postinstall-cursor-skill.mjs +382 -0
  8. package/scripts/setup.mjs +520 -0
  9. package/skills/tfds/CHECKLIST.md +205 -0
  10. package/skills/tfds/COMMON_FAILURES.md +238 -0
  11. package/skills/tfds/DESIGN_PRINCIPLES.md +477 -0
  12. package/skills/tfds/GLOBAL_DESIGN_RULES.md +636 -0
  13. package/skills/tfds/LAYOUT_RECIPES.md +140 -0
  14. package/skills/tfds/LAYOUT_RULES.md +1355 -0
  15. package/skills/tfds/PAGE_ARCHETYPES.md +201 -0
  16. package/skills/tfds/SKILL.md +188 -0
  17. package/skills/tfds/components.index.json +7305 -0
  18. package/skills/tfds/components.summary.json +1809 -0
  19. package/src/_b_end_runtime/components/AiSuggestionShared.jsx +166 -0
  20. package/src/_b_end_runtime/components/Avatar.jsx +325 -0
  21. package/src/_b_end_runtime/components/Avatar.tokens.js +76 -0
  22. package/src/_b_end_runtime/components/AvatarGridPreview.jsx +56 -0
  23. package/src/_b_end_runtime/components/AvatarGroup.jsx +80 -0
  24. package/src/_b_end_runtime/components/AvatarGroup.tokens.js +28 -0
  25. package/src/_b_end_runtime/components/Button.jsx +144 -0
  26. package/src/_b_end_runtime/components/Button.tokens.js +90 -0
  27. package/src/_b_end_runtime/components/Card.jsx +460 -0
  28. package/src/_b_end_runtime/components/Card.tokens.js +124 -0
  29. package/src/_b_end_runtime/components/CardPreview.jsx +51 -0
  30. package/src/_b_end_runtime/components/ChatBubble.jsx +384 -0
  31. package/src/_b_end_runtime/components/ChatBubble.tokens.js +60 -0
  32. package/src/_b_end_runtime/components/ChatBubblePreview.jsx +129 -0
  33. package/src/_b_end_runtime/components/ChatInput.jsx +1399 -0
  34. package/src/_b_end_runtime/components/ChatInput.tokens.js +75 -0
  35. package/src/_b_end_runtime/components/ChatMessage.jsx +2215 -0
  36. package/src/_b_end_runtime/components/ChatMessage.tokens.js +257 -0
  37. package/src/_b_end_runtime/components/ChatMessagePreview.jsx +388 -0
  38. package/src/_b_end_runtime/components/Checkbox.jsx +317 -0
  39. package/src/_b_end_runtime/components/Checkbox.tokens.js +59 -0
  40. package/src/_b_end_runtime/components/ConversationList.jsx +1264 -0
  41. package/src/_b_end_runtime/components/ConversationList.tokens.js +135 -0
  42. package/src/_b_end_runtime/components/ConversationListPreview.jsx +108 -0
  43. package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.jsx +324 -0
  44. package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.tokens.js +69 -0
  45. package/src/_b_end_runtime/components/DatePicker.jsx +739 -0
  46. package/src/_b_end_runtime/components/DatePicker.tokens.js +99 -0
  47. package/src/_b_end_runtime/components/Empty.jsx +141 -0
  48. package/src/_b_end_runtime/components/Empty.tokens.js +40 -0
  49. package/src/_b_end_runtime/components/Form.jsx +609 -0
  50. package/src/_b_end_runtime/components/Form.tokens.js +77 -0
  51. package/src/_b_end_runtime/components/FormFieldStack.jsx +123 -0
  52. package/src/_b_end_runtime/components/FormFieldStack.tokens.js +12 -0
  53. package/src/_b_end_runtime/components/FormTitle.jsx +119 -0
  54. package/src/_b_end_runtime/components/FormTitle.tokens.js +87 -0
  55. package/src/_b_end_runtime/components/FullScreenPage.jsx +97 -0
  56. package/src/_b_end_runtime/components/FullScreenPage.tokens.js +19 -0
  57. package/src/_b_end_runtime/components/Icon.jsx +172 -0
  58. package/src/_b_end_runtime/components/Icon.tokens.js +26 -0
  59. package/src/_b_end_runtime/components/IconGridPreview.jsx +277 -0
  60. package/src/_b_end_runtime/components/InfoDisplayPanel.jsx +620 -0
  61. package/src/_b_end_runtime/components/InfoDisplayPanel.tokens.js +71 -0
  62. package/src/_b_end_runtime/components/InfoDisplayPanelPreview.jsx +133 -0
  63. package/src/_b_end_runtime/components/Input.jsx +258 -0
  64. package/src/_b_end_runtime/components/Input.tokens.js +68 -0
  65. package/src/_b_end_runtime/components/InputNumber.jsx +242 -0
  66. package/src/_b_end_runtime/components/InputNumber.tokens.js +55 -0
  67. package/src/_b_end_runtime/components/Modal.jsx +155 -0
  68. package/src/_b_end_runtime/components/Modal.tokens.js +73 -0
  69. package/src/_b_end_runtime/components/NavBar.jsx +842 -0
  70. package/src/_b_end_runtime/components/NavBar.tokens.js +97 -0
  71. package/src/_b_end_runtime/components/NavBarPreview.jsx +11 -0
  72. package/src/_b_end_runtime/components/Radio.jsx +227 -0
  73. package/src/_b_end_runtime/components/Radio.tokens.js +59 -0
  74. package/src/_b_end_runtime/components/Select.jsx +766 -0
  75. package/src/_b_end_runtime/components/Select.tokens.js +99 -0
  76. package/src/_b_end_runtime/components/Sheet.jsx +132 -0
  77. package/src/_b_end_runtime/components/Sheet.tokens.js +61 -0
  78. package/src/_b_end_runtime/components/Slider.jsx +346 -0
  79. package/src/_b_end_runtime/components/Slider.tokens.js +47 -0
  80. package/src/_b_end_runtime/components/Switch.jsx +124 -0
  81. package/src/_b_end_runtime/components/Switch.tokens.js +38 -0
  82. package/src/_b_end_runtime/components/Table.jsx +1338 -0
  83. package/src/_b_end_runtime/components/Table.tokens.js +147 -0
  84. package/src/_b_end_runtime/components/TablePreview.jsx +599 -0
  85. package/src/_b_end_runtime/components/Tabs.jsx +149 -0
  86. package/src/_b_end_runtime/components/Tabs.tokens.js +102 -0
  87. package/src/_b_end_runtime/components/Tag.jsx +199 -0
  88. package/src/_b_end_runtime/components/Tag.tokens.js +171 -0
  89. package/src/_b_end_runtime/components/TagBar.jsx +1134 -0
  90. package/src/_b_end_runtime/components/TagBar.tokens.js +75 -0
  91. package/src/_b_end_runtime/components/TagGridPreview.jsx +23 -0
  92. package/src/_b_end_runtime/components/TagInput.jsx +382 -0
  93. package/src/_b_end_runtime/components/TagInput.tokens.js +52 -0
  94. package/src/_b_end_runtime/components/TextArea.jsx +363 -0
  95. package/src/_b_end_runtime/components/TextArea.tokens.js +65 -0
  96. package/src/_b_end_runtime/components/TimePicker.jsx +444 -0
  97. package/src/_b_end_runtime/components/TimePicker.tokens.js +77 -0
  98. package/src/_b_end_runtime/components/Toast.jsx +120 -0
  99. package/src/_b_end_runtime/components/Toast.tokens.js +146 -0
  100. package/src/_b_end_runtime/components/Tooltip.jsx +282 -0
  101. package/src/_b_end_runtime/components/Tooltip.tokens.js +48 -0
  102. package/src/_b_end_runtime/components/TooltipPreview.jsx +50 -0
  103. package/src/_b_end_runtime/components/Upload.jsx +455 -0
  104. package/src/_b_end_runtime/components/Upload.tokens.js +47 -0
  105. package/src/_b_end_runtime/components/avatar-assets/avatar-default.png +0 -0
  106. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-1.png +0 -0
  107. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-2.png +0 -0
  108. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-3.png +0 -0
  109. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-4.png +0 -0
  110. package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-5.png +0 -0
  111. package/src/_b_end_runtime/components/empty-assets/administrator-1.svg +40 -0
  112. package/src/_b_end_runtime/components/empty-assets/administrator-2.svg +33 -0
  113. package/src/_b_end_runtime/components/empty-assets/construction.svg +33 -0
  114. package/src/_b_end_runtime/components/empty-assets/failure.svg +49 -0
  115. package/src/_b_end_runtime/components/empty-assets/idle.svg +34 -0
  116. package/src/_b_end_runtime/components/empty-assets/no-access.svg +36 -0
  117. package/src/_b_end_runtime/components/empty-assets/no-content.svg +77 -0
  118. package/src/_b_end_runtime/components/empty-assets/no-result.svg +61 -0
  119. package/src/_b_end_runtime/components/empty-assets/not-found.svg +46 -0
  120. package/src/_b_end_runtime/components/empty-assets/success.svg +38 -0
  121. package/src/_b_end_runtime/components/file-type-assets/batch-report.png +0 -0
  122. package/src/_b_end_runtime/components/file-type-assets/catcat.svg +21 -0
  123. package/src/_b_end_runtime/components/file-type-assets/code.png +0 -0
  124. package/src/_b_end_runtime/components/file-type-assets/conversation.png +0 -0
  125. package/src/_b_end_runtime/components/file-type-assets/document.png +0 -0
  126. package/src/_b_end_runtime/components/file-type-assets/feishu-card.png +0 -0
  127. package/src/_b_end_runtime/components/file-type-assets/feishu-sheet.png +0 -0
  128. package/src/_b_end_runtime/components/file-type-assets/feishu.png +0 -0
  129. package/src/_b_end_runtime/components/file-type-assets/image.png +0 -0
  130. package/src/_b_end_runtime/components/file-type-assets/index.js +105 -0
  131. package/src/_b_end_runtime/components/file-type-assets/knowledge.png +0 -0
  132. package/src/_b_end_runtime/components/file-type-assets/pdf.png +0 -0
  133. package/src/_b_end_runtime/components/file-type-assets/pe.png +0 -0
  134. package/src/_b_end_runtime/components/file-type-assets/strategy.png +0 -0
  135. package/src/_b_end_runtime/components/file-type-assets/table.png +0 -0
  136. package/src/_b_end_runtime/components/file-type-assets/webpage.png +0 -0
  137. package/src/_b_end_runtime/components/file-type-assets/xmind.png +0 -0
  138. package/src/_b_end_runtime/components/icons/icon-data.js +12496 -0
  139. package/src/_b_end_runtime/components/nav-bar-assets/bytehi-logo-mark.svg +21 -0
  140. package/src/_b_end_runtime/components/table-assets/avatar.png +0 -0
  141. package/src/_b_end_runtime/components/table-assets/button.png +0 -0
  142. package/src/_b_end_runtime/components/table-assets/icon-chevron-down.png +0 -0
  143. package/src/_b_end_runtime/components/table-cell-assets/avatar.png +0 -0
  144. package/src/_b_end_runtime/components/table-cell-assets/button.png +0 -0
  145. package/src/_b_end_runtime/components/table-cell-assets/checkbox.png +0 -0
  146. package/src/_b_end_runtime/components/table-cell-assets/icon-chevron-right.png +0 -0
  147. package/src/_b_end_runtime/components/table-cell-assets/icon.png +0 -0
  148. package/src/_b_end_runtime/components/table-cell-assets/semi-icons-handle.png +0 -0
  149. package/src/_b_end_runtime/components/table-cell-assets/semi-icons-tree-triangle-right.png +0 -0
  150. package/src/_b_end_runtime/components/table-cell-assets/switch.png +0 -0
  151. package/src/_b_end_runtime/components/tagShared.js +3 -0
  152. package/src/_b_end_runtime/components/team-avatar-assets/chengcheng-murphy.png +0 -0
  153. package/src/_b_end_runtime/components/team-avatar-assets/duan-ran.png +0 -0
  154. package/src/_b_end_runtime/components/team-avatar-assets/guo-zhezhi.png +0 -0
  155. package/src/_b_end_runtime/components/team-avatar-assets/li-siru.png +0 -0
  156. package/src/_b_end_runtime/components/team-avatar-assets/liu-delin.png +0 -0
  157. package/src/_b_end_runtime/components.js +3499 -0
  158. package/src/_b_end_runtime/index.js +9 -0
  159. package/src/_b_end_runtime/page-patterns/BasePageFramePattern.jsx +395 -0
  160. package/src/_b_end_runtime/page-patterns/ChatConversationPattern.jsx +989 -0
  161. package/src/_b_end_runtime/page-patterns/ChatHomePagePattern.jsx +281 -0
  162. package/src/_b_end_runtime/page-patterns/CopilotPagePattern.jsx +380 -0
  163. package/src/_b_end_runtime/page-patterns/CustomerServiceWorkspaceFramePattern.jsx +392 -0
  164. package/src/_b_end_runtime/page-patterns/IMConversationPattern.jsx +590 -0
  165. package/src/_b_end_runtime/page-patterns/McpManagementPage.jsx +237 -0
  166. package/src/_b_end_runtime/page-patterns/StrategyListPage.jsx +189 -0
  167. package/src/_b_end_runtime/page-patterns/TabTopBarListPage.jsx +594 -0
  168. package/src/_b_end_runtime/page-patterns/VariableManagementPage.jsx +87 -0
  169. package/src/_b_end_runtime/page-patterns/pageListShared.jsx +177 -0
  170. package/src/_b_end_runtime/patterns.js +428 -0
  171. package/src/_b_end_runtime/preview-registry.jsx +4719 -0
  172. package/src/_b_end_runtime/teamMembers.js +56 -0
  173. package/src/_b_end_runtime/tokens.js +500 -0
  174. package/src/index.d.ts +1073 -0
  175. package/src/index.js +52 -0
  176. package/theme.css +350 -0
@@ -0,0 +1,3499 @@
1
+ // B端设计系统(id: b-end)— 组件注册表
2
+ //
3
+ // 此文件是平台展示 & AI 读取的"组件目录"
4
+ // 实际组件实现:./components/{Name}.jsx(Tailwind 内联)
5
+ // Token 映射表:./components/{Name}.tokens.js
6
+ //
7
+ // ── category: basic(基础组件)AI 选型速查 ──
8
+ // Avatar:单个用户/机器人/AI 的身份锚点(表格主列、消息气泡侧、导航底栏)。
9
+ // AvatarGroup:同一语境下「多人」的缩略表达(固定 5 人交叠),不做可配人数列表头。
10
+ // Icon:装饰性或功能性矢量符号(按钮内、列表前缀、空状态),不承担「人像」语义。
11
+ // Button:触发提交/打开弹窗/行内操作等离散动作;不负责「切换同页多块内容」。
12
+ // Tabs:同一容器内多块平级内容的视图切换(不离开当前页);不负责路由级站点导航。
13
+ // Input:用户自造字符串(名称、搜索词、备注);选项来自固定枚举时不要用 Input 冒充选择器。
14
+ // Select:从较多或会增长的枚举里选一个/多选(tag 模式);选项很少且需一眼比较时用 Radio。
15
+ // Radio:互斥单选且选项少(建议 ≤5)并平铺展示;与 Checkbox(多选)、Switch(单布尔)区分。
16
+ // Checkbox:同一维度下多选或「勾选同意」;与 Radio(单选)、Switch(即时开/关)区分。
17
+ // Toast:页面级轻量反馈条(成功/信息/警示/错误);本条目为条形容器规范,定时关闭与堆叠由业务层实现。
18
+
19
+ const FONT_WEIGHT_RUNTIME_RULE = '【字重实现规范】涉及 semibold / bold 的运行时字重时,不要直接依赖 `font-semibold`、`font-bold` 等 Tailwind 语义类;统一改用显式 `font-weight` token 写法:600 → `[font-weight:var(--font-semibold)]`,700 → `[font-weight:var(--font-bold)]`(需提高优先级时对整条任意属性加 Tailwind important 前缀)。原因:Tailwind v4 下以 `--font-` 开头的 token 名可能被误解析到 font-family 语义,导致字重不稳定。分档与典型落点见 Skill `GLOBAL_DESIGN_RULES.md` §2.2:`FormTitle`/表单字段标题/会话列表项标题/Button/Tabs 选中等主路径为 600;700 仅组件 rules 已约定处。';
20
+ const LOCAL_MEMBER_AVATAR_RULE = '【头像素材来源】所有人像类头像默认优先使用包内本地成员头像素材(team-avatar-assets:李思儒 / 程程murphy / 刘德琳 / 段然 / 郭泽智,可随机或按数据 seed 取用);Avatar 图片态未传 src 时会自动随机使用本地成员头像。生成 IM、ChatBubble、NavBar、Table avatar/avatarText、ConversationList 等头像场景时,禁止使用 pravatar/Unsplash/placeholder/随机外链头像;只有业务真实接口已返回用户头像 URL 时才传远程 src。';
21
+
22
+ const AVATAR_PREVIEW = {
23
+ shape: {
24
+ round: { borderRadius: '9999px' },
25
+ rect: { borderRadius: '3px' },
26
+ },
27
+ type: {
28
+ image: { content: 'photo', objectFit: 'cover', objectPosition: 'center top' },
29
+ eng: { bgColor: '#A7A8B0', textColor: '#FFFFFF' },
30
+ ch: { roundBg: '#56D3BC', rectBg: '#56D3BC', textColor: '#FFFFFF' },
31
+ fallback: { bgColor: '#E6E7EA', iconColor: '#C6C7CD' },
32
+ robot: { bgColor: '#24B9D9', glyphColor: '#FCFCFC', detailColor: '#000000' },
33
+ },
34
+ size: {
35
+ xxs: { width: '16px', height: '16px', fontSize: '12px' },
36
+ mini: { width: '20px', height: '20px', fontSize: '12px' },
37
+ xs: { width: '24px', height: '24px', fontSize: '12px' },
38
+ s: { width: '32px', height: '32px', fontSize: '18px' },
39
+ default: { width: '40px', height: '40px', fontSize: '18px' },
40
+ m: { width: '48px', height: '48px', fontSize: '20px' },
41
+ },
42
+ };
43
+
44
+ const BUTTON_PREVIEW = {
45
+ variant: {
46
+ primary: { bgColor: '#111116', textColor: '#FFFFFF', borderColor: 'transparent', hoverBg: '#1C1C23' },
47
+ 'outline-brand': { bgColor: '#FFFFFF', textColor: '#18B49D', borderColor: '#56D3BC', hoverBg: '#EAFAF6' },
48
+ 'outline-black': { bgColor: '#FFFFFF', textColor: '#182230', borderColor: '#E4E7EC', hoverBg: '#FCFCFD' },
49
+ 'ghost-brand': { bgColor: 'transparent', textColor: '#18B49D', borderColor: 'transparent', hoverBg: '#EAFAF6' },
50
+ 'ghost-black': { bgColor: 'transparent', textColor: '#182230', borderColor: 'transparent', hoverBg: '#F9FAFB' },
51
+ 'text-brand': { bgColor: 'transparent', textColor: '#18B49D', borderColor: 'transparent', hoverColor: '#129683' },
52
+ 'text-black': { bgColor: 'transparent', textColor: '#182230', borderColor: 'transparent', hoverColor: '#475467' },
53
+ },
54
+ size: {
55
+ sm: { height: '24px', paddingX: '8px', paddingY: '4px', fontSize: '12px', gap: '4px' },
56
+ md: { height: '36px', paddingX: '16px', paddingY: '8px', fontSize: '14px', gap: '8px' },
57
+ },
58
+ radius: { rounded: { borderRadius: '8px' }, full: { borderRadius: '9999px' } },
59
+ base: { fontWeight: '500', borderWidth: '1px' },
60
+ };
61
+
62
+
63
+
64
+ const TABS_PREVIEW = {
65
+ variant: {
66
+ 'line': {
67
+ activeTextColor: '#182230', inactiveTextColor: '#667085',
68
+ indicatorColor: '#56D3BC', indicatorHeight: '2px',
69
+ borderColor: '#E4E7EC',
70
+ },
71
+ 'button': {
72
+ activeBg: '#EAFAF6', activeTextColor: '#18B49D',
73
+ inactiveTextColor: '#667085', hoverBg: 'rgba(83, 96, 143, 0.07)',
74
+ borderRadius: '8px',
75
+ },
76
+ 'pill': {
77
+ containerBg: 'rgba(255, 255, 255, 0.5)', containerBorder: '#FFFFFF',
78
+ activeBg: '#111116', activeTextColor: '#FFFFFF',
79
+ inactiveTextColor: '#475467', hoverBg: 'rgba(83, 96, 143, 0.07)',
80
+ borderRadius: '9999px',
81
+ },
82
+ 'segment': {
83
+ containerBg: 'rgba(83, 96, 143, 0.07)',
84
+ activeBg: '#FFFFFF', activeTextColor: '#182230',
85
+ inactiveTextColor: '#667085', hoverBg: 'rgba(83, 96, 143, 0.07)',
86
+ borderRadius: '8px',
87
+ },
88
+ },
89
+ size: {
90
+ // segment(分段器)SM:整体 36px(item 32px + 容器内距 2px×2)
91
+ sm: { lineHeight: '38px', buttonHeight: '32px', pillHeight: '28px', segmentHeight: '36px', paddingX: '12px', pillPaddingX: '16px', fontSize: '12px' },
92
+ md: { lineHeight: '46px', buttonHeight: '36px', pillHeight: '32px', segmentHeight: '36px', paddingX: '12px', pillPaddingX: '16px', fontSize: '14px' },
93
+ lg: { lineHeight: '54px', buttonHeight: '40px', pillHeight: '36px', segmentHeight: '40px', paddingX: '16px', pillPaddingX: '20px', fontSize: '14px' },
94
+ },
95
+ base: { fontWeight: '400', activeFontWeight: '600', lineHeight: '20px' },
96
+ };
97
+
98
+ const INPUT_PREVIEW = {
99
+ status: {
100
+ default: { bgColor: '#FFFFFF', borderColor: '#E4E7EC', hoverBorderColor: '#D0D5DD', focusBorderColor: '#56D3BC' },
101
+ error: { bgColor: '#FEF2F1', borderColor: '#E4E7EC', hoverBg: '#FCD9D7', focusBorderColor: '#F74331' },
102
+ },
103
+ size: {
104
+ sm: { height: '24px', paddingLeft: '8px', paddingRight: '8px', fontSize: '12px', gap: '4px' },
105
+ md: { height: '36px', paddingLeft: '12px', paddingRight: '12px', fontSize: '14px', gap: '8px' },
106
+ },
107
+ base: { width: '300px', borderRadius: '8px', borderWidth: '1px', textColor: '#182230', placeholderColor: '#667085', focusPlaceholderColor: '#98A2B3', disabledBg: '#F9FAFB', disabledTextColor: '#98A2B3', disabledOpacity: '0.6', lineHeight: '20px' },
108
+ };
109
+
110
+ const TEXTAREA_PREVIEW = {
111
+ status: INPUT_PREVIEW.status,
112
+ /** 组件详情「配置」侧栏与 props 默认对齐(预览控件 + 基组件枚举) */
113
+ panelDefaults: {
114
+ fillMode: 'empty',
115
+ countOverflow: 'off',
116
+ availability: 'default',
117
+ minRows: 3,
118
+ status: 'default',
119
+ resize: 'vertical',
120
+ },
121
+ body: { paddingX: '12px', paddingY: '6px(minRows=1 时上下各 8px)', fontSize: '14px', lineHeight: '20px' },
122
+ code: {
123
+ paddingX: '16px',
124
+ paddingY: '16px',
125
+ fontSize: '14px',
126
+ lineHeight: '20px',
127
+ gutterFontSize: '14px',
128
+ gutterLineHeight: '20px',
129
+ gutterColor: '#98A2B3',
130
+ },
131
+ minRows: {
132
+ 1: { minHeight: '36px(8+20+8,与 Input md 同高)' },
133
+ 2: { minHeight: '52px' },
134
+ 3: { minHeight: '72px', note: '默认' },
135
+ 4: { minHeight: '92px' },
136
+ 5: { minHeight: '112px' },
137
+ },
138
+ base: {
139
+ ...INPUT_PREVIEW.base,
140
+ counterFontSize: '12px',
141
+ counterLineHeight: '16px',
142
+ },
143
+ };
144
+
145
+ const INPUT_NUMBER_PREVIEW = {
146
+ ...INPUT_PREVIEW,
147
+ controls: {
148
+ outer: { width: '16px', height: '36px', gap: '4px', iconSize: '8px' },
149
+ inner: { width: '16px', height: '32px', right: '1px', iconSize: '8px', visibility: 'hover/focus' },
150
+ },
151
+ };
152
+
153
+ const FORM_PREVIEW = {
154
+ layout: {
155
+ vertical: { desc: '单列纵向表单,字段间距 24px' },
156
+ grid: { desc: '双列全量表单,按 Figma FormField top/left 成对展示' },
157
+ },
158
+ labelPosition: {
159
+ top: { labelWidth: 'auto', fieldWidth: '300px', fieldGap: '4px' },
160
+ left: { labelWidth: '96px', fieldWidth: '700px', columnGap: '24px' },
161
+ },
162
+ field: {
163
+ labelColor: '#475467',
164
+ labelWeight: '600',
165
+ helpColor: '#667085',
166
+ errorColor: '#F74331',
167
+ controlWidth: '300px / 自适应',
168
+ },
169
+ };
170
+
171
+ const FORM_TITLE_PREVIEW = {
172
+ variant: {
173
+ form: {
174
+ layout: 'column',
175
+ titleFontSize: '20px',
176
+ titleLineHeight: '28px',
177
+ titleFontWeight: '600',
178
+ titleColor: '#182230',
179
+ descriptionFontSize: '14px',
180
+ descriptionLineHeight: '20px',
181
+ descriptionFontWeight: '400',
182
+ descriptionColor: '#667085',
183
+ indicator: 'none',
184
+ gap: '4px',
185
+ },
186
+ 'level-1': {
187
+ layout: 'column',
188
+ titleFontSize: '16px',
189
+ titleLineHeight: '22px',
190
+ titleFontWeight: '600',
191
+ titleColor: '#182230',
192
+ descriptionFontSize: '14px',
193
+ descriptionLineHeight: '20px',
194
+ descriptionFontWeight: '400',
195
+ descriptionColor: '#667085',
196
+ indicator: '3px × 16px',
197
+ gap: '4px',
198
+ },
199
+ 'level-2': {
200
+ layout: 'row',
201
+ titleFontSize: '14px',
202
+ titleLineHeight: '20px',
203
+ titleFontWeight: '600',
204
+ titleColor: '#182230',
205
+ descriptionFontSize: '14px',
206
+ descriptionLineHeight: '20px',
207
+ descriptionFontWeight: '400',
208
+ descriptionColor: '#667085',
209
+ indicator: '3px × 14px',
210
+ gap: '4px',
211
+ },
212
+ 'level-3': {
213
+ layout: 'row',
214
+ titleFontSize: '14px',
215
+ titleLineHeight: '20px',
216
+ titleFontWeight: '600',
217
+ titleColor: '#182230',
218
+ descriptionFontSize: '14px',
219
+ descriptionLineHeight: '20px',
220
+ descriptionFontWeight: '400',
221
+ descriptionColor: '#667085',
222
+ indicator: '3px × 14px',
223
+ gap: '4px',
224
+ },
225
+ card: {
226
+ layout: 'row',
227
+ titleFontSize: '14px',
228
+ titleLineHeight: '20px',
229
+ titleFontWeight: '600',
230
+ titleColor: '#475467',
231
+ descriptionFontSize: '14px',
232
+ descriptionLineHeight: '20px',
233
+ descriptionFontWeight: '400',
234
+ descriptionColor: '#667085',
235
+ indicator: 'none',
236
+ gap: '4px',
237
+ },
238
+ },
239
+ };
240
+
241
+ const SELECT_PREVIEW = {
242
+ type: {
243
+ default: { desc: '默认文字选择器' },
244
+ tag: { desc: 'TagSelect 标签选择器,属于 Select 的标签分类' },
245
+ },
246
+ status: {
247
+ default: { bgColor: '#FFFFFF', borderColor: '#E4E7EC', hoverBorderColor: '#D0D5DD', openBorderColor: '#56D3BC' },
248
+ error: { bgColor: '#FEF2F1', borderColor: '#E4E7EC', hoverBg: '#FCD9D7', openBorderColor: '#F74331' },
249
+ },
250
+ size: {
251
+ sm: { minHeight: '24px', paddingX: '8px', fontSize: '12px', gap: '4px' },
252
+ md: { minHeight: '36px', paddingX: '12px', fontSize: '14px', gap: '8px' },
253
+ },
254
+ base: {
255
+ width: '300px', borderRadius: '8px', borderWidth: '1px', lineHeight: '20px',
256
+ panelMaxHeight: '240px', panelShadow: '0 10px 15px -3px rgba(0,0,0,0.08)',
257
+ optionSelectedBg: '#EAFAF6', optionSelectedText: '#56D3BC', optionHoverBg: '#FCFCFD',
258
+ },
259
+ };
260
+
261
+ const RADIO_PREVIEW = {
262
+ variant: {
263
+ brand: { checkedFill: '#56D3BC', checkedHover: '#32C4AC', checkedActive: '#18B49D', centerDot: '#FFFFFF', focusRing: '#A8E8D7', disabledChecked: '#A8E8D7' },
264
+ black: { checkedFill: '#111116', checkedHover: '#1C1C23', checkedActive: '#2E2F38', centerDot: '#FFFFFF', focusRing: '#878892', disabledChecked: '#D0D5DD' },
265
+ },
266
+ size: {
267
+ sm: { outer: '14px', innerDot: '5px', fontSize: '12px', lineHeight: '20px' },
268
+ md: { outer: '16px', innerDot: '6px', fontSize: '14px', lineHeight: '20px' },
269
+ },
270
+ base: {
271
+ borderIdle: '#667085',
272
+ hoverFill: '#FCFCFD', hoverBorder: '#D0D5DD', activeFill: '#F9FAFB',
273
+ gapItem: '8px', gapVerticalGroup: '12px', gapHorizontalGroup: '16px', innerMarginTop: '2px',
274
+ disabledOpacity: '0.75', disabledUncheckedBg: '#F9FAFB', disabledUncheckedBorder: '#E4E7EC',
275
+ },
276
+ };
277
+
278
+ const CHECKBOX_PREVIEW = {
279
+ variant: {
280
+ brand: { checkedFill: '#56D3BC', checkedHover: '#32C4AC', checkedActive: '#18B49D', iconOnChecked: '#FFFFFF', focusRing: '#A8E8D7', disabledChecked: '#A8E8D7' },
281
+ black: { checkedFill: '#111116', checkedHover: '#1C1C23', checkedActive: '#2E2F38', iconOnChecked: '#FFFFFF', focusRing: '#878892', disabledChecked: '#D0D5DD' },
282
+ },
283
+ size: {
284
+ sm: { box: '14px', icon: '10px', fontSize: '12px', lineHeight: '20px' },
285
+ md: { box: '16px', icon: '12px', fontSize: '14px', lineHeight: '20px' },
286
+ },
287
+ base: {
288
+ borderIdle: '#667085',
289
+ cornerRadius: '4px',
290
+ hoverFill: '#FCFCFD', hoverBorder: '#D0D5DD', activeFill: '#F9FAFB',
291
+ gapItem: '8px', gapVerticalGroup: '12px', gapHorizontalGroup: '16px',
292
+ disabledUncheckedBg: '#F9FAFB', disabledUncheckedBorder: '#E4E7EC',
293
+ },
294
+ };
295
+
296
+ const CHATINPUT_PREVIEW = {
297
+ variant: {
298
+ 'default-sm': { desc: '收起态,点击展开', interactive: true },
299
+ 'default': { desc: '展开态,可输入发送', interactive: true },
300
+ 'im-basic': { desc: '纯 IM 基础输入框:无猫条,左下图片+表情', interactive: true },
301
+ 'replying': { desc: '回复中,点击停止回默认', interactive: true },
302
+ 'busy': { desc: '忙碌(执行/排队),点击停止回默认', interactive: true, gradient: true },
303
+ 'readonly': { desc: '只读(分享/抢单),带操作按钮' },
304
+ },
305
+ base: {
306
+ inputRadius: '16px',
307
+ backdropRadius: '20px',
308
+ inputPadding: '14px',
309
+ shadow: '-12px 20px 30px -27px rgba(46,202,180,0.8), 12px 20px 30px -27px rgba(184,46,202,0.8)',
310
+ catBarHeight: '36px',
311
+ sendBtnSize: '36px',
312
+ },
313
+ };
314
+
315
+ const CHAT_MESSAGE_PREVIEW = {
316
+ scene: {
317
+ single: { desc: '单个执行流节点预览' },
318
+ multiple: { desc: '多个执行流节点纵向展示,可滚动查看' },
319
+ 'pure-text': { desc: '仅展示 AI 输出的纯文本字段,不显示任何执行类节点' },
320
+ 'card-only': { desc: '仅展示单张人工确认卡片,不显示执行流与结果文本' },
321
+ },
322
+ base: {
323
+ width: '自适应(最长撑满预览内容区)',
324
+ itemMinHeight: '80px',
325
+ railWidth: '16px',
326
+ cardRadius: '8px',
327
+ finalResult: '支持隐藏 / 纯文本 / 文本+产物 / 文本+人工确认卡片 / 单个人工确认卡片;纯文本与仅卡片场景分别只展示对应内容',
328
+ },
329
+ };
330
+
331
+ const NAVBAR_PREVIEW = {
332
+ scene: {
333
+ default: { desc: '模块高亮 + 常驻提醒点' },
334
+ strategy: { desc: '主导航高亮,适合策略页等深层业务页' },
335
+ alerts: { desc: '通知入口强调,适合待处理消息聚集场景' },
336
+ },
337
+ base: {
338
+ width: '120px',
339
+ height: '100%(随父容器)',
340
+ itemRadius: '8px',
341
+ sectionGap: '4px',
342
+ },
343
+ };
344
+
345
+ const TAGBAR_PREVIEW = {
346
+ base: {
347
+ expandedWidth: '240px',
348
+ collapsedWidth: '72px',
349
+ itemHeight: '40px',
350
+ iconSlot: '24px',
351
+ searchHeight: '36px',
352
+ },
353
+ state: {
354
+ defaultBg: '#FFFFFF',
355
+ hoverBg: 'rgba(83, 96, 143, 0.07)',
356
+ activeBg: '#EAFAF6',
357
+ activeHoverBg: '#CFF3EA',
358
+ textColor: '#475467',
359
+ activeTextColor: '#182230',
360
+ },
361
+ };
362
+
363
+ const CHATBUBBLE_PREVIEW = {
364
+ variant: {
365
+ incoming: { alignment: 'left', bubbleBg: 'rgba(52,59,57,0.05)', avatarSide: 'left' },
366
+ outgoing: { alignment: 'right', bubbleBg: '#E5F7F7', avatarSide: 'right', receipt: 'optional' },
367
+ },
368
+ layout: {
369
+ single: { count: 1, gapY: '0px' },
370
+ multiple: { count: 2, gapY: '4px' },
371
+ },
372
+ avatarType: {
373
+ image: { type: 'image', size: '24px' },
374
+ robot: { type: 'robot', size: '24px' },
375
+ },
376
+ receipt: {
377
+ hidden: { visible: false },
378
+ read: { visible: true, size: '12px' },
379
+ },
380
+ base: { textColor: '#182230', fontSize: '14px', lineHeight: '20px', maxWidth: '500px' },
381
+ };
382
+
383
+ const ICON_PREVIEW = {
384
+ size: {
385
+ xs: { px: 12 },
386
+ sm: { px: 16 },
387
+ md: { px: 20 },
388
+ lg: { px: 24 },
389
+ xl: { px: 32 },
390
+ },
391
+ base: { color: 'currentColor', viewBox: '0 0 24 24' },
392
+ };
393
+
394
+ const SWITCH_PREVIEW = {
395
+ variant: {
396
+ brand: { onTrack: '#56D3BC', onTrackHover: '#32C4AC', focusRing: '#A8E8D7' },
397
+ black: { onTrack: '#111116', onTrackHover: '#1C1C23', focusRing: '#878892' },
398
+ },
399
+ size: {
400
+ sm: { trackW: '36px', trackH: '20px', thumb: '16px' },
401
+ md: { trackW: '44px', trackH: '24px', thumb: '20px' },
402
+ },
403
+ base: { offTrack: '#E4E7EC', offTrackHover: '#D0D5DD', thumbBg: '#FFFFFF', disabledOpacity: '0.5' },
404
+ };
405
+
406
+ const MODAL_PREVIEW = {
407
+ panel: {
408
+ background: '#FFFFFF',
409
+ borderRadius: '12px',
410
+ shadow: '0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1)',
411
+ paddingX: '24px',
412
+ maxHeight: '85vh',
413
+ bodyMinHeight: '200px',
414
+ },
415
+ size: {
416
+ sm: { maxWidth: '450px' },
417
+ md: { maxWidth: '684px' },
418
+ lg: { maxWidth: '920px' },
419
+ },
420
+ header: { titleSize: '18px', subtitleSize: '14px', titleColor: '#182230', subtitleColor: '#667085' },
421
+ close: { size: '32px', iconColor: '#667085', hoverIconColor: '#475467', hoverBg: '#F9FAFB' },
422
+ footer: { hintSize: '12px', hintColor: '#475467', buttonGap: '12px' },
423
+ };
424
+
425
+ const SHEET_PREVIEW = {
426
+ panel: {
427
+ background: '#FFFFFF',
428
+ shadow: '0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px -6px rgba(0,0,0,0.1)',
429
+ paddingX: '24px',
430
+ height: '100%',
431
+ bodyMinHeight: '0',
432
+ },
433
+ size: {
434
+ sm: { width: '400px' },
435
+ md: { width: '560px' },
436
+ lg: { width: '800px' },
437
+ },
438
+ header: { titleSize: '18px', subtitleSize: '14px', titleColor: '#182230', subtitleColor: '#667085' },
439
+ close: { size: '32px', iconColor: '#667085', hoverIconColor: '#475467', hoverBg: '#F9FAFB' },
440
+ footer: { hintSize: '12px', hintColor: '#475467', buttonGap: '12px' },
441
+ };
442
+
443
+ const SLIDER_PREVIEW = {
444
+ track: {
445
+ height: '4px',
446
+ bg: '#F2F4F7',
447
+ activeBg: '#56D3BC',
448
+ disabledBg: '#E4E7EC',
449
+ borderRadius: '9999px',
450
+ },
451
+ handle: {
452
+ size: '20px',
453
+ bg: '#FFFFFF',
454
+ borderColor: '#56D3BC',
455
+ disabledBorderColor: '#D0D5DD',
456
+ disabledBg: '#E4E7EC',
457
+ shadow: '0 1px 2px rgba(0,0,0,0.05)',
458
+ draggingShadow: '0 0 0 4px rgba(86, 211, 188, 0.15)',
459
+ },
460
+ tooltip: {
461
+ bg: '#101828',
462
+ color: '#FFFFFF',
463
+ padding: '4px 8px',
464
+ borderRadius: '6px',
465
+ fontSize: '12px',
466
+ },
467
+ marks: {
468
+ markSize: '4px',
469
+ inactiveBg: '#D0D5DD',
470
+ activeBg: '#56D3BC',
471
+ labelFontSize: '12px',
472
+ labelColor: '#667085',
473
+ },
474
+ base: {
475
+ width: '300px',
476
+ disabledOpacity: '0.6',
477
+ },
478
+ };
479
+
480
+ const DATEPICKER_PREVIEW = {
481
+ type: {
482
+ date: { width: '300px', placeholder: '请选择日期', sample: '2019-03-15' },
483
+ datetime: { width: '300px', placeholder: '请选择日期及时间', sample: '2019-03-15 16:00:00' },
484
+ daterange: { width: '300px', placeholder: '开始日期 ~ 结束日期', sample: '2019-03-15 ~ 2019-03-16' },
485
+ datetimerange: { width: '400px', placeholder: '开始日期 ~ 结束日期', sample: '2019-03-15 09:00:00 ~ 2019-03-16 16:00:00' },
486
+ },
487
+ trigger: {
488
+ height: '36px',
489
+ paddingLeft: '12px',
490
+ paddingRight: '8px',
491
+ gap: '8px',
492
+ borderRadius: '8px',
493
+ borderColor: '#E4E7EC',
494
+ hoverBorderColor: '#D0D5DD',
495
+ focusBorderColor: '#56D3BC',
496
+ },
497
+ text: {
498
+ fontSize: '14px',
499
+ lineHeight: '20px',
500
+ valueColor: '#182230',
501
+ placeholderColor: '#667085',
502
+ },
503
+ range: {
504
+ bgColor: '#FFFFFF',
505
+ separatorColor: '#667085',
506
+ },
507
+ icon: {
508
+ size: '16px',
509
+ color: '#98A2B3',
510
+ },
511
+ panel: {
512
+ date: { width: 'same as trigger', minWidth: '280px', height: '312px' },
513
+ datetime: { width: 'same as trigger', minWidth: '280px', height: '348px' },
514
+ daterange: { width: 'same as trigger', minWidth: '280px', height: '312px' },
515
+ datetimerange: { width: 'same as trigger', minWidth: '280px', height: '348px' },
516
+ borderColor: '#E4E7EC',
517
+ radius: '6px',
518
+ shadow: '0 4px 14px rgba(0,0,0,0.1)',
519
+ },
520
+ calendar: {
521
+ monthTitleSize: '14px',
522
+ monthTitleWeight: '600',
523
+ weekSize: '12px',
524
+ weekLineHeight: '16px',
525
+ dayCell: '36px',
526
+ dayInner: '32px',
527
+ todayBg: 'rgba(52,59,58,0.05)',
528
+ rangeBg: '#EAFAF6',
529
+ selectedBg: '#56D3BC',
530
+ selectedText: '#FFFFFF',
531
+ },
532
+ footer: {
533
+ height: '52px',
534
+ borderColor: '#E4E7EC',
535
+ dateText: '#182230',
536
+ timeText: '#667085',
537
+ iconColor: '#475467',
538
+ },
539
+ disabled: {
540
+ bgColor: '#F9FAFB',
541
+ textColor: '#98A2B3',
542
+ opacity: '0.6',
543
+ },
544
+ };
545
+
546
+ const TAG_PREVIEW = {
547
+ variant: {
548
+ red: { bgColor: '#FEF2F1', textColor: '#D32E24' },
549
+ orange: { bgColor: '#FFF5EE', textColor: '#D17310' },
550
+ yellow: { bgColor: '#FEFACF', textColor: '#A5760A' },
551
+ green: { bgColor: '#F0FDF4', textColor: '#329538' },
552
+ cyan: { bgColor: '#EAF9FA', textColor: '#0E94A0' },
553
+ blue: { bgColor: '#ECF6FE', textColor: '#0B69D4' },
554
+ purple: { bgColor: '#F7EAF7', textColor: '#8B239F' },
555
+ pink: { bgColor: '#FDEDF1', textColor: '#C32165' },
556
+ teal: { bgColor: '#EAFAF6', textColor: '#129683' },
557
+ grey: { bgColor: 'rgba(83, 96, 143, 0.07)', textColor: '#475467' },
558
+ white: { bgColor: '#FFFFFF', textColor: '#182230', borderColor: '#E4E7EC' },
559
+ brand: { bgColor: '#EAFAF6', textColor: '#56D3BC' },
560
+ },
561
+ size: {
562
+ s: { height: '16px', paddingLeft: '4px', paddingRight: '4px', paddingY: '0px', fontSize: '12px', textToCloseGap: '4px' },
563
+ m: { height: '20px', paddingLeft: '8px', paddingRight: '8px', paddingY: '0px', fontSize: '12px', textToCloseGap: '4px' },
564
+ l: { height: '24px', paddingLeft: '8px', paddingRight: '8px', paddingY: '0px', fontSize: '12px', textToCloseGap: '4px' },
565
+ },
566
+ radius: {
567
+ md: { borderRadius: '8px' },
568
+ square: { borderRadius: '3px' },
569
+ full: { borderRadius: '9999px' },
570
+ },
571
+ };
572
+
573
+ const TAGINPUT_PREVIEW = {
574
+ status: {
575
+ default: { bgColor: '#FFFFFF', borderColor: 'rgba(45,66,107,0.12)', hoverBorderColor: '#D0D5DD', focusBorderColor: '#56D3BC' },
576
+ error: { bgColor: '#FEF2F1', borderColor: 'rgba(45,66,107,0.12)', hoverBg: '#FCD9D7', focusBorderColor: '#F74331' },
577
+ },
578
+ size: {
579
+ sm: { minHeight: '24px', paddingX: '8px', paddingY: '2px' },
580
+ md: { minHeight: '36px', paddingLeft: '12px', paddingRight: '8px', paddingY: '6px' },
581
+ },
582
+ base: {
583
+ width: '300px',
584
+ borderRadius: '8px',
585
+ tagGap: '4px',
586
+ placeholderColor: '#667085',
587
+ overflow: '+N 折叠,Tooltip 展示隐藏标签',
588
+ },
589
+ };
590
+
591
+ const TOOLTIP_PREVIEW = {
592
+ base: {
593
+ bgColor: '#2E2F38',
594
+ textColor: '#FFFFFF',
595
+ fontSize: '14px',
596
+ lineHeight: '20px',
597
+ fontWeight: '400',
598
+ paddingX: '12px',
599
+ paddingY: '8px',
600
+ borderRadius: '8px',
601
+ boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1)',
602
+ maxWidth: '280px',
603
+ arrowWidth: '12px',
604
+ arrowHeight: '6px',
605
+ triggerGap: '8px',
606
+ enterDelay: '100ms',
607
+ leaveDelay: '100ms',
608
+ },
609
+ };
610
+
611
+ const TOAST_PREVIEW = {
612
+ type: {
613
+ info: { bgColor: '#EAFAF6', borderColor: '#129683', iconColor: '#129683' },
614
+ success: { bgColor: '#F0FDF4', borderColor: '#3EB346', iconColor: '#3EB346' },
615
+ warning: { bgColor: '#FFF5EE', borderColor: '#FA8B14', iconColor: '#FA8B14' },
616
+ error: { bgColor: '#FEF2F1', borderColor: '#F74331', iconColor: '#F74331' },
617
+ },
618
+ text: { fontSize: '14px', lineHeight: '20px', fontWeight: '600', color: '#182230' },
619
+ shell: {
620
+ minHeight: '44px',
621
+ padding: '12px',
622
+ borderRadius: '12px',
623
+ gap: '8px',
624
+ contentGap: '12px',
625
+ width: 'fit-content',
626
+ maxWidth: 'min(100%, 560px)',
627
+ boxShadow: '0 0 1px rgba(0,0,0,0.3), 0 4px 14px rgba(0,0,0,0.1)',
628
+ },
629
+ };
630
+
631
+ const CARD_PREVIEW = {
632
+ base: {
633
+ width: '389px',
634
+ minHeight: '201px',
635
+ padding: '20px',
636
+ borderRadius: '16px',
637
+ borderColor: '#FFFFFF',
638
+ bgColor: 'rgba(255,255,255,0.65)',
639
+ hoverBg: '#FFFFFF',
640
+ hoverShadow: '0 30px 50px 0 rgba(0, 9, 36, 0.05)',
641
+ },
642
+ product: {
643
+ imageSize: '56px',
644
+ imageRadius: '12px',
645
+ contentHeight: '56px',
646
+ gap: '16px',
647
+ minHeight: '96px',
648
+ titleLineClamp: 1,
649
+ subtitleLineClamp: 1,
650
+ statusTag: 'Tag / green / l / md',
651
+ },
652
+ };
653
+
654
+ const CONVERSATION_LIST_PREVIEW = {
655
+ base: {
656
+ width: '400px',
657
+ padding: '20px',
658
+ borderRadius: '12px',
659
+ bgColor: '#FFFFFF',
660
+ gap: '16px',
661
+ },
662
+ header: {
663
+ height: '26px',
664
+ titleFontSize: '14px',
665
+ titleLineHeight: '20px',
666
+ titleFontWeight: '600',
667
+ actionButtonSize: '26px',
668
+ iconSize: '16px',
669
+ },
670
+ item: {
671
+ padding: '12px',
672
+ avatarSize: '32px',
673
+ radius: '12px',
674
+ titleFontSize: '14px',
675
+ titleLineHeight: '24px',
676
+ metaFontSize: '12px',
677
+ metaLineHeight: '16px',
678
+ activeRailWidth: '3px',
679
+ },
680
+ tag: {
681
+ size: 'Tag m / 20px',
682
+ radius: 'square / 3px',
683
+ },
684
+ card: {
685
+ width: '333px',
686
+ radius: '16px',
687
+ shadow: 'shadow-list',
688
+ messageViewportHeight: '152px',
689
+ footerStates: ['generating', 'editable'],
690
+ },
691
+ };
692
+
693
+ export const COMPONENTS = [
694
+ {
695
+ id: 'avatar',
696
+ name: 'Avatar',
697
+ element: 'span',
698
+ category: 'basic',
699
+ description:
700
+ '单个实体(人/机器人/AI/兜底)的头像锚点。用于表格、会话、导航等需要「一眼认出是谁」的位置;不用于多人交叠缩略(那是 AvatarGroup)。',
701
+ componentFile: './components/Avatar.jsx',
702
+ tokensFile: './components/Avatar.tokens.js',
703
+ props: [
704
+ { name: 'shape', type: 'enum', options: ['round', 'rect'], default: 'round' },
705
+ { name: 'size', type: 'enum', options: ['xxs', 'mini', 'xs', 's', 'default', 'm'], default: 'default' },
706
+ { name: 'type', type: 'enum', options: ['image', 'eng', 'ch', 'fallback', 'robot', 'ai'], default: 'image' },
707
+ { name: 'src', type: 'string', default: null },
708
+ { name: 'label', type: 'string', default: null },
709
+ { name: 'alt', type: 'string', default: '头像' },
710
+ ],
711
+ labels: {
712
+ shape: {
713
+ round: '圆形',
714
+ rect: '方形',
715
+ },
716
+ size: {
717
+ xxs: 'XXS',
718
+ mini: 'MINI',
719
+ xs: 'XS',
720
+ s: 'S',
721
+ default: 'DEFAULT',
722
+ m: 'M',
723
+ },
724
+ type: {
725
+ image: '图片',
726
+ eng: '英文',
727
+ ch: '中文',
728
+ fallback: '兜底',
729
+ robot: '机器人',
730
+ ai: 'AI头像',
731
+ },
732
+ },
733
+ _preview: AVATAR_PREVIEW,
734
+ rules: [
735
+ FONT_WEIGHT_RUNTIME_RULE,
736
+ LOCAL_MEMBER_AVATAR_RULE,
737
+ '【选型·身份】需要展示「一个」主体的肖像/字母/机器人/AI 时用 Avatar;列表里每一行一个主体 → 每行一个 Avatar,不要用 AvatarGroup',
738
+ '【选型·与 Icon】Icon 只表达抽象符号;凡是「人像、角色、账号」语义必须用 Avatar(含 eng/ch/fallback/robot/ai)',
739
+ '【形状】round 为圆形头像,rect 为 3px 圆角方形头像;两者共用同一套尺寸体系',
740
+ '【尺寸】XXS / MINI / XS / S / Default / M 分别对应 16 / 20 / 24 / 32 / 40 / 48px',
741
+ '【图片态】image 优先展示传入 src;未传 src 时默认随机使用本地成员头像图片素材;传入 src 加载失败时先回退到本地成员头像,再兜底到内置默认头像,保证平台预览稳定',
742
+ '【英文态】eng 使用灰底白字,默认文案统一为 TF;超小尺寸自动退化为单字 T 以保证可读性',
743
+ '【中文态】ch 使用品牌绿底白字;方形与圆形仅形状不同,其余视觉保持一致',
744
+ '【兜底态】fallback 使用浅灰底 + 用户轮廓图形,适合默认头像或匿名场景',
745
+ '【机器人态】robot 使用设计稿蓝底 + 机器人头像细节图形,适合传统机器人助手场景',
746
+ '【AI头像态】ai 使用 ai-fill-1 渐变背景 + Icon/hiai-logo,沿用 Avatar 现有 round / rect 与 6 档尺寸能力',
747
+ '【扩展文案】eng / ch 可通过 label 覆盖默认示例文字;建议控制在 1-2 个字符内,避免小尺寸溢出',
748
+ ],
749
+ examples: [
750
+ { label: '默认图片', code: '<Avatar />' },
751
+ { label: '圆形英文', code: '<Avatar size="s" type="eng" label="TF" />' },
752
+ { label: '圆形中文', code: '<Avatar size="default" type="ch" label="体服" />' },
753
+ { label: '方形图片', code: '<Avatar shape="rect" size="s" type="image" alt="用户头像" />' },
754
+ { label: '方形兜底', code: '<Avatar shape="rect" size="xs" type="fallback" />' },
755
+ { label: '机器人头像', code: '<Avatar size="m" type="robot" />' },
756
+ { label: 'AI头像', code: '<Avatar size="m" type="ai" />' },
757
+ { label: '❌ Bad(用 img 标签或外链随机头像)', code: '/* 禁止!缺尺寸 token、加载失败回退、圆形裁切规则,也不要用 pravatar/Unsplash/placeholder 外链头像 */\n<img src="https://i.pravatar.cc/80" className="w-10 h-10 rounded-full" />' },
758
+ { label: '✅ Good(用 Avatar 组件,默认本地成员头像)', code: '<Avatar size="default" type="image" alt="用户头像" />' },
759
+ ],
760
+ keywords: [
761
+ 'Avatar', 'avatar', '头像', '用户头像', '账号头像', '人物头像', 'profile picture',
762
+ 'image', 'eng', 'ch', 'fallback', 'robot', 'ai',
763
+ '机器人头像', 'AI头像', '默认头像', '占位头像', '英文头像', '中文头像',
764
+ // 错误信号词
765
+ '<img>', 'img rounded-full', 'div w-10 h-10 rounded-full', '手搓圆头像',
766
+ ],
767
+ },
768
+ {
769
+ id: 'avatar-group',
770
+ name: 'AvatarGroup',
771
+ element: 'div',
772
+ category: 'basic',
773
+ description:
774
+ '多人参与的缩略表达:固定 5 个圆形图片头像、固定交叠间距,用于「协作成员、围观者」等氛围装饰。需要可配置人数/顺序/文案时请用列表 + 多个 Avatar,不要用本组件硬凑。',
775
+ componentFile: './components/AvatarGroup.jsx',
776
+ tokensFile: './components/AvatarGroup.tokens.js',
777
+ props: [
778
+ { name: 'size', type: 'enum', options: ['s', 'default', 'm'], default: 's' },
779
+ ],
780
+ labels: {
781
+ size: {
782
+ s: 'S',
783
+ default: 'DEFAULT',
784
+ m: 'M',
785
+ },
786
+ },
787
+ rules: [
788
+ LOCAL_MEMBER_AVATAR_RULE,
789
+ '【选型·与 Avatar】AvatarGroup 只回答「这里有一群人」;要展示可点的成员列表、不同角色类型(机器人/AI)→ 用 Avatar 分列或列表',
790
+ '【选型·与表格】表格「负责人」列用单个 Avatar;「多负责人」若需可读性 → 用 Avatar + 文案或 Tag,不用 AvatarGroup 塞进单元格',
791
+ '【结构】AvatarGroup 是复合组件,内部固定渲染 5 个头像项,不对外暴露单项数量、顺序和内容类型配置',
792
+ '【尺寸】S / Default / M 分别对应单项 32 / 40 / 48px,并沿用 Avatar 的圆形图片裁切规则',
793
+ '【排列】相邻头像固定使用 10px 重叠间距,容器补 10px 右侧内边距,保证最后一项不被裁掉',
794
+ '【内容】当前头像组只沉淀图片态圆形头像,内部固定使用本地成员头像图片素材,用于多人协作、参与者列表等群组场景,不承载文字态 / 机器人态',
795
+ ],
796
+ examples: [
797
+ { label: '默认头像组', code: '<AvatarGroup />' },
798
+ { label: '中号头像组', code: '<AvatarGroup size="default" />' },
799
+ { label: '大号头像组', code: '<AvatarGroup size="m" />' },
800
+ { label: '❌ Bad(手搓堆叠头像)', code: '/* 禁止!自制 div + 负 margin 缺统一描边/重叠间距 token */\n<div className="flex -space-x-2">\n <img src="a.png" className="w-8 h-8 rounded-full border-2" />\n <img src="b.png" className="w-8 h-8 rounded-full border-2" />\n</div>' },
801
+ { label: '✅ Good(用 AvatarGroup)', code: '<AvatarGroup size="default" />' },
802
+ ],
803
+ keywords: [
804
+ 'AvatarGroup', 'avatar-group', '头像组', '多人头像', '协作成员', '参与者头像',
805
+ '叠加头像', '群组头像', 'avatar stack', 'overlapping avatars',
806
+ '协作', '团队头像', '小组头像', '围观者', '参与人', '抄送人',
807
+ // 错误信号词
808
+ '手搓头像组', '-space-x', 'flex -space-x', '负 margin 头像', 'overlapping img',
809
+ ],
810
+ },
811
+ {
812
+ id: 'icon',
813
+ name: 'Icon',
814
+ element: 'svg',
815
+ category: 'basic',
816
+ previewType: 'icon-grid',
817
+ description:
818
+ '矢量符号(1198+ 图标名)。用于按钮/输入框前后缀、导航、状态提示等「非人像」图形;需要人像或账号识别请用 Avatar,需要文字标签请用 Button/Tag。',
819
+ componentFile: './components/Icon.jsx',
820
+ tokensFile: './components/Icon.tokens.js',
821
+ props: [
822
+ { name: 'name', type: 'string', default: 'check-stroked' },
823
+ { name: 'size', type: 'enum', options: ['xs', 'sm', 'md', 'lg', 'xl'], default: 'sm' },
824
+ { name: 'color', type: 'string', default: 'currentColor' },
825
+ ],
826
+ labels: {
827
+ size: { xs: '12px', sm: '16px', md: '20px', lg: '24px', xl: '32px' },
828
+ },
829
+ _preview: ICON_PREVIEW,
830
+ rules: [
831
+ '【选型·粒度】同一交互既可用「图标」也可用「短文案按钮」时:工具栏/紧凑行内优先 Icon 或 icon+文字 Button;需要阅读性时用 Button 纯文字或 Tabs',
832
+ '【选型·与 Button】仅图标按钮 = `Button iconOnly tooltip="操作文案"` + Icon;不要单独把 Icon 包一层 div 当按钮(无键盘、无 role、无 Tooltip 操作说明)',
833
+ '【命名规则】图标名使用 kebab-case,如 arrow-down-stroked、check-circle-stroked',
834
+ '【尺寸选择】xs(12) 极紧凑;sm(16) 默认最常用——**与 36px 高控件搭配的前后缀、行内图标**(与 Input md 内 16px 槽一致);md(20) 独立展示;lg(24) 标题/导航;xl(32) 强调展示',
835
+ '【禁止散图标】B 端页面 UI 中的符号图形必须用 `<Icon name="kebab-case" />` 引用包内映射;不要用 `lucide-react` 等外置图标库替代(避免线宽/网格与 TFDS 不一致)。Form 内装饰性 Help/Alert 等若沿用 lucide,仅限该组件边界内',
836
+ '【颜色继承】默认 currentColor,自动继承父容器文字色,不需要手动指定颜色:深色填充按钮内→图标白色;浅色/描边按钮内→图标跟随按钮文字色;功能色场景(如 danger 描边按钮)→图标自动变 danger 色',
837
+ '【何时覆盖颜色】仅在图标颜色需要独立于周围文字色时,才传 color 属性覆盖',
838
+ '【无障碍】纯装饰图标使用 aria-hidden;有语义的图标需配合 aria-label 或相邻文字说明',
839
+ ],
840
+ examples: [
841
+ { label: '基础用法', code: '<Icon name="check-stroked" />' },
842
+ { label: '指定尺寸', code: '<Icon name="heart-stroked" size="lg" />' },
843
+ { label: '自定义颜色', code: '<Icon name="zap-stroked" color="#56D3BC" />' },
844
+ { label: '按钮内图标', code: '<Button icon={<Icon name="plus-stroked" />}>添加</Button>' },
845
+ { label: '❌ Bad(用 lucide-react 图标)', code: '/* 禁止!外置图标库与 TFDS 线宽/网格不一致 */\nimport { Plus } from "lucide-react";\n<Plus />' },
846
+ { label: '✅ Good(用 Icon kebab-case)', code: '<Icon name="plus-stroked" size="sm" />' },
847
+ { label: '❌ Bad(自己写 svg)', code: '/* 禁止!手写 svg 缺尺寸/颜色继承/aria 处理 */\n<svg width="16" height="16">…</svg>' },
848
+ { label: '✅ Good(统一 Icon)', code: '<Icon name="check-stroked" size="sm" />' },
849
+ ],
850
+ keywords: [
851
+ 'Icon', 'icon', '图标', '矢量图标', '符号', 'svg icon', 'iconfont',
852
+ 'kebab-case', 'stroked', '描边图标',
853
+ // 高频图标名
854
+ 'plus-stroked', 'check-stroked', 'search-md', 'arrow-down', 'chevron-down',
855
+ 'trash-01-stroked', 'edit-01-stroked', 'settings-01-stroked', 'bell-03-stroked',
856
+ // 错误信号词
857
+ 'lucide-react', 'lucide', 'Plus icon', 'react-icons', 'antd-icon', 'mui icon',
858
+ '<svg>', '手写 svg', 'inline svg', 'native svg',
859
+ ],
860
+ },
861
+ {
862
+ id: 'nav-bar',
863
+ name: 'NavBar',
864
+ element: 'aside',
865
+ category: 'business',
866
+ description: '业务竖向导航栏,集成品牌区、业务入口切换、平台首页模块高亮、主导航、底部工具按钮和用户头像。',
867
+ componentFile: './components/NavBar.jsx',
868
+ tokensFile: './components/NavBar.tokens.js',
869
+ props: [
870
+ { name: 'platform', type: 'enum', options: ['ola', 'bytehi'], default: 'ola' },
871
+ { name: 'promptText', type: 'string', default: '' },
872
+ { name: 'brandName', type: 'string', default: 'OLA' },
873
+ { name: 'appLabel', type: 'string', default: '抖音社区' },
874
+ { name: 'appBusinesses', type: 'array', default: null },
875
+ { name: 'currentAppId', type: 'string' },
876
+ { name: 'defaultAppId', type: 'string', default: null },
877
+ { name: 'onAppChange', type: 'function', default: null },
878
+ { name: 'moduleLabel', type: 'string', default: '平台首页' },
879
+ { name: 'activeModule', type: 'boolean', default: true },
880
+ { name: 'navItems', type: 'array', default: null },
881
+ { name: 'activeNavId', type: 'string', default: null },
882
+ { name: 'activeUtilityId', type: 'string', default: null },
883
+ { name: 'selectedItemId', type: 'string', default: null },
884
+ { name: 'defaultSelectedItemId', type: 'string', default: null },
885
+ { name: 'showUtilityActions', type: 'boolean', default: false },
886
+ { name: 'utilityItems', type: 'array', default: null },
887
+ { name: 'onSelect', type: 'function', default: null },
888
+ { name: 'avatarType', type: 'enum', options: ['image', 'robot', 'fallback'], default: 'image' },
889
+ { name: 'avatarSrc', type: 'string', default: null },
890
+ { name: 'avatarAlt', type: 'string', default: '当前用户头像' },
891
+ ],
892
+ labels: {
893
+ platform: { ola: 'OLA', bytehi: 'ByteHi' },
894
+ avatarType: { image: '图片', robot: '机器人', fallback: '兜底' },
895
+ },
896
+ _preview: NAVBAR_PREVIEW,
897
+ rules: [
898
+ FONT_WEIGHT_RUNTIME_RULE,
899
+ LOCAL_MEMBER_AVATAR_RULE,
900
+ '【平台默认】NavBar 默认一律使用 platform="ola" 的 OLA 类型菜单栏结构。这里的 OLA 指导航结构类型,不代表顶部品牌文案必须固定显示为“OLA”;图中的“OLA”仅为模板示意。',
901
+ '【品牌文案】brandName 是顶部平台名称展示入口,必须根据真实页面类型动态传入对应平台名称,例如“体验服务”“抖音体验与服务”“服务治理”等;不要把“OLA”当成所有页面固定品牌名。只有在缺少真实平台名称的组件预览或模板占位中,才允许继续显示 OLA。',
902
+ '【ByteHi 限定】platform="bytehi" 仅限明确提到“客服工作台”相关页面场景时使用,例如“客服工作台首页”“在线客服工作台”“客服工作台会话页”。普通客服、服务、体验、治理、策略、知识、工具、洞察等页面都不要自动切换 ByteHi,仍默认使用 OLA 类型菜单栏。',
903
+ '【提示词】未显式传 platform 时,默认使用 OLA;只有 prompt / promptText 明确包含“客服工作台”时才默认使用 ByteHi。显式传 platform 时以显式值为准,但 AI 生成页面时不得在非客服工作台场景主动传 platform="bytehi"。',
904
+ '【站点级骨架】`NavBar` 是平台级站点导航锚点,不是普通内容区组件。只要页面使用 `NavBar`,一级骨架默认必须是左侧固定导航 + 右侧弹性主工作区的横向 app shell;`NavBar` 永远默认位于页面最左侧',
905
+ '【结构】OLA 固定由顶部品牌区、业务入口卡、分割线、主导航滚动区、可选底部操作按钮和用户头像组成;其中 moduleLabel 与主导航项同层级并一起滚动',
906
+ '【布局位置】`NavBar` 必须放在页面一级容器中作为 `shrink-0` 固定区;右侧主内容区用 `flex-1 min-w-0 flex flex-col overflow-hidden`。⛔ 禁止把 `NavBar` 放进白卡,⛔ 禁止把 `NavBar` 放到顶部 header,⛔ 禁止与主内容做上下排列',
907
+ '【尺寸】OLA 版宽度固定 120px;ByteHi 版外层布局占位宽度始终固定为 60px,hover / focus 展开时仅内部面板宽度变为 180px,左侧边缘保持固定并向右覆盖页面内容,不触发右侧主内容区重新布局或被挤压;两者高度都默认跟随父容器撑满,设计稿示例高度为 1080px',
908
+ '【业务入口】业务入口卡始终作为首个高亮块展示,承载当前业务域名与图形标识,不与普通导航项混排;在 OLA 下它不是普通菜单项,而是一个可点击切换的业务方按钮,点击后展开抖音业务方下拉列表',
909
+ '【业务入口下拉】OLA 的业务方下拉样式应复用 TagBar 的业务切换视觉语言:黑底业务图标 + 文字 + 白底浮层列表 + 当前项 check;下拉面板选项之间的上下间距固定使用 `gap-1 / --spacing-1 / 4px`;推荐通过 `appBusinesses` / `currentAppId` / `onAppChange` 控制,默认提供抖音社区 / 抖音生活服务 / 抖音电商三项',
910
+ '【首页归属】当 AI 新生成一个带 NavBar 的页面,且业务没有明确指定所属菜单时,默认必须归属于第一个菜单入口 `moduleLabel`,即 OLA 的“平台首页”。当用户明确描述某个页面为“首页 / 平台首页 / 工作台首页 / 首页看板”时,也必须默认选中 `moduleLabel="平台首页"`,保持 activeModule=true,不要误选“策略管理”“知识与案例”等普通主导航项。',
911
+ '【模块】moduleLabel 对应当前主工作区首页,默认文案为“平台首页”;activeModule=true 时使用次级白底 + 白描边 + 主文字 + 品牌色图标;hover 时背景提升到纯白。OLA 中 moduleLabel 是第一层菜单项,不是装饰标题;它与下方“策略管理 / 知识与案例 / 工具管理 / 对话洞察 / 智能盯盘”等主导航项属于同一套可选中菜单体系。',
912
+ '【导航】moduleLabel 与主导航项都使用 88px × 60px 的固定按钮规格;默认灰字灰图标,hover 进入次级白底但不改字色,selectedItemId / activeNavId 命中时提升为高亮块,图标切换到品牌色',
913
+ '【自由切换】所有菜单栏项都必须支持自由切换,包括 OLA 的平台首页 module、所有 navItems、可选 utilityItems,以及 ByteHi 的完整导航项。生成页面时必须接入 `selectedItemId` + `onSelect` 受控模式,或至少保留组件非受控点击选中能力;禁止把菜单项做成只展示不可点击的静态文字,也禁止只让当前页菜单可点、其它菜单不可切换。',
914
+ '【选中状态源】推荐外层页面维护单一 `currentNavId`,通过 `<NavBar selectedItemId={currentNavId} onSelect={setCurrentNavId} />` 驱动右侧主内容切换。首页默认值使用 `"module"`;普通导航使用对应 navItems.id;底部工具使用 utilityItems.id。不要同时混用 activeModule、activeNavId、activeUtilityId 和 selectedItemId 造成多个选中源。',
915
+ '【工具】showUtilityActions=false 时默认隐藏底部操作按钮;OLA 开启后展示铃铛与设置,ByteHi 开启后展示操作指南与消息,关闭时只保留网络信息区和头像区;底部按钮支持选中态,选中后容器与主导航保持同一套高亮逻辑',
916
+ '【ByteHi】ByteHi 版收起态不显示阴影,仅在 hover 或聚焦展开后显示阴影;展开面板必须是覆盖层,不得撑宽 NavBar 的布局占位,也不得挤压右侧页面内容;导航项默认使用深文字 + 45% 透明辅助态,选中态提升为白底;hover 展开后,未选中导航项文字字重固定为 `[font-weight:var(--font-medium)]`,选中导航项文字字重固定为 `[font-weight:var(--font-semibold)]`,不要使用 font-bold 或手写 700。',
917
+ '【交互】组件支持非受控点击选中(defaultSelectedItemId)和受控选中(selectedItemId + onSelect);hover 与 selected 的菜单项宽高必须保持一致',
918
+ '【头像】底部头像复用 Avatar 组件 xs 档位;avatarType 允许 image / robot / fallback,保持圆形裁切;ByteHi 底部头像在 avatarType="image" 且未传 avatarSrc 时,默认固定使用本地成员头像“段然”(team-avatar-assets/duan-ran.png),展开/收起或 hover 时不得重新随机变化;不要为了导航底部用户头像去接随机外链图',
919
+ '【图标】除顶部业务图形标外,其余图标统一复用 Icon 组件;所有 UI 容器颜色必须落到现有 token,不允许新增导航专属 token',
920
+ '【职责边界】`NavBar` 负责全局导航;`TagBar` 负责模块内分类 / 左侧筛选;`Tabs` 负责当前上下文的局部切换。三者不能互相替代;需要左侧全局站点导航时必须优先用 `NavBar`',
921
+ ],
922
+ examples: [
923
+ { label: '默认导航栏', code: '<NavBar />' },
924
+ { label: '真实平台名称', code: '<NavBar brandName="体验服务" selectedItemId="module" onSelect={(nextId) => setCurrentNavId(nextId)} />' },
925
+ { label: '业务方切换', code: '<NavBar appBusinesses={[{ id: "douyin-community", label: "抖音社区", iconSrc: "icon-logo-douyin" }, { id: "douyin-local-services", label: "抖音生活服务", iconSrc: "icon-logo-douyin" }, { id: "douyin-ecommerce", label: "抖音电商", iconSrc: "icon-logo-douyin" }]} />' },
926
+ { label: '显示底部操作', code: '<NavBar showUtilityActions />' },
927
+ { label: 'ByteHi 平台', code: '<NavBar platform="bytehi" />' },
928
+ { label: '提示词推断 ByteHi', code: '<NavBar promptText="客服工作台首页导航" />' },
929
+ { label: 'ByteHi 显示底部操作', code: '<NavBar platform="bytehi" showUtilityActions />' },
930
+ { label: '首页默认选中平台首页', code: '<NavBar selectedItemId="module" onSelect={(nextId) => setCurrentNavId(nextId)} />' },
931
+ { label: '策略页高亮', code: '<NavBar activeModule={false} activeNavId="strategy" />' },
932
+ { label: '受控选中', code: '<NavBar selectedItemId={current} onSelect={(nextId) => setCurrent(nextId)} />' },
933
+ { label: '机器人头像', code: '<NavBar avatarType="robot" />' },
934
+ { label: '自定义主导航', code: '<NavBar navItems={[{ id: "dashboard", label: "首页看板", iconName: "home-smile-stroked", active: true }, { id: "strategy", label: "策略管理", iconName: "if-stroked" }]} />' },
935
+ { label: '自定义底部操作', code: '<NavBar showUtilityActions utilityItems={[{ id: "notice", label: "通知消息", iconName: "bell-03-stroked" }, { id: "settings", label: "系统设置", iconName: "settings-02-stroked" }]} />' },
936
+ { label: '标准平台壳骨架', code: '<div className="flex h-dvh w-full min-w-0 flex-row overflow-hidden"><NavBar className="shrink-0" /><div className="flex flex-1 min-w-0 flex-col overflow-hidden">...</div></div>' },
937
+ { label: '❌ Bad(手搓侧栏)', code: '/* 禁止!自制 aside 缺品牌区/业务入口/选中态/底部头像 */\n<aside className="w-[120px] h-screen bg-[#F5F5F5]">\n <div>OLA</div>\n <div>平台首页</div>\n <div>策略</div>\n</aside>' },
938
+ { label: '❌ Bad(NavBar 上下堆叠)', code: '/* 禁止!NavBar 是站点级左侧导航,不能和主内容上下排列 */\n<div className="flex h-dvh flex-col overflow-hidden">\n <NavBar />\n <main className="flex-1">...</main>\n</div>' },
939
+ { label: '❌ Bad(首页误选普通菜单)', code: '/* 禁止!新生成首页或平台首页时,不要默认选中 strategy 等普通主导航 */\n<NavBar activeModule={false} activeNavId="strategy" />' },
940
+ { label: '❌ Bad(菜单不可切换)', code: '/* 禁止!只画出菜单文字但不接 onSelect / selectedItemId,也不保留组件点击选中能力 */\n<NavBar selectedItemId="module" />' },
941
+ { label: '❌ Bad(非客服工作台误用 ByteHi)', code: '/* 禁止!策略、知识、体验服务等非“客服工作台”页面不要使用 ByteHi 类型 */\n<NavBar platform="bytehi" brandName="体验服务" />' },
942
+ { label: '❌ Bad(固定 OLA 品牌)', code: '/* 禁止!真实业务页面有明确平台名称时,不要继续把 OLA 当固定品牌文案 */\n<NavBar brandName="OLA" />' },
943
+ { label: '✅ Good(用 NavBar)', code: '<NavBar platform="ola" activeNavId="strategy" showUtilityActions />' },
944
+ ],
945
+ keywords: [
946
+ 'NavBar', 'nav-bar', '导航栏', '侧边导航', '左侧导航', '主导航', 'sidebar', 'side nav',
947
+ 'platform', 'ola', 'bytehi', 'OLA', 'ByteHi',
948
+ '工作台导航', '体验与服务平台', '客服工作台', '平台首页',
949
+ '主导航项', '业务入口', '业务切换', '底部工具', '顶部品牌',
950
+ // 错误信号词
951
+ '手搓侧栏', '自制 sidebar', 'div w-[120px] h-screen', 'aside w-60',
952
+ ],
953
+ },
954
+ {
955
+ id: 'tag-bar',
956
+ name: 'TagBar',
957
+ element: 'aside',
958
+ category: 'business',
959
+ description: '业务标签栏,集成顶部搜索、业务切换、树形标签与最小宽度折叠,适合服务平台左侧标签导航场景。',
960
+ componentFile: './components/TagBar.jsx',
961
+ tokensFile: './components/TagBar.tokens.js',
962
+ props: [
963
+ { name: 'businesses', type: 'array', default: null },
964
+ { name: 'currentBusinessId', type: 'string' },
965
+ { name: 'defaultBusinessId', type: 'string', default: null },
966
+ { name: 'onBusinessChange', type: 'function', default: null },
967
+ { name: 'items', type: 'array', default: null },
968
+ { name: 'selectedItemId', type: 'string' },
969
+ { name: 'defaultSelectedItemId', type: 'string', default: null },
970
+ { name: 'onSelect', type: 'function', default: null },
971
+ { name: 'expandedIds', type: 'array', default: null },
972
+ { name: 'defaultExpandedIds', type: 'array', default: null },
973
+ { name: 'onExpandedChange', type: 'function', default: null },
974
+ { name: 'searchable', type: 'boolean', default: true },
975
+ { name: 'searchValue', type: 'string' },
976
+ { name: 'defaultSearchValue', type: 'string', default: '' },
977
+ { name: 'onSearchChange', type: 'function', default: null },
978
+ { name: 'collapsible', type: 'boolean', default: true },
979
+ { name: 'collapsed', type: 'boolean' },
980
+ { name: 'defaultCollapsed', type: 'boolean', default: false },
981
+ { name: 'onCollapsedChange', type: 'function', default: null },
982
+ { name: 'tone', type: 'enum', options: ['transparent', 'panel'], default: 'transparent' },
983
+ { name: 'resizable', type: 'boolean', default: true },
984
+ { name: 'width', type: 'number' },
985
+ { name: 'defaultWidth', type: 'number', default: 240 },
986
+ { name: 'minWidth', type: 'number', default: 160 },
987
+ { name: 'maxWidth', type: 'number', default: 360 },
988
+ { name: 'collapseThreshold', type: 'number', default: 130 },
989
+ { name: 'onWidthChange', type: 'function', default: null },
990
+ ],
991
+ labels: {
992
+ tone: {
993
+ transparent: '嵌入白底(组件无底色)',
994
+ panel: '浅灰侧栏条(panel 浅灰底)',
995
+ },
996
+ },
997
+ _preview: TAGBAR_PREVIEW,
998
+ rules: [
999
+ FONT_WEIGHT_RUNTIME_RULE,
1000
+ '【使用场景】用于服务平台、规则平台等左侧业务标签导航,不替代全局站点导航',
1001
+ '【布局分两类,勿混用】① **大卡片 / 双白卡分栏**(灰底上左右各一张白圆角卡,如 McpManagementPage):这类“横向并列的大卡工作区”默认就要支持宽度拖拽。左侧辅助大卡由**外层容器**提供 `background:#fff`、`border-radius:12px`、`overflow-visible`(便于拖拽把手外露),内层 `<TagBar tone="transparent" />` 且可设 `style={{ background: "transparent" }}` + `className="!border-r-0"`;宽度必须用 `width + onWidthChange + minWidth + maxWidth + resizable` 与白卡同步,右侧主卡保持 `flex-1 min-w-0` 弹性撑满。**禁止**把「独立白卡」误当成 `tone="panel"`:`panel` 是**浅灰侧条**,背景固定为 `--color-blueGrey-100`,不是白卡片。② **单一大白卡内嵌**(整块主内容区共一张白底,TagBar 仅占左列一条,如工作台主白卡内顶栏+TagBar+内容):使用 `tone="transparent"`,让 TagBar **上下撑满**父级高度(父级 `flex` + `min-h-0` 链完整),**不要再**外包一层白圆角容器,避免「白套白」缝。',
1002
+ '【tone 语义】`transparent`(默认)= 组件根节点背景透明,由外层白底或灰底决定视觉;`panel` = 组件自带浅灰底,背景固定为 `--color-blueGrey-100`,用于**灰色独立侧条**场景,不是白色独立卡片。',
1003
+ '【顶部区】展开态支持搜索标签与业务切换;收起到最小宽度时仅在 searchable=true 时显示搜索按钮,searchable=false 时展开态与收起态都不显示搜索入口',
1004
+ '【树形】items 支持多层 children;默认以单选高亮为主,父节点展开与收起仅由箭头按钮控制,点击整行只负责选中节点',
1005
+ '【搜索】搜索只过滤标签树,不影响业务切换列表;命中子节点时需保留父级上下文',
1006
+ '【业务切换】当前业务以按钮形式展示,点击后以下拉菜单切换其它业务',
1007
+ '【折叠】collapsed=true 时整栏宽度收为 72px,仍保留选中高亮、hover、focus-visible 与 aria-label;宽度说明、预览和实现需保持一致',
1008
+ '【拖拽调宽(独立白卡)】当 TagBar 作为横向大卡工作区中的辅助白卡时,默认必须支持调宽:至少同时提供 `resizable`、`width`、`onWidthChange`、`minWidth`、`maxWidth`,并让外层白卡保持 `overflow-visible`,否则拖拽条会被裁切;`McpManagementPage` 是该规则的标准示例。',
1009
+ '【图标】标签前置图标统一复用 B 端 Icon 库,图标槽尺寸固定 24px;背景色必须来自 token,不允许写死色值',
1010
+ '【复用】搜索框复用 Input,前置图标与折叠/展开箭头复用 Icon,不与 NavBar 或 Tabs 混用语义',
1011
+ ],
1012
+ examples: [
1013
+ { label: '嵌入白底(默认 transparent)', code: '<TagBar />' },
1014
+ { label: '浅灰侧栏条(panel,非白卡)', code: '<TagBar tone="panel" />' },
1015
+ { label: '收起状态', code: '<TagBar collapsed />' },
1016
+ { label: '关闭搜索', code: '<TagBar searchable={false} />' },
1017
+ { label: '多业务切换', code: '<TagBar businesses={[{ id: "douyin-community", label: "抖音社区" }, { id: "douyin-search", label: "抖音搜索" }, { id: "douyin-ecommerce", label: "抖音电商" }]} />' },
1018
+ { label: '受控选中', code: '<TagBar selectedItemId={current} onSelect={(nextId) => setCurrent(nextId)} />' },
1019
+ { label: '受控搜索', code: '<TagBar searchValue={keyword} onSearchChange={setKeyword} />' },
1020
+ { label: '受控折叠', code: '<TagBar collapsed={collapsed} onCollapsedChange={setCollapsed} />' },
1021
+ { label: '自定义树项', code: '<TagBar items={[{ id: "account", label: "账号", iconName: "user-01-stroked", iconBgToken: "blueGrey-100", children: [{ id: "account-security", label: "账号安全", iconName: "lock-03-stroked", iconBgToken: "red-50" }] }]} />' },
1022
+ { label: '双白卡左列(可拖拽辅助大卡)', code: '<div style={{ background: "var(--color-white)", borderRadius: 12, overflow: "visible", width: tagBarWidth }}><TagBar tone="transparent" className="!border-r-0" style={{ background: "transparent" }} width={tagBarWidth} onWidthChange={setTagBarWidth} minWidth={200} maxWidth={360} resizable /></div>' },
1023
+ { label: '❌ Bad(手搓树形导航)', code: '/* 禁止!自制 aside + ul/li 树缺搜索/折叠/选中/拖拽宽 */\n<aside className="w-[240px] border-r">\n <input placeholder="搜索" />\n <ul>\n <li>账号 <ul><li>账号安全</li></ul></li>\n </ul>\n</aside>' },
1024
+ { label: '✅ Good(用 TagBar)', code: '<TagBar items={[{ id: "account", label: "账号", iconName: "user-01-stroked", children: [{ id: "account-security", label: "账号安全" }] }]} resizable />' },
1025
+ ],
1026
+ keywords: [
1027
+ 'TagBar', 'tag-bar', '标签栏', '标签导航', '左侧标签栏', '业务标签栏', 'category bar',
1028
+ 'tree nav', '树形导航', '业务切换', '业务下拉',
1029
+ '可拖拽宽度', 'resizable', 'collapsed', '折叠', '收起',
1030
+ 'tone transparent', 'tone panel', '透明嵌入', '浅灰侧条',
1031
+ // 高频场景
1032
+ 'MCP 管理', '规则管理', '策略管理', '账号管理',
1033
+ // 错误信号词
1034
+ '手搓 tree nav', '自制标签树', 'aside w-[240px] tree', '双白卡左列手搓',
1035
+ ],
1036
+ },
1037
+ {
1038
+ id: 'card',
1039
+ name: 'Card',
1040
+ element: 'article',
1041
+ category: 'business',
1042
+ description: '业务信息摘要卡片,支持数据卡片、商品卡片和信息卡片三类结构。颜色使用遵循背景反衬原则:灰色背景用白底卡,白色背景用灰底卡。',
1043
+ componentFile: './components/Card.jsx',
1044
+ tokensFile: './components/Card.tokens.js',
1045
+ props: [
1046
+ { name: 'type', type: 'enum', options: ['data', 'product', 'info'], default: 'data' },
1047
+ { name: 'color', type: 'enum', options: ['white', 'grey'], default: 'white' },
1048
+ { name: 'title', type: 'string', default: '智能服务近7天趋势分析' },
1049
+ { name: 'description', type: 'string', default: '针对指定业务场景下智能服务核心指标、运行路径、渠道等进行趋势分析,并输出总结报告' },
1050
+ { name: 'stats', type: 'array', default: null },
1051
+ { name: 'tags', type: 'array', default: null },
1052
+ { name: 'productStatus', type: 'string', default: '已使用' },
1053
+ { name: 'productImageSrc', type: 'string', default: null },
1054
+ { name: 'productImageAlt', type: 'string', default: '商品图' },
1055
+ { name: 'infoIconName', type: 'string', default: 'magic-wand-01-stroked' },
1056
+ { name: 'infoIconTone', type: 'enum', options: ['pink', 'blue', 'green', 'orange', 'purple', 'brand', 'grey'], default: 'pink' },
1057
+ { name: 'infoIconStyle', type: 'enum', options: ['tone', 'inverse'], default: 'inverse' },
1058
+ { name: 'infoLayout', type: 'enum', options: ['default', 'icon-right'], default: 'icon-right' },
1059
+ { name: 'dataIconVisible', type: 'enum', options: ['hidden', 'visible'], default: 'hidden' },
1060
+ { name: 'dataIconName', type: 'string', default: 'edit-04-stroked' },
1061
+ { name: 'dataIconTone', type: 'enum', options: ['pink', 'blue', 'green', 'orange', 'purple', 'brand', 'grey'], default: 'blue' },
1062
+ { name: 'dataIconStyle', type: 'enum', options: ['tone', 'inverse'], default: 'inverse' },
1063
+ { name: 'infoMetaLabel', type: 'string', default: '段然' },
1064
+ { name: 'infoMetaBadge', type: 'string', default: '官方能力' },
1065
+ { name: 'infoMetaBadgeVariant', type: 'enum', options: ['brand', 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'pink', 'teal', 'grey', 'white', 'ai'], default: null },
1066
+ { name: 'infoMetaAvatarSrc', type: 'string', default: null },
1067
+ { name: 'infoMetaAvatarAlt', type: 'string', default: '用户头像' },
1068
+ { name: 'infoStats', type: 'array', default: null },
1069
+ { name: 'onAction', type: 'function', default: null },
1070
+ { name: 'actionAriaLabel', type: 'string', default: '查看卡片详情' },
1071
+ ],
1072
+ labels: {
1073
+ type: { data: '数据卡片', product: '商品卡片', info: '信息卡片' },
1074
+ color: { white: '白底', grey: '灰底' },
1075
+ infoIconStyle: { tone: '彩色浅底', inverse: '黑底白标' },
1076
+ infoLayout: { default: '图标在左', 'icon-right': '图标在右' },
1077
+ productInfoLayout: { default: '图标在左', 'icon-right': '图标在右' },
1078
+ dataIconVisible: { hidden: '不显示图标', visible: '显示图标' },
1079
+ dataIconStyle: { tone: '彩色浅底', inverse: '黑底白标' },
1080
+ },
1081
+ _preview: CARD_PREVIEW,
1082
+ rules: [
1083
+ FONT_WEIGHT_RUNTIME_RULE,
1084
+ '【类型】type=data 为数据卡片;type=product 为商品卡片 / 业务对象卡;type=info 为信息卡片,用于展示带图标、主副标题和底部辅助信息的业务信息对象。',
1085
+ '【数据卡片适用场景】数据卡片不仅适用于指标摘要、趋势分析、服务能力入口和工具入口,也适用于模板、脚本、案例类内容入口,例如优秀知识模版、优秀案例模版、脚本模版、话术模版、内容生成模版、服务 SOP 模版等。此类内容仍使用数据卡片的摘要入口结构:标题 + 描述 + 可选指标/元信息 + 标签 + 右侧进入箭头;如果需要突出缩略图/封面图或具体商品、订单、附件对象,应改用商品卡片;如果需要强调 AI 能力、智能体、知识库或应用服务身份,应优先使用信息卡片。',
1086
+ '【数据卡片结构】上半区固定为标题 + 描述 + 1-3 项指标,下半区为左下角标签 + 右侧单步进入按钮。数据卡片支持可选右侧图标:默认 `dataIconVisible="hidden"` 不显示图标;需要图标时设置 `dataIconVisible="visible"` 并传入 `dataIconName`,标题/描述/指标区域居左,图标容器 48×48px、圆角必须使用 rounded-xl token(16px),仅显示在数据卡片右上角并与文字区域顶部对齐,图标与左侧标题内容区左右间距必须等于数据卡片四周 padding,即 p-5 / 20px。图标样式复用信息卡片同名容器逻辑:`dataIconStyle="inverse"` 为黑底白 icon(默认),`dataIconStyle="tone"` 为彩色浅底;色系通过 `dataIconTone` 选取。',
1087
+ '【信息卡片结构】信息卡片支持两种布局:`infoLayout="icon-right"` 为默认布局,即“图标在右”;左侧承载标题/副标题/底部辅助信息,图标整体放在最右侧,状态徽标紧跟主标题右侧。`infoLayout="default"` 即“图标在左”;左侧固定 48px 图标容器,右侧上半区为主标题 + 副标题 + 右上角状态徽标,底部为辅助显示区。信息卡片图标与标题内容区左右间距必须等于信息卡片四周 padding,即 p-6 / 24px。信息卡片适合内容对象、智能体、知识库、应用、服务入口等需要“图标 + 长说明 + 关键辅助信息”的场景。',
1088
+ '【信息卡片主图标】左侧主视觉定义为图标,不使用图片。默认复用组件库 Icon(magic-wand-01-stroked),图标容器为 48×48px、内部图标 24px、圆角 12px。图标支持两层配置:`infoIconStyle` 控制样式,`infoIconTone` 控制色系。默认使用 `infoIconStyle="inverse"`,即黑背景 + 白色 icon 的反色样式;当 `infoIconStyle="tone"` 时走彩色浅底图标容器,并通过 infoIconTone 选择 pink / blue / green / orange / purple / brand / grey,每种都使用同色系 50 背景、100 浅描边、600 或品牌强调图标色。禁止手写 className / style 覆盖图标容器颜色。',
1089
+ '【信息卡片基础组件复用】信息卡片内部禁止手搓标签、标题、图标和头像:主图标和底部辅助项图标必须复用 Icon;主副标题必须复用 FormTitle(variant=form / showDescription);徽标必须复用 Tag;昵称头像必须复用 Avatar。辅助数字/名称文案使用 Card 内置文字 token 承载,不新增 Text/Typography 组件。',
1090
+ '【信息卡片身份区】默认模拟抖音官方 AI 能力场景:infoMetaBadge 使用“官方能力”,徽标必须复用 Tag variant,可表达官方能力、内测中、已启用、异常、推荐、Beta 等状态。徽标颜色必须与 infoIconStyle 联动:`infoIconStyle="tone"`(彩色浅底)时一律使用 grey 标签样式;`infoIconStyle="inverse"`(黑底白标)时默认使用彩色标签样式,并按 purple(紫色)/ teal(青绿色)/ blue(蓝色)/ cyan(青色)/ orange(橙色)的优先级选色,运行时默认使用 purple,业务可通过 infoMetaBadgeVariant 显式指定这组推荐彩色 Tag variant。信息卡片无论 `infoLayout="default"` 还是 `infoLayout="icon-right"`,徽标都必须紧跟主标题右侧展示,标题与徽标间距固定 8px,且标题文字与徽标需要在同一标题行内垂直居中对齐,不允许把徽标推到标题行最右端。infoMetaLabel 默认使用“段然”,固定在底部左侧,字号 12px,左侧必须显示 Avatar 最小尺寸 token(size=xxs,16px),默认头像使用本地成员头像 team-avatar-assets/duan-ran.png;业务可通过 infoMetaLabel / infoMetaAvatarSrc / infoMetaAvatarAlt 动态适配创建人、负责人、智能体归属或团队头像;头像与名称间距固定 8px,头像图片需取消额外 translate/scale 偏移,保证与文字同一行垂直居中。',
1091
+ '【标题右侧标签统一规则】信息卡片的徽标标签不论图标在左还是图标在右,都必须紧跟主标题右侧展示;凡是使用 `infoLayout="icon-right"` 的卡片,默认标签也必须紧跟主标题右侧展示,不允许被推到标题行最右端;标题与标签固定间距 8px,并在同一标题行内垂直居中对齐。该规则同时适用于商品卡片 icon-right 状态标签和信息卡片所有布局的徽标标签。',
1092
+ '【商品卡片适用场景】适用于客服工作台、企业后台、信息查询、商品信息、订单信息等需要在紧凑空间展示“图片 + 关键文字 + 状态”的业务对象场景;对象可以是商品、订单、门店、内容素材、工单附件或查询结果,不仅限商品',
1093
+ '【触发条件】当用户需要快速识别一个具体业务对象,并在同一视线内判断当前状态或处理进度时使用;典型位置包括 IM 对话上下文、订单详情摘要、检索结果卡片、关联商品/订单信息块、后台审核或售后处理侧栏',
1094
+ '【不适用场景】纯指标摘要、趋势解读、统计入口使用 type=data;需要展示 3 个以上结构化字段、批量比较或排序筛选时优先使用 Table / 列表模板;需要突出大图检查时不要压缩成 56px 商品卡片',
1095
+ '【商品卡片结构】商品卡片支持 `infoLayout="default" | "icon-right"` 两种主视觉布局。`default` 为左侧商品图 + 右侧标题/副标题/状态;`icon-right` 为左侧标题/副标题/状态,商品图整体移动到右侧。缩略图尺寸固定 56px,圆角 12px(--radius-lg);商品图与标题内容区左右间距必须等于商品卡片四周 padding,即 p-5 / 20px;封面图高度必须与文案区总高保持一致;未传 productImageSrc 时使用与 Upload 图片预览一致的蓝色图片占位',
1096
+ '【商品状态】商品卡片标题栏显示状态 Tag,默认模板为绿色大标签“已使用”,不要放在主标题左侧;`default` 布局下标签位于标题行右上位置,`icon-right` 布局下标签必须紧跟主标题右侧,间距固定 8px。Tag 文案应根据真实业务状态替换,例如“待处理”“退款中”“已完成”“已失效”“审核中”。颜色按语义选择:成功/可用用 green,等待/处理中用 orange,失败/异常用 red,关闭/草稿/失效用 grey;保持 size=l、radius=md',
1097
+ '【商品文案】标题最多 1 行,字号统一为 text-base / 16px / 600,优先放用户识别对象所需的名称 / 订单号 / 关键标识;副标题最多 1 行,用于展示数量、价格、规格、订单金额、下单时间、售后进度等辅助信息;不要把长说明、操作指引或多段备注塞入副标题',
1098
+ '【信息卡片文案】默认文案按抖音官方 AI 能力模拟,标题为“抖音 AI 创作助手”,副标题说明短视频脚本、标题推荐、封面文案和热点灵感生成能力。标题建议 1 行,字号统一为 text-base / 16px / 600;副标题建议 2 行内,字号 12px / 400,不要堆叠多段正文。底部辅助项通过 infoStats 自定义,数据结构固定为 `{ iconName, value }`,建议 2-4 项,可表达启用量、评分、调用量、响应率、转化率、更新时间、操作入口等不同类型;默认使用“启用量 / 评分 / 立即体验”,并与数据卡片指标行保持一致:图标 14px、文字 12px / 400、项间距 20px;底部整栏所有头像、图标、文字必须保持 16px 行高内垂直居中并对齐到同一行。',
1099
+ '【颜色】color=white 使用 65% 白底 + 白色描边;color=grey 使用 Blue Grey 100 背景 + Blue Grey 300 描边',
1100
+ '【容器映射(强约束)】Card 的颜色必须与所处背景做反衬:当 Card 所在父容器是“灰底页面/灰底区块”(如 blueGrey-200 页面底、白卡内的浅灰分区)→ Card 用 `color="white"`;当父容器本身是“白底卡片/白底面板”→ Card 用 `color="grey"`,用灰底卡片做二级区块分层,避免白底叠白底',
1101
+ '【一句话记忆】如果卡片背景环境是灰色,就选“白底”分类卡片;如果卡片背景环境是纯白色,就选“灰底”分类卡片。不要让 Card 和父级背景同色相贴。',
1102
+ '【状态】白底卡默认态使用 65% 白底 + 轻白描边;hover 后补满白底并出现业务卡片专用投影;灰底卡保持灰底与灰描边并保留投影反馈',
1103
+ '【指标】指标项推荐控制在 3 项以内,图标 14px、文字 12px,项间距 20px',
1104
+ '【标签】通用 tags 只用于数据卡片,展示在左下角并与右侧操作按钮水平对齐;商品卡片和信息卡片禁止渲染通用 tags,主标题左侧不得出现“标签”等前缀标签,运行时会隐藏误注入的 white Tag。Card 内所有标签默认必须使用圆角矩形样式 `radius="md"`,不使用全圆胶囊 `radius="full"`;数据卡片、商品状态标签、信息卡片徽标都遵守该规则。数据卡片不论是否展示图标、也不论 dataIconStyle 为 tone 或 inverse,默认标签都必须统一使用 grey 标签样式。商品状态标签和信息卡片徽标仅可展示在右上角并与标题水平对齐。信息卡片徽标与 infoIconStyle 联动:`tone` 时一律 grey,`inverse` 时默认彩色 purple;卡片彩色标签优先使用 purple(紫色)/ teal(青绿色)/ blue(蓝色)/ cyan(青色)/ orange(橙色),避免优先使用 pink、red、yellow 等过强警示或装饰色。商品状态标签复用 green + l + md;不显示图标和关闭按钮,建议使用 2-4 字短标签。',
1105
+ '【操作】数据卡片右侧箭头按钮只承载进入 / 查看详情等单步操作,aria-label 需说明目标;商品卡片不使用箭头按钮',
1106
+ '【信息卡片语义映射】AI / 智能能力推荐使用 magic-wand-01-stroked + pink 或 brand;数据洞察可用 bar-chart / line-chart 类图标 + blue;任务自动化可用 zap / settings 类图标 + orange;安全、审核、风控可用 shield / alert 类图标 + red 或 orange;知识库、文档、客服可用 book / message 类图标 + green 或 blue。色系只表达类别和状态,不用于装饰。',
1107
+ '【文案长度】数据卡片标题优先控制在 1 行内,描述建议 2 行内;商品卡片标题和副标题均为 1 行截断,避免状态标签被挤压或换行;信息卡片标题 1 行、副标题 2 行,底部辅助区允许换行但不得撑破卡片宽度。',
1108
+ ],
1109
+ examples: [
1110
+ { label: '灰色背景里用白底卡(推荐)', code: '<div className="bg-blueGrey-200 p-6"><Card color="white" onAction={() => {}} /></div>' },
1111
+ { label: '白色背景里用灰底卡(推荐)', code: '<div className="bg-white p-6"><Card color="grey" onAction={() => {}} /></div>' },
1112
+ { label: '商品卡片', code: '<Card type="product" title="海底捞门店通用双人套餐" description="数量 1 · ¥128.00 · 月售 2,361" productStatus="已使用" />' },
1113
+ { label: '订单信息卡片', code: '<Card type="product" title="订单 202604300018" description="共 3 件 · 实付 ¥268.00 · 退款中" productStatus="退款中" />' },
1114
+ { label: '信息卡片', code: '<Card type="info" infoIconName="magic-wand-01-stroked" infoIconTone="pink" title="抖音 AI 创作助手" description="抖音官方 AI 创作能力,支持短视频脚本、标题推荐、封面文案和热点灵感生成,帮助创作者提升内容生产效率。" infoMetaBadge="官方能力" infoMetaLabel="段然" />' },
1115
+ { label: '信息卡片(黑底白 icon)', code: '<Card type="info" infoIconStyle="inverse" infoIconName="magic-wand-01-stroked" title="抖音 AI 创作助手" description="抖音官方 AI 创作能力,支持短视频脚本、标题推荐、封面文案和热点灵感生成,帮助创作者提升内容生产效率。" infoMetaBadge="官方能力" infoMetaBadgeVariant="pink" infoMetaLabel="段然" />' },
1116
+ { label: '信息卡片(图标在右)', code: '<Card type="info" infoLayout="icon-right" infoIconName="magic-wand-01-stroked" infoIconTone="pink" title="抖音 AI 创作助手" description="抖音官方 AI 创作能力,支持短视频脚本、标题推荐、封面文案和热点灵感生成,帮助创作者提升内容生产效率。" infoMetaBadge="官方能力" infoMetaBadgeVariant="pink" infoMetaLabel="段然" />' },
1117
+ { label: '信息卡片(数据洞察)', code: '<Card type="info" infoIconName="bar-chart-10-stroked" infoIconTone="blue" title="直播数据洞察" description="汇总直播间流量、互动、成交和内容表现,自动生成经营分析与优化建议。" infoMetaBadge="已启用" infoMetaBadgeVariant="green" infoMetaLabel="刘德琳" infoStats={[{ iconName: "users-01-stroked", value: "8.2K" }, { iconName: "trend-up-01-stroked", value: "转化 +12%" }, { iconName: "clock-stroked", value: "刚刚更新" }]} />' },
1118
+ { label: '信息卡片(风控能力)', code: '<Card type="info" infoIconName="shield-01-stroked" infoIconTone="orange" title="内容安全巡检" description="对视频、评论和私信内容进行风险识别,辅助运营及时处理异常内容。" infoMetaBadge="内测中" infoMetaBadgeVariant="orange" infoMetaLabel="郭泽智" infoStats={[{ iconName: "alert-circle-stroked", value: "23 条风险" }, { iconName: "check-circle-stroked", value: "98.6%" }]} />' },
1119
+ { label: '自定义标题', code: '<Card title="重点客户满意度追踪" description="汇总近 7 天满意度变化、问题归因和渠道分布,并给出跟进建议。" onAction={() => {}} />' },
1120
+ { label: '自定义指标', code: '<Card stats={[{ iconName: "users-01-stroked", value: "860" }, { iconName: "message-chat-square-stroked", value: "3,421" }, { iconName: "hearts-stroked", value: "95" }]} onAction={() => {}} />' },
1121
+ { label: '自定义标签', code: '<Card tags={["客服", "高优先"]} actionAriaLabel="查看客服趋势分析" onAction={() => {}} />' },
1122
+ { label: '❌ Bad(手搓 div 当业务摘要卡)', code: '/* 禁止!手搓卡片缺投影、圆角、hover、token;推荐用 Card 业务组件 */\n<div className="bg-white rounded p-4 border"><h3>近 7 天</h3><p>描述</p></div>' },
1123
+ { label: '✅ Good(Card 业务卡)', code: '<Card title="近 7 天趋势" description="…" stats={[{ iconName: "users-01-stroked", value: "860" }]} onAction={() => {}} />' },
1124
+ { label: '❌ Bad(白色背景里继续放白底卡)', code: '/* 禁止!白卡内嵌白底卡视觉无层次 */\n<div className="bg-white p-6"><Card color="white" /></div>' },
1125
+ { label: '✅ Good(白色背景里用灰底 Card)', code: '<div className="bg-white p-6"><Card color="grey" /></div>' },
1126
+ ],
1127
+ keywords: [
1128
+ 'Card', 'card', '业务卡片', '信息卡片', '摘要卡片', '指标卡片', '统计卡', 'data card',
1129
+ 'summary card', 'metric card', 'info card', '信息卡片', '商品卡片', 'product card', 'goods card',
1130
+ '订单卡片', 'order card', '图片文字卡片', '业务对象卡', 'object card',
1131
+ '趋势分析', '近 7 天', '指标摘要', '入口卡片', '快捷入口',
1132
+ '白底卡', '灰底卡', 'color white', 'color grey',
1133
+ 'stats', 'indicator', '指标项', 'tags', '卡片标签', 'productImageSrc', '商品图', '商品标题', '商品价格',
1134
+ '客服工作台', '企业后台', '信息查询', '订单信息', '商品状态', '订单状态',
1135
+ 'onAction', '查看详情', '进入按钮',
1136
+ // 容器 / 嵌套场景
1137
+ '白卡内卡片', '灰底容器卡片', '页面卡片', 'page card', 'panel card',
1138
+ // 伪手搓信号词
1139
+ 'bg-white rounded p-4 border', '手搓卡片', '自制卡片', 'div card',
1140
+ ],
1141
+ },
1142
+ {
1143
+ id: 'conversation-list',
1144
+ name: 'ConversationList',
1145
+ element: 'section',
1146
+ category: 'business',
1147
+ description: '会话 / 工单 / 线程侧栏列表,用于 IM 对话、即时通讯工作台、客服接待、工单处理、托管队列等需要按状态分组浏览并快速切换当前处理线程的业务场景,默认支持“默认列表 / 卡片列表”双布局切换。',
1148
+ componentFile: './components/ConversationList.jsx',
1149
+ tokensFile: './components/ConversationList.tokens.js',
1150
+ props: [
1151
+ { name: 'variant', type: 'enum', options: ['default', 'card'], default: 'default' },
1152
+ { name: 'defaultVariant', type: 'enum', options: ['default', 'card'], default: 'default' },
1153
+ { name: 'onVariantChange', type: 'function', default: null },
1154
+ { name: 'title', type: 'string', default: '会话列表' },
1155
+ { name: 'tabs', type: 'array', default: null },
1156
+ { name: 'activeTab', type: 'string', default: null },
1157
+ { name: 'defaultActiveTab', type: 'string', default: 'all' },
1158
+ { name: 'onTabChange', type: 'function', default: null },
1159
+ { name: 'sections', type: 'array', default: null },
1160
+ { name: 'activeItemId', type: 'string', default: null },
1161
+ { name: 'defaultActiveItemId', type: 'string', default: null },
1162
+ { name: 'onItemClick', type: 'function', default: null },
1163
+ { name: 'onSend', type: 'function', default: null },
1164
+ { name: 'showActions', type: 'boolean', default: true },
1165
+ { name: 'showLayoutToggle', type: 'boolean', default: true },
1166
+ { name: 'resizable', type: 'boolean', default: true },
1167
+ { name: 'collapsible', type: 'boolean', default: true },
1168
+ { name: 'autoCollapseOnNarrow', type: 'boolean', default: true },
1169
+ { name: 'onLayoutWidthRequest', type: 'function', default: null },
1170
+ { name: 'className', type: 'string', default: '' },
1171
+ { name: 'style', type: 'object', default: null },
1172
+ ],
1173
+ labels: {
1174
+ variant: {
1175
+ default: '默认列表',
1176
+ card: '卡片列表',
1177
+ },
1178
+ },
1179
+ _preview: CONVERSATION_LIST_PREVIEW,
1180
+ rules: [
1181
+ FONT_WEIGHT_RUNTIME_RULE,
1182
+ LOCAL_MEMBER_AVATAR_RULE,
1183
+ '【组件定位】ConversationList 用于承载 IM 对话线程、客服接待队列、工单队列、托管任务、人工干预池等“需要持续处理的一组业务对象”,核心目标是帮助用户快速完成“识别对象 → 判断状态 → 进入处理”。',
1184
+ '【触发引用】当页面同时满足“存在多个会话/任务线程”“用户需逐条切入当前对象”“对象带有明确状态或处理阶段”“主内容区会随选中项联动刷新”中的 3 项及以上时,优先用 ConversationList。',
1185
+ '【适用场景】适用于 IM 工作台、即时通讯收件箱、在线接待台、工单处理台、托管运营台等需要持续切换当前处理线程的场景;客服工作台只是典型子场景,不是唯一适用范围。',
1186
+ '【引用位置】优先用于工作台左栏、双栏布局的线程切换列、主对话区旁的任务队列;组件只负责队列浏览与切换,详情、消息流、表单、执行记录放在相邻主内容区。',
1187
+ '【嵌入客服工作台框架】当 `ConversationList` 放入 `CustomerServiceWorkspaceFrame` 左侧半透明容器时,左侧容器宽度由外层框架统一拖拽控制,组件自身应传 `resizable={false}`,保留 `collapsible` 与 `autoCollapseOnNarrow`,并用 `style={{ width: "100%", height: "100%" }}` 填满左栏。外层框架必须同步传 `leftContentMinWidth` 并接 `onLayoutWidthRequest`:默认列表纯头像锁定宽度为 88px,框架最小宽度为 `88px + 32px 覆盖区 = 120px`;卡片列表最小宽度为 333px,框架最小宽度为 365px。点击收起 / 展开 / 自动窄宽收起时,外层容器必须跟随组件宽度一起变化,避免只缩组件但左栏留下大片空白。',
1188
+ '【结构】顶部为标题 + 操作按钮,其下为状态 tabs;`variant="default"` 与 `variant="card"` 复用同一份 `sections/items` 数据,只是列表项视觉结构不同。',
1189
+ '【顶部标题】顶部标题(默认“会话列表”)字号固定使用 `text-sm / 14px`,行高使用 `leading-5 / 20px`,字重使用 `[font-weight:var(--font-semibold)]`;不要使用 16px 或更大的标题字号,避免左侧队列标题抢占主内容层级。',
1190
+ '【默认能力·双布局切换】`ConversationList` 默认同时支持“默认列表 / 卡片列表”两种布局形态;未被业务明确禁止时,AI 生成代码应保留组件内置的右上角布局切换按钮,让用户可在两种布局之间直接切换。',
1191
+ '【布局切换开关】当业务需要锁定单一布局时,可通过 `showLayoutToggle={false}` 隐藏右上角切换按钮;若页面已有更高层布局切换入口,则必须保证它与 `variant / defaultVariant / onVariantChange` 的状态一致,不要出现两个不同步的切换源。',
1192
+ '【默认列表结构】`variant="default"` 时,内容区按状态分组展示会话项,并保留分组标题与展开/收起能力。',
1193
+ '【卡片列表结构】`variant="card"` 时,每条会话项切换为卡片样式:顶部为标题 + 标签 + 头像,中部为限定高度且可滚动查看的消息气泡区,底部必须支持“已回复用户”“回复内容生成中”“输入框文案 + 发送按钮”三种状态。',
1194
+ '【列表区间距】列表区使用 4px 纵向节奏:分组之间 `gap-1`,分组标题与第一条列表之间 `gap-1`,同组内每条列表项之间也必须 `gap-1`。禁止把展开内容写成普通 div 导致列表项贴在一起。',
1195
+ '【卡片列表间距】`variant="card"` 时所有卡片之间的垂直间距统一为 `16px`,包含“全部”视图下跨状态顺序相邻的卡片;不要继续沿用默认列表的 4px 节奏。',
1196
+ '【状态组织】顶部 tabs、默认列表下的分组标题、列表项最右侧状态标签必须属于同一套状态口径,命名、颜色和语义保持一致。',
1197
+ '【会话标题字重】展开态每条会话项自己的标题统一使用 `semibold` token,即 `[font-weight:var(--font-semibold)]`;不要继续使用 `font-bold` 或手写 700 字重。',
1198
+ '【交互主原则】点击列表项的首要结果必须是切换当前处理对象并驱动主内容区刷新;保持“点列表 = 切对象”的稳定心智,不要拆成多个主入口。',
1199
+ '【嵌入客服工作台时的联动职责】当 `ConversationList` 作为客服工作台框架左栏时,默认选中项就是右侧整块主白卡的上下文源。点击或切换任意列表项时,右侧 `IMConversationPattern` 聊天区和 `InfoDisplayPanel` 信息区都必须同步切换到同一个当前会话 / 工单 / 托管对象;不要只刷新其中一个区域,也不要让右侧两个区域分别维护不同的当前对象。',
1200
+ '【状态源规范】接真实接口、URL、埋点或缓存时,推荐以 `currentConversationId` / `currentTicketId` 这类单一 id 作为外层状态源,再传给右侧聊天线程和信息区派生内容;不要让 `ConversationList`、IM 区和信息区各自保留独立当前对象 state,避免上下文错位。',
1201
+ '【选中态】当前处理项必须使用背景、左侧色条、层级变化的组合反馈;禁止只靠字重变化表达选中。',
1202
+ '【选中态阴影】展开态当前选中会话项与收起态当前选中头像项的阴影 token 均固定为 `shadow-list`;不要继续使用 `shadow-card`、`shadow-lg`、`shadow-DEFAULT`、`shadow-sm` 或手写自定义阴影。',
1203
+ '【卡片列表选中交互】`variant="card"` 时必须有明确选中态:未传 `activeItemId` / `defaultActiveItemId` 时默认选中当前可见的第一个卡片;点击任意卡片区域即可选中并切换当前处理对象。当前选中卡片必须使用 `1.5px` 内描边高亮,描边色固定为 Brand 500(`--color-brand-500`),并保留 `shadow-list` 投影;不要使用 green-500、外描边、1px ring、2px border 或额外自定义投影。',
1204
+ '【信息层级】列表项按“标题 > 状态 > 辅助信息 > 时间”组织;标题负责识别对象,状态负责判断是否处理,辅助信息负责身份校验,时间负责优先级判断。',
1205
+ '【信息密度】标题最多 1 行,主状态标签建议 1 个,辅助标签建议总数不超过 2 个;超过时优先保留最影响当前决策的信息。',
1206
+ '【辅助信息】用户名、单号、时间属于辅助识别信息;空间不足时允许截断,但应通过 Tooltip 提供完整信息。',
1207
+ '【标签语义】右侧状态标签只承载“当前处理状态”;待干预 / 异常用 red,托管中 / 正常托管用 green,挂起 / 等待用 orange,已处理 / 已完成用 blue;原因、规则、倒计时等信息作为辅助标签。',
1208
+ '【收起态】仅当空间受限但仍需保留对象切换能力时收起为纯头像态;收起态只承担切换,不承担复杂状态判断,展开后应恢复用户上次工作宽度。',
1209
+ '【卡片列表限制】`variant="card"` 时顶部 tabs 是唯一的分类切换入口;卡片列表不显示分组标题,也不支持分组展开/收起。选中“全部”时,卡片必须按状态分类顺序 `待干预 → 托管中 → 其它` 自上而下连续排列;禁止再引入独立 `cards` 数据源或把 card 形态拆成另一套列表。',
1210
+ '【卡片列表宽度】`variant="card"` 时不进入纯头像收起态,但保留整体宽度拖拽能力;拖拽变宽后卡片宽度必须按实际可用宽度自适应扩展,拖窄时不得小于 333px。',
1211
+ '【卡片列表栅格】当 `variant="card"` 时,列数必须基于组件实际可用宽度动态判断:`<=580px` 为 1 列,`>580px` 为 2 列,`>950px` 为 3 列;嵌入客服工作台等外层可拖拽容器时,也必须读取外层给到的实际宽度,而不是固定按默认 400px 判断。卡片横向与纵向间距都统一为 `16px`。',
1212
+ '【卡片消息区】card 形态的消息区必须使用限定高度 + 内部滚动(`overflow-y-auto`);已回复 / 生成中状态的消息区高度为 `196px`,输入文案状态的消息区高度为 `155px`。消息多时只滚动卡片内部,不把整张卡片无限撑高。',
1213
+ '【卡片底部状态】底部必须支持三态:`replied` 显示“已回复用户”,`generating` 显示“回复内容生成中”,`editable` 显示输入文案和右下角发送按钮。只有 `editable` 状态展示分割线与按钮行,其余两态不展示输入区结构。',
1214
+ '【卡片底部间距】已回复和生成中状态底部内边距为 `16px`;输入文案状态先展示 1px 分割线,再使用 `12px` 容器内边距和 `12px` 纵向间距组织文案与按钮,发送按钮尺寸固定 `24px`、圆角 `8px`。',
1215
+ '【卡片发送交互】`variant="card"` 且卡片处于 `editable` 时,点击底部发送按钮会把当前 `draftText` 追加为一条新的 `agent` 消息气泡,并将该卡片状态切换为 `replied`;此交互默认在组件内部可直接生效。',
1216
+ '【卡片发送回调】如业务需要接接口或埋点,可通过 `onSend({ item, draftText, nextItem, sectionId })` 获取发送前卡片、发送文案、发送后卡片快照和所在分组 id;点击发送按钮不得冒泡成普通卡片选中点击。',
1217
+ '【关键交互·收起必须带动布局自适应】ConversationList 收起为纯头像态时自身宽度会缩到 88px;外层页面布局必须让右侧主内容区自动扩展(自适应撑宽)。⛔ 禁止把会话列表放在固定宽度网格列(如 `grid-cols-[400px_1fr]`)或外层写死 `w-[400px]`,否则收起只会在固定列内“缩内容不缩列”,造成右侧不能变宽的严重体验问题。',
1218
+ '【推荐布局骨架】外层用两栏 flex:左侧 `<ConversationList className="shrink-0" />`;右侧容器必须 `flex-1 min-w-0`(白卡/主工作区在里面自滚动)。若必须用 grid,则左列必须是 `auto`(`grid-cols-[auto_1fr]`),不要写死像素列宽。',
1219
+ '【不适用场景】单一当前对话详情直接用 `IMConversationPattern` / `ChatConversationPattern`;纯消息内容流用 ChatBubble / ChatMessage;可排序、可批量操作、多字段对比的列表用 Table;分类导航用 NavBar / TagBar;如果页面不需要切换当前对象,也不要用 ConversationList。',
1220
+ '【引用组件】头像必须用 Avatar,默认数据和缺省 avatarSrc 必须落到本地成员头像素材;状态必须用 Tag,图标必须用 Icon;顶栏 iconOnly 操作为 `Button iconOnly tooltip="操作文案"`(推荐 `variant="ghost-black"`,hover 为浅灰底小方块),禁止手写 badge、svg、img 替代。',
1221
+ ],
1222
+ examples: [
1223
+ { label: '默认会话列表', code: '<ConversationList />' },
1224
+ { label: '默认支持布局切换', code: '<ConversationList sections={ticketSections} />' },
1225
+ { label: '受控筛选与选中', code: '<ConversationList activeTab={currentTab} activeItemId={currentId} onTabChange={(tabId) => setCurrentTab(tabId)} onItemClick={(item) => setCurrentId(item.id)} />' },
1226
+ { label: '受控布局切换', code: '<ConversationList variant={layoutVariant} onVariantChange={setLayoutVariant} sections={ticketSections} />' },
1227
+ { label: '卡片发送回调', code: '<ConversationList variant="card" sections={ticketSections} onSend={({ item, draftText, nextItem, sectionId }) => submitConversationReply({ item, draftText, nextItem, sectionId })} />' },
1228
+ { label: '隐藏顶部操作', code: '<ConversationList showActions={false} />' },
1229
+ { label: '嵌入客服工作台左栏', code: '<CustomerServiceWorkspaceFrame sideWidth={sideWidth} minSideWidth={100} leftContentMinWidth={leftContentMinWidth} mainMinWidth={380} onSideWidthChange={setSideWidth} leftPanel={<ConversationList resizable={false} collapsible autoCollapseOnNarrow onLayoutWidthRequest={(width) => setSideWidth(width + 32)} onVariantChange={(variant) => setLeftContentMinWidth(variant === "card" ? 333 : 88)} style={{ width: "100%", height: "100%" }} />} />' },
1230
+ { label: '锁定单一布局并隐藏切换', code: '<ConversationList variant="card" showLayoutToggle={false} sections={ticketSections} />' },
1231
+ { label: '自定义分组数据', code: '<ConversationList sections={[{ id: "pending", title: "待干预", count: 1, items: [{ id: "ticket-1", title: "退款仲裁处理", userName: "小奶芋圆蹦蹦跳", orderId: "2918575148379876", time: "13:32", tags: [{ label: "待干预", variant: "red" }] }] }]} />' },
1232
+ { label: '卡片列表(生成中)', code: '<ConversationList variant="card" sections={[{ id: "pending", title: "待干预", count: 1, items: [{ id: "pending-1", title: "酒旅·仲裁退款", userName: "小兔叽掉毛毛🐰", orderId: "2918575148379876", time: "13:32", avatarSrc: avatarUrl, tags: [{ label: "异常监控提醒", variant: "grey" }, { label: "待干预", variant: "red" }], messages: [{ id: "m1", role: "user", text: "但是我不知道问题出在哪里" }, { id: "m2", role: "user", text: "你帮我看下这个有啥问题呢" }, { id: "m3", role: "agent", text: "《抖音网络社区自律公约》:抖音app-我-右上角≡-设置-抖音规则中心,这是咱们抖音官方的一个规则呢" }], status: "generating", draftText: "" }] }]} />' },
1233
+ { label: '卡片列表(可发送)', code: '<ConversationList variant="card" sections={[{ id: "managed", title: "托管中", count: 1, items: [{ id: "managed-1", title: "物流状态异常跟进", userName: "软软想吃糖葫芦", orderId: "2918575148472390", time: "11:22", avatarSrc: avatarUrl, tags: [{ label: "24时30分", variant: "grey", iconName: "alarm-clock-stroked" }, { label: "托管中", variant: "green" }], messages: [{ id: "m1", role: "user", text: "你帮我看下这个有啥问题呢" }, { id: "m2", role: "agent", text: "《抖音网络社区自律公约》:抖音app-我-右上角≡-设置-抖音规则中心,这是咱们抖音官方的一个规则呢" }], status: "editable", draftText: "祝您生活愉快~如果您觉得我的服务态度还可以的话,辛苦您点击左下角结束服务为本次服务点亮小星星哦~" }] }]} />' },
1234
+ { label: '❌ Bad(用 Table 做会话侧栏)', code: '/* 禁止!会话侧栏需要头像、状态、选中左色条和紧凑分组,不是表格数据比较 */\n<Table columns={columns} dataSource={tickets} />' },
1235
+ { label: '✅ Good(用 ConversationList)', code: '<ConversationList sections={ticketSections} activeItemId={currentTicketId} onItemClick={openTicket} />' },
1236
+ ],
1237
+ keywords: [
1238
+ 'ConversationList', 'conversation-list', '会话列表', '工单列表', 'IM 对话列表', '即时通讯列表',
1239
+ 'ticket list', 'conversation queue', '客服工作台', 'IM 工作台', '即时通讯工作台', '工单队列', '托管队列',
1240
+ 'card list', 'conversation card', '卡片列表', '消息卡片', '可发送回复卡片', '回复内容生成中', '布局切换', '双布局',
1241
+ '待干预', '托管中', '已挂起', '已处理', '异常监控提醒',
1242
+ '分组列表', '状态筛选', '会话侧栏', '左侧会话列表',
1243
+ 'Avatar Tag Icon', 'activeItemId', 'onItemClick', 'onTabChange', 'variant', 'sections',
1244
+ // 错误信号词
1245
+ '手搓会话列表', '自制工单列表', 'Table 当会话列表', 'div conversation item',
1246
+ ],
1247
+ },
1248
+ {
1249
+ id: 'info-display-panel',
1250
+ name: 'InfoDisplayPanel',
1251
+ element: 'section',
1252
+ category: 'business',
1253
+ description: '信息展示面板框架,用于客服工作台、在线 Agent、工单详情右侧信息区等场景;默认以主栏承载全部 tabs,并可按当前选中 tab 拆分出独立栏,最多支持 3 栏。',
1254
+ componentFile: './components/InfoDisplayPanel.jsx',
1255
+ tokensFile: './components/InfoDisplayPanel.tokens.js',
1256
+ props: [
1257
+ { name: 'panels', type: 'array', default: null },
1258
+ { name: 'columnCount', type: 'number', default: null },
1259
+ { name: 'defaultColumnCount', type: 'number', default: 3 },
1260
+ { name: 'onColumnCountChange', type: 'function', default: null },
1261
+ { name: 'minPanelWidth', type: 'number', default: 200 },
1262
+ { name: 'activeTabs', type: 'object', default: null },
1263
+ { name: 'defaultActiveTabs', type: 'object', default: null },
1264
+ { name: 'onTabChange', type: 'function', default: null },
1265
+ { name: 'onSplitChange', type: 'function', default: null },
1266
+ { name: 'renderPanelContent', type: 'function', default: null },
1267
+ { name: 'className', type: 'string', default: '' },
1268
+ { name: 'style', type: 'object', default: null },
1269
+ ],
1270
+ _preview: {
1271
+ base: {
1272
+ height: '866px',
1273
+ radius: '16px',
1274
+ panelMinWidth: '200px',
1275
+ headerHeight: '56px',
1276
+ contentPadding: '16px',
1277
+ dividerWidth: '1px',
1278
+ },
1279
+ columns: {
1280
+ one: '>= 200px',
1281
+ two: '>= 401px',
1282
+ three: '>= 602px',
1283
+ },
1284
+ },
1285
+ rules: [
1286
+ FONT_WEIGHT_RUNTIME_RULE,
1287
+ '【组件定位】InfoDisplayPanel 是“信息展示面板框架”,只负责外框、顶部 Tabs、拆分/合并、分栏调宽、内容插槽和基础间距;它不提供业务内容默认占位,不替代 Card、Table、Form、ConversationList 或客服工作台整页框架。',
1288
+ '【触发引用】当页面需要在同一工作区右侧并列查看“托管助手 / 历史工单 / 工单日志 / 信息工具 / 视频信息 / 用户信息 / 沟通记录”等多个详情面板,并且用户需要把当前关注的 tab 临时拆出来并排对照时,优先使用 InfoDisplayPanel;如果只是单块内容展示或普通 tabs 切换,不应使用该业务框架。',
1289
+ '【适用场景】客服工作台主白卡内部的信息区、在线 Agent 辅助信息区、工单处理详情侧、多来源上下文对照区、质检 / 审核 / 申诉处理工作区。',
1290
+ '【不适用场景】整页客服工作台外框用 CustomerServiceWorkspaceFrame;左侧会话 / 工单队列用 ConversationList;单个业务摘要用 Card;字段型详情或可批量操作数据用 Form / Table。',
1291
+ '【结构】外层为一个白色圆角容器,默认使用 `w-full` 撑满父容器可用宽度;内部按“拆分栏数组 + 主栏”组织。每一栏都必须包含 56px 顶部 Tabs 栏和内容区;顶部横线只保留 Header 内 1 条通栏下描边,左右贴满当前栏容器。Tabs 交互、文字、选中态与 3px Brand 指示线必须复用基础 `Tabs variant="line" size="lg"`,禁止手写 tab button 或手写选中线。栏与栏之间用 1px 垂直分隔线,禁止通过 gap 制造分栏空隙。',
1292
+ '【分栏能力】默认单栏时,全部 tabs 位于主栏;主栏永远在最右侧,拆分出的独立栏固定追加在主栏左侧。每栏右上角始终只有 1 个动作按钮,拆分与合并统一使用 `Button variant="ghost-black" size="md" iconOnly tooltip="拆分/合并"` 显示提示文案。最多支持 3 栏,即 1 个主栏 + 最多 2 个拆分栏。',
1293
+ '【Tabs来源】Tabs 的数量、名称和顺序必须跟随具体生成页面的业务信息架构动态生成,由 `panels[].tabs` 提供;不要把默认示例 tabs 当作真实业务规则,也不要为了凑满 3 栏强行生成无意义的 tab。',
1294
+ '【Tab数量与拆分上限】可拆栏数同时受容器宽度和 tab 总数限制:仅 1 个 tab 时不使用 Tabs,运行时自动降级为 `FormTitle variant="card"` 标题展示且不显示拆分按钮;仅 2 个 tab 时最多拆分为 2 栏;大于等于 3 个 tab 且容器宽度达到 602px 时才允许拆分为 3 栏。',
1295
+ '【拆分规则】点击主栏右上角“拆分”时,只拆出当前选中的主栏 tab;拆分后该 tab 从主栏 tabs 中移除,并以独立栏形式出现在主栏左侧。主栏保留剩余未拆分 tabs,顺序始终保持传入时的原始顺序。',
1296
+ '【合并规则】点击独立栏右上角“合并”后,该 tab 回到主栏,并按原始 tabs 顺序插回;按钮 hover 时 tooltip 文案为“合并”。当容器宽度不足以支持更多栏位,或已达到当前可见栏数上限时,主栏“拆分”按钮进入基础 Button 的 disabled 态,不允许把面板压到 200px 以下,且 hover 仍需通过 Tooltip 显示“空间不够,暂不支持拆分”。',
1297
+ '【分栏门槛】每栏最小宽度固定 `200px`,双栏最小需求空间为 `401px`,三栏最小需求空间为 `602px`;小于 `401px` 时只能单栏,`401px-601px` 时最多双栏,`>= 602px` 时最多三栏。',
1298
+ '【拖拽调宽】组件整体宽度必须通过左外边界那 1 条手柄统一调整;当拆分为 2 栏或 3 栏后,组件内部只保留栏与栏之间的分隔线拖拽,用于调整左右相邻两栏宽度。禁止在组件最左侧再叠加第二条“第一栏宽度”手柄。所有栏位最小宽度统一为 `200px`,最大宽度不单独限制,只受外层容器可用宽度约束。',
1299
+ '【预览验收】组件预览默认撑满预览容器,并提供组件左侧边缘的整体宽度拖拽;单栏、双栏和三栏切换都应通过同一预览宽度拖拽链路验证,不再依赖固定宽度画板。',
1300
+ '【默认内容】组件默认不渲染任何骨架屏、灰色占位卡片或示意内容;InfoDisplayPanel 只提供框架、Tabs、拆分/合并、拖拽调宽和内容插槽。业务内容必须由 `panels[].content` 或 `renderPanelContent` 注入。',
1301
+ '【内容内距】每栏内容区四周内边距固定 16px,内容块之间纵向间距固定 16px;内容区默认 `flex-col` 排列,子元素默认 `min-width: 0; width: 100%`,需要跟随当前 tab 栏宽度自动伸缩适配。每栏内容区独立滚动,内容多时只滚动当前栏,不撑高外层面板。AI 生成内容时应优先使用响应式宽度、自动换行和内部滚动,避免 `w-[固定值]`、绝对定位和固定大宽度表格。',
1302
+ '【Tabs规范】每栏顶部使用页签语义组织,统一使用基础 `Tabs variant="line" size="lg"`;Header 横向内边距为 16px,Tabs 文案之间保持基础 Tabs 的 item padding 与间距,不额外手写按钮间距。右侧动作按钮与 Tabs 区域保持 12px 间距。选中态为基础 Tabs 的 semibold + Brand 500 3px 指示线;未选中态为基础 Tabs 的 normal 字重和次级色。主栏展示未拆分 tabs 集合,拆分栏只展示各自承载的单个 tab。Tabs 只切换本栏内容,不改变页面路由。',
1303
+ '【单Tab降级】当业务只有 1 个分类时,容器不应为了形式感渲染 Tabs;运行时会用 `FormTitle variant="card"` 展示该分类标题,内容区仍按 16px 内距承载业务组件。AI 生成页面时,如果只有单个分类,应优先直接使用标题 + 内容结构,除非后续明确会扩展为多 tab。',
1304
+ '【内容承载】每一个 tab 栏都可以承载任意业务组件元素,包括信息卡片、表单、表格片段、日志列表、图片/视频信息、实验命中详情或自定义复合组件;被承载组件必须优先使用 `w-full min-w-0` 等自适应写法,避免固定宽度导致拆分栏变窄时溢出。',
1305
+ '【AI生成约束】AI 生成具体业务页面时,应把真实信息卡片、表单、工单日志或内容详情放进 `panels[].content` 或 `renderPanelContent`;不要手写外层分栏容器、不要用多个 Card + gap 临时拼出三栏框架,也不要依赖 InfoDisplayPanel 的默认占位内容。',
1306
+ '【组合关系】InfoDisplayPanel 可作为 CustomerServiceWorkspaceFrame 的 mainPanel 内容;放入主白卡时宽高使用 `size-full` 或父容器约束,外层不要再套一层大白卡。',
1307
+ '【嵌入客服工作台时的语义】在客服工作台框架模版里,InfoDisplayPanel 不是独立静态侧栏,而是“当前左侧选中会话 / 工单”的辅助信息区。左侧会话列表默认选中项变化时,InfoDisplayPanel 内各 tab 的内容也必须同步切换到该当前对象对应的数据上下文,例如用户信息、历史工单、工单日志、视频信息、沟通记录都应属于同一个当前处理对象。',
1308
+ '【联动边界】客服工作台里的右侧白卡应被视为一个整体工作区:左侧 IM 对话区负责当前线程,右侧 InfoDisplayPanel 负责同一线程的辅助信息。两者由外层模板统一接收当前对象 id 并同步刷新;InfoDisplayPanel 不应自己脱离左侧上下文单独维持另一份“当前对象”。',
1309
+ '【可控状态】业务需要同步 URL / 埋点 / 右侧主区状态时,使用 `activeTabs + onTabChange` 控制每栏当前 tab,并使用 `onSplitChange` 感知当前拆分出来的 tab 列表与实际栏数;`columnCount` / `defaultColumnCount` 仅保留为兼容字段,不再作为主交互入口推荐,默认最大栏数按组件逻辑自动支持到 3 栏。',
1310
+ ],
1311
+ examples: [
1312
+ { label: '默认主栏', code: '<InfoDisplayPanel />' },
1313
+ { label: '监听拆分状态', code: '<InfoDisplayPanel onSplitChange={({ splitTabIds, visibleColumns }) => console.log(splitTabIds, visibleColumns)} />' },
1314
+ { label: '可选限制最大栏数', code: '<InfoDisplayPanel defaultColumnCount={2} />' },
1315
+ { label: '自定义面板', code: '<InfoDisplayPanel panels={[{ id: "assistant", tabs: [{ id: "overview", label: "托管助手" }], content: <AssistantPanel /> }, { id: "history", tabs: [{ id: "tickets", label: "历史工单" }], content: <HistoryTickets /> }]} />' },
1316
+ { label: '单分类自动降级为标题', code: '<InfoDisplayPanel panels={[{ id: "profile", tabs: [{ id: "profile", label: "用户信息" }], content: <UserProfile /> }]} />' },
1317
+ { label: '放入客服工作台主面板', code: '<CustomerServiceWorkspaceFrame mainPanel={<InfoDisplayPanel className="size-full" />} />' },
1318
+ { label: '❌ Bad(手搓三栏信息区)', code: '/* 禁止!缺少 200px 最小栏宽判断、主栏固定最右与 tab 拆分/合并逻辑 */\n<div className="flex gap-4"><Card /><Card /><Card /></div>' },
1319
+ { label: '✅ Good(用 InfoDisplayPanel)', code: '<InfoDisplayPanel panels={panels} onSplitChange={handleSplitChange} />' },
1320
+ ],
1321
+ keywords: [
1322
+ 'InfoDisplayPanel', 'info-display-panel', '信息展示面板', '信息面板', '详情区', '详情面板',
1323
+ '托管助手', '历史工单', '工单日志', '信息工具', '视频信息', '用户命中实验', '沟通记录',
1324
+ '三栏面板', '双栏面板', '单栏面板', '分栏切换', 'columns', 'split panel',
1325
+ '客服工作台信息区', '在线 Agent 信息区', '工单详情右侧', '多面板工作区',
1326
+ 'panel tabs', '顶部tabs栏', '右侧分栏按钮',
1327
+ // 错误信号词
1328
+ '手搓三栏', 'Card 拼三栏', 'div flex gap-4 info panel', '自制信息面板',
1329
+ ],
1330
+ },
1331
+ {
1332
+ id: 'chat-bubble',
1333
+ name: 'ChatBubble',
1334
+ element: 'div',
1335
+ category: 'business',
1336
+ description: 'IM 聊天气泡,支持左右 2 种角色布局、单条/多条 2 种气泡组织,以及图片/机器人/AI/隐藏 4 种头像类型,并支持 hover 查看分层消息时间。',
1337
+ componentFile: './components/ChatBubble.jsx',
1338
+ tokensFile: './components/ChatBubble.tokens.js',
1339
+ props: [
1340
+ { name: 'variant', type: 'enum', options: ['incoming', 'outgoing'], default: 'incoming' },
1341
+ { name: 'layout', type: 'enum', options: ['single', 'multiple'], default: 'single' },
1342
+ { name: 'avatarType', type: 'enum', options: ['image', 'robot', 'ai', 'none'], default: 'none' },
1343
+ { name: 'size', type: 'enum', options: ['default', 'large'], default: 'large' },
1344
+ { name: 'incomingTone', type: 'enum', options: ['default', 'white', 'grey', 'ai'], default: 'default' },
1345
+ { name: 'outgoingTone', type: 'enum', options: ['default', 'white', 'grey', 'ai'], default: 'white' },
1346
+ { name: 'receipt', type: 'enum', options: ['hidden', 'read'], default: 'hidden' },
1347
+ { name: 'messages', type: 'array', default: null },
1348
+ { name: 'timestamps', type: 'array', default: null },
1349
+ { name: 'avatarSrc', type: 'string', default: null },
1350
+ { name: 'avatarAlt', type: 'string', default: '头像' },
1351
+ ],
1352
+ labels: {
1353
+ variant: { incoming: '用户左侧', outgoing: '客服右侧' },
1354
+ layout: { single: '单条', multiple: '多条' },
1355
+ avatarType: { image: '图片', robot: '机器人', ai: 'AI头像', none: '隐藏' },
1356
+ size: { default: 'Default', large: 'Large' },
1357
+ incomingTone: { default: '绿色', white: '白色', grey: '灰色', ai: 'AI色' },
1358
+ outgoingTone: { default: '绿色', white: '白色', grey: '灰色', ai: 'AI色' },
1359
+ receipt: { hidden: '隐藏', read: '已读' },
1360
+ },
1361
+ _preview: CHATBUBBLE_PREVIEW,
1362
+ rules: [
1363
+ LOCAL_MEMBER_AVATAR_RULE,
1364
+ '【角色】incoming 为左侧来消息,avatar 固定在左;outgoing 为右侧客服消息,avatar 固定在右',
1365
+ '【数量】single 展示单条消息;multiple 用于同角色连续回复,内部气泡组间距为 4px;当 messages.length > 1 时组件会自动按连续气泡布局展示',
1366
+ '【气泡色】左侧 incomingTone、右侧 outgoingTone 各自独立,可选 default(浅青/左为浅灰透明底)、white、grey(fill-default 填充底)、ai(渐变底);未传 outgoingTone 时回退兼容 prop tone(仅右侧)',
1367
+ '【先判定会话背景】AI 问答、任务执行、Copilot、流程跟进等“执行类对话”优先使用浅灰会话背景 `var(--color-blueGrey-200)`;常规 IM、客服进线、人工接待、站内信回复等“沟通类对话”默认优先使用白色会话背景 `var(--color-surface)`',
1368
+ '【灰色背景气泡策略】父级为浅灰背景时,左侧用户气泡用 `incomingTone="grey"`,右侧普通回复用 `outgoingTone="white"`;若右侧明确是 AI / 机器人执行回复,可改用 `outgoingTone="ai"`,但不要把 AI 渐变当普通装饰色',
1369
+ '【白色背景气泡策略】父级为纯白背景时,禁止无描边白气泡叠白底;左侧用户气泡用 `incomingTone="grey"`,右侧人工/客服回复用 `outgoingTone="default"`(浅绿色/浅青服务方气泡),右侧 AI/机器人回复用 `outgoingTone="ai"`',
1370
+ '【禁止默认碰运气】生成页面时不要只写 `<ChatBubble />` 或只靠默认 tone;必须先判断背景类型,再显式传入 incomingTone / outgoingTone,确保气泡边界和左右角色识别稳定',
1371
+ '【尺寸】size=default 使用左右 12px / 上下 8px 内边距;size=large 使用左右 16px / 上下 12px 内边距',
1372
+ '【头像】头像复用 Avatar 组件的 xs 档位,支持 image / robot / ai / none;avatarType="image" 未传 avatarSrc 时默认随机使用本地成员头像素材;avatarType=none 时保留头像槽位但不渲染头像(含 AI 色气泡场景);头像顶部需与第一条气泡顶部对齐;非 none 且任一侧为 AI 色气泡或 avatarType=ai 时,头像与 AI 渐变底联动',
1373
+ '【组装宿主】用于 IM 会话白卡时,外层消息组默认上下间距为 20px;聊天内容区建议上下留白 24px、整体白卡圆角 12px,并在宿主区域内纵向撑满',
1374
+ '【时间】timestamps 提供与 messages 一一对应的完整时间文案;单条和连续消息第 1 条在 hover 时显示上方“月日 + 时间”,连续消息从第 2 条起改为在气泡左/右侧垂直居中显示精简时间,继续 hover 该时间标签时再展示完整年月日和秒',
1375
+ '【回执】receipt=read 时仅在 outgoing 模式显示 12px 已读徽标;incoming 模式自动忽略该配置',
1376
+ '【文案】单条建议 1-3 行,多条建议控制在 2 条内,避免超过 500px 最大宽度后挤压聊天流布局',
1377
+ ],
1378
+ examples: [
1379
+ { label: '默认展示', code: '<ChatBubble variant="outgoing" avatarType="none" size="large" outgoingTone="white" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中"]} />' },
1380
+ { label: '灰底推荐(左灰右白)', code: '<div style={{ background: "var(--color-blueGrey-200)" }}><ChatBubble incomingTone="grey" messages={["为什么我无法设置点赞列表查看权限"]} /><ChatBubble variant="outgoing" outgoingTone="white" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中"]} /></div>' },
1381
+ { label: '白底推荐(左灰右绿)', code: '<div style={{ background: "var(--color-surface)" }}><ChatBubble incomingTone="grey" messages={["为什么我无法设置点赞列表查看权限"]} /><ChatBubble variant="outgoing" outgoingTone="default" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中"]} /></div>' },
1382
+ { label: '左侧单条', code: '<ChatBubble />' },
1383
+ { label: '悬浮时间', code: '<ChatBubble timestamps={["2026-02-26 10:24:14"]} messages={["为什么我无法设置点赞列表查看权限"]} />' },
1384
+ { label: '右侧单条', code: '<ChatBubble variant="outgoing" receipt="read" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中"]} />' },
1385
+ { label: 'Large 尺寸', code: '<ChatBubble size="large" messages={["为什么我无法设置点赞列表查看权限"]} />' },
1386
+ { label: '右侧白底', code: '<ChatBubble variant="outgoing" outgoingTone="white" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中"]} />' },
1387
+ { label: '右侧AI渐变', code: '<ChatBubble variant="outgoing" outgoingTone="ai" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中"]} />' },
1388
+ { label: '左侧灰底', code: '<ChatBubble incomingTone="grey" messages={["为什么我无法设置点赞列表查看权限"]} />' },
1389
+ { label: '右侧多条', code: '<ChatBubble variant="outgoing" layout="multiple" receipt="read" messages={["您好,因产品功能更新,“作品点赞信息”设置开关正逐步下线中", "目前不支持设置“作品点赞信息”权限。该功能下线后,您发布的作品的点赞列表仅对您自己可见。"]} />' },
1390
+ { label: '机器人头像', code: '<ChatBubble variant="outgoing" avatarType="robot" messages={["您好,请选择你想咨询的问题"]} />' },
1391
+ { label: 'AI头像', code: '<ChatBubble variant="outgoing" avatarType="ai" messages={["您好,我是智能助手小Q,请问有什么可以帮您?"]} />' },
1392
+ { label: '隐藏头像', code: '<ChatBubble avatarType="none" messages={["为什么我无法设置点赞列表查看权限"]} />' },
1393
+ { label: '❌ Bad(手写气泡 div + 随机外链头像)', code: '/* 禁止!手写气泡缺角色布局/连续/已读/avatar,也不要用随机外链头像 */\n<div className="flex"><img src="https://i.pravatar.cc/80" className="w-8 h-8 rounded-full" /><div className="bg-gray-100 p-2 rounded">消息</div></div>' },
1394
+ { label: '✅ Good(用 ChatBubble,image 头像默认本地成员图)', code: '<ChatBubble variant="incoming" avatarType="image" messages={["消息内容"]} />' },
1395
+ ],
1396
+ keywords: [
1397
+ 'ChatBubble', 'chat-bubble', '聊天气泡', 'IM 气泡', '消息气泡', '会话气泡',
1398
+ 'incoming', 'outgoing', '左侧消息', '右侧消息', '客服消息', '用户消息',
1399
+ 'single', 'multiple', '单条消息', '连续消息', '多条气泡',
1400
+ 'IM 客服', 'IM 会话', '客服聊天',
1401
+ // 错误信号词
1402
+ '手写气泡', '自制气泡', 'flex img rounded-full', 'bg-gray-100 p-2 rounded',
1403
+ ],
1404
+ },
1405
+ {
1406
+ id: 'chat-input',
1407
+ name: 'ChatInput',
1408
+ element: 'div',
1409
+ category: 'business',
1410
+ description: '对话/消息发送输入框,统一覆盖所有「输入内容并发出」场景。变体选择只看一个维度——UI 上是否有 Agent/工具栏:有(Agent 切换 / @ / 指令 / 附件)→ default;没有 → im-basic。im-basic(基础输入框)是使用频率最高的变体,适用范围:IM 消息输入、会话底部发送栏、评论框、工单回复、模型 Playground 发送栏(type prompt → 「发送并运行模型」)、任何 user/assistant 测试界面。default/default-sm/replying/busy/readonly 专用于内置完整 AI 工具栏的 Agent 对话场景(三层结构:底层氛围背景 → 输入区域 → 猫条)。im-basic 左下角按钮通过声明式 `imActions` 场景化配置,默认是图片 + 表情,最多展示 5 个。',
1411
+ componentFile: './components/ChatInput.jsx',
1412
+ tokensFile: './components/ChatInput.tokens.js',
1413
+ props: [
1414
+ { name: 'variant', type: 'enum', options: ['default-sm', 'default', 'im-basic', 'replying', 'busy', 'readonly'], default: 'default' },
1415
+ { name: 'agentName', type: 'string', default: 'Auto' },
1416
+ { name: 'agentOptions', type: 'array', default: '内置 5 个示例 Agent' },
1417
+ { name: 'mentionOptions', type: 'array', default: '内置 5 个示例 Mention' },
1418
+ { name: 'commandOptions', type: 'array', default: '内置 4 个示例 Command' },
1419
+ { name: 'acceptFiles', type: 'string', default: '*' },
1420
+ { name: 'multipleFiles', type: 'boolean', default: true },
1421
+ { name: 'catBarText', type: 'string', default: null },
1422
+ { name: 'catBarIcon', type: 'node', default: null },
1423
+ { name: 'placeholder', type: 'string', default: '需要我为你做什么' },
1424
+ { name: 'statusText', type: 'string', default: null },
1425
+ { name: 'actionText', type: 'string', default: null },
1426
+ { name: 'onSend', type: 'function', default: null },
1427
+ { name: 'onStop', type: 'function', default: null },
1428
+ { name: 'onAction', type: 'function', default: null },
1429
+ { name: 'onAgentChange', type: 'function', default: null },
1430
+ { name: 'onMentionsChange', type: 'function', default: null },
1431
+ { name: 'onCommandsChange', type: 'function', default: null },
1432
+ { name: 'onAttachmentsChange', type: 'function', default: null },
1433
+ { name: 'imActions', type: 'array', default: '仅 im-basic 生效;默认图片+表情,最多展示 5 个' },
1434
+ { name: 'onImImageClick', type: 'function', default: null },
1435
+ { name: 'onImEmojiClick', type: 'function', default: null },
1436
+ { name: 'toolbar', type: 'node', default: null },
1437
+ ],
1438
+ labels: {
1439
+ variant: {
1440
+ 'default-sm': '默认-小',
1441
+ 'default': '默认',
1442
+ 'im-basic': '基础输入框',
1443
+ 'replying': '回复中',
1444
+ 'busy': '忙碌(执行/排队)',
1445
+ 'readonly': '只读(分享/抢单)',
1446
+ },
1447
+ },
1448
+ _preview: CHATINPUT_PREVIEW,
1449
+ rules: [
1450
+ '【默认-小 default-sm】收起单行;点击展开为默认态,Agent 按钮收成 36×36 图标钮;适用于侧边栏、浮窗等空间受限的 AI 对话入口',
1451
+ '【默认 default · 完整 AI 工具栏场景】UI 上需要展示 Agent 切换 / @ 提及 / 指令 / 附件工具栏时使用;展开多行可输入;focus 时背景出现青紫光晕;点击发送后自动流转到「回复中」状态。判断是否用 default:看 UI 设计稿有没有 Agent 按钮或工具栏行——有就用 default,没有就用 im-basic',
1452
+ '【基础输入框 im-basic · 通用"输入并发出"组件】适用于所有需要用户输入内容并提交的场景:IM 消息输入、会话底部发送栏、评论框、工单回复、模型 Playground 输入栏等。无猫条、无 AI 氛围背景;外层四边留白一致、圆角白色卡片。左下角按钮使用声明式 `imActions` 配置,默认展示「图片 / 表情」两个入口(图标 image-01-stroked / face-smile-stroked);业务可按场景替换为其它轻量快捷动作。',
1453
+ '【imActions】仅 `variant="im-basic"` 生效,单项结构建议为 `{ id, icon, ariaLabel, onClick }`;组件内部按数组顺序渲染,未传时自动回落到默认图片 + 表情。',
1454
+ '【按钮数量上限】`imActions` 最多展示 5 个;超过 5 个时只展示前 5 个,业务必须主动收敛,而不是继续横向堆叠更多入口。',
1455
+ '【兼容策略】旧的 `onImImageClick` / `onImEmojiClick` 继续兼容默认图片/表情按钮:未传 `imActions` 时,图片按钮默认触发附件选择器,表情按钮默认预留给业务挂表情面板。',
1456
+ '【按钮边界】im-basic 左下角按钮只承载轻量快捷动作,如图片、表情、拍照、快捷回复、语音入口;不要在这里承载复杂下拉工具链、Agent 切换或多级菜单。需要 Agent / @ / 指令 / 附件体系时应改用 default 变体。',
1457
+ '【发送行为】im-basic 发送后不自动进入「回复中」状态;可仅附件发送;图片场景建议 acceptFiles="image/*"',
1458
+ '【AI 变体选择决策树 · 必读】选择 ChatInput 变体前,先判断场景是否需要 AI 工具栏(Agent 切换 / @ 提及 / 指令 / 附件):① 需要 Agent 切换或 @/指令/附件工具栏 → 使用 default(或 default-sm);② 不需要上述工具栏、只需要「输入内容并提交/发送/运行」→ 一律使用 im-basic。im-basic 覆盖的典型场景:IM 消息输入、会话底部消息发送栏、评论/回复框、工单回复、模型 Playground 发送栏(输入 prompt 后点击「发送并运行模型」)、任何带 user/assistant 角色切换但无工具栏的测试界面。判断有无 AI 能力不等于判断「是否和 AI 交互」,而是判断「UI 上有没有 Agent/工具栏」:有 → default;无 → im-basic',
1459
+ '【回复中】AI 短回复中;状态文案 + 36×36 停止按钮;猫条始终眨眼',
1460
+ '【忙碌】AI 执行中或排队中(statusText 区分场景);渐变彩虹背景 + 36×36 停止按钮',
1461
+ '【只读】无编辑权限的两类场景:他人分享的只读预览 / 系统抢单引导(actionText 区分动作);白底 + md 文字按钮',
1462
+ '【猫条文案 catBarText · 动态 AI 上下文建议区】猫条(灰色背景条,位于输入框正上方)的文案通过 catBarText prop 注入,是 AI 基于当前页面场景实时生成的建议或互动引导,而非固定标语。文案内容应随页面状态、用户行为、数据上下文动态变化。写法原则:① 感知当前页面——结合用户正在查看的内容给出针对性建议(如"检测到 3 条待回复工单,需要我帮你起草回复?");② 延续上下文——用户上次对话后可衔接追问(如"上次分析了 Q1 数据,需要继续看 Q2 吗?");③ 主动互动——首次进入或无历史时用轻松引导(如"Hi~ 我是 AI 小助手,快来试试和我交流吧~");④ 执行状态反馈——busy/replying 时展示执行进度(如"正在查询知识库…")。catBarText 不传时猫条区域仍会渲染但文案为空;catBarIcon 可替换猫头图标为自定义节点。AI 生成页面时应根据页面业务场景设计合理的 catBarText 内容,而非直接留空或使用通用占位文案',
1463
+ '【三层结构】底层氛围背景(z-1) → 输入区域白色卡片(z-2) → 猫条(z-3)',
1464
+ '【工具栏】不传 toolbar prop 时自动渲染内置默认工具栏(Agent / @ / 指令 / 附件,全部 36px)',
1465
+ '【发送按钮】三态自动切换:disabled(无内容,灰底图标钮) / active(有内容,黑底胶囊+发送) / running(忙碌/回复中,黑底图标钮+停止)',
1466
+ '【Agent 切换】点击 Agent 按钮弹下拉,单选切换,按钮文字实时更新;选项数据由 agentOptions 注入,未传则用内置 5 个示例',
1467
+ '【@ 提及 / 指令】点击对应按钮弹下拉;选中后以浅青 chip 形式追加在 textarea 上方一行,可重复添加,每个 chip 自带 X 删除',
1468
+ '【附件】点击附件按钮唤起本地文件选择器(支持多选);选中后以 180px 文件卡形式平铺在 chip 行之上,每张卡自带 X 删除;通过 acceptFiles 控制文件类型',
1469
+ '【浮层】所有浮层从工具栏向上弹出,菜单内点击不会让输入框失焦;点击浮层外区域自动收起',
1470
+ '【发送上下文】onSend(text, ctx) 第二参数携带当前 { agent, mentions, commands, attachments },方便业务直接拿到完整上下文',
1471
+ ],
1472
+ examples: [
1473
+ { label: '默认-小', code: '<ChatInput variant="default-sm" />' },
1474
+ { label: '默认', code: '<ChatInput variant="default" />' },
1475
+ { label: '基础输入框(默认图片+表情)', code: '<ChatInput variant="im-basic" placeholder="说点什么…" acceptFiles="image/*" />' },
1476
+ { label: '基础输入框(仅发送,无工具栏)', code: '<ChatInput variant="im-basic" placeholder="输入消息后回车发送" />' },
1477
+ { label: '基础输入框(自定义 3 个快捷按钮)', code: '<ChatInput variant="im-basic" imActions={[{ id: "image", icon: "image-01-stroked", ariaLabel: "图片" }, { id: "emoji", icon: "face-smile-stroked", ariaLabel: "表情", onClick: openEmojiPanel }, { id: "camera", icon: "camera-01-stroked", ariaLabel: "拍照", onClick: openCamera }]} />' },
1478
+ { label: '基础输入框(评论/回复框)', code: '<ChatInput variant="im-basic" placeholder="回复…" imActions={[{ id: "emoji", icon: "face-smile-stroked", ariaLabel: "表情", onClick: openEmojiPanel }]} />' },
1479
+ { label: '基础输入框(隐藏左下角按钮)', code: '<ChatInput variant="im-basic" placeholder="输入消息后回车发送" imActions={[]} />' },
1480
+ { label: '基础输入框(Playground 发送栏)', code: '<ChatInput variant="im-basic" placeholder="输入消息后回车发送并运行模型" />' },
1481
+ { label: '默认(首次进入)', code: '<ChatInput variant="default" catBarText="Hi~ 我是 AI 小助手,快来试试和我交流吧~" />' },
1482
+ { label: '默认(感知页面数据)', code: '<ChatInput variant="default" catBarText="检测到 3 条待回复工单,需要我帮你起草回复?" />' },
1483
+ { label: '默认(延续上下文)', code: '<ChatInput variant="default" catBarText="上次分析了 Q1 数据,需要继续看 Q2 吗?" />' },
1484
+ { label: '默认(执行后引导)', code: '<ChatInput variant="default" catBarText="已完成订单分析,还有什么需要处理的吗?" />' },
1485
+ { label: '回复中', code: '<ChatInput variant="replying" statusText="回复中..." />' },
1486
+ { label: '忙碌-执行中', code: '<ChatInput variant="busy" catBarText="正在查询数据接口" statusText="已耗时 3 min,运行结束后会通过飞书消息告知" />' },
1487
+ { label: '忙碌-排队中', code: '<ChatInput variant="busy" catBarText="当前请求排队约 8 位" statusText="当前请求量过大,需排队执行任务" />' },
1488
+ { label: '只读-分享预览', code: '<ChatInput variant="readonly" statusText="来自其他成员的任务分享,当前页面仅支持预览" actionText="继续对话" onAction={() => {}} />' },
1489
+ { label: '只读-抢单引导', code: '<ChatInput variant="readonly" statusText="任务由系统发送,开启后将归属开启人管理" actionText="开启任务" onAction={() => {}} />' },
1490
+ { label: '自定义 Agent 列表', code: '<ChatInput agentName="GPT-4o" agentOptions={[{ id: "gpt-4o", label: "GPT-4o" }, { id: "claude", label: "Claude" }]} />' },
1491
+ { label: '业务接入完整回调', code: '<ChatInput onSend={(text, { agent, mentions, commands, attachments }) => api.send({ text, agent, mentions, commands, files: attachments })} />' },
1492
+ { label: '限制只能传图片', code: '<ChatInput acceptFiles="image/*" multipleFiles={false} />' },
1493
+ { label: '❌ Bad(手搓 textarea + 发送按钮当 IM 输入框)', code: '/* 禁止!黑边、无 token、缺三态、缺工具栏 */\n<div className="border rounded p-2"><textarea /><button>发送</button></div>' },
1494
+ { label: '✅ Good(用 ChatInput im-basic)', code: '<ChatInput variant="im-basic" placeholder="说点什么…" />' },
1495
+ { label: '❌ Bad(评论框/工单回复用 default 变体)', code: '/* 禁止!没有 Agent 工具栏的场景应该用 im-basic,default 会带出多余的 @ / 指令 / 附件 */\n<ChatInput variant="default" placeholder="回复…" />' },
1496
+ { label: '✅ Good(评论框用 im-basic)', code: '<ChatInput variant="im-basic" placeholder="回复…" imActions={[{ id: "emoji", icon: "face-smile-stroked", ariaLabel: "表情", onClick: openEmojiPanel }]} />' },
1497
+ ],
1498
+ keywords: [
1499
+ 'ChatInput', 'chat-input', 'chatinput',
1500
+ 'im-basic', 'default', 'default-sm', 'replying', 'busy', 'readonly',
1501
+ '基础输入框', '聊天输入框', '发送框', '消息输入', '消息发送',
1502
+ '会话底部', '会话发送栏', '底部输入', '底部发送',
1503
+ 'IM 消息输入', 'IM 输入', 'IM 发送', 'IM 聊天', 'IM 客服',
1504
+ '评论框', '评论输入', '评论回复', '工单回复', '回复框',
1505
+ 'Playground 输入', 'Playground 发送栏', 'Prompt 输入', 'prompt 发送',
1506
+ 'AI 对话框', 'AI 输入', 'AI 助手输入', 'Copilot 输入', '助手输入',
1507
+ 'send button', 'send box', 'message box', 'message input', 'comment input',
1508
+ 'chat box', 'chatbot input', 'chat composer',
1509
+ // 猫条
1510
+ 'catBarText', '猫条', '猫条文案', 'AI 建议', 'AI 互动',
1511
+ // 工具栏 / Agent 相关
1512
+ 'Agent 切换', '@ 提及', '指令', '附件', '工具栏', 'mention', 'command', 'attachment',
1513
+ // 伪手搓信号词
1514
+ 'textarea + 按钮', '原生 textarea', 'border rounded p-2', 'manual chat input',
1515
+ ],
1516
+ },
1517
+ {
1518
+ id: 'chat-message',
1519
+ name: 'ChatMessage',
1520
+ element: 'div',
1521
+ category: 'business',
1522
+ description: 'AI 对话页「一条消息」原子单元(行业对齐 assistant-ui Message / Microsoft ChatMessage)。一个 ChatMessage = 一条消息(user 气泡 / AI 头像 / 文本回复 / 执行流 / 卡片回复 / 深度思考 / 操作栏),按 role 与 7 类子能力组合呈现。',
1523
+ componentFile: './components/ChatMessage.jsx',
1524
+ tokensFile: './components/ChatMessage.tokens.js',
1525
+ props: [
1526
+ /* 角色与基础 */
1527
+ { name: 'role', type: 'string', default: 'ai' },
1528
+ { name: 'timestamp', type: 'string', default: '' },
1529
+
1530
+ /* AI 头像(原 aiHeader) */
1531
+ { name: 'header', type: 'object|boolean', default: null },
1532
+
1533
+ /* 深度思考(原 thinkingProcess) */
1534
+ { name: 'thinking', type: 'object', default: null },
1535
+
1536
+ /* 引导文案(plan / confirms 之上) */
1537
+ { name: 'leadText', type: 'string', default: '' },
1538
+
1539
+ /* 卡片回复·任务规划(原 taskPlan) */
1540
+ { name: 'plan', type: 'object', default: null },
1541
+
1542
+ /* 执行流:标题 + 状态 + 步骤 + 任务组 */
1543
+ { name: 'title', type: 'string', default: '梳理抖音电商客服售后退换货政策并生成标准答复口径' },
1544
+ { name: 'status', type: 'string', default: 'completed' },
1545
+ { name: 'statusIconName', type: 'string', default: 'check-circle-stroked' },
1546
+ { name: 'steps', type: 'array', default: null },
1547
+ { name: 'taskGroups', type: 'array', default: null },
1548
+
1549
+ /* 结果区:文本 + 产物卡 + 人工确认(原 humanConfirmNodes) */
1550
+ { name: 'resultText', type: 'string', default: '' },
1551
+ { name: 'resultArtifacts', type: 'array', default: null },
1552
+ { name: 'confirms', type: 'array', default: null },
1553
+
1554
+ /* 任务进行中徽章 */
1555
+ { name: 'taskBadge', type: 'string', default: null },
1556
+
1557
+ /* 追问(原 followUpQuestions) */
1558
+ { name: 'followUps', type: 'array|object', default: null },
1559
+
1560
+ /* 操作栏(原 messageActions,user/AI 都可挂) */
1561
+ { name: 'actions', type: 'object|boolean', default: null },
1562
+
1563
+ /* 用户气泡(role='user')*/
1564
+ { name: 'userContent', type: 'array', default: null },
1565
+ { name: 'userAttachments', type: 'array', default: null },
1566
+ { name: 'userQuote', type: 'object', default: null },
1567
+ { name: 'userBubbleTone', type: 'enum', options: ['auto', 'surface', 'fill'], default: 'auto' },
1568
+
1569
+ /* 折叠态兼容标记 */
1570
+ { name: 'defaultExpanded', type: 'boolean', default: true },
1571
+ { name: 'expanded', type: 'boolean', default: undefined },
1572
+ { name: 'onExpandedChange', type: 'function', default: null },
1573
+ ],
1574
+ labels: {
1575
+ userBubbleTone: { auto: '自动适配', surface: '白色', fill: '灰色' },
1576
+ },
1577
+ _preview: CHAT_MESSAGE_PREVIEW,
1578
+ rules: [
1579
+ FONT_WEIGHT_RUNTIME_RULE,
1580
+ /* —— 整体定位 —— */
1581
+ '【消息原子】一个 ChatMessage = AI 对话页中的一条消息原子,role="ai" 渲染 AI 消息(适配容器宽度),role="user" 渲染用户气泡(右对齐 + 8% 容器宽度左缩进 ≈ 500px 容器下 40px 缩进)',
1582
+ '【AI 友好命名】props 一律用最短同义词:header / thinking / plan / confirms / followUps / actions / userContent;读 props 即知 DOM 结构(<ChatMessage header thinking plan ... />)',
1583
+ '【7 类子组件】内部按 AI 头像 / 文本回复 / 执行流 / 卡片回复 / 深度思考 / 操作栏 / 用户气泡 7 类子组件按需组合,全部通过 props 启用,互相正交不冲突',
1584
+ '【theme 必引】入口 CSS 必须 @import "@tf-designsystem/b-end/theme.css"。ChatMessage 子区块使用 --color-border-line-light、--color-neutral、--color-neutral-500/300/700、--tfds-ai-execution-* 等;未引入 theme 时 var() 无效,描边易退化为浏览器默认深黑(追问按钮等)。',
1585
+ '【禁止手搓描边】勿包一层 div 并加 border-black / ring-black;追问 followUps 为白底 + border-border-line-light + hover:border-border-strong。',
1586
+
1587
+ /* —— 1. AI 头像 —— */
1588
+ '【AI 头像】header 传 true 启用默认 "OLA AI" + catcat 头像;传对象 { name, avatarSrc } 自定义名称与头像;建议每条 AI 消息都开启,保持身份一致;不传则该消息没有头像',
1589
+ '【自定义头像素材】ChatMessage 默认 AI 头像使用包内 catcat.svg;若 header 对象自定义 avatarSrc(如拟人助手、成员身份或人工协作身份),优先使用包内本地成员头像图片素材,不要使用 pravatar/Unsplash/placeholder 随机外链头像。',
1590
+ '【消息加载态】status="requesting" 仅渲染 AI 头像 + 三圆点 bouncing 动画,用于刚发出请求未返回内容时;不渲染标题、步骤、结果区',
1591
+
1592
+ /* —— 2. 文本回复 —— */
1593
+ '【文本回复】title="" + steps={null} + resultText="..." 即可渲染纯文本 AI 回复(仅 14px/20px 段落 + 可选 actions),常用于短答、确认、致谢等非任务型回复',
1594
+
1595
+ /* —— 3. 执行流 —— */
1596
+ '【执行流·结构】title + steps[] 构成完整执行流;步骤左侧 16px 轨道槽位 + 12px 内容间距 + 14px/20px 正文;右侧 chevron 控制收起展开',
1597
+ '【执行流·状态】status="completed" 绿色对勾;status="processing" 旋转环形动效 + 最新一条操作卡片浅灰骨架屏扫光',
1598
+ '【执行流·结果区】resultText 独立通栏文本;resultArtifacts[] 多张产物卡(网页/代码/表格,统一 32px 玻璃感图标 + 标题 + meta + 更多按钮 + 单行省略号);confirms[] 是结果区的另一种形态——人工确认卡(mode="text-card" 文本+卡片,"card-only" 仅卡片)',
1599
+ '【执行流·任务进行中徽章】taskBadge 传字符串启用 indigo 徽章 + 星标,位于结果区之后、消息操作之前,常用于"任务进行中"提示',
1600
+
1601
+ /* —— 4. 卡片回复 —— */
1602
+ '【卡片·任务规划】plan 传对象启用:灰底圆角 12 容器 + 头部图标 + 任务数徽章 + chevron;展开后白底内容卡内紫色编号方块(gradient violet)+ 任务标题 + 灰圆点子项;右下"开始执行任务"黑底主按钮',
1603
+ '【卡片·配置表单】配置表单类卡片复用执行流的 confirms(mode="text-card" 或 "card-only"),需要"次操作 + 主操作"按钮组的人工确认场景',
1604
+
1605
+ /* —— 5. 深度思考 —— */
1606
+ '【深度思考·进行中】status="thinking" + thinking={ state:"thinking", inProgressLabel:"深度思考中 ..." },仅显示头像 + 思考文案,不渲染其他内容',
1607
+ '【深度思考·已完成】thinking={ state:"completed", durationLabel, content, defaultExpanded } 渲染"深度思考(用时 X 秒)"+ chevron + 灰色引用线左侧 + 思考文本;位于 AI 头像之后、标题/步骤之前',
1608
+
1609
+ /* —— 6. 操作栏 —— */
1610
+ '【操作栏】actions 传 true 启用复制/引用/赞/踩四件套;传对象可单独 showCopy/showQuote/showLike/showDislike + copyCount + onXxx 回调;时间戳由 timestamp 字段单独控制',
1611
+ '【操作栏·历史模式】actions.historyMode=true 时操作栏 opacity:0 默认隐藏 + 父消息 hover 时 opacity:1 显示,但 height 始终保留占位(避免历史消息布局抖动);用于会话历史区,发出当下的最新消息保持常显',
1612
+ '【追问按钮组】followUps 传字符串数组或 { items, onSelect };纵向排列白底圆角 8 + 0.08 透明黑描边 + 高度 40 的按钮;放在结果区与操作栏之后,承担"建议追问"语义',
1613
+
1614
+ /* —— 7. 用户气泡 —— */
1615
+ '【用户气泡·结构】role="user" 时右对齐用户气泡(圆角 12 12 12 0),左侧按容器宽度 8% 缩进(500px 容器≈40px),气泡宽度自适应内容(w-fit + max-w-full)',
1616
+ '【用户气泡·背景自动适配】默认 `userBubbleTone="auto"`:组件会向上查找最近的实际父级背景色;若父级为白色 / `--color-surface`,用户气泡自动使用 `bg-fill` 灰底;若父级为浅灰、蓝灰、AI 会话灰底或其他非白背景,用户气泡自动使用 `bg-surface` 白底。生成页面默认不要手动传 userBubbleTone,让组件自动适配。',
1617
+ '【用户气泡·背景显式覆盖】仅在容器背景无法被运行时识别(如复杂背景图、透明叠层、截图态)时才显式传值:非白背景(AI 问答 / 执行流 / Copilot 等)传 `userBubbleTone="surface"` 白气泡;白色大背景 / 白色工作卡内传 `userBubbleTone="fill"` 灰气泡。禁止白色背景上继续使用纯白用户气泡。',
1618
+ '【用户气泡·内容】3 种内容形态混排:text token 纯文字;entity token 渲染带 icon + chevron 的彩色 chip(teal=场景 / indigo=任务 / violet=策略 / neutral=兜底);userAttachments 数组渲染附件卡(180px 文件卡,与 ChatInput 已发送态一致)',
1619
+ '【用户气泡·附件】userAttachments=[{name, size, iconSrc?}, ...],文件名 truncate + 文件大小灰字次行;附件行位于文本之上独立一行(与 ChatInput 中"已选附件"的展示形态一致,避免用户从输入到发出后视觉跳变)',
1620
+
1621
+ /* —— 典型组合 —— */
1622
+ '【典型组合·完整 AI 任务消息】header + thinking + plan + title + steps + 结果区(resultText/resultArtifacts/confirms) + taskBadge + actions + followUps',
1623
+ '【典型组合·用户消息】role="user" + userContent + (可选)userAttachments + actions + timestamp',
1624
+ '【典型组合·完整对话页】外层 500px 宽容器 + 消息区 overflow-y-auto + 底部 ChatInput 吸底,列表中混排多个 ChatMessage(user / ai 形态自由组合)',
1625
+ ],
1626
+ examples: [
1627
+ /* AI 头像 / 文本回复 */
1628
+ { label: 'AI 头像 + 名称', code: '<ChatMessage header title="" steps={null} timestamp="18:16" actions />' },
1629
+ { label: '消息加载态(requesting)', code: '<ChatMessage header status="requesting" steps={null} />' },
1630
+ { label: '文本回复', code: '<ChatMessage header title="" steps={null} resultText="收到,我先去拉平台规则和商家补充条款,分析完会给你一份统一答复口径。" actions timestamp="18:16" />' },
1631
+
1632
+ /* 执行流 */
1633
+ { label: '执行流 · 单个完整', code: '<ChatMessage header title="梳理抖音电商客服售后退换货政策并生成标准答复口径" />' },
1634
+ { label: '执行流 · 执行中', code: '<ChatMessage header status="processing" title="梳理抖音电商客服售后退换货政策并生成标准答复口径" taskBadge="任务进行中" />' },
1635
+ { label: '执行流 · 文本+产物结果', code: `<ChatMessage header resultText="已输出政策摘要..." resultArtifacts={[{ type: "web", title: "抖音电商客服售后退换货政策说明", meta: "370.3 KB", iconName: "google-chrome-stroked", actionIconName: "dots-horizontal-stroked" }]} />` },
1636
+
1637
+ /* 卡片回复 */
1638
+ { label: '卡片 · 任务规划', code: '<ChatMessage header title="" steps={null} plan={{ tasks: [{ title: "信息准备", items: ["分析背景获取接入方信息", "明确 Workflow 改动范围"] }, { title: "结构准备", items: ["查询 DSL 语法", "整理可复用组件"] }] }} />' },
1639
+ { label: '卡片 · 配置表单(人工确认)', code: `<ChatMessage header title="" steps={null} resultText="已完成售后政策摘要整理,以下审核口径需要人工确认后再继续生成标准答复。" confirms={[{ mode: "text-card", title: "售后政策确认", description: "请确认退货退款、换货与仅退款的适用范围", iconName: "sticker-square-stroked", secondaryActionLabel: "返回修改", primaryActionLabel: "确认继续" }]} />` },
1640
+
1641
+ /* 深度思考 */
1642
+ { label: '深度思考 · 思考中', code: '<ChatMessage header status="thinking" steps={null} thinking={{ state: "thinking", inProgressLabel: "深度思考中 ..." }} />' },
1643
+ { label: '深度思考 · 已完成展开', code: '<ChatMessage header title="" steps={null} thinking={{ state: "completed", durationLabel: "深度思考(用时 21.00 秒)", content: "深度思考结合上下文,猜测用户想问的是 2025 年人工解决率变化情况", defaultExpanded: true }} resultText="好的,我将开始为您分析。" />' },
1644
+
1645
+ /* 追问 + 操作栏 */
1646
+ { label: '追问按钮组', code: '<ChatMessage header title="" steps={null} resultText="已完成对未解决问题的分析。" actions timestamp="18:16" followUps={["批量测试当前策略", "批量测试当前节点", "开启 AB 实验"]} />' },
1647
+ { label: '操作栏 · 历史模式(hover 显示)', code: '<ChatMessage header title="" steps={null} resultText="历史消息内容..." actions={{ showCopy: true, showQuote: true, showLike: true, showDislike: true, historyMode: true }} timestamp="14:02" />' },
1648
+
1649
+ /* 用户气泡 */
1650
+ { label: '用户气泡 · 自动适配背景', code: '<ChatMessage role="user" timestamp="14:02" userContent={[{ type: "text", value: "帮我整理一下抖音电商的售后政策,重点是退货退款、换货和拒收的判责口径" }]} />' },
1651
+ { label: '用户气泡 · 白底场景灰气泡(显式覆盖)', code: '<ChatMessage role="user" userBubbleTone="fill" timestamp="14:02" userContent={[{ type: "text", value: "帮我整理一下抖音电商的售后政策" }]} />' },
1652
+ { label: '用户气泡 · 非白背景白气泡(显式覆盖)', code: '<ChatMessage role="user" userBubbleTone="surface" timestamp="14:02" userContent={[{ type: "text", value: "帮我整理一下抖音电商的售后政策" }]} />' },
1653
+ { label: '用户气泡 · 文本+@TAG', code: '<ChatMessage role="user" timestamp="18:16" actions userContent={[{ type: "text", value: "分析一批" }, { type: "entity", icon: "message-chat-square-stroked", label: "智能会话:社交私信", tone: "teal", showChevron: true }, { type: "text", value: "的问题与机会点" }]} />' },
1654
+ { label: '用户气泡 · 带附件', code: '<ChatMessage role="user" timestamp="14:08" userContent={[{ type: "text", value: "附件里是售后规则汇编,请基于这两份文件输出统一答复口径" }]} userAttachments={[{ name: "抖音电商售后政策汇编.pdf", size: 327680 }, { name: "商家补充协议-生鲜定制.pdf", size: 184320 }]} />' },
1655
+
1656
+ /* 反例对照 */
1657
+ { label: '❌ Bad(手搓 AI 消息卡)', code: '/* 禁止!自制 div + 描边 + 头像 + 段落,缺执行流/思考/追问/操作栏 */\n<div className="border rounded p-4">\n <div>OLA AI</div>\n <div>已完成对未解决问题的分析。</div>\n</div>' },
1658
+ { label: '✅ Good(用 ChatMessage)', code: '<ChatMessage header title="" steps={null} resultText="已完成对未解决问题的分析。" actions timestamp="18:16" followUps={["批量测试当前策略", "开启 AB 实验"]} />' },
1659
+ { label: '❌ Bad(用 ChatBubble 自拼 AI 思考块)', code: '/* 禁止!深度思考必须用 ChatMessage.thinking,不要用 ChatBubble + 自定义内容 */\n<ChatBubble role="ai">深度思考中...</ChatBubble>' },
1660
+ { label: '✅ Good(用 ChatMessage thinking)', code: '<ChatMessage header status="thinking" steps={null} thinking={{ state: "thinking", inProgressLabel: "深度思考中 ..." }} />' },
1661
+ ],
1662
+ keywords: [
1663
+ 'ChatMessage', 'chat-message', 'AI 对话消息', 'AI 消息', 'AI 回复',
1664
+ 'role ai', 'role user', 'AI 角色', '用户角色',
1665
+ 'header', 'thinking', 'plan', 'confirms', 'followUps', 'actions',
1666
+ '深度思考', '思考过程', 'reasoning', 'CoT',
1667
+ '执行流', '步骤', 'steps', '任务规划', '任务卡',
1668
+ '追问按钮', '建议追问', '追问 chip',
1669
+ '操作栏', '复制', '点赞', '点踩', 'thumbs up', 'thumbs down',
1670
+ 'AI 对话页', 'AI 消息流', 'Agent 消息', 'Copilot 消息',
1671
+ // 错误信号词
1672
+ '手写 AI 回复', '手搓 AI 卡', '自制思考过程',
1673
+ ],
1674
+ },
1675
+ {
1676
+ id: 'button',
1677
+ name: 'Button',
1678
+ element: 'button',
1679
+ category: 'basic',
1680
+ description:
1681
+ '触发一次性动作(提交、取消、打开弹窗、行内「编辑/删除」)。不负责在同一视图内切换多块内容(那是 Tabs);不负责从枚举里选题(那是 Select/Radio)。primary 为每视图唯一主按钮(黑底)。⚠️ 尺寸硬规则:全局默认 size="md"(36px),AI 生成页面时禁止擅自使用 size="sm",仅在设计明确标注或卡片空间极度受限时才可使用 SM。',
1682
+ componentFile: './components/Button.jsx',
1683
+ tokensFile: './components/Button.tokens.js',
1684
+ props: [
1685
+ { name: 'variant', type: 'enum', options: ['primary', 'outline-brand', 'outline-black', 'ghost-brand', 'ghost-black', 'text-brand', 'text-black'], default: 'primary' },
1686
+ { name: 'size', type: 'enum', options: ['sm', 'md'], default: 'md' },
1687
+ { name: 'radius', type: 'enum', options: ['rounded', 'full'], default: 'rounded' },
1688
+ { name: 'icon', type: 'node', default: null },
1689
+ { name: 'iconOnly', type: 'boolean', default: false },
1690
+ { name: 'tooltip', type: 'node', default: null },
1691
+ { name: 'disabled', type: 'boolean', default: false },
1692
+ { name: 'loading', type: 'boolean', default: false },
1693
+ ],
1694
+ labels: {
1695
+ variant: {
1696
+ primary: '主按钮(黑底)',
1697
+ 'outline-brand': '绿边绿字', 'outline-black': '黑边黑字',
1698
+ 'ghost-brand': '无边绿字', 'ghost-black': '无边黑字',
1699
+ 'text-brand': '纯绿字', 'text-black': '纯黑字',
1700
+ },
1701
+ size: { sm: 'SM', md: 'MD' },
1702
+ radius: { rounded: '圆角矩形', full: '全圆角' },
1703
+ },
1704
+ _preview: BUTTON_PREVIEW,
1705
+ rules: [
1706
+ FONT_WEIGHT_RUNTIME_RULE,
1707
+ '【选型·与 Tabs】Tabs 切换「内容面板」且通常不改变 URL;Button 触发「动作」或打开浮层。不要用一排 outline Button 冒充 Tabs',
1708
+ '【选型·与 Select】从固定列表里「选题」用 Select/Radio;Button 只触发流程,不把枚举项做成大量并列 Button(除非刻意做快筛 chips 且设计明确)',
1709
+ '【字重】所有 Button 变体统一使用 semibold 600;运行时实现固定为 `[font-weight:var(--font-semibold)]`,不要继续使用 `font-medium`、`font-semibold` 语义类或手写 500/600。',
1710
+ '【⛔ 尺寸全局强制规则 · AI 必须遵守】所有页面所有位置的 Button,默认且唯一正确的尺寸是 `size="md"`(高度 36px)。以下两种情况才允许使用 `size="sm"`:① 设计稿或需求文案中明确写出「小按钮」「SM 尺寸」;② 按钮位于空间极度受限的小型卡片/标签内(容器高度 < 40px),且不存在 md 尺寸的空间。任何未满足上述条件的场景,AI 生成代码时必须写 `size="md"` 或省略 size 属性(默认即 md),绝对禁止凭感觉或「看起来更紧凑」等理由降为 sm。包括但不限于:表单、页头、弹窗/抽屉底部、表格操作列、空状态、列表行内操作、侧边栏、卡片操作区等,全部使用 md',
1711
+ '【主操作】primary(黑底白字,grey-950)为页面主按钮,每个视图只放 1 个,用于最核心的操作',
1712
+ '【次操作】outline-black(黑边黑字)为次级按钮,与 primary 搭配使用',
1713
+ '【强调】需要品牌色强调时,用 outline-brand / ghost-brand / text-brand(绿字 brand-700,边框/底色保留 brand-500);本系统不再提供绿底主按钮',
1714
+ '【文字链】页面中「跳转链接」「次要操作入口」「辅助说明跳转」等纯文字点击区域,必须使用 `Button variant="text-brand"` 或 `variant="text-black"`,不得使用原生 `<a>` 标签或自定义带下划线的 `<span>`。典型场景:「Model List」「查看全部」「了解更多」等文字链接',
1715
+ '【嵌套操作】列表行内、卡片内等嵌套场景用 ghost(无边)按钮,默认用 ghost-black,需强调时用 ghost-brand;尺寸仍使用 `size="md"`,仅在容器高度 < 40px 等极端空间限制且设计明确时才用 sm',
1716
+ '【极紧凑】嵌套层级更深、空间不足时用 text(纯文字)按钮,默认 text-black,需强调时 text-brand;尺寸仍使用 `size="md"`',
1717
+ '【按钮组】按钮组合间距 8px(spacing-2),主按钮放右侧',
1718
+ '【Icon-only 硬约束】所有仅图标纯图标按钮(`iconOnly=true`,不论 primary / outline / ghost / text 任一变体)必须传 `tooltip`,hover/focus 时由 Button 内部 Tooltip 展示真实操作文案;禁止只靠 icon 图形让用户猜操作含义。',
1719
+ '【Icon-only 可访问性】`tooltip` 为字符串时 Button 会自动作为 `aria-label`;若 tooltip 是复杂节点,必须额外传 `aria-label`。禁用态 iconOnly 也必须保留 tooltip,用于说明操作或禁用原因。',
1720
+ '按钮文字推荐 4-6 个汉字以内',
1721
+ '禁用态需同时禁用点击事件和视觉反馈',
1722
+ ],
1723
+ examples: [
1724
+ { label: '主操作(默认 MD)', code: '<Button variant="primary">提交</Button>' },
1725
+ { label: '次操作(默认 MD)', code: '<Button variant="outline-black">取消</Button>' },
1726
+ { label: '品牌强调(默认 MD)', code: '<Button variant="outline-brand">确认</Button>' },
1727
+ { label: '嵌套操作(默认 MD)', code: '<Button variant="ghost-black">更多</Button>' },
1728
+ { label: '纯文字(默认 MD)', code: '<Button variant="text-brand">查看详情</Button>' },
1729
+ { label: '全圆角', code: '<Button variant="primary" radius="full">确认</Button>' },
1730
+ { label: '禁用态', code: '<Button variant="primary" disabled>不可点击</Button>' },
1731
+ { label: '图标+文字(默认 MD)', code: '<Button variant="outline-black" icon={<PlusIcon />}>添加</Button>' },
1732
+ { label: '仅图标(必须有 tooltip)', code: '<Button variant="ghost-black" icon={<SearchIcon />} iconOnly tooltip="搜索" />' },
1733
+ { label: '⚠️ SM 尺寸(仅限设计明确指定或容器 <40px 时)', code: '<Button variant="ghost-black" size="sm">行内</Button>' },
1734
+ { label: '❌ Bad(原生 button,黑边样式不一致)', code: '/* 禁止!原生 button 没有 token 控制,会出现黑边/字号错位 */\n<button className="px-4 py-2 bg-black text-white rounded">提交</button>' },
1735
+ { label: '✅ Good(用 Button 组件)', code: '<Button variant="primary">提交</Button>' },
1736
+ { label: '❌ Bad(用 <a> 做文字链)', code: '/* 禁止!<a> 没有 token、focus 态、loading 态 */\n<a href="#" className="text-teal-600 underline">查看详情</a>' },
1737
+ { label: '✅ Good(用 text-brand)', code: '<Button variant="text-brand">查看详情</Button>' },
1738
+ { label: '❌ Bad(默认用 SM 尺寸,违反全局规则)', code: '/* 禁止!全局默认 MD(36px),SM 仅在设计明确指定时使用 */\n<Button variant="primary" size="sm">提交</Button>' },
1739
+ { label: '❌ Bad(iconOnly 缺少 tooltip)', code: '/* 禁止!纯图标按钮必须告诉用户真实操作,不能只靠 icon 猜 */\n<Button variant="ghost-black" icon={<SearchIcon />} iconOnly />' },
1740
+ { label: '✅ Good(iconOnly + tooltip)', code: '<Button variant="ghost-black" icon={<SearchIcon />} iconOnly tooltip="搜索" />' },
1741
+ { label: '✅ Good(禁用态解释原因)', code: '<Button variant="ghost-black" icon={<SplitIcon />} iconOnly disabled tooltip="空间不够,暂不支持拆分" />' },
1742
+ ],
1743
+ keywords: [
1744
+ 'Button', 'button', '按钮', 'btn',
1745
+ 'primary', 'outline-brand', 'outline-black', 'ghost-brand', 'ghost-black', 'text-brand', 'text-black',
1746
+ '主按钮', '次按钮', '次要按钮', '强调按钮', '幽灵按钮', '文字按钮', '黑底按钮', '品牌色按钮', '危险按钮',
1747
+ 'submit', 'cancel', 'confirm', 'delete', 'add', 'create', 'save', 'reset', 'apply',
1748
+ '提交', '取消', '确认', '删除', '新建', '保存', '重置', '应用', '编辑', '查看', '更多',
1749
+ '文字链', '文字链接', 'link', 'hyperlink', 'a tag', 'anchor', '跳转链接',
1750
+ 'icon button', '图标按钮', '纯图标按钮', '仅图标按钮', 'iconOnly', 'icon-only', 'tooltip', '按钮提示', 'hover 提示',
1751
+ 'size md', 'size sm', '小按钮', 'MD 尺寸', 'SM 尺寸', '36px', '32px',
1752
+ 'disabled', 'loading', '禁用', '加载',
1753
+ 'bg-black', 'rounded', 'px-4 py-2',
1754
+ ],
1755
+ },
1756
+ {
1757
+ id: 'tabs',
1758
+ name: 'Tabs',
1759
+ element: 'div',
1760
+ category: 'basic',
1761
+ description:
1762
+ '同一页面或同一卡片内,多块平级内容的切换(视图状态保持在本页)。典型:详情子页签、表单分段、筛选维度切换。跨模块站点级导航优先侧栏/路由,而不是 Tabs 堆满一级入口。',
1763
+ componentFile: './components/Tabs.jsx',
1764
+ tokensFile: './components/Tabs.tokens.js',
1765
+ props: [
1766
+ { name: 'variant', type: 'enum', options: ['line', 'button', 'pill', 'segment'], default: 'line' },
1767
+ { name: 'size', type: 'enum', options: ['sm', 'md', 'lg'], default: 'sm' },
1768
+ { name: 'items', type: 'array', default: null },
1769
+ { name: 'activeIndex', type: 'number', default: 0 },
1770
+ { name: 'defaultIndex', type: 'number', default: 0 },
1771
+ { name: 'onChange', type: 'function', default: null },
1772
+ ],
1773
+ labels: {
1774
+ variant: {
1775
+ line: '线条型', button: '按钮型', pill: '胶囊型', segment: '分段器',
1776
+ },
1777
+ size: { sm: 'SM', md: 'MD', lg: 'LG' },
1778
+ },
1779
+ _preview: TABS_PREVIEW,
1780
+ rules: [
1781
+ FONT_WEIGHT_RUNTIME_RULE,
1782
+ '【选中态字重】所有 Tabs 变体选中文字统一使用 semibold 600;运行时实现固定为内联 `style={{ fontWeight: "var(--font-semibold)" }}`,并保留 `[font-weight:var(--font-semibold)]` 作为 class 兜底,避免被 Tailwind 生成顺序或全局 reset 覆盖;不要使用 `font-bold` 或手写 700。',
1783
+ '【硬约束·必须用 Tabs】凡「同一容器 / 同一卡片 / 同一主工作区内」多块**互斥**内容、通过点选切换面板的交互,**必须**使用基础组件 `<Tabs />`(含 `variant`/`size`/`items`/`onChange`);禁止用一排 `Button`(outline/ghost)、手写 `div[role=tablist]`、第三方 Tab、或 **`Tag` 组件**冒充切换器(`Tag` 仅用于状态/分类标签展示,不承担面板切换)。典型误用场景:Playground 中 user/assistant 角色切换、模型参数/Input/Output 结果切换——均必须使用 `<Tabs variant="segment" />` 或 `<Tabs variant="pill" />`,不得手写 div 胶囊或用 Button 组并排',
1784
+ '【尺寸默认铁律·最新】所有 4 种 Tabs 变体(line / button / pill / segment)在内容展示区、白卡内、卡片内、表单分段、筛选维度、Playground 面板内切换时,**默认尺寸统一为 `size="sm"`**。AI 生成页面时可以省略 size(组件默认即 SM),或显式写 `size="sm"`;⛔ 禁止在内容区默认写 `size="md"` / `size="lg"`。',
1785
+ '【尺寸例外】只有 Tabs 位于**整个平台顶部 header / 页面级顶导 / 产品级一级导航**时,才允许根据场景使用 `size="md"` 或 `size="lg"`:例如页面级 Pill Tabs、平台顶部模块切换、顶部导航胶囊。只要 Tabs 在白卡、面板、内容区、表单区、筛选区内,就回到 SM。',
1786
+ '【分段器 = segment 变体】设计稿「分段器」对应代码 **`variant="segment"`**(灰底轨道 + 白底选中块),与 `Tag` 无关。① **卡片内**小模块、指标维度、白/灰卡内并列区块切换 → **默认优先** `variant="segment" size="sm"`。② **页面主内容区顶部**、整块**布局级**主视图切换(并排等大内容区,如「列表/看板/日历」并列)→ **默认优先** `variant="segment" size="sm"`,除非它是页面级顶部 header 导航。',
1787
+ '【其它 variant 自判】详情子页签、面板内横向主导航感 → `line`;内容区内强调「块状」分类 → `button`;顶导/浮层/工具条胶囊规格 → `pill`;表单分段、筛选维度、与上两类「分段器优先」场景之外的 2~6 项切换 → 可用 `segment` 或按视觉在 `line`/`button` 间选择。',
1788
+ '【选型·与 Button】Tabs 的每一项是「切换可见内容」;Button 是「执行操作」。同一组互斥展示若无需下划线/胶囊视觉,且只有 2 个选项,可用 Radio horizontal,但「多面板」仍优先 Tabs',
1789
+ '【选型·与 Select】2~6 个面板级分区用 Tabs;单个字段在表单里从 10+ 枚举中选一个值 → Select,不要把每个枚举值做成 Tab',
1790
+ '【页面级导航】line(线条型)用于页面顶部或面板内的主导航,底部有绿色指示器',
1791
+ '【内容区切换】button(按钮型)用于内容区域的分类切换,选中态为浅绿底色 + 绿字',
1792
+ '【浮层/工具栏】pill(胶囊型)用于浮层、工具栏等紧凑场景,支持图标,选中态为深色填充。内容区 / 工具条 / 浮层内部默认 `size="sm"`;只有平台顶部 header / 页面级顶导胶囊才可根据场景用 `size="md"` 或 `size="lg"`,不要手写胶囊样式',
1793
+ '【内描边】pill 容器的白色描边为内描边(ring-inset / box-shadow inset),不占布局空间,所以容器实际高度 = padding × 2 + 选项高度。若手写胶囊,描边必须用 ring-inset 或 box-shadow inset,不能用 border,否则会撑高 2px',
1794
+ '【表单/筛选器】segment(分段器)用于表单切换、筛选条件等场景,灰色容器 + 白色选中块',
1795
+ '【悬浮反馈】button、pill、segment 三种变体的非选中项悬浮均使用语义填充色(fill-default)',
1796
+ '【标签数量】建议 2-6 个标签,超过时考虑可折叠或下拉',
1797
+ '【文字长度】标签文字推荐 2-4 个汉字,保持简洁',
1798
+ '【无障碍】每个标签使用 role="tab",容器使用 role="tablist",选中态用 aria-selected',
1799
+ ],
1800
+ examples: [
1801
+ { label: '线条型(默认 SM)', code: '<Tabs variant="line" items={[{label:"标签一"},{label:"标签二"},{label:"标签三"}]} />' },
1802
+ { label: '按钮型', code: '<Tabs variant="button" items={[{label:"标签一"},{label:"标签二"},{label:"标签三"}]} />' },
1803
+ { label: '胶囊型', code: '<Tabs variant="pill" items={[{label:"标签一"},{label:"标签二"}]} />' },
1804
+ { label: '胶囊型+图标', code: '<Tabs variant="pill" items={[{label:"标签",icon:<HeartIcon />},{label:"标签",icon:<StarIcon />}]} />' },
1805
+ { label: '分段器', code: '<Tabs variant="segment" items={[{label:"SM"},{label:"MD"},{label:"LG"}]} defaultIndex={1} />' },
1806
+ { label: '卡片内分段器(小模块切换,默认 SM)', code: '<Tabs variant="segment" items={[{label:"概览"},{label:"明细"},{label:"日志"}]} />' },
1807
+ { label: '主区顶部分段器(布局切换,默认 SM)', code: '<Tabs variant="segment" items={[{label:"列表"},{label:"看板"},{label:"日历"}]} />' },
1808
+ { label: '内容区显式 SM', code: '<Tabs variant="line" size="sm" items={[{label:"Tab1"},{label:"Tab2"}]} />' },
1809
+ { label: '顶部 Header 例外(可用 MD/LG)', code: '<Tabs variant="pill" size="md" items={[{label:"服务策略"},{label:"运行数据"}]} />' },
1810
+ { label: '❌ Bad(内容区默认使用 MD)', code: '/* 禁止!Tabs 在白卡/内容区/表单分段/筛选维度内默认必须 SM */\n<Tabs variant="segment" size="md" items={[{label:"概览"},{label:"明细"}]} />' },
1811
+ { label: '❌ Bad(用一排 outline Button 冒充 Tabs)', code: '/* 禁止!按钮组无指示器、无 role=tab、无键盘导航 */\n<div className="flex gap-2"><Button variant="outline-black">概览</Button><Button variant="ghost-black">明细</Button></div>' },
1812
+ { label: '✅ Good(用 Tabs segment,默认 SM)', code: '<Tabs variant="segment" items={[{label:"概览"},{label:"明细"}]} />' },
1813
+ { label: '❌ Bad(手写 div role=tablist)', code: '/* 禁止!手写 Tab 缺 token、缺指示器、键盘交互不全 */\n<div role="tablist"><span className="px-3 border-b-2">列表</span><span className="px-3">看板</span></div>' },
1814
+ { label: '✅ Good(user/assistant 切换用 segment)', code: '<Tabs variant="segment" items={[{label:"User"},{label:"Assistant"}]} />' },
1815
+ { label: '❌ Bad(用 Tag 当面板切换)', code: '/* 禁止!Tag 是状态/分类标签,不承担面板切换;切换面板必须 Tabs */\n<div className="flex gap-2"><Tag variant="brand">Input</Tag><Tag variant="grey">Output</Tag></div>' },
1816
+ ],
1817
+ keywords: [
1818
+ 'Tabs', 'tabs', '标签页', '页签', '选项卡', '切换器',
1819
+ 'line', 'button', 'pill', 'segment', '线条型', '按钮型', '胶囊型', '分段器',
1820
+ // 高频场景
1821
+ '面板切换', '内容切换', '主区切换', '视图切换', '列表 看板 日历', '概览 明细 日志',
1822
+ 'user assistant 切换', 'user 切换', 'Input Output 切换', 'Playground 切换',
1823
+ '模型参数切换', '结果切换', '维度切换', '维度筛选',
1824
+ // 英文
1825
+ 'tab', 'tab list', 'tablist', 'role tablist', 'role tab', 'segment control', 'segmented control',
1826
+ 'panel switch', 'view switch', 'view toggle',
1827
+ // 错误信号词
1828
+ '一排 outline Button', '一排 Button 当 Tabs', '手写 div role=tablist', '手搓 Tab',
1829
+ 'Tag 当切换器', 'Tag 切换面板',
1830
+ ],
1831
+ },
1832
+ {
1833
+ id: 'form-title',
1834
+ name: 'FormTitle',
1835
+ element: 'div',
1836
+ category: 'basic',
1837
+ description: 'B端标题组件,统一收敛整体页面总标题、工作台标题、一级/二级/三级标题和卡片标题 5 种变体;描述文案默认隐藏,仅在有板块介绍/场景说明/明确要求时通过 showDescription 显示;支持标题后缀 titleSuffix(如状态 Tag)。⚠️ 字体强约束:所有变体的标题文字均通过组件内部 `[font-weight:var(--font-semibold)]` 固定为 600 semibold,字号、行高、颜色由组件 token 固定,禁止通过 className 或 style 覆写字体粗细、字号、颜色。',
1838
+ componentFile: './components/FormTitle.jsx',
1839
+ tokensFile: './components/FormTitle.tokens.js',
1840
+ props: [
1841
+ { name: 'variant', type: 'enum', options: ['form', 'level-1', 'level-2', 'level-3', 'card'], default: 'form' },
1842
+ { name: 'title', type: 'string', default: '表单标题' },
1843
+ { name: 'description', type: 'string', default: '一级标题描述' },
1844
+ { name: 'titleSuffix', type: 'ReactNode', default: null },
1845
+ { name: 'showDescription', type: 'boolean', default: false },
1846
+ { name: 'className', type: 'string', default: '' },
1847
+ ],
1848
+ labels: {
1849
+ variant: {
1850
+ form: '表单标题',
1851
+ 'level-1': '一级标题',
1852
+ 'level-2': '二级标题',
1853
+ 'level-3': '三级标题',
1854
+ card: '卡片标题',
1855
+ },
1856
+ },
1857
+ _preview: FORM_TITLE_PREVIEW,
1858
+ rules: [
1859
+ '【统一入口】通过 variant 在 5 种标题形态间切换,不为同一套标题系统再拆分多个独立基础组件',
1860
+ '【页面/面板标题强约束】全新生成 Page 页面、白卡、面板、卡片、section 时,凡出现「主标题 +(可选)描述」的标题行或面板头,**必须**使用 `FormTitle`;禁止 `h1/h2/h3` + `font-size`/`font-weight` 手写。整体页面左上角总标题、工作台名、Playground 页名统一用 `FormTitle variant="form"`;列表页单张主白卡顶栏、表单页主编辑区标题或页内一级内容块主标题再使用 `variant="level-1"`。',
1861
+ '【描述文案默认隐藏·强约束】`FormTitle` 的副标题/描述文案默认不显示,AI 生成页面时不要因为组件有 `description` prop 就自动添加描述。只有出现明确场景诉求时才写 `description` + `showDescription`:① 用户明确要求“显示描述/副标题/说明”;② 板块需要解释复杂业务规则、使用方式、空白原因或风险提醒;③ 页面首屏需要介绍当前模块目标。普通白卡标题、面板标题、表单分组标题、Playground 卡片标题默认只写 `title`,不写 `description`。',
1862
+ '【层级对照表·按板块选 variant】① **整体页面左上角总标题 / 工作台名 / Playground 页名**(如 `Model Playground`)→ `variant="form"`(20px;无竖条;如需说明放标题下方;同页通常只出现一次)。② **列表/管理页单张主白卡顶栏主标题,或页面总标题之下的一级板块标题** → `variant="level-1"`;表单页主编辑区、详情页主白卡标题、单列编辑区主标题也归这一层。默认无副标题;需右侧主按钮时:`<div className="flex items-start justify-between gap-3"><div className="min-w-[120px] flex-1"><FormTitle variant="level-1" ... /></div><div className="shrink-0 flex flex-wrap justify-end gap-2"><Button ... /></div></div>`。③ **level-1 下的第一大分区**(白卡内章节)→ `variant="level-2"`。④ **再嵌套的子分区**(折叠区、表格上小块说明区)→ `variant="level-3"`。⑤ **Card 或信息摘要条、表格卡片外框**的弱化标题(无竖条;需要说明时标题与描述同排)→ `variant="card"`。',
1863
+ '【同页层级强约束】同一页面必须拉开标题层级:整体页面总标题用 `form`,一级白卡/大板块标题用 `level-1`,板块内 section 再按 `level-2` / `level-3` 递进;⛔ 禁止把整个页面所有卡片左上角标题都统一写成 `form` 或 `level-1`。',
1864
+ '【与模板示例映射】`Model Playground`、`Prompt Editor`、`API Playground` 这类工作台页面左上角总标题 → `form`;列表/管理页里的单张主白卡顶栏 → `level-1`;白卡内多个 Workspace / section 标题 → `level-2`;其下子分区 → `level-3`;Card 内摘要条 → `card`。',
1865
+ '【⛔ 字体样式硬约束·严禁覆写】FormTitle 所有变体的标题文字必须保持组件内置字体规格,组件内部使用 `[font-weight:var(--font-semibold)]` 输出 600 semibold,避免 Tailwind v4 将字体 token 误解析为 font-family token。AI 生成代码时绝对禁止:① 在 `title` 外层包裹 `<span className="font-normal">` 或 `style={{ fontWeight: "normal" }}` 降低粗细;② 通过 `className` 传入任意 `text-*`(字号)、`font-*`(字重)、`text-[color]`(颜色)覆盖标题样式;③ 用 `<h2>` / `<p>` 等原生标签手写「看起来像标题」的样式代替 FormTitle。标题字体规格由组件 token 严格固定,不对外暴露字体覆写接口',
1866
+ '【⛔ 标题区禁止竖排 / 逐字换行】任何白卡标题、面板标题、section 标题都不允许出现“原始信息只读”这类一字一行的竖状显示。禁止给标题容器设置 `writing-mode` / `text-orientation` / `w-[12px]` / `w-[16px]` / `max-w-[24px]` / `break-all` / `whitespace-normal` 等会压窄标题的样式。FormTitle 的主标题必须保持 `whitespace-nowrap`,横向完整显示;空间不足时应隐藏/下移描述或让右侧操作区换行,不能压缩标题。',
1867
+ '【标题后缀 Tag 位置·强约束】当标题右侧需要展示状态、数量、身份、分类等短标签(如 `0 条消息`、`Beta`、`只读`、`推荐`)时,必须通过 `titleSuffix={<Tag ... />}` 放在 **FormTitle 主标题文字的右侧**,视觉上紧跟标题,位于描述文案之前。它是标题语义的一部分,不是 header 右侧操作区。禁止写成 `<FormTitle ... /><Tag ... />` 放在整个标题组件之后,也禁止把这类 Tag 放进 `justify-between` 右侧 action 区。',
1868
+ '【标题后缀 vs 右侧操作区】`titleSuffix` 只放短状态 Tag/业务胶囊,推荐使用 `Tag variant="grey" size="m"` 或按状态色选 Tag;右侧操作区只放 Button、Tabs、筛选入口、更多菜单等可交互控件。若同一 header 同时有标题 Tag 和右侧 Button,结构应为:左侧 `<FormTitle titleSuffix={<Tag ... />} />`,右侧 `<div className="shrink-0 ..."><Button /></div>`。',
1869
+ '【标题栏布局最小宽度】白卡/面板 header 推荐结构:`<header className="shrink-0 flex items-start justify-between gap-3 px-6 py-4"><div className="min-w-[120px] flex-1"><FormTitle ... /></div><div className="shrink-0 flex flex-wrap justify-end gap-2">操作区</div></header>`。左侧标题区必须 `flex-1 min-w-[120px]`,右侧操作区 `shrink-0`;当面板宽度小于 260px 或标题较长时,保持默认无描述,必要说明移到正文说明或 Tooltip,禁止把标题列挤成竖排。',
1870
+ '【⚡ FormTitle 真伪自检·五件套】生成完区块标题后**必须**逐项目测,任一不中即视为"伪 FormTitle"必须改:① **标题字重 = 600**(实现上应来自组件内置 `[font-weight:var(--font-semibold)]`,不要手写 `font-bold`);② **标题字色 = `var(--color-foreground)` 深色**(不是 secondary 灰色);③ **竖条 = brand-500 蓝绿色 3×14/16px 圆角条**(不是品牌浅底、不是 border-left 1px 直线);④ **描述默认隐藏**——没有明确说明诉求时不写 `description`,需要说明时必须同时写 `showDescription`;⑤ **描述位置正确**——`form` / `level-1` 描述在**标题下方独占一行**,`level-2` / `level-3` 描述在**标题同行右侧**(次要色),`card` 标题描述同排且无竖条。任一项目测偏离 → 100% 是 AI 手搓了"伪 FormTitle"或选错了 variant,⛔ 必须删除手搓代码、改用正确 variant 的 `<FormTitle />`。',
1871
+ '【⛔ 伪 FormTitle 识别表(高频误用)】凡命中下面任一形态 → **必须**整体替换为 `<FormTitle />`,⛔ 严禁保留:① **手搓竖条**:`<div className="w-1 h-4 bg-teal-500 rounded" />` + 相邻 `<h3 / span / p>` —— 字重很容易偏离 600,是 AI 最常犯的错;② **错用 card variant 当主标题**:白卡顶栏 / 面板头用 `<FormTitle variant="card" />` —— card 是"弱化标题"(字色 secondary),只能用于 Card 内嵌摘要条,不能当面板主标题;③ **用了 FormTitle 但 className 压回了字重**:`<FormTitle className="font-normal text-gray-600" />` —— 违反【⛔ 字体样式硬约束】;④ **变体选错**:整体页面左上角总标题用了 `variant="level-1"` 或 `variant="level-2"` —— 应该用 `form`;白卡/面板一级主标题用了 `variant="form"` —— 应该用 `level-1` 或 `level-2`;⑤ **把"标题 + 描述"压成同一行 plain text**:`<div>System Prompt 使用 TFDS TextArea 重构编辑区域。</div>` —— 完全没用组件,更没有字重和竖条;⑥ **用 `<p className="text-sm text-secondary">` 代替 `description`**:描述应作为 prop 传给 FormTitle,不是单独 `<p>`。',
1872
+ '【⚡ 卡片标题专项 Bad/Good(图同款)】白卡内出现"Prompt Workspace / System Prompt / Conversation / Tools / Run Result / Model Settings / Inspector / Inputs / Outputs"等**面板/区块标题**:⛔ Bad:`<div className="flex gap-2"><div className="w-1 h-4 bg-brand-500 rounded" /><span className="text-base">Prompt Workspace</span><span className="text-sm text-gray-500">结构化信息支持组件化编辑。</span></div>`(字重缺失、颜色错且默认不应堆描述);✅ Good:`<FormTitle variant="level-2" title="Prompt Workspace" />`。只有确实需要解释该板块作用时才写:`<FormTitle variant="level-2" title="Prompt Workspace" description="结构化信息支持组件化编辑。" showDescription />`。',
1873
+ '【各变体字体规格(只读参考,不可手动设置)】form → 20px / line-height 28px / font-weight **600(semibold)** / 颜色 text-foreground-primary / **无竖条**;level-1 → 16px / 22px / **600** / primary + 左侧 3px brand-500 竖条;level-2 → 14px / 20px / **600** / primary + 左侧 3px brand-500 竖条;level-3 → 14px / 20px / **600** / primary + 左侧 3px brand-500 竖条;card → 14px / 20px / **600** / text-foreground-secondary(无竖条)。所有变体标题统一 600 semibold,描述文案均为 text-foreground-secondary 常规字重',
1874
+ '【表单标题】form 只用于整体页面左上角总标题、工作台名、Playground 页名等页面级标题;标题为 20px/28px/600 semibold,且左侧无品牌竖条;描述默认隐藏,有整体说明诉求时通过 `showDescription` 显示在标题下方',
1875
+ '【一级标题】level-1 使用左侧 3×16px 品牌色竖条(brand-500),标题 16px/22px/600 semibold;用于页面总标题之下的一级白卡/大板块主标题,以及表单页主编辑区标题,不用于整页左上角总标题;描述默认隐藏,有模块介绍诉求时通过 `showDescription` 显示在标题**下方**',
1876
+ '【二三级标题】level-2 与 level-3 都使用左侧 3×14px 品牌色竖条(brand-500),标题 14px/20px/600 semibold;描述默认隐藏,有板块说明诉求时通过 `showDescription` 显示在标题**同行右侧**;同级多个分区按内容深度在 2/3 间择一,保持全页一致',
1877
+ '【标题描述换行规则】仅当 `showDescription` 开启时,level-2 / level-3 / card 的右侧描述允许自动换行,窄卡片中会从标题右侧折到下一行,确保说明文字完整可读;但主标题本身永远不逐字换行。AI 不得在 FormTitle 外层包 `overflow-hidden` / `whitespace-nowrap` 强行裁切描述;如果描述不是核心说明而只是资源名/ID,才可在外层另行使用 `truncate + Tooltip` 模式。',
1878
+ '【卡片标题】card 不带竖条,标题 14px/20px/600 semibold,字色为 secondary,标题和描述同排;适合 Card 头、指标条、嵌套信息块,不替代列表页主标题',
1879
+ '【可选展示】showDescription 默认 false;showDescription=false 或 description 为空时隐藏描述文案,布局不保留空占位',
1880
+ ],
1881
+ examples: [
1882
+ { label: '表单/页面总标题(默认无描述)', code: '<FormTitle variant="form" title="表单标题" />' },
1883
+ { label: '一级标题(默认无描述)', code: '<FormTitle variant="level-1" title="一级标题" />' },
1884
+ { label: '二级标题(默认无描述)', code: '<FormTitle variant="level-2" title="二级标题" />' },
1885
+ { label: '三级标题(默认无描述)', code: '<FormTitle variant="level-3" title="三级标题" />' },
1886
+ { label: '卡片标题(默认无描述)', code: '<FormTitle variant="card" title="卡片标题" />' },
1887
+ { label: '明确展示描述', code: '<FormTitle variant="form" title="表单标题" description="一级标题描述" showDescription />' },
1888
+ { label: '整体页面总标题(灰底 header)', code: '<header className="shrink-0 flex items-start justify-between gap-3 px-4 pt-3 pb-2"><div className="min-w-[120px] flex-1"><FormTitle variant="form" title="Model Playground" titleSuffix={<Tag variant="success" size="m">TFDS</Tag>} description="保留原有主工作区、中会话、右设置与结果的工作流位置。" showDescription /></div><div className="shrink-0 flex flex-wrap justify-end gap-2"><Button variant="outline-black" size="md">查看源稿</Button><Button variant="primary" size="md">导入信息</Button></div></header>' },
1889
+ { label: '列表页白卡顶栏(默认无描述)', code: '<div className="flex items-start justify-between gap-3"><div className="min-w-[120px] flex-1"><FormTitle variant="level-1" title="MCP 工具管理" /></div><div className="shrink-0 flex flex-wrap justify-end gap-2"><Button variant="primary" size="md">新建 MCP</Button></div></div>' },
1890
+ { label: '✅ Good·同页拉开标题层级', code: '<><FormTitle variant="form" title="Model Playground" /><FormTitle variant="level-1" title="Prompt Workspace" /><FormTitle variant="level-2" title="System Prompt" /><FormTitle variant="level-3" title="原始信息" /></>' },
1891
+ { label: '标题右侧状态 Tag(默认无描述)', code: '<FormTitle variant="level-2" title="Conversation" titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>} />' },
1892
+ { label: '✅ Good·标题 Tag 紧跟主标题,按钮留在右侧操作区', code: '<header className="shrink-0 flex items-start justify-between gap-3 px-6 py-4"><div className="min-w-[120px] flex-1"><FormTitle variant="level-2" title="Conversation" titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>} /></div><div className="shrink-0 flex flex-wrap justify-end gap-2"><Button variant="outline-black" size="md">清空</Button></div></header>' },
1893
+ { label: '❌ Bad·手搓竖条+h3(最常见)', code: '/* AI 最常犯的错:竖条+文字看着像 FormTitle,但字重和层级很容易偏离 600 semibold 规范 */\n<div className="flex items-center gap-2"><div className="w-1 h-4 bg-teal-500 rounded-full" /><h3 className="text-base text-gray-900">Prompt Workspace</h3></div>' },
1894
+ { label: '❌ Bad·标题描述同行 plain text(图同款)', code: '/* 完全没用组件,字重和描述位置全错 */\n<div className="flex items-center gap-2"><span>System Prompt</span><span className="text-sm text-gray-500">使用 TFDS TextArea 重构编辑区域。</span></div>' },
1895
+ { label: '❌ Bad·整体页面总标题误用 level-1', code: '/* 整体页面左上角总标题应使用 form:大字号且无绿色竖条 */\n<FormTitle variant="level-1" title="Model Playground" />' },
1896
+ { label: '❌ Bad·错用 card variant 当面板主标题', code: '/* card variant 是弱化标题(字色 secondary),不能用作面板主标题 */\n<FormTitle variant="card" title="System Prompt" description="使用 TFDS TextArea 重构编辑区域。" />' },
1897
+ { label: '❌ Bad·外层强行裁切描述', code: '/* 禁止!核心说明会被卡片截断,用户读不到全文 */\n<div className="overflow-hidden whitespace-nowrap"><FormTitle variant="level-2" title="System Prompt" description="使用 TFDS TextArea 重构编辑区域,并保留原始提示词结构。" /></div>' },
1898
+ { label: '❌ Bad·标题被挤成竖排', code: '/* 禁止!标题区过窄会变成一字一行,严重破坏可读性 */\n<header className="flex"><div className="w-4 break-all">原始信息只读</div><Tabs /></header>' },
1899
+ { label: '❌ Bad·把标题状态 Tag 放到整体标题组件右侧', code: '/* 禁止!0 条消息是 Conversation 的标题状态,应紧跟主标题,不应被推到 header 右侧 */\n<header className="flex items-start justify-between"><FormTitle variant="level-2" title="Conversation" description="输入消息后回车发送。" /><Tag variant="grey">0 条消息</Tag></header>' },
1900
+ { label: '✅ Good·窄面板标题保持横排', code: '<header className="shrink-0 flex items-start justify-between gap-3 px-6 py-4"><div className="min-w-[120px] flex-1"><FormTitle variant="level-2" title="原始信息" /></div><div className="shrink-0 flex flex-wrap justify-end gap-2"><Tabs variant="segment" size="sm" items={[...]} /></div></header>' },
1901
+ { label: '❌ Bad·className 压回字重', code: '/* 违反【⛔ 字体样式硬约束】*/\n<FormTitle variant="level-2" title="Prompt Workspace" className="font-normal text-gray-600" />' },
1902
+ { label: '✅ Good·白卡内的 section 标题(默认无描述)', code: '<FormTitle variant="level-2" title="Prompt Workspace" />' },
1903
+ { label: '✅ Good·有明确说明诉求时展示描述', code: '<FormTitle variant="level-2" title="System Prompt" description="使用 TFDS TextArea 重构编辑区域,并保留原始提示词结构。" showDescription />' },
1904
+ ],
1905
+ keywords: [
1906
+ 'FormTitle', 'form-title',
1907
+ // 场景关键词:AI 看到下面这些词时必须联想到 FormTitle
1908
+ 'Page 页面表头', 'Page表头', '页面表头', '页面级 header', '页面 header', '页面标题', '灰底页面标题',
1909
+ '区块标题', '面板标题', '卡片标题', '白卡标题', '白卡顶栏', '标题行', '分区标题',
1910
+ '标题竖排', '标题逐字换行', '一字一行', '标题被挤压', '标题区过窄',
1911
+ '标题后缀', 'titleSuffix', '标题右侧标签', '标题状态', '标题标签', '主标题右侧 Tag',
1912
+ '0 条消息', 'Beta', '只读', '推荐', '状态 Tag',
1913
+ '竖条标题', '左竖条', '品牌色竖条', '带竖条', 'section title', 'panel title',
1914
+ 'panel header', 'card header', 'workspace title', 'block title', 'section header',
1915
+ 'level-1', 'level-2', 'level-3', '一级标题', '二级标题', '三级标题',
1916
+ // 禁止手搓的信号词:凡 AI 想到用这些,就应该改用 FormTitle
1917
+ 'border-l', 'border-l-4', 'border-left', '左边框', '左边线', 'h2', 'h3',
1918
+ 'writing-mode', 'text-orientation', 'break-all', 'w-[16px]', 'max-w-[24px]',
1919
+ ],
1920
+ },
1921
+ {
1922
+ id: 'form',
1923
+ name: 'Form',
1924
+ element: 'form',
1925
+ category: 'basic',
1926
+ description: 'B端表单组合组件,负责字段标题、说明、错误反馈与 top/left 布局;字段控件复用平台已有基础组件,覆盖 13 类表单字段。',
1927
+ componentFile: './components/Form.jsx',
1928
+ tokensFile: './components/Form.tokens.js',
1929
+ props: [
1930
+ { name: 'layout', type: 'enum', options: ['vertical', 'grid'], default: 'vertical' },
1931
+ { name: 'labelPosition', type: 'enum', options: ['top', 'left'], default: 'top' },
1932
+ { name: 'size', type: 'enum', options: ['sm', 'md'], default: 'md' },
1933
+ { name: 'items', type: 'array', default: null },
1934
+ { name: 'columns', type: 'number', default: 1 },
1935
+ { name: 'disabled', type: 'boolean', default: false },
1936
+ { name: 'requiredMark', type: 'boolean', default: true },
1937
+ { name: 'labelOptional', type: 'boolean', default: false },
1938
+ { name: 'labelRequired', type: 'boolean', default: false },
1939
+ { name: 'labelAi', type: 'boolean', default: false },
1940
+ { name: 'labelHelp', type: 'boolean', default: false },
1941
+ { name: 'className', type: 'string', default: '' },
1942
+ ],
1943
+ labels: {
1944
+ layout: { vertical: '纵向', grid: '网格' },
1945
+ labelPosition: { top: '顶部', left: '左侧' },
1946
+ size: { sm: 'SM', md: 'MD' },
1947
+ controlVariant: { brand: '品牌绿', black: '主色黑' },
1948
+ controlLayout: { horizontal: '横向', vertical: '纵向' },
1949
+ pickerType: {
1950
+ date: '日期',
1951
+ datetime: '日期时间',
1952
+ daterange: '日期范围',
1953
+ datetimerange: '日期时间范围',
1954
+ },
1955
+ timePickerType: {
1956
+ time: '时间',
1957
+ timerange: '时间范围',
1958
+ },
1959
+ inputNumberButtons: {
1960
+ outer: '外置',
1961
+ inner: '内置',
1962
+ },
1963
+ fillMode: { empty: '空', filled: '有值' },
1964
+ countOverflow: { off: '关', on: '开' },
1965
+ state: { default: '默认可用', disabled: '禁用' },
1966
+ minRows: { 1: '1 行', 2: '2 行', 3: '3 行', 4: '4 行', 5: '5 行' },
1967
+ status: { default: '默认', error: '错误' },
1968
+ resize: { vertical: '纵向拉伸', none: '固定高度' },
1969
+ },
1970
+ _preview: FORM_PREVIEW,
1971
+ rules: [
1972
+ '【组件定位】Form 是字段组合层,只管理 label、提示、错误反馈、top/left 布局和字段编排,不重新定义已有表单控件样式',
1973
+ '【控件复用】已有基础组件必须复用:input 复用 Input,textarea 复用 TextArea(仅传基组件已有 props,默认 minRows=3、resize 纵向增高与 TextArea 一致;System Prompt / JSON / 代码参数字段必须传 variant="code"),input-number 复用 InputNumber,select 复用 Select,tag-input 复用 TagInput,checkbox 复用 CheckboxGroup,radio 复用 RadioGroup,switch 复用 Switch,date-picker 复用 DatePicker,time-picker 复用 TimePicker,slider 复用 Slider,upload 复用 Upload',
1974
+ '【布局】Form 在页面主内容区、白色卡片容器与纵向堆叠场景中默认 `w-full min-w-0 self-stretch`;labelPosition="top" 时字段容器与控件区都默认全宽,label 与控件间距 4px;labelPosition="left" 时标签列固定 96px、间距 24px、右侧控件区使用 `flex-1 min-w-0 w-full` 撑满剩余宽度',
1975
+ '【详情页全集预览】组件详情页的“全集”分类按字段类型从上到下单列排列,每个组件间距 40px,超出预览画布时在画布内滚动查看',
1976
+ '【字段配置】items 数组每项支持 id、label、type、required、labelPosition、middleHelpText、helpText、error、errorText、disabled、value/defaultValue/onChange 等字段;当字段命中 AI 推荐语义时,可继续透传 aiSuggestion、aiSuggestions、onAdoptSuggestion、onRefreshAiSuggestions 到 Input / Select / TextArea / TagInput',
1977
+ '【标题附加样式】单字段标题支持 labelOptional、labelRequired、labelAi、labelHelp 四个布尔开关,可与纯文字标题组合显示;渲染顺序固定为 可选 -> 必填 -> AI -> Help',
1978
+ '【标题字重】字段标题、必填星号与可选文案统一使用 `semibold` token,即 `[font-weight:var(--font-semibold)]`;不要继续使用 `font-bold` 或手写 700 字重',
1979
+ '【AI 标签】当 labelAi=true 时,必须复用标准 `<Tag variant="ai" radius="full" fontWeight="bold" size="m">AI</Tag>`;禁止在 Form 内手写渐变胶囊、手写圆角块或用普通文字 span 模拟 AI 标签',
1980
+ '【标题必填】labelRequired 用于标题星号装饰;若未单独配置,仍可通过 required + requiredMark 复用现有星号逻辑',
1981
+ '【配置联动】组件详情页切换字段类型时,左侧配置区需动态追加当前被引用基础组件自身的枚举配置,并透传到对应字段控件(textarea 与 TextArea 详情配置侧栏一致:内容、超长计数、状态、默认高度、状态、高度适配)',
1982
+ '【TagInput 字段】Form 中 type="tag-input" 的标签颜色固定为灰色、Tag 尺寸固定为 M,不暴露颜色或尺寸配置',
1983
+ '【反馈优先级】error=true 时展示红色图标 + 错误文案,并将可复用控件 status 设为 error;无错误时展示 helpText',
1984
+ '【未独立组件】InputGroup 尚未作为独立基础组件存在,Form 内部仅做轻量组合展示;如后续沉淀为基础组件,应替换为独立组件引用',
1985
+ '【尺寸·页面默认 MD】Form 默认 `size="md"` 并透传给 Input / Select / TagInput / InputNumber / RadioGroup / CheckboxGroup;**页面主工作区、筛选栏、抽屉/弹窗表单**不要默认写 `size="sm"`。TextArea 固定 MD(不透传 size);Switch 固定 MD 轨道,与 Input 36px 体系一致,不再区分尺寸',
1986
+ '【无障碍】Form 根节点使用原生 form;单字段 label 保留可见文本,错误信息用图标和文本同时表达,不只依赖颜色',
1987
+ ],
1988
+ examples: [
1989
+ { label: '基础表单', code: '<Form items={[{ id: "name", label: "字段标题", type: "input", required: true }]} />' },
1990
+ { label: '左侧标签', code: '<Form labelPosition="left" items={[{ id: "name", label: "字段标题", type: "input" }]} />' },
1991
+ { label: '错误反馈', code: '<Form items={[{ id: "email", label: "邮箱", type: "input", error: true, errorText: "该项目为必填项" }]} />' },
1992
+ { label: '标题附加样式', code: '<Form items={[{ id: "name", label: "字段标题", type: "input", labelOptional: true, labelAi: true, labelHelp: true }]} />' },
1993
+ { label: '选择器字段', code: '<Form items={[{ id: "city", label: "城市", type: "select", options: [{ value: "hz", label: "杭州" }] }]} />' },
1994
+ { label: '全量示例', code: '<Form className="!gap-10" />' },
1995
+ { label: '❌ Bad(手搓 form + label)', code: '/* 禁止!手搓表单缺统一 label 间距、必填星号、错误反馈 */\n<form><label>姓名</label><input /></form>' },
1996
+ { label: '✅ Good(用 Form 组件)', code: '<Form items={[{ id: "name", label: "姓名", type: "input", required: true }]} />' },
1997
+ ],
1998
+ keywords: [
1999
+ 'Form', 'form', '表单', '表单组合', '表单容器', '表单组件',
2000
+ 'labelPosition top', 'labelPosition left', '左侧标签', '顶部标签',
2001
+ '字段', 'items', 'required', 'requiredMark', '必填', '必填星号', '可选',
2002
+ 'helpText', 'errorText', 'middleHelpText', '错误提示', '辅助说明',
2003
+ 'top 布局', 'left 布局', 'vertical 布局', 'grid 布局',
2004
+ // 错误信号词
2005
+ 'native form label', 'form label input', '手搓表单', '原生 form',
2006
+ ],
2007
+ },
2008
+ {
2009
+ id: 'input',
2010
+ name: 'Input',
2011
+ element: 'input',
2012
+ category: 'basic',
2013
+ description:
2014
+ '用户自输入的短文本/搜索词。值不可穷举或需键盘高效录入时用 Input;若合法值来自有限枚举列表,应优先 Select 或 Radio,避免用 Input 接收「只能是 A/B/C」的值。',
2015
+ componentFile: './components/Input.jsx',
2016
+ tokensFile: './components/Input.tokens.js',
2017
+ props: [
2018
+ { name: 'status', type: 'enum', options: ['default', 'error'], default: 'default' },
2019
+ { name: 'disabled', type: 'boolean', default: false },
2020
+ { name: 'prefix', type: 'node', default: null },
2021
+ { name: 'suffix', type: 'node', default: null },
2022
+ { name: 'allowClear', type: 'boolean', default: false },
2023
+ { name: 'aiSuggestion', type: 'string', default: null },
2024
+ { name: 'aiSuggestions', type: 'array', default: null },
2025
+ { name: 'placeholder', type: 'string', default: '请输入' },
2026
+ { name: 'value', type: 'string | array' },
2027
+ { name: 'defaultValue', type: 'string | array' },
2028
+ { name: 'onChange', type: 'function', default: null },
2029
+ { name: 'onClear', type: 'function', default: null },
2030
+ { name: 'onAdoptSuggestion', type: 'function', default: null },
2031
+ { name: 'onRefreshAiSuggestions', type: 'function', default: null },
2032
+ ],
2033
+ labels: {
2034
+ status: { default: '默认', error: '错误' },
2035
+ },
2036
+ _preview: INPUT_PREVIEW,
2037
+ rules: [
2038
+ '【⛔ AI 禁止使用原生元素】绝对禁止用原生 `<input>` / `<input type="text">` / `<input type="number">` 代替本组件。原生元素会产生黑色边框(浏览器默认样式),破坏设计一致性。所有单行文本录入必须使用 TFDS Input 组件,边框颜色由组件内部 token 控制(浅灰色),禁止在外部通过 className 覆写 border 颜色',
2039
+ '【选型·与 Select】需要展示当前选中项标签、且选项较多或需搜索(若后续扩展)→ Select;需要用户自由拼写(姓名、备注、URL)→ Input',
2040
+ '【选型·与 InputNumber】纯数字且需步进/范围约束 → InputNumber,不要用 Input type 伪装',
2041
+ '【默认状态】白底 + 灰边框,悬浮时边框加深,聚焦时仅 1px 绿色描边(无外发光聚焦环)',
2042
+ '【聚焦占位符】聚焦时占位符文字颜色变更浅(text-disabled),引导用户输入',
2043
+ '【错误状态】status="error" 改为浅红底,聚焦时 1px 红色描边',
2044
+ '【禁用态】在当前状态(有值或无值)基础上叠加浅灰底 + 降低透明度,保留内容展示',
2045
+ '【前后缀】通过 prefix / suffix 传入 ReactNode(图标或文字),传入才显示;输入框右侧图标插槽与内部 svg 必须固定为 16×16px',
2046
+ '【清除按钮】allowClear=true 且有值时,容器 hover 或聚焦时出现清除按钮',
2047
+ '【AI推荐】支持单条 aiSuggestion 和多条 aiSuggestions 两种模式;当 aiSuggestions 有值时优先按多条列表渲染,列表容器使用 AI Fill-1,Hover 条目使用 AI Fill-2',
2048
+ '【AI推荐默认触发】当需求、字段说明或页面描述中出现“AI推荐”“智能推荐”“推荐回复”“刷新推荐”“智能建议”“推荐文案”“建议填写”“AI帮助填写”等语义时,Input 默认应展示 AI 推荐区,而不是只渲染普通输入态。',
2049
+ '【AI推荐默认形态】命中上述语义后,AI 推荐区固定展示在 Input 组件下方;推荐内容必须贴合当前页面业务场景,例如客服回复建议、工单建议文案、搜索建议词、表单引导填写文案。',
2050
+ '【AI推荐误判边界】普通 placeholder、helpText、默认值、静态示例文案不等于 AI 推荐能力;只有明确存在“推荐/建议/AI辅助填写”语义时,才默认展示 Input 的 AI 推荐区。',
2051
+ '【刷新推荐】当存在 AI 推荐时,Input 默认应支持真实刷新;未接业务服务时,组件内部仍需轮换下一组建议,不允许点了刷新但内容不变。若传入 onRefreshAiSuggestions,应在本地刷新闭环之外继续向外通知;按钮位于清空按钮右侧,tooltip 固定为“刷新推荐”,icon 使用 `ai-fill-3` 渐变 token',
2052
+ '【采纳回调】点击任一推荐内容时都会触发 onAdoptSuggestion(suggestion),并默认把推荐值真实写入输入框;受控模式下需配合 value + onChange 完成外部值更新',
2053
+ '【宽度】默认随父容器满宽(避免板块内输入框不撑满);需要固定宽(如筛选栏)时,用外层 style.width(240/200/160)或传 `style={{ "--size-input-width": "240px" }}` 控制',
2054
+ '【尺寸】仅保留 MD 一种尺寸,高度固定 36px;与 Button md 高度一致',
2055
+ '【页面默认】页面主表单、筛选栏、白卡内录入区、侧栏与弹窗正文中的单行 Input 默认使用 36px 高度,不再提供 size 配置',
2056
+ ],
2057
+ examples: [
2058
+ { label: '默认', code: '<Input placeholder="请输入内容" />' },
2059
+ { label: '有值', code: '<Input value="已填写内容" />' },
2060
+ { label: '带清除', code: '<Input value="可清除" allowClear onClear={() => {}} />' },
2061
+ { label: '带前缀图标', code: '<Input prefix={<SearchIcon />} placeholder="搜索" />' },
2062
+ { label: '带后缀图标', code: '<Input suffix={<EyeIcon />} placeholder="密码" />' },
2063
+ { label: 'AI推荐-单条', code: '<Input aiSuggestion="您好,您的问题已收到,抖音客服正在为您核实处理。" onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(suggestion) => console.log(suggestion)} />' },
2064
+ { label: 'AI推荐-多条', code: '<Input aiSuggestions={["您好,正在为您核实,请稍候。", "抱歉给您带来不便,这边已经帮您加急处理。", "您好,您反馈的问题我们已经收到,建议您先提供订单号和问题截图,方便抖音客服尽快为您核实处理。"]} onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(suggestion) => console.log(suggestion)} />' },
2065
+ { label: '智能建议-表单填写', code: '<Input aiSuggestion="建议补充订单号和问题截图,方便客服尽快核实。" onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(suggestion) => console.log(suggestion)} placeholder="请输入处理说明" />' },
2066
+ { label: '推荐文案-搜索词', code: '<Input aiSuggestions={["退款进度", "发票补开", "账号申诉"]} onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(suggestion) => console.log(suggestion)} placeholder="请输入搜索词" />' },
2067
+ { label: '错误状态', code: '<Input status="error" value="格式不正确" />' },
2068
+ { label: '禁用', code: '<Input disabled value="不可编辑" />' },
2069
+ { label: '❌ Bad(原生 input,会出现黑色边框)', code: '/* 禁止!原生 input 浏览器默认黑边,破坏设计一致性 */\n<input type="text" placeholder="搜索" className="border rounded px-2 py-1" />' },
2070
+ { label: '✅ Good(用 Input 组件)', code: '<Input placeholder="搜索" />' },
2071
+ { label: '❌ Bad(数字录入用 Input/原生)', code: '/* 禁止!数字字段必须用 InputNumber */\n<input type="number" min={0} max={1} step={0.01} />' },
2072
+ { label: '✅ Good(数字用 InputNumber)', code: '<InputNumber min={0} max={1} step={0.01} />' },
2073
+ ],
2074
+ keywords: [
2075
+ 'Input', 'input', '输入框', '单行输入', '文本输入', '搜索框',
2076
+ 'text input', 'search input', 'text field', 'textbox',
2077
+ '姓名', '名称', '用户名', '邮箱', '手机号', 'URL', '链接', '关键词搜索', '备注',
2078
+ 'placeholder', '占位符', '前缀图标', '后缀图标', 'prefix', 'suffix',
2079
+ 'allowClear', '清除按钮', 'clearable',
2080
+ 'AI 推荐', 'aiSuggestion', 'ai-suggestion', '智能推荐', '推荐回复', '刷新推荐',
2081
+ '智能建议', '推荐文案', '建议填写', 'AI帮助填写', '搜索建议',
2082
+ // 伪手搓 / 错误信号词
2083
+ '<input>', '<input type="text">', 'input type=text', 'native input',
2084
+ 'border-gray', 'border rounded', '黑边输入框', '黑色边框',
2085
+ ],
2086
+ },
2087
+ {
2088
+ id: 'textarea',
2089
+ name: 'TextArea',
2090
+ element: 'textarea',
2091
+ category: 'basic',
2092
+ description:
2093
+ '多行文本输入,与 Input 共用边框与状态语义;固定 MD 尺寸(14px 字、与 Input md 一致的内边距)。新增 code 类型用于代码 / Prompt 编辑,左侧显示行号并使用等宽字体。默认占位 3 行高度,可选 1~5 行作为最小高度(1 行与 Input md 同高)。高度仅按内容与规则自动变化,不可拖拽改高。maxLength 可选,不设则字数不限。',
2094
+ componentFile: './components/TextArea.jsx',
2095
+ tokensFile: './components/TextArea.tokens.js',
2096
+ props: [
2097
+ { name: 'variant', type: 'enum', options: ['default', 'code'], default: 'default' },
2098
+ { name: 'minRows', type: 'enum', options: [1, 2, 3, 4, 5], default: 3 },
2099
+ { name: 'status', type: 'enum', options: ['default', 'error'], default: 'default' },
2100
+ { name: 'disabled', type: 'boolean', default: false },
2101
+ { name: 'maxLength', type: 'number' },
2102
+ { name: 'showCount', type: 'boolean', default: false },
2103
+ { name: 'enforceMaxLength', type: 'boolean', default: true },
2104
+ { name: 'resize', type: 'enum', options: ['vertical', 'none'], default: 'vertical' },
2105
+ { name: 'fillHeight', type: 'boolean', default: false },
2106
+ { name: 'aiSuggestion', type: 'string', default: null },
2107
+ { name: 'aiSuggestions', type: 'array', default: null },
2108
+ { name: 'placeholder', type: 'string', default: '请输入' },
2109
+ { name: 'value', type: 'string | array' },
2110
+ { name: 'defaultValue', type: 'string | array' },
2111
+ { name: 'onChange', type: 'function', default: null },
2112
+ { name: 'onAdoptSuggestion', type: 'function', default: null },
2113
+ { name: 'onRefreshAiSuggestions', type: 'function', default: null },
2114
+ ],
2115
+ labels: {
2116
+ variant: { default: '默认文本', code: '代码 / Prompt' },
2117
+ minRows: { 1: '1 行', 2: '2 行', 3: '3 行', 4: '4 行', 5: '5 行' },
2118
+ status: { default: '默认', error: '错误' },
2119
+ resize: { vertical: '纵向拉伸', none: '固定高度' },
2120
+ },
2121
+ _preview: TEXTAREA_PREVIEW,
2122
+ rules: [
2123
+ '【⛔ AI 禁止使用原生 textarea】绝对禁止用原生 `<textarea>` 元素。System Prompt 输入框、模型 ID/代码输入框、备注框、描述栏、JSON 编辑框等一切多行或可能较长的文本录入场景必须使用 TFDS TextArea 组件,否则会出现黑色边框、无法对齐尺寸 token',
2124
+ '【代码 / Prompt 输入框类型】当输入内容是代码、JSON、Markdown、System Prompt、Agent 指令、规则配置、DSL、SQL、正则等需要逐行阅读/定位的文本时,必须使用 `<TextArea variant="code" />`。code 类型左侧固定显示小号行号、正文使用等宽字体、禁用拼写检查,更接近专业代码编辑器的阅读习惯。外框圆角必须与默认 TextArea 完全一致;行号区不加底色、不加分割线,整体保持白底,数字颜色使用比 text-tertiary 更浅两个色阶的 blueGrey-400。',
2125
+ '【code 字体 token 强约束】`variant="code"` 的正文文字与左侧行号必须统一使用 **14px / 20px**,并从组件内部 token 固定输出;AI 生成代码时严禁在外部再传 `text-base` / `text-lg` / `text-[15px]` / `leading-6` / `leading-7` / `style={{ fontSize: ... }}` / `style={{ lineHeight: ... }}` 覆写代码输入框字号与行高。代码输入框看起来必须与平台其它 MD 正文字体保持一致,只是换成等宽字与带行号。',
2126
+ '【模型参数识别边界】模型参数不是一律用同一个组件:Temperature / Top-P / Max Tokens / 阈值 / 权重 / 重试次数等**纯数值参数**必须用 `<InputNumber />`;System Prompt、模型高级参数 JSON、请求参数、代码参数、tools/function schema、response_format、stop sequences、headers/body、SQL/DSL/正则等**结构化文本或代码型参数**必须用 `<TextArea variant="code" />`。',
2127
+ '【code 类型高度建议】普通代码片段用 `variant="code" minRows={5}`;卡片主编辑区用 `variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full"`。Prompt/代码内容超长时必须在 TextArea 内部滚动,不允许让页面整体滚动或让卡片标题被滚走。',
2128
+ '【高度场景化定制·必读】TextArea 高度通过 `minRows`(1~5)控制,不同容器场景应选择合适的初始行高:① 卡片/面板内主要内容区(System Prompt、模型高级参数 JSON、请求参数、代码参数、工单描述)→ `minRows={4}` 或 `minRows={5}`,给用户充足输入空间;② 表单内备注/补充说明字段 → `minRows={3}`(默认值);③ 模型 ID/URL 输入、单行但内容可能较长的字段 → `minRows={1}`,与 Input md 同高(36px),随内容自动增高;④ 操作描述/回复文本等中等内容 → `minRows={2}`。AI 生成页面时必须根据容器语义选择 minRows,禁止所有场景都用默认 `minRows={3}`',
2129
+ '【卡片主编辑区高度·图同款强约束】当 TextArea 是白卡/面板里的**主要内容区**(如 System Prompt、模型高级参数 JSON、请求参数、代码参数、长文本编辑、JSON 编辑、工单详情、规则描述),且标题栏需要固定不滚时,外层必须是 `flex flex-col min-h-0`,标题 `shrink-0`,正文容器 `flex-1 min-h-0 overflow-hidden`,代码/Prompt 类 TextArea 必须写 `variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full"`;普通长文本主编辑区写 `fillHeight resize="none" className="flex-1 min-h-0 w-full"`。此时组件填满正文剩余高度,文字超长只在 TextArea 内部滚动,不能让白卡正文空着一大片,也不能把标题滚走。',
2130
+ '【fillHeight 使用边界】`fillHeight` 只用于“面板主要编辑器/主要长内容控件”。普通表单备注、弹窗字段、筛选项说明仍按 `minRows` 自然高度,不要默认全页面到处写 `fillHeight`。判断口诀:这个 TextArea 是否承担整张卡片的主要工作区?是 → fillHeight;只是表单里的一个字段 → minRows。',
2131
+ '【定位】Input 的多行变体,用于备注、描述、工单反馈等需要换行的录入;单行检索词、名称等仍用 Input',
2132
+ '【视觉对齐】外框圆角 8px、默认白底灰边、悬浮边框加深、聚焦 1px 主色描边,与 Input 一致',
2133
+ '【错态】status="error" 为浅红底,聚焦时红色描边;已设 maxLength 且超出时计数变红(可与错态并存)',
2134
+ '【禁用】浅灰底 + 透明度,与 Input 禁用态一致',
2135
+ '【规格】仅 MD 一种尺寸,不提供 size;与 Input size="md" 的字号与内边距一致。默认正文 14px / 20px;`variant="code"` 也必须保持 14px / 20px,不得自行放大。',
2136
+ '【默认高度·minRows】可选 1~5,默认 3;行高同 leading-5(20px)。minRows=1 时外框 36px:上下各 8px + 一行正文,与 Input md 同高;minRows≥2 时最小高度 = 12px + 行数×20px(上下各 6px)。仍可随换行自动增高',
2137
+ '【自动增高】resize="vertical" 时高度随正文 scrollHeight 变化;禁止原生拖拽(resize-none)。resize="none" 时高度锁定在 minRows 对应高度,超出部分框内滚动',
2138
+ '【撑满父容器】fillHeight=true 时,TextArea 外层、字段框和正文区都会进入 `flex-1 min-h-0` 撑满模式,并强制正文区内部滚动;父容器若缺 `flex-1 min-h-0 overflow-hidden`,撑满不会生效。',
2139
+ '【字数】maxLength 可选,不传则不限制字数、不施加原生 maxLength;showCount=true 且无 maxLength 时仅展示已输入字数;有 maxLength 时展示「当前/上限」,enforceMaxLength=false 可超出仅标红',
2140
+ '【宽度】默认随父容器满宽;筛选栏/工具条等并排场景才使用固定宽(240/200/160)。主编辑区配合 fillHeight 可撑满高度,宽度仍默认满宽',
2141
+ '【AI推荐】支持单条 aiSuggestion 和多条 aiSuggestions;推荐区固定展示在 TextArea 下方,并支持真实点击采纳与刷新推荐。未接业务服务时,组件内部也必须能够轮换下一组草稿建议,不能只展示静态文案',
2142
+ '【AI推荐采纳】点击推荐草稿后,TextArea 默认应真实写入推荐内容,并触发 onAdoptSuggestion(suggestion);若传入 onRefreshAiSuggestions,应在本地轮换草稿之外继续对外通知',
2143
+ '【详情预览·侧栏默认】与 _preview.panelDefaults 一致:内容「空」、超长计数「关」、可用性「默认可用」、默认高度「3 行」、校验态「默认」、高度适配「纵向拉伸」',
2144
+ '【Form】Form 的 type="textarea" 必须渲染基组件 TextArea;默认行为与 TextArea 一致:minRows=3、resize 纵向增高(仅 item.resize==="none" 时固定高度)、status 由 error 推导;可选 maxLength;不设 maxLength 时默认不展示计数(显式 showCount: true 可仅展示已输入字数)。当表单字段是 System Prompt、模型高级参数 JSON、代码参数等结构化文本时,item 必须传 `variant: "code"`。',
2145
+ ],
2146
+ examples: [
2147
+ { label: '默认(表单备注)', code: '<TextArea placeholder="请输入内容" />' },
2148
+ { label: '代码 / Prompt 输入框(带行号)', code: '<TextArea variant="code" minRows={5} placeholder="请输入 System Prompt、JSON 或代码" />' },
2149
+ { label: '模型 ID / 代码输入(单行起)', code: '<TextArea minRows={1} placeholder="输入模型 ID,如 doubao-seed-1.8-pro-251215" />' },
2150
+ { label: 'System Prompt 输入框', code: '<TextArea variant="code" minRows={5} placeholder="你是一名专业的客服助手,请根据知识库内容回答用户问题。" />' },
2151
+ { label: '模型高级参数 JSON', code: '<TextArea variant="code" minRows={5} placeholder="{\\n \\"temperature\\": 0.7,\\n \\"response_format\\": { \\"type\\": \\"json_object\\" }\\n}" />' },
2152
+ { label: '代码参数 / 请求参数', code: '<TextArea variant="code" minRows={5} placeholder="请输入 headers、body、tools schema 或代码参数" />' },
2153
+ { label: '卡片主编辑区撑满高度', code: '<div className="flex-1 min-h-0 flex flex-col overflow-hidden"><TextArea variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full" placeholder="请输入 System Prompt" /></div>' },
2154
+ { label: '代码 / Prompt 主编辑区撑满高度', code: '<div className="flex-1 min-h-0 flex flex-col overflow-hidden"><TextArea variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full" placeholder="请输入 System Prompt" /></div>' },
2155
+ { label: '操作描述 / 回复文本(2行起)', code: '<TextArea minRows={2} placeholder="请输入回复内容" />' },
2156
+ { label: '仅计数', code: '<TextArea showCount placeholder="无字数上限" />' },
2157
+ { label: '带字数', code: '<TextArea showCount maxLength={140} defaultValue="" placeholder="请输入" />' },
2158
+ { label: '超长计数', code: '<TextArea showCount maxLength={140} enforceMaxLength={false} placeholder="请输入" />' },
2159
+ { label: '错误状态', code: '<TextArea status="error" defaultValue="格式或长度不符合要求" />' },
2160
+ { label: '禁用', code: '<TextArea disabled defaultValue="不可编辑" />' },
2161
+ { label: '固定高度', code: '<TextArea resize="none" placeholder="请输入" />' },
2162
+ { label: '❌ Bad(原生 textarea,黑边+无 token)', code: '/* 禁止!原生 textarea 黑色边框、无尺寸 token、无错态 */\n<textarea rows={5} placeholder="请输入" className="border w-full p-2" />' },
2163
+ { label: '✅ Good(System Prompt 用 code 类型)', code: '<TextArea variant="code" minRows={5} placeholder="你是一名专业的客服助手…" />' },
2164
+ { label: '❌ Bad(卡片主编辑区只写 minRows,无法撑满卡片)', code: '/* 错误:TextArea 只占 5 行,下方大片空白;长文本不会在组件内充分滚动 */\n<TextArea minRows={5} placeholder="System Prompt" />' },
2165
+ { label: '✅ Good(卡片主编辑区 fillHeight,标题固定,正文内滚)', code: '<section className="flex-1 min-h-0 flex flex-col overflow-hidden"><header className="shrink-0"><FormTitle variant="level-2" title="System Prompt" /></header><div className="flex-1 min-h-0 overflow-hidden"><TextArea variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full" /></div></section>' },
2166
+ { label: '❌ Bad(System Prompt 用普通文本框,缺少行号不便定位)', code: '/* Prompt / JSON / 代码类内容需要行号辅助定位,不要只用普通 TextArea */\n<TextArea minRows={5} placeholder="请输入 System Prompt" />' },
2167
+ { label: '✅ Good(System Prompt 用 code 类型,带行号)', code: '<TextArea variant="code" minRows={5} placeholder="请输入 System Prompt" />' },
2168
+ { label: '✅ Good(Form 字段里的 Prompt 也透传 code 类型)', code: '<Form items={[{ id: "systemPrompt", label: "System Prompt", type: "textarea", variant: "code", minRows: 5 }]} />' },
2169
+ { label: '❌ Bad(模型 ID 用 Input,内容超长被截断)', code: '/* 禁止!模型 ID 可能很长,应该用 TextArea minRows=1 自动增高 */\n<Input placeholder="输入模型 ID" />' },
2170
+ { label: '✅ Good(模型 ID 用 minRows=1)', code: '<TextArea minRows={1} placeholder="输入模型 ID,如 doubao-seed-1.8-pro-251215" />' },
2171
+ ],
2172
+ keywords: [
2173
+ 'TextArea', 'textarea', 'text-area', '多行输入', '多行文本',
2174
+ '备注', '描述', '说明', '工单描述', '反馈', '回复内容',
2175
+ 'System Prompt', 'system prompt', 'Prompt', 'prompt 输入',
2176
+ '模型 ID', '模型代码', '模型ID输入', 'model ID', 'JSON 编辑', 'JSON 输入',
2177
+ '模型高级参数', '模型参数 JSON', '模型参数配置', 'model parameters', 'advanced parameters',
2178
+ '代码参数', '参数代码', '请求参数', '请求体', 'request body', 'headers', 'body',
2179
+ 'tools schema', 'function schema', 'function calling', 'response_format', 'stop sequences',
2180
+ '代码输入', '代码编辑器', 'code editor', 'code textarea', 'line number', 'line numbers',
2181
+ '行号', '带行号', '小数字', '每行数字', 'Prompt 编辑器', 'System Prompt 编辑器',
2182
+ 'minRows', 'fillHeight', '撑满高度', '卡片主编辑区', '面板主编辑区', '内部滚动',
2183
+ '1 行', '2 行', '3 行', '4 行', '5 行',
2184
+ // 伪手搓 / 错误信号词
2185
+ '<textarea>', 'native textarea', 'textarea rows', 'rows={5}', 'rows=5',
2186
+ 'border w-full p-2', '黑色边框 textarea', '原生多行输入',
2187
+ ],
2188
+ },
2189
+ {
2190
+ id: 'input-number',
2191
+ name: 'InputNumber',
2192
+ element: 'input',
2193
+ category: 'basic',
2194
+ description: '数字输入框,复用 Input 触发器视觉,支持最小值、最大值、步长、小数精度,以及外置/内置步进按钮。',
2195
+ componentFile: './components/InputNumber.jsx',
2196
+ tokensFile: './components/InputNumber.tokens.js',
2197
+ props: [
2198
+ { name: 'status', type: 'enum', options: ['default', 'error'], default: 'default' },
2199
+ { name: 'disabled', type: 'boolean', default: false },
2200
+ { name: 'innerButtons', type: 'boolean', default: false },
2201
+ { name: 'controls', type: 'boolean', default: true },
2202
+ { name: 'min', type: 'number' },
2203
+ { name: 'max', type: 'number' },
2204
+ { name: 'step', type: 'number', default: 1 },
2205
+ { name: 'precision', type: 'number' },
2206
+ { name: 'placeholder', type: 'string', default: '请输入' },
2207
+ { name: 'value', type: 'number | string' },
2208
+ { name: 'defaultValue', type: 'number | string' },
2209
+ { name: 'onChange', type: 'function', default: null },
2210
+ { name: 'onStep', type: 'function', default: null },
2211
+ ],
2212
+ labels: {
2213
+ status: { default: '默认', error: '错误' },
2214
+ },
2215
+ _preview: INPUT_NUMBER_PREVIEW,
2216
+ rules: [
2217
+ '【⛔ AI 强制使用场景】所有数字类参数字段必须使用 InputNumber,禁止用 `<input type="number">` 或 Input 组件代替。典型场景:Temperature(0~2 精度 0.01)、Top-P(0~1 精度 0.01)、Max Tokens(整数步进)、学习率、权重、阈值等一切数值配置项。原生 input 会产生黑色边框并丢失步进、范围约束、精度控制能力',
2218
+ '【组件定位】InputNumber 是数字输入原子组件,输入框视觉必须复用 Input:默认宽度 300px、MD 高 36px、圆角 8px、白底灰边框、聚焦 1px 绿色描边',
2219
+ '【按钮布局】默认 outer 外置步进按钮:位于输入框右侧,宽 16px、高 36px,与输入框间距 4px,上下两个箭头图标均为 8×8px',
2220
+ '【内置按钮】innerButtons=true 时步进按钮内嵌在输入框右侧,默认隐藏,仅在容器 hover 或 focus-within 时显示,输入内容需预留右侧空间',
2221
+ '【禁用态】disabled 时输入框与步进按钮同步进入浅灰底、禁用文字和 0.6 透明度;按钮不可点击',
2222
+ '【交互】点击上/下按钮按 step 增减,受 min/max 约束;precision 存在时按指定小数位输出',
2223
+ '【输入】使用 text 输入隐藏浏览器原生 number spinner,保留 role="spinbutton" 与 aria-valuemin/aria-valuemax/aria-valuenow',
2224
+ '【回调】onChange(nextValue, meta) 在输入和步进时触发;onStep(nextValue, meta) 仅在步进按钮触发',
2225
+ '【Form 复用】Form 的 type="input-number" 必须完整引用 InputNumber,不允许手写数字后缀或浏览器原生 number 控件样式',
2226
+ '【尺寸】仅保留 MD 一种尺寸,高度固定 36px;与 Input 保持一致',
2227
+ ],
2228
+ examples: [
2229
+ { label: '默认', code: '<InputNumber placeholder="请输入" />' },
2230
+ { label: '有值', code: '<InputNumber defaultValue={12} />' },
2231
+ { label: '范围限制', code: '<InputNumber min={0} max={99} step={1} defaultValue={12} />' },
2232
+ { label: '内置按钮', code: '<InputNumber innerButtons defaultValue={12} />' },
2233
+ { label: '错误态', code: '<InputNumber status="error" defaultValue={120} />' },
2234
+ { label: '禁用', code: '<InputNumber disabled defaultValue={12} />' },
2235
+ { label: '❌ Bad(参数面板用 input type=number)', code: '/* 禁止!原生 number 输入浏览器自带 spinner、黑边、无 token */\n<input type="number" min={0} max={1} step={0.01} placeholder="Top-P" />' },
2236
+ { label: '✅ Good(用 InputNumber)', code: '<InputNumber min={0} max={1} step={0.01} precision={2} placeholder="Top-P" />' },
2237
+ { label: '❌ Bad(用 Input 装数字,丢失步进/范围)', code: '/* 禁止!数字字段必须用 InputNumber,Input 不能锁定数字格式 */\n<Input placeholder="Temperature" />' },
2238
+ { label: '✅ Good(Temperature 参数)', code: '<InputNumber min={0} max={2} step={0.01} precision={2} defaultValue={0.7} />' },
2239
+ ],
2240
+ keywords: [
2241
+ 'InputNumber', 'input-number', '数字输入', '数字输入框', '数值输入', 'number input',
2242
+ '步进', '步进按钮', 'spinner', 'step', 'min', 'max', 'precision', '精度', '小数',
2243
+ // 业务高频场景词
2244
+ 'Temperature', 'temperature', '温度', 'Top-P', 'top-p', 'topP', 'Max Tokens', 'max-tokens',
2245
+ '学习率', '权重', '阈值', '参数', '模型参数', '参数面板', '参数配置',
2246
+ '0~1', '0~2', '0-1', '0-2', '比例', '百分比',
2247
+ // 伪手搓 / 错误信号词
2248
+ '<input type="number">', 'input type=number', 'type="number"', 'native number input',
2249
+ '原生 number', '浏览器 spinner',
2250
+ ],
2251
+ },
2252
+ {
2253
+ id: 'select',
2254
+ name: 'Select',
2255
+ element: 'div',
2256
+ category: 'basic',
2257
+ description:
2258
+ '从预定义 options 中选值:节省纵向空间、选项可较多、与 Input 视觉同构。单选枚举默认 mode=default;同一字段多选且以标签形式回显用 mode=tag。少量互斥项且需一眼比较布局 → 优先 Radio。',
2259
+ componentFile: './components/Select.jsx',
2260
+ tokensFile: './components/Select.tokens.js',
2261
+ props: [
2262
+ { name: 'status', type: 'enum', options: ['default', 'error'], default: 'default' },
2263
+ { name: 'disabled', type: 'boolean', default: false },
2264
+ { name: 'allowClear', type: 'boolean', default: false },
2265
+ { name: 'mode', type: 'enum', options: ['default', 'tag'], default: 'default' },
2266
+ { name: 'aiSuggestion', type: 'string', default: null },
2267
+ { name: 'aiSuggestions', type: 'array', default: null },
2268
+ { name: 'placeholder', type: 'string', default: '请选择' },
2269
+ { name: 'options', type: 'array', default: null },
2270
+ { name: 'value', type: 'string | array' },
2271
+ { name: 'defaultValue', type: 'string | array', default: null },
2272
+ { name: 'onChange', type: 'function', default: null },
2273
+ { name: 'onClear', type: 'function', default: null },
2274
+ { name: 'onAdoptSuggestion', type: 'function', default: null },
2275
+ { name: 'onRefreshAiSuggestions', type: 'function', default: null },
2276
+ ],
2277
+ labels: {
2278
+ status: { default: '默认', error: '错误' },
2279
+ mode: { default: '默认', tag: '标签' },
2280
+ },
2281
+ _preview: SELECT_PREVIEW,
2282
+ rules: [
2283
+ '【⛔ AI 禁止使用原生下拉】绝对禁止用原生 `<select>` 元素或手写 div+ul 模拟下拉菜单。原生 select 边框为黑色且不可定制,与设计系统完全不符。所有枚举选择场景(模型列表、Reasoning Effort、状态筛选等)必须使用 TFDS Select 组件',
2284
+ '【选型·与 Radio】选项 ≤5 且表单内需并排展示全部文案 → Radio;选项多、需占位窄、或选项会动态增长 → Select',
2285
+ '【选型·与 Checkbox】同一字段要选多个离散值 → CheckboxGroup 或 Select mode=tag;仅单个开/关语义 → Switch 或单个 Checkbox',
2286
+ '【数据】options 为 { value, label, disabled? };value 与 option.value 用字符串比较',
2287
+ '【TagSelect】mode="tag" 属于 Select 的标签选择分类,不新增独立基础组件;触发器内已选项、+N 折叠项、下拉项都必须复用 Tag 组件',
2288
+ '【TagSelect 规格】Tag 默认颜色与尺寸跟 TagInput 保持一致,不单独暴露颜色或尺寸配置;如需特殊颜色可在单个 option.variant 上覆盖',
2289
+ '【TagSelect 交互】mode="tag" 时 value/defaultValue 为数组;点击下拉标签多选/取消,触发器内标签可关闭,宽度不足时折叠为 +N 并用白底 Tooltip 展示隐藏标签',
2290
+ '【AI推荐】仅 mode="default" 支持 `aiSuggestion` / `aiSuggestions`;推荐区展示在 Select 触发器下方,视觉样式、关闭逻辑、hover 展开和刷新交互与 Input 的 AI 推荐保持一致',
2291
+ '【AI推荐默认触发】当需求、字段说明或页面描述中出现“AI推荐”“智能推荐”“推荐回复”“刷新推荐”“智能建议”“推荐文案”“建议填写”“推荐选项”等语义时,Select 默认应展示 AI 推荐区,而不是只渲染普通下拉态。',
2292
+ '【AI推荐默认形态】命中上述语义后,AI 推荐区固定展示在 Select 组件下方,推荐内容应贴合页面场景,并优先给出适合当前任务的推荐选项或推荐分类。',
2293
+ '【AI推荐映射】Select 的 AI 推荐内容必须映射到现有 options;字符串推荐按 option.label 精确匹配,结构化推荐可显式传 { label, value };映射不到 options 的推荐项必须静默过滤,不能生成自由输入值',
2294
+ '【AI推荐误判边界】普通 placeholder、helpText、默认值、静态选项说明不等于 AI 推荐能力;只有明确存在“推荐/建议/AI辅助选择”语义时,才默认展示 Select 的 AI 推荐区。',
2295
+ '【刷新推荐】当存在 AI 推荐时,Select 默认应支持真实刷新;未接业务服务时,组件内部仍需轮换下一组合法 option 建议,不允许点了刷新但内容不变。若传入 onRefreshAiSuggestions,应在本地刷新闭环之外继续向外通知;tooltip 固定为“刷新推荐”,刷新 icon 使用 `ai-fill-3` 渐变 token',
2296
+ '【采纳推荐】点击 AI 推荐后,Select 默认应真实选中对应 option,并触发 onAdoptSuggestion(value, option);受控模式下继续通过 value + onChange 接管最终状态',
2297
+ '【TagSelect 禁止接入】mode="tag" 暂不支持 AI 推荐配置;即使传入 aiSuggestion / aiSuggestions 也必须忽略,不显示推荐区或刷新按钮',
2298
+ '【受控】默认模式使用 value + onChange(value, option);tag 模式使用数组 value + onChange(nextValues, option);非受控用 defaultValue',
2299
+ '【清除】allowClear 时悬浮或展开可出现清除;清除后 onChange(undefined, undefined) 并触发 onClear',
2300
+ '【状态】status 与 Input 一致:error 为浅红底 + 展开时红描边',
2301
+ '【浮层】列表用 fixed + Portal 渲染,避免 overflow 裁切;根据视口上下空间自动翻转并限制 max-height',
2302
+ '【关闭】点击触发器、选项或外部 mousedown(触发器与面板均排除)关闭',
2303
+ '【键盘】Enter/Space 展开或选中高亮项;↑/↓ 移动高亮;Esc 关闭;Tab 失焦关闭',
2304
+ '【无障碍】触发器 role="combobox" aria-expanded;列表 role="listbox" 与 role="option"',
2305
+ '【宽度】触发器为 w-full min-w-0 max-w-full,随父容器撑满;需要固定 300px 示意时在父级包一层 w-[300px](平台 Select 详情预览已包)',
2306
+ '【右侧图标】下拉箭头、清除按钮与自定义 indicator 均固定为 16×16px',
2307
+ '【尺寸】仅保留 MD 一种尺寸,触发器高度固定 36px;与 Input 保持一致',
2308
+ ],
2309
+ examples: [
2310
+ { label: '基础', code: '<Select options={[{ value: "a", label: "选项 A" }, { value: "b", label: "选项 B" }]} placeholder="请选择" />' },
2311
+ { label: '标签选择', code: '<Select mode="tag" defaultValue={["a", "b"]} options={[{ value: "a", label: "标签", variant: "grey" }, { value: "b", label: "标签", variant: "grey" }]} />' },
2312
+ { label: '默认选中', code: '<Select options={[{ value: "a", label: "选项 A" }]} defaultValue="a" />' },
2313
+ { label: '可清除', code: '<Select allowClear options={[{ value: "x", label: "已选" }]} defaultValue="x" />' },
2314
+ { label: 'AI推荐-单条', code: '<Select options={[{ value: "zj", label: "浙江省" }, { value: "js", label: "江苏省" }, { value: "sh", label: "上海市" }]} aiSuggestion="江苏省" onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(value, option) => console.log(value, option)} placeholder="请选择" />' },
2315
+ { label: 'AI推荐-多条', code: '<Select options={[{ value: "zj", label: "浙江省" }, { value: "js", label: "江苏省" }, { value: "sh", label: "上海市" }]} aiSuggestions={["江苏省", "上海市", "浙江省"]} onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(value, option) => console.log(value, option)} placeholder="请选择" />' },
2316
+ { label: '推荐选项-分类选择', code: '<Select options={[{ value: "refund", label: "退款问题" }, { value: "invoice", label: "发票问题" }, { value: "account", label: "账号问题" }]} aiSuggestions={["退款问题", "账号问题", "发票问题"]} onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(value, option) => console.log(value, option)} placeholder="请选择问题分类" />' },
2317
+ { label: '智能建议-处理状态', code: '<Select options={[{ value: "pending", label: "待处理" }, { value: "reviewing", label: "核实中" }, { value: "resolved", label: "已解决" }]} aiSuggestion="核实中" onRefreshAiSuggestions={() => {}} onAdoptSuggestion={(value, option) => console.log(value, option)} placeholder="请选择处理状态" />' },
2318
+ { label: '错误态', code: '<Select status="error" options={[{ value: "1", label: "请选择" }]} placeholder="必填" />' },
2319
+ { label: '禁用', code: '<Select disabled options={[{ value: "1", label: "不可点" }]} defaultValue="1" />' },
2320
+ { label: '受控', code: '<Select value={v} onChange={(val, opt) => setV(val)} options={[{ value: "1", label: "一" }]} />' },
2321
+ { label: '❌ Bad(原生 select,黑色边框)', code: '/* 禁止!原生 select 黑边、字号错位、不可定制 */\n<select className="border rounded px-2"><option>doubao</option><option>gpt-4o</option></select>' },
2322
+ { label: '✅ Good(Select + options)', code: '<Select options={[{ value: "doubao", label: "doubao-seed-1.8" }, { value: "gpt-4o", label: "GPT-4o" }]} placeholder="选择模型" />' },
2323
+ { label: '❌ Bad(手写 div+ul 模拟下拉)', code: '/* 禁止!手写 div 下拉缺少键盘、Portal、aria,黑边/层级错乱 */\n<div className="border rounded"><ul><li>选项 A</li><li>选项 B</li></ul></div>' },
2324
+ { label: '✅ Good(多选用 mode="tag")', code: '<Select mode="tag" defaultValue={["a", "b"]} options={[{ value: "a", label: "标签 A" }, { value: "b", label: "标签 B" }]} />' },
2325
+ ],
2326
+ keywords: [
2327
+ 'Select', 'select', '下拉框', '下拉选择', '下拉菜单', '选择器', 'dropdown', 'combobox',
2328
+ '枚举', '枚举选择', '单选下拉', '多选下拉', 'tag select', '标签选择',
2329
+ '模型选择', 'Reasoning Effort', 'reasoning-effort', 'effort 选择',
2330
+ '状态筛选', '类型筛选', '分类筛选', '过滤器', 'filter',
2331
+ '城市选择', '部门选择', '角色选择', '账号选择',
2332
+ 'AI 推荐', 'aiSuggestion', 'aiSuggestions', '刷新推荐', '推荐选项',
2333
+ '智能推荐', '推荐回复', '智能建议', '推荐文案', '建议填写', 'AI辅助选择',
2334
+ 'options', 'mode default', 'mode tag',
2335
+ // 伪手搓 / 错误信号词
2336
+ '<select>', 'native select', '<option>', 'select option',
2337
+ '手写下拉', '手写 ul li', 'div ul 下拉', 'border rounded ul',
2338
+ '黑边下拉', '黑色边框下拉',
2339
+ ],
2340
+ },
2341
+ {
2342
+ id: 'radio',
2343
+ name: 'Radio',
2344
+ element: 'div',
2345
+ category: 'basic',
2346
+ description:
2347
+ '互斥单选:同一组内只能选一个值。适合选项少、需要并排或纵列展示全部标签的表单场景。多选请用 CheckboxGroup;布尔即时开关请用 Switch;选项很多请用 Select。',
2348
+ componentFile: './components/Radio.jsx',
2349
+ tokensFile: './components/Radio.tokens.js',
2350
+ props: [
2351
+ { name: 'variant', type: 'enum', options: ['brand', 'black'], default: 'brand' },
2352
+ { name: 'layout', type: 'enum', options: ['vertical', 'horizontal'], default: 'vertical' },
2353
+ { name: 'disabled', type: 'boolean', default: false },
2354
+ { name: 'value', type: 'string' },
2355
+ { name: 'defaultValue', type: 'string', default: null },
2356
+ { name: 'onChange', type: 'function', default: null },
2357
+ { name: 'name', type: 'string', default: null },
2358
+ ],
2359
+ labels: {
2360
+ variant: { brand: '品牌绿', black: '主色黑' },
2361
+ layout: { vertical: '纵向', horizontal: '横向' },
2362
+ },
2363
+ _preview: RADIO_PREVIEW,
2364
+ rules: [
2365
+ '【选型·与 Checkbox】Radio 组值最多一个;Checkbox 组值零个或多个。同意条款「勾选」用 Checkbox 而非 Radio',
2366
+ '【选型·与 Switch】都是二值时:若语义是「设置项立即生效」→ Switch;若语义是「表单里选 A 方案或 B 方案」→ Radio',
2367
+ '【结构】RadioGroup 包裹多个 Radio;Radio 必须设置 value,文案放在子节点',
2368
+ '【受控】RadioGroup 使用 value + onChange(nextValue, event);非受控用 defaultValue',
2369
+ '【禁用】RadioGroup disabled 禁用整组;Radio 可单独 disabled',
2370
+ '【变体】brand 为选中实心主色 + 白点;black 为深灰主色 + 白点',
2371
+ '【布局】vertical 组间距 12px;horizontal 组间距 16px(对齐 Semi radioGroup)',
2372
+ '【样式来源】HIUIpackage/es/components/radio-group 为 Semi 封装,视觉以 umd/view.css 为准',
2373
+ '【表单】未传 name 时自动用稳定 id,同一组内各 input 共享 name',
2374
+ '【无障碍】使用原生 type="radio" 与 radiogroup 容器;聚焦可见环在指示圈上',
2375
+ '【与 Select】互斥单选在选项 ≤5 且需平铺展示时优先 Radio;选项很多用 Select',
2376
+ ],
2377
+ examples: [
2378
+ { label: '基础', code: '<RadioGroup defaultValue="b" onChange={(v) => {}}><Radio value="a">选项 A</Radio><Radio value="b">选项 B</Radio></RadioGroup>' },
2379
+ { label: '横向', code: '<RadioGroup layout="horizontal" defaultValue="a"><Radio value="a">月付</Radio><Radio value="b">年付</Radio></RadioGroup>' },
2380
+ { label: '主色黑', code: '<RadioGroup variant="black" defaultValue="1"><Radio value="1">是</Radio><Radio value="0">否</Radio></RadioGroup>' },
2381
+ { label: '整组禁用', code: '<RadioGroup disabled defaultValue="a"><Radio value="a">A</Radio><Radio value="b">B</Radio></RadioGroup>' },
2382
+ { label: '单项禁用', code: '<RadioGroup><Radio value="a">可选</Radio><Radio value="b" disabled>不可选</Radio></RadioGroup>' },
2383
+ { label: '受控', code: '<RadioGroup value={v} onChange={setV}><Radio value="a">A</Radio><Radio value="b">B</Radio></RadioGroup>' },
2384
+ { label: '❌ Bad(用 input type=radio)', code: '/* 禁止!原生 radio 视觉与 token 不一致 */\n<><label><input type="radio" name="x" value="a" />A</label><label><input type="radio" name="x" value="b" />B</label></>' },
2385
+ { label: '✅ Good(用 RadioGroup)', code: '<RadioGroup defaultValue="a"><Radio value="a">A</Radio><Radio value="b">B</Radio></RadioGroup>' },
2386
+ ],
2387
+ keywords: [
2388
+ 'Radio', 'radio', 'RadioGroup', '单选', '互斥单选', '单选组', '单选按钮', 'radio button',
2389
+ '互斥选项', '二选一', '多选一',
2390
+ '月付 年付', '是 否', '布尔单选', 'A 方案 B 方案',
2391
+ // 错误信号词
2392
+ '<input type="radio">', 'input type=radio', 'native radio',
2393
+ ],
2394
+ },
2395
+ {
2396
+ id: 'checkbox',
2397
+ name: 'Checkbox',
2398
+ element: 'div',
2399
+ category: 'basic',
2400
+ description:
2401
+ '多选或二元勾选(含「全选/半选」树形父级)。用于权限、标签、筛选条件、同意协议等。互斥单选用 Radio;单一功能开闭且立即生效优先 Switch。',
2402
+ componentFile: './components/Checkbox.jsx',
2403
+ tokensFile: './components/Checkbox.tokens.js',
2404
+ props: [
2405
+ { name: 'variant', type: 'enum', options: ['brand', 'black'], default: 'brand' },
2406
+ { name: 'size', type: 'enum', options: ['sm', 'md'], default: 'md' },
2407
+ { name: 'layout', type: 'enum', options: ['vertical', 'horizontal'], default: 'vertical' },
2408
+ { name: 'disabled', type: 'boolean', default: false },
2409
+ { name: 'value', type: 'array' },
2410
+ { name: 'defaultValue', type: 'array', default: null },
2411
+ { name: 'onChange', type: 'function', default: null },
2412
+ { name: 'checked', type: 'boolean', default: null },
2413
+ { name: 'defaultChecked', type: 'boolean', default: false },
2414
+ { name: 'indeterminate', type: 'boolean', default: false },
2415
+ ],
2416
+ labels: {
2417
+ variant: { brand: '品牌绿', black: '主色黑' },
2418
+ size: { sm: 'SM', md: 'MD' },
2419
+ layout: { vertical: '纵向', horizontal: '横向' },
2420
+ },
2421
+ _preview: CHECKBOX_PREVIEW,
2422
+ rules: [
2423
+ '【选型·与 Select 多选】既要多个值又要下拉节省空间 → Select mode=tag;要在表单中一眼看到全部可选项 → CheckboxGroup',
2424
+ '【选型·与 Switch】列表「每行一个功能开关」常用 Switch;「一组里勾选多项能力」用 CheckboxGroup',
2425
+ '【结构】CheckboxGroup 包裹多个 Checkbox;组内 Checkbox 必须设置 value,文案放在子节点',
2426
+ '【受控】CheckboxGroup 使用 value(选中 id 数组)+ onChange(nextValues, event);非受控用 defaultValue',
2427
+ '【单独使用】Checkbox 支持 checked / defaultChecked、indeterminate(半选,树形父级)、onChange(event)',
2428
+ '【禁用】CheckboxGroup disabled 禁用整组;Checkbox 可单独 disabled',
2429
+ '【变体】brand 为选中填充主色 + 白勾/横杠;black 为深灰主色 + 白勾/横杠',
2430
+ '【布局】vertical 组间距 12px;horizontal 组间距 16px(对齐 Semi checkboxGroup)',
2431
+ '【样式来源】HIUIpackage 为 Semi 封装,视觉以 umd/view.css 中 `.semi-checkbox` 为准',
2432
+ '【无障碍】使用原生 type="checkbox";半选时 aria-checked="mixed";组容器 role="group"',
2433
+ '【与 Radio】多选并列展示用 CheckboxGroup;互斥单选使用 RadioGroup',
2434
+ ],
2435
+ examples: [
2436
+ { label: '基础', code: '<CheckboxGroup defaultValue={["b"]} onChange={() => {}}><Checkbox value="a">选项 A</Checkbox><Checkbox value="b">选项 B</Checkbox></CheckboxGroup>' },
2437
+ { label: '横向', code: '<CheckboxGroup layout="horizontal" defaultValue={["a"]}><Checkbox value="a">月付</Checkbox><Checkbox value="b">年付</Checkbox></CheckboxGroup>' },
2438
+ { label: '主色黑', code: '<CheckboxGroup variant="black"><Checkbox value="1">是</Checkbox><Checkbox value="0">否</Checkbox></CheckboxGroup>' },
2439
+ { label: '小尺寸', code: '<CheckboxGroup size="sm"><Checkbox value="x">X</Checkbox><Checkbox value="y">Y</Checkbox></CheckboxGroup>' },
2440
+ { label: '整组禁用', code: '<CheckboxGroup disabled defaultValue={["a"]}><Checkbox value="a">A</Checkbox><Checkbox value="b">B</Checkbox></CheckboxGroup>' },
2441
+ { label: '单项禁用', code: '<CheckboxGroup><Checkbox value="a">可选</Checkbox><Checkbox value="b" disabled>不可选</Checkbox></CheckboxGroup>' },
2442
+ { label: '半选', code: '<Checkbox indeterminate>部分子项已选</Checkbox>' },
2443
+ { label: '受控', code: '<CheckboxGroup value={ids} onChange={setIds}><Checkbox value="a">A</Checkbox><Checkbox value="b">B</Checkbox></CheckboxGroup>' },
2444
+ { label: '❌ Bad(用 input type=checkbox)', code: '/* 禁止!原生 checkbox 与设计 token 不一致 */\n<label><input type="checkbox" />同意条款</label>' },
2445
+ { label: '✅ Good(用 Checkbox)', code: '<Checkbox defaultChecked>同意条款</Checkbox>' },
2446
+ ],
2447
+ keywords: [
2448
+ 'Checkbox', 'checkbox', 'CheckboxGroup', '多选', '复选框', '勾选', 'check box',
2449
+ '权限多选', '标签多选', '同意条款', '协议勾选',
2450
+ 'indeterminate', '半选', '父级半选', '树形父级',
2451
+ // 错误信号词
2452
+ '<input type="checkbox">', 'input type=checkbox', 'native checkbox',
2453
+ ],
2454
+ },
2455
+ {
2456
+ id: 'switch',
2457
+ name: 'Switch',
2458
+ element: 'button',
2459
+ category: 'basic',
2460
+ description: '开关组件,两种状态间即时切换,支持品牌绿/主色黑两种视觉变体,尺寸固定为 MD(36px 体系)。',
2461
+ componentFile: './components/Switch.jsx',
2462
+ tokensFile: './components/Switch.tokens.js',
2463
+ props: [
2464
+ { name: 'variant', type: 'enum', options: ['brand', 'black'], default: 'brand' },
2465
+ { name: 'checked', type: 'boolean', default: null },
2466
+ { name: 'defaultChecked', type: 'boolean', default: false },
2467
+ { name: 'onChange', type: 'function', default: null },
2468
+ { name: 'disabled', type: 'boolean', default: false },
2469
+ ],
2470
+ labels: {
2471
+ variant: { brand: '品牌绿', black: '主色黑' },
2472
+ },
2473
+ _preview: SWITCH_PREVIEW,
2474
+ rules: [
2475
+ '【选型·vs Checkbox】Switch 代表「立即生效的布尔开关」(设置项切换后无需提交即生效);需要用户确认后再提交的布尔字段(如同意条款、勾选多项)→ Checkbox',
2476
+ '【选型·vs Radio】都是二值时:语义是「设置项打开/关闭」→ Switch;语义是「表单里选 A 方案还是 B 方案」→ Radio',
2477
+ '【场景】设置页中的功能开关(开启推送通知、自动续费、隐私模式);表格行内的即时状态切换(启用/停用账号);配置面板中的批量选项',
2478
+ '【禁忌】不要在需要二次确认的危险操作上使用 Switch(如"删除所有数据"),应先 Switch 触发再弹 Modal 确认;不要用 Switch 承载超过 2 个值的枚举选择(→ Radio / Select)',
2479
+ '【变体】brand(绿色轨道)用于需要强调当前激活状态的开关;black(深灰轨道)用于中性场景如列表行、设置面板,视觉更内敛',
2480
+ '【尺寸】仅保留 MD 一种尺寸,与 Input 的 36px 高度体系保持协调',
2481
+ '【受控】使用 checked + onChange(val, event);非受控用 defaultChecked;Form 内必须受控',
2482
+ '【组合】配 Form 使用时包在 Form.Item 里,label 由 Form.Item 提供;单独行内使用时必须添加可见文案或 aria-label,否则屏幕阅读器无法理解开关含义',
2483
+ '【无障碍】已设置 role="switch"、aria-checked;单独使用时必须补充 aria-label 或紧邻可见文案',
2484
+ ],
2485
+ examples: [
2486
+ { label: '默认(非受控关)', code: '<Switch />' },
2487
+ { label: '默认开启', code: '<Switch defaultChecked />' },
2488
+ { label: '主色黑', code: '<Switch variant="black" defaultChecked />' },
2489
+ { label: '禁用', code: '<Switch disabled defaultChecked />' },
2490
+ { label: '受控', code: '<Switch checked={on} onChange={(v) => setOn(v)} />' },
2491
+ { label: '❌ Bad(用 Checkbox 当立即生效开关)', code: '/* 禁止!立即生效的设置项布尔开关用 Switch */\n<Checkbox>开启自动续费</Checkbox>' },
2492
+ { label: '✅ Good(用 Switch)', code: '<Switch defaultChecked /> 开启自动续费' },
2493
+ ],
2494
+ keywords: [
2495
+ 'Switch', 'switch', '开关', '开关组件', 'toggle',
2496
+ '设置开关', '功能开关', '推送通知', '隐私模式', '深色模式',
2497
+ '启用 停用', '开启 关闭', '即时生效', '立即生效',
2498
+ // 错误信号词
2499
+ 'Checkbox 当开关', '手搓 toggle',
2500
+ ],
2501
+ },
2502
+ {
2503
+ id: 'slider',
2504
+ name: 'Slider',
2505
+ element: 'div',
2506
+ category: 'basic',
2507
+ description: '滑动输入条,支持单值和区间双滑块,拖动时显示数值气泡并支持刻度标签。',
2508
+ componentFile: './components/Slider.jsx',
2509
+ tokensFile: './components/Slider.tokens.js',
2510
+ props: [
2511
+ { name: 'min', type: 'number', default: 0 },
2512
+ { name: 'max', type: 'number', default: 100 },
2513
+ { name: 'step', type: 'number', default: 1 },
2514
+ { name: 'value', type: 'array' },
2515
+ { name: 'defaultValue', type: 'array', default: [20, 80] },
2516
+ { name: 'disabled', type: 'boolean', default: false },
2517
+ { name: 'showTooltip', type: 'boolean', default: true },
2518
+ { name: 'marks', type: 'node', default: null },
2519
+ { name: 'onChange', type: 'function', default: null },
2520
+ { name: 'onAfterChange', type: 'function', default: null },
2521
+ ],
2522
+ labels: {},
2523
+ _preview: SLIDER_PREVIEW,
2524
+ rules: [
2525
+ '【选型·vs InputNumber】Slider 适合「用户对精确值要求不高、更关注相对位置」的场景;需要用户输入精确数字或超过 3 位数的数值 → InputNumber;两者可联动:Slider 拖动粗调,右侧 InputNumber 精调',
2526
+ '【选型·vs Radio/Select】枚举离散选项(如"低中高")若只有 3-5 个 → Radio;数值在连续区间内滑动 → Slider',
2527
+ '【场景·单值】音量/亮度调节(0-100)、播放进度、百分比配置、评分输入',
2528
+ '【场景·区间双滑块】价格区间筛选(传数组如 defaultValue={[min, max]})、时间段选择、数值范围过滤',
2529
+ '【禁忌】不要用 Slider 让用户输入精确数字(视觉精度差,误操作率高);数值范围超过 1000 或需要输入小数时不适合单独用 Slider;不要把 Slider 放在宽度小于 120px 的容器里(拖拽不可用)',
2530
+ '【模式判断】defaultValue/value 传数字 → 单值模式;传数组 [min, max] → 区间双滑块模式;组件自动识别,不需要额外配置',
2531
+ '【步长与刻度】step 控制吸附精度(默认 1);marks 对象({值: 标签})显示刻度标记,key 须在 min-max 范围内',
2532
+ '【提示气泡】showTooltip=true 时,数值 tooltip 默认静止隐藏,仅在用户实际拖动滑块过程中实时显示;松手后立即消失。showTooltip=false 时拖动中也不显示。',
2533
+ '【区间保护】双滑块模式下左值始终 ≤ 右值,拖拽到边界时自动限制,不会交叉',
2534
+ '【受控】使用 value + onChange(value);非受控使用 defaultValue;onAfterChange 在拖拽结束后触发,适合防抖请求',
2535
+ '【组合】与 InputNumber 联动时:`<Slider value={v} onChange={setV} /><InputNumber value={v} onChange={setV} />`;两个组件共享同一 state,双向同步',
2536
+ '【无障碍】已设置 role="slider"、aria-valuemin / aria-valuemax / aria-valuenow;键盘方向键 / Home / End 均可操作',
2537
+ ],
2538
+ examples: [
2539
+ { label: '基础区间滑块', code: '<Slider defaultValue={[20, 80]} />' },
2540
+ { label: '单值滑块', code: '<Slider defaultValue={50} />' },
2541
+ { label: '带刻度', code: '<Slider marks={{0: "0%", 25: "25%", 50: "50%", 75: "75%", 100: "100%"}} defaultValue={[30, 70]} />' },
2542
+ { label: '自定义范围步长', code: '<Slider min={0} max={200} step={10} defaultValue={[50, 150]} />' },
2543
+ { label: '禁用态', code: '<Slider disabled defaultValue={[20, 80]} />' },
2544
+ { label: '隐藏Tooltip', code: '<Slider showTooltip={false} defaultValue={[40, 60]} />' },
2545
+ { label: '受控模式', code: '<Slider value={[val1, val2]} onChange={(newVal) => setVal(newVal)} />' },
2546
+ { label: '❌ Bad(用 input type=range)', code: '/* 禁止!原生 range 跨浏览器样式不一致 */\n<input type="range" min={0} max={100} />' },
2547
+ { label: '✅ Good(用 Slider)', code: '<Slider defaultValue={50} />' },
2548
+ ],
2549
+ keywords: [
2550
+ 'Slider', 'slider', '滑动条', '滑块', '滑动输入',
2551
+ '区间滑块', '双滑块', '范围滑块', 'range slider',
2552
+ '音量', '亮度', '播放进度', '百分比', '评分滑块',
2553
+ '价格区间', '时间段', '数值范围', '阈值滑块',
2554
+ // 错误信号词
2555
+ '<input type="range">', 'input type=range', 'native range slider',
2556
+ ],
2557
+ },
2558
+ {
2559
+ id: 'upload',
2560
+ name: 'Upload',
2561
+ element: 'div',
2562
+ category: 'basic',
2563
+ description: '图片宫格上传,96×96 缩略图卡片,支持上传进度、错误重试和禁用态。',
2564
+ componentFile: './components/Upload.jsx',
2565
+ tokensFile: './components/Upload.tokens.js',
2566
+ props: [
2567
+ { name: 'accept', type: 'string', default: 'image/*' },
2568
+ { name: 'multiple', type: 'boolean', default: true },
2569
+ { name: 'limit', type: 'number' },
2570
+ { name: 'maxSize', type: 'number' },
2571
+ { name: 'minSize', type: 'number' },
2572
+ { name: 'disabled', type: 'boolean', default: false },
2573
+ { name: 'value', type: 'array' },
2574
+ { name: 'defaultValue', type: 'array', default: null },
2575
+ { name: 'onChange', type: 'function', default: null },
2576
+ { name: 'onRemove', type: 'function', default: null },
2577
+ { name: 'beforeUpload', type: 'function', default: null },
2578
+ { name: 'onSuccess', type: 'function', default: null },
2579
+ { name: 'onError', type: 'function', default: null },
2580
+ { name: 'onProgress', type: 'function', default: null },
2581
+ ],
2582
+ labels: {},
2583
+ _preview: null,
2584
+ rules: [
2585
+ '【选型·形态限制】当前版本「只支持图片宫格上传」,不提供按钮上传、拖拽上传或文件列表;需要上传 PDF / Excel / 视频等非图片文件 → 用原生 input[type=file] 或专门文件管理组件',
2586
+ '【场景】用户头像上传、商品主图/副图宫格(电商后台)、工单附件图片、表单中的图片佐证',
2587
+ '【禁忌】不要用 Upload 管理大批量文件(无列表视图、无文件类型分组);不要把 Upload 放在宽度不足 120px 的列里(单卡片 96px + 间距);非图片场景禁止使用此组件',
2588
+ '【卡片规格】单卡片固定 96×96px,圆角 8px,卡片间距 8px;加号卡片点击打开系统图片选择器',
2589
+ '【状态反馈】上传中:深色遮罩 + 圆形进度环;上传成功:恢复缩略图,悬浮显示删除按钮;上传失败:遮罩 + 中心重试图标 + 右下角红色告警点(点击图标可重试)',
2590
+ '【数量与体积限制】limit 控制最大数量(达上限后隐藏加号卡);minSize / maxSize 控制单文件体积(字节),超出时触发 onError',
2591
+ '【受控】传 value 时为受控模式,必须同时传 onChange 维护列表;非受控使用 defaultValue',
2592
+ '【组合·表单】配 Form 使用时包在 Form.Item(type="upload")里,上传成功后 onChange 的 fileList 自动同步给 Form;必填校验通过 Form.Item required + errorText 提示',
2593
+ '【禁用态】disabled=true 时整体 0.6 透明度,且禁止新增/删除交互;通常在表单只读态时使用',
2594
+ ],
2595
+ examples: [
2596
+ { label: '基础图片上传', code: '<Upload accept="image/*" multiple limit={3} onChange={(fileList) => {}} />' },
2597
+ { label: '禁用态', code: '<Upload accept="image/*" limit={3} disabled />' },
2598
+ { label: '带默认图片', code: `<Upload defaultValue={[{ uid: '1', name: 'cover.png', url: 'https://example.com/cover.png', status: 'done' }]} />` },
2599
+ { label: '受控模式', code: '<Upload value={fileList} onChange={(list) => setFileList(list)} onRemove={(file, next) => {}} />' },
2600
+ { label: '❌ Bad(用 input type=file 当图片上传)', code: '/* 禁止!缺缩略图、进度环、删除、错误重试 */\n<input type="file" accept="image/*" multiple />' },
2601
+ { label: '✅ Good(用 Upload 宫格)', code: '<Upload accept="image/*" multiple limit={3} onChange={(fileList) => {}} />' },
2602
+ ],
2603
+ keywords: [
2604
+ 'Upload', 'upload', '上传', '图片上传', '宫格上传', '图片宫格', 'image upload',
2605
+ '头像上传', '商品图片', '主图副图', '工单附件图', '图片佐证',
2606
+ '上传中', '上传进度', '上传失败', '上传成功', '错误重试',
2607
+ // 错误信号词
2608
+ '<input type="file">', 'input type=file', 'native file input',
2609
+ ],
2610
+ },
2611
+ {
2612
+ id: 'form-field-stack',
2613
+ name: 'FormFieldStack',
2614
+ element: 'div',
2615
+ category: 'business',
2616
+ description:
2617
+ '纵向堆叠 Form 的业务片段(无独立背景/描边、无 FormTitle、无底栏按钮):按 FormFieldStack 间距样式规范(p-6、gap-6、全宽 stretch)组合任意数量与类型的基础表单控件。stackLayout=select-stack 为 5 段单列 select 示意;stackLayout=mixed-fields 为单 Form 内含 2×input / 2×select / 2×radio / 2×checkbox / 3×textarea 示意。',
2618
+ componentFile: './components/FormFieldStack.jsx',
2619
+ tokensFile: './components/FormFieldStack.tokens.js',
2620
+ props: [
2621
+ {
2622
+ name: 'stackLayout',
2623
+ type: 'enum',
2624
+ options: ['select-stack', 'mixed-fields'],
2625
+ default: 'select-stack',
2626
+ },
2627
+ { name: 'className', type: 'string', default: '' },
2628
+ ],
2629
+ labels: {
2630
+ stackLayout: {
2631
+ 'select-stack': '纯下拉(5 段)',
2632
+ 'mixed-fields': '多类型(11 项)',
2633
+ },
2634
+ },
2635
+ _preview: null,
2636
+ rules: [
2637
+ '【组件定义】支持根据实际诉求场景,按照 FormFieldStack 间距样式规范,展示不同数量与样式的各类 Form 基础组件(字段类型、段数、单段多字段或「多 Form 每段少字段」均由业务按场景编排)',
2638
+ '【间距样式规范】根容器 w-full min-w-0、无独立卡片底/描边;四周内边距 p-6(24px);相邻 Form 区块或字段纵向节奏 gap-6(24px);各 Form 使用 w-full + !items-stretch,通栏字段配合 fullWidth 与基础控件自身宽度规则',
2639
+ '【预览示意】stackLayout=select-stack:5 个 Form 各 1 条 select;stackLayout=mixed-fields:单个 Form,items 自上而下为 2 input、2 select、2 radio、2 checkbox、3 textarea(示意数据来自 FORM_SAMPLE_ITEMS)',
2640
+ '【平台预览】详情页对 form-field-stack 使用纵向预览区 + scaler width:100%,避免 .detail-preview-scaler 随内容收缩导致内部宽度百分比失效',
2641
+ '【复用】字段一律通过 Form 与平台已有基础控件组合,不在本壳内重写控件样式',
2642
+ ],
2643
+ examples: [
2644
+ { label: '默认(5 段下拉)', code: '<FormFieldStack />' },
2645
+ { label: '多类型单表单', code: '<FormFieldStack stackLayout="mixed-fields" />' },
2646
+ { label: '外层加 className', code: '<FormFieldStack className="max-w-xl" />' },
2647
+ { label: '❌ Bad(多个 Form 之间间距随手写)', code: '/* 禁止!多 Form 编排应使用 FormFieldStack 统一节奏 */\n<div className="space-y-3"><Form items={…} /><Form items={…} /></div>' },
2648
+ { label: '✅ Good(用 FormFieldStack)', code: '<FormFieldStack stackLayout="select-stack" />' },
2649
+ ],
2650
+ keywords: [
2651
+ 'FormFieldStack', 'form-field-stack', '表单堆栈', '多段表单',
2652
+ '多个 Form', '多 Form 编排', '表单组合', '字段堆叠',
2653
+ '5 段下拉', 'select-stack', 'mixed-fields', '混合字段',
2654
+ // 错误信号词
2655
+ '手写多 form 间距', 'space-y-3 form', 'space-y-6 form',
2656
+ ],
2657
+ },
2658
+ {
2659
+ id: 'table',
2660
+ name: 'Table',
2661
+ element: 'div',
2662
+ category: 'business',
2663
+ description: 'B端业务列表表格,支持标准表格与卡片型表单两种类型。标准表格按列配置单元格类型与尺寸;卡片型表单用于策略、流程、脚本、模板等带父项和版本子项的业务列表,并带展开、查看、更多和分页交互。',
2664
+ componentFile: './components/Table.jsx',
2665
+ tokensFile: './components/Table.tokens.js',
2666
+ props: [
2667
+ { name: 'variant', type: 'enum', options: ['table', 'card-form'], default: 'table' },
2668
+ { name: 'columns', type: 'array', default: null },
2669
+ { name: 'dataSource', type: 'array', default: null },
2670
+ { name: 'rowKey', type: 'string', default: 'id' },
2671
+ { name: 'pagination', type: 'object', default: null },
2672
+ { name: 'expandedRowKeys', type: 'array', default: null },
2673
+ { name: 'defaultExpandedRowKeys', type: 'array', default: [] },
2674
+ { name: 'onExpandedRowKeysChange', type: 'function', default: null },
2675
+ { name: 'onView', type: 'function', default: null },
2676
+ { name: 'onMore', type: 'function', default: null },
2677
+ { name: 'onPageChange', type: 'function', default: null },
2678
+ { name: 'onPageSizeChange', type: 'function', default: null },
2679
+ { name: 'emptyText', type: 'string', default: '暂无数据' },
2680
+ { name: 'className', type: 'string', default: '' },
2681
+ { name: 'fixedColumnsMode', type: 'enum', options: ['none', 'first', 'last', 'both'], default: 'none' },
2682
+ ],
2683
+ labels: {
2684
+ variant: {
2685
+ table: '标准表格',
2686
+ 'card-form': '卡片型表单',
2687
+ },
2688
+ fixedColumnsMode: {
2689
+ none: '不固定',
2690
+ first: '固定第一栏',
2691
+ last: '固定尾栏',
2692
+ both: '首尾固定',
2693
+ },
2694
+ },
2695
+ _preview: {
2696
+ base: {
2697
+ width: '987px',
2698
+ height: '540px',
2699
+ headerBorder: '2px',
2700
+ rowBorder: '1px',
2701
+ },
2702
+ },
2703
+ rules: [
2704
+ FONT_WEIGHT_RUNTIME_RULE,
2705
+ LOCAL_MEMBER_AVATAR_RULE,
2706
+ '【类型】variant="table" 为标准表格,适合无父子展开关系的多列数据对齐、批量比较、排序筛选和管理列表;variant="card-form" 为卡片型表单,适合策略、流程、脚本、模板、SOP、知识条目等“父项 + 多版本/多子项”的业务列表。',
2707
+ '【类型判定优先级】只要列表存在父子级关系,且每个父级表单项需要支持展开/收起查看子级内容,就必须默认优先使用 Table variant="card-form"。即使视觉上包含多列字段、状态、负责人、更新时间、操作列或子级明细表,也不要优先判定为标准表格;标准表格只用于没有父子展开结构的平铺数据列表。',
2708
+ '【截图类场景判定】类似“父行展示名称/状态/引用次数/应用渠道/负责人/更新时间/操作,展开后展示版本号、版本名称、发布状态、完成率、执行次数、满意度、转人工率”的业务列表,虽然看起来像表格,但核心交互是父子级展开查看,必须归类为卡片型表单,而不是标准表格。',
2709
+ '【卡片型表单结构】卡片型表单由父卡片和版本子行组成。父卡片左侧固定两行:标题 + 副标题;禁止展示“引用次数 / 应用渠道 / 描述”等多段拼接元信息。右侧固定展示分类标签、头像、更新时间、查看和更多操作;右侧区域必须 shrink-0,不允许被标题挤压或覆盖。',
2710
+ '【卡片型表单子行】展开后子行展示版本号、版本标题、状态、指标区、头像、更新时间、查看和更多操作。版本号是展示序号,按当前父项子行展示顺序从 v1 开始递增,不读取业务传入的 v/version 字段;示例数据不要再传 v/version 误导 AI。metrics 默认最多展示 4 项,超出字段进入 Tooltip 或更多信息,不要横向撑破布局。',
2711
+ '【卡片型表单交互】父卡片存在非空 versions 时通过左侧展开 Button 展开/收起;每个模板父项都应提供 versions 数组,保证支持展开子级表单。展开状态支持 expandedRowKeys 受控,也支持 defaultExpandedRowKeys 非受控;onExpandedRowKeysChange 返回当前展开 key 列表;查看与更多操作分别通过 onView(record, context) 与 onMore(record, context) 暴露,context.type 为 parent 或 version。',
2712
+ '【卡片型表单基础组件复用】所有按钮型元素必须复用基础组件 Button,包括展开、查看、更多、分页页码、翻页箭头;禁止用原生 button 或 div role=button 手写。Tag 只用于展示,不承担切换或按钮动作;Avatar 复用本地成员头像兜底;页容量选择器复用 Select。',
2713
+ '【卡片型表单复用】基础页面框架模版-页面示例0 的表单列表必须完整引用 Table variant="card-form",禁止再在页面模板内手写 StrategyCard / VersionRow / 分页 / Tag / Avatar / Button 组合。需要类似策略管理、优秀知识模版、优秀案例模版、脚本模版、话术模版、服务 SOP 等卡片表单列表时优先使用该类型。',
2714
+ '【卡片型表单数据约定】父项推荐字段:id/title/subtitle/category/updatedAt/avatar/versions;category 标签文案必须控制在 8 个字以内,模拟数据使用“账号服务 / 权益申诉 / 处罚解释 / 举报处理 / 售后协同”等抖音体验与服务短文案。versions 必须是数组,子项推荐字段:id/title/status/metrics/updatedAt/avatar;metrics 格式为 { label, value }。categoryEmpty、quoteCount、channels、v、version 属于历史或业务字段,不参与卡片型表单视觉决策,AI 生成示例时不要主动使用。',
2715
+ '【卡片型表单状态词表】版本状态标签按状态语义复用 Tag:草稿中/暂未生效/已下线使用 grey,实验中使用 blue,已发布/线上生效使用 green;不要随意扩展高饱和警示色,除非业务明确表达失败、风险或异常。',
2716
+ '【卡片型表单视觉】父卡片使用 rounded-xl、border-border-default、bg-surface;父行内距为 px-8 py-4;子行使用 bg-blueGrey-100、rounded-lg、p-4;版本号为 24×24 黑底白字 rounded-md;父项分类标签固定复用 Tag size=m radius=md variant=white;版本状态标签按状态语义复用 Tag;头像复用 Avatar mini,本地成员头像仅作为预览和 mock 兜底,业务真实接入应传 avatar。',
2717
+ '【卡片型表单省略规则】所有可截断文本必须接入 Tooltip 回显:父标题、父副标题、版本标题、日期和版本指标区都不能把右侧头像/时间/操作按钮挤出容器。左侧标题区必须使用 min-w-0 + overflow-hidden;文本自身必须使用 truncate / text-overflow;发生真实截断时 hover/focus 通过 Tooltip 展示完整文案。',
2718
+ '【卡片型表单指标区】版本指标区固定为单行可收缩区域:宽度足够时横向完整展示;宽度不足时使用 text-overflow 省略为“…”;发生截断时 hover/focus 必须通过 Tooltip 展示完整指标字段。不允许换行、溢出或遮挡右侧操作区。',
2719
+ '【卡片型表单字段承载】卡片型表单可以在父项右侧承载状态、短标签、负责人、更新时间、查看和更多操作,也可以在子项承载版本号、版本名称、状态、指标和操作;这些字段应由 card-form 的父项/子项结构表达,不应为了多字段横向展示而退回标准表格。',
2720
+ '【卡片型表单禁止项】card-form 不适用 columns、fixedColumnsMode、tableSize 或标准表格列配置;不要将标准表格 cell type 迁移到卡片父项;不要在父项标题旁堆叠多枚标签;不要把长分类名直接塞进 category。',
2721
+ '【列级配置】columns 支持 type 与 cellSize,两者组合决定单元格结构、高度、内边距和图文关系',
2722
+ '【类型全集】内置 text/link/iconText/textButton/status/tag/switch/checkbox/avatar/avatarText/linkDescription/image/progress/datetime/actions/dragHandle',
2723
+ '【基础组件复用】凡是平台已有基础组件可承载的类型,Table 内必须直接复用:switch→Switch、checkbox→Checkbox、avatar/avatarText→Avatar、tag→Tag、link/textButton/actions/dragHandle/分页按钮→Button、所有图标→Icon、页容量选择器→Select',
2724
+ '【尺寸】cellSize 支持 default / middle / small,按设计稿映射为 12px / 8px / 4px 内边距与对应单元高度',
2725
+ '【标题与操作】link、actions 使用 Button 的 text-brand 变体;textButton 使用 Button 的 iconOnly 组合;linkDescription 保留标题+描述双行信息密度',
2726
+ '【复合内容】avatar、avatarText、image、progress 等类型优先保证内容完整显示,再参与列宽比例伸缩;avatar/avatarText 单元格未传 avatarSrc 时默认按 name/description seed 取本地成员头像,禁止为表格负责人列生成随机外链头像',
2727
+ '【分页】pagination 为对象配置,支持 current/pageSize/total/pageSizeOptions/summaryText/displayPages/pageSizeLabel。分页区包含统计文案、页码、翻页箭头和每页条数选择器;页码选中态固定为 bg-brand-50 + text-brand-600 + semibold,数字颜色必须是 Brand 600。',
2728
+ '【分页真实数量】预览和模板内不得写死虚假 total。若 dataSource 是完整本地数据,pagination.total 必须等于 dataSource.length,Table 会按 current/pageSize 对 dataSource 自动切片,左侧“10条/20条”、实际展示行数、底部统计范围、总条数和页码数量必须保持一致。若业务使用服务端分页且 total 大于当前页 dataSource.length,需显式传入后端 total,并只传当前页数据。',
2729
+ ],
2730
+ examples: [
2731
+ { label: '卡片型表单', code: '<Table variant="card-form" dataSource={experienceForms} rowKey="id" defaultExpandedRowKeys={["experience-1"]} pagination={{ current: 1, pageSize: 20, total: experienceForms.length, pageSizeOptions: [10, 20] }} onExpandedRowKeysChange={() => {}} onView={() => {}} onMore={() => {}} />' },
2732
+ { label: '卡片型表单数据结构', code: 'const experienceForms = [\n { id: "experience-1", title: "抖音体验与服务-账号绑定进线流程", subtitle: "用户进线后的路由编排", category: "账号服务", updatedAt: "2026-04-21 10:30", versions: [{ id: "experience-1-a", title: "优化身份校验引导", status: "草稿中", metrics: [{ label: "完成率", value: "100%" }] }] },\n { id: "experience-2", title: "抖音体验与服务-创作者权益申诉", subtitle: "权益问题自动分流处理", category: "权益申诉", updatedAt: "2026-04-20 18:45", versions: [{ id: "experience-2-a", title: "新增申诉链路兜底", status: "实验中", metrics: [{ label: "执行次数", value: "100" }] }] },\n { id: "experience-3", title: "抖音体验与服务-违规处罚解释", subtitle: "处罚原因说明与安抚", category: "处罚解释", updatedAt: "2026-04-19 14:12", versions: [{ id: "experience-3-a", title: "更新处罚说明模板", status: "已发布", metrics: [{ label: "满意度", value: "100%" }] }] },\n { id: "experience-4", title: "抖音体验与服务-直播间举报处理", subtitle: "举报受理与风险分级", category: "举报处理", updatedAt: "2026-04-18 11:20", versions: [{ id: "experience-4-a", title: "补充举报分级策略", status: "已发布", metrics: [{ label: "转人工率", value: "10%" }] }] },\n { id: "experience-5", title: "抖音体验与服务-订单售后协同", subtitle: "售后咨询与工单协同", category: "售后协同", updatedAt: "2026-04-17 09:40", versions: [{ id: "experience-5-a", title: "优化售后协同话术", status: "草稿中", metrics: [{ label: "完成率", value: "98%" }] }] },\n];' },
2733
+ { label: '按列配置类型', code: "<Table columns={[{ title: '选择', type: 'checkbox', cellSize: 'default', dataIndex: 'select' }, { title: '标题', type: 'linkDescription', cellSize: 'default', dataIndex: 'title' }, { title: '状态', type: 'status', cellSize: 'middle', dataIndex: 'status' }]} dataSource={data} />" },
2734
+ { label: '默认列表', code: '<Table columns={columns} dataSource={data} pagination={{ current: 1, pageSize: 20, total: data.length, pageSizeOptions: [10, 20] }} onPageChange={() => {}} onPageSizeChange={() => {}} />' },
2735
+ { label: '无分页', code: '<Table columns={columns} dataSource={data} pagination={null} />' },
2736
+ { label: '空态', code: '<Table columns={columns} dataSource={[]} emptyText="暂无数据" pagination={null} />' },
2737
+ { label: '头像列默认本地成员图', code: '<Table columns={[{ title: "创建人", type: "avatar", dataIndex: "creator" }]} dataSource={[{ id: 1, creator: { name: "段然" } }]} pagination={null} />' },
2738
+ { label: '❌ Bad(手写 table)', code: '/* 禁止!手写 <table> 缺分页/排序/状态列/空态/响应式 */\n<table><thead><tr><th>名称</th></tr></thead><tbody><tr><td>...</td></tr></tbody></table>' },
2739
+ { label: '✅ Good(用 Table 组件)', code: '<Table columns={[{ title: "名称", type: "text", dataIndex: "name" }, { title: "状态", type: "status", dataIndex: "status" }]} dataSource={data} pagination={{ current: 1, pageSize: 20, total: 46 }} />' },
2740
+ { label: '❌ Bad(用 div 列表代替 Table)', code: '/* 禁止!业务列表应用 Table,而不是 div+grid 手搓 */\n<div className="grid grid-cols-4 gap-2">{data.map(item => <div key={item.id}>{item.name}</div>)}</div>' },
2741
+ { label: '❌ Bad(卡片型表单标题覆盖右侧区)', code: '/* 禁止!标题区没有 min-w-0 / overflow-hidden,长标题会覆盖标签、头像、日期和按钮 */\n<div className="flex"><span className="truncate">{item.title}</span><Tag>{item.category}</Tag></div>' },
2742
+ { label: '✅ Good(卡片型表单标题可省略并回显)', code: '<Table variant="card-form" dataSource={experienceForms} rowKey="id" defaultExpandedRowKeys={["experience-1"]} />' },
2743
+ ],
2744
+ keywords: [
2745
+ 'Table', 'table', '表格', '业务列表', '列表表格', '数据表格', 'data table', 'grid table',
2746
+ '卡片型表单', 'card-form', '策略列表', '版本列表', '父子列表', '表单卡片',
2747
+ // 列类型
2748
+ 'text', 'link', 'iconText', 'textButton', 'status', 'tag', 'switch', 'checkbox',
2749
+ 'avatar', 'avatarText', 'linkDescription', 'image', 'progress', 'datetime', 'actions', 'dragHandle',
2750
+ // 高频场景
2751
+ '管理列表', '列表页', '后台列表', '数据列表', '记录列表', 'CRUD 列表',
2752
+ '分页', 'pagination', 'pageSize', '每页条数',
2753
+ '操作列', '状态列', '日期列', '头像列',
2754
+ // 错误信号词
2755
+ '<table>', '<thead>', '<tbody>', 'native table', '手写 table', '原生 table',
2756
+ 'grid grid-cols 列表', 'div 列表代替 table',
2757
+ ],
2758
+ },
2759
+ {
2760
+ id: 'date-picker',
2761
+ name: 'DatePicker',
2762
+ element: 'div',
2763
+ category: 'basic',
2764
+ description: '日期选择器,支持日期、日期时间、日期范围、日期时间范围 4 种类型。',
2765
+ componentFile: './components/DatePicker.jsx',
2766
+ tokensFile: './components/DatePicker.tokens.js',
2767
+ props: [
2768
+ { name: 'type', type: 'enum', options: ['date', 'datetime', 'daterange', 'datetimerange'], default: 'date' },
2769
+ { name: 'value', type: 'string' },
2770
+ { name: 'defaultValue', type: 'string' },
2771
+ { name: 'placeholder', type: 'string', default: '' },
2772
+ { name: 'disabled', type: 'boolean', default: false },
2773
+ { name: 'defaultOpen', type: 'boolean', default: false },
2774
+ { name: 'onChange', type: 'function', default: null },
2775
+ { name: 'className', type: 'string', default: '' },
2776
+ ],
2777
+ labels: {
2778
+ type: {
2779
+ date: '日期',
2780
+ datetime: '日期时间',
2781
+ daterange: '日期范围',
2782
+ datetimerange: '日期时间范围',
2783
+ },
2784
+ },
2785
+ _preview: DATEPICKER_PREVIEW,
2786
+ rules: [
2787
+ FONT_WEIGHT_RUNTIME_RULE,
2788
+ '【选型·vs TimePicker】只需选时间(时/分)不需要日期 → TimePicker;需要同时选日期+时间 → DatePicker type="datetime";只需日期范围 → type="daterange";同时需要日期时间范围 → type="datetimerange"',
2789
+ '【选型·vs Input】不要用 Input 手动录入日期字符串,始终使用 DatePicker,它提供格式校验和统一展示',
2790
+ '【场景·date】任务截止日、证件有效期、生日录入',
2791
+ '【场景·datetime】预约/会议时间安排、日志时间戳录入',
2792
+ '【场景·daterange / datetimerange】表格/列表顶部时间范围筛选、统计报表数据周期选择、合同有效期区间',
2793
+ '【禁忌】不要用 type="date" 代替 type="datetime"(会丢失时分信息);不要把两个 DatePicker 手拼成范围选择(直接用 type="daterange");不要在极紧凑行内(宽度 < 200px)使用,面板最小宽度 280px',
2794
+ '【类型说明】date / datetime → 单月面板单值选择;daterange / datetimerange → 双月面板范围选择,范围中间区间浅色连续高亮',
2795
+ '【默认宽度】date / datetime / daterange → 300px;datetimerange → 400px;可通过外层容器或 className 自适应撑宽',
2796
+ '【面板行为】单值点击即选中并关闭;范围需点击两次确定起止后自动关闭',
2797
+ '【时间栏】datetime / datetimerange 面板底部固定显示 52px 日期/时间信息栏',
2798
+ '【组合·表单】配 Form 使用时包在 Form.Item(type="date-picker")里;时间范围筛选常配合 Table 顶部工具栏 + "查询"Button 使用',
2799
+ '【受控】使用 value + onChange(val);非受控使用 defaultValue;范围类型 value 为长度 2 的字符串数组',
2800
+ '【禁用态】disabled 时 bg-disabled + 0.6 透明度,禁止任何交互',
2801
+ ],
2802
+ examples: [
2803
+ { label: '日期(无值)', code: '<DatePicker type="date" />' },
2804
+ { label: '日期(展开面板)', code: '<DatePicker type="date" defaultOpen />' },
2805
+ { label: '日期时间(有值)', code: '<DatePicker type="datetime" value="2019-03-15 16:00:00" />' },
2806
+ { label: '日期范围(有值)', code: '<DatePicker type="daterange" value={["2019-03-15", "2019-03-16"]} />' },
2807
+ { label: '日期时间范围(有值)', code: '<DatePicker type="datetimerange" value={["2019-03-15 09:00:00", "2019-03-16 16:00:00"]} />' },
2808
+ { label: '禁用态', code: '<DatePicker disabled />' },
2809
+ { label: '❌ Bad(用 input type=date)', code: '/* 禁止!原生 date 输入跨浏览器样式不一致、无 token */\n<input type="date" />' },
2810
+ { label: '✅ Good(用 DatePicker)', code: '<DatePicker type="date" />' },
2811
+ { label: '❌ Bad(两个 DatePicker 拼范围)', code: '/* 禁止!日期范围必须用 daterange */\n<><DatePicker type="date" /> ~ <DatePicker type="date" /></>' },
2812
+ { label: '✅ Good(用 daterange)', code: '<DatePicker type="daterange" />' },
2813
+ ],
2814
+ keywords: [
2815
+ 'DatePicker', 'date-picker', '日期选择', '日期选择器', '日期组件', 'date input',
2816
+ 'datetime', 'daterange', 'datetimerange', '日期时间', '日期范围', '日期时间范围',
2817
+ // 高频场景
2818
+ '截止日期', '生日', '过期时间', '有效期', '统计周期', '时间范围筛选', '日期筛选',
2819
+ '预约时间', '会议时间', '日志时间', 'created_at', 'updated_at',
2820
+ // 错误信号词
2821
+ '<input type="date">', 'input type=date', 'native date',
2822
+ '两个 DatePicker 拼范围', '手拼日期范围',
2823
+ ],
2824
+ },
2825
+ {
2826
+ id: 'time-picker',
2827
+ name: 'TimePicker',
2828
+ element: 'div',
2829
+ category: 'basic',
2830
+ description: '时间选择器,支持单时间与时间范围,触发器视觉对齐 DatePicker,浮层内按 Figma 滚轮面板选择时/分。',
2831
+ componentFile: './components/TimePicker.jsx',
2832
+ tokensFile: './components/TimePicker.tokens.js',
2833
+ props: [
2834
+ { name: 'type', type: 'enum', options: ['time', 'timerange'], default: 'time' },
2835
+ { name: 'value', type: 'string | array' },
2836
+ { name: 'defaultValue', type: 'string | array' },
2837
+ { name: 'placeholder', type: 'string', default: '' },
2838
+ { name: 'disabled', type: 'boolean', default: false },
2839
+ { name: 'defaultOpen', type: 'boolean', default: false },
2840
+ { name: 'onChange', type: 'function', default: null },
2841
+ { name: 'className', type: 'string', default: '' },
2842
+ ],
2843
+ labels: {
2844
+ type: {
2845
+ time: '时间',
2846
+ timerange: '时间范围',
2847
+ },
2848
+ },
2849
+ _preview: {
2850
+ base: {
2851
+ height: '36px',
2852
+ width: '300px',
2853
+ panelMinWidth: '150px',
2854
+ panelGap: '4px',
2855
+ },
2856
+ type: {
2857
+ time: { panelWidth: 'same as trigger', panelHeight: '252px' },
2858
+ timerange: { panelWidth: 'same as trigger', panelHeight: '304px' },
2859
+ },
2860
+ },
2861
+ rules: [
2862
+ FONT_WEIGHT_RUNTIME_RULE,
2863
+ '【类型】time 为单时间输入框;timerange 为时间范围输入框',
2864
+ '【触发器】视觉对齐 Figma / DatePicker:300px 宽、36px 高、白底、1px 默认描边、8px 圆角,内容默认左对齐,右侧时钟图标固定为 16×16px',
2865
+ '【面板】点击 Trigger 展开浮层;面板默认与 Trigger 等宽,最小宽度 150px;单时间面板高 252px,无标题;时间范围面板高 304px,顶部显示开始/结束标题',
2866
+ '【固定选择区】面板中间固定 36px 高浅绿选择区,时/分列按面板宽度等分并居中对齐;timerange 使用同一条横跨四列的固定选择区,四列都支持真实滚动选择',
2867
+ '【选择】点击任一时间项会滚动到中间选择区;滚动时/分列时实时同步选择区内最近项,停止后自动吸附',
2868
+ '【有值与无值】无值显示占位符;有值按 HH:mm 展示;范围使用“~”分隔开始与结束时间',
2869
+ '【禁用态】disabled 时使用 bg-disabled + 0.6 透明度,并禁止交互',
2870
+ '【Form】Form 的 type="time-picker" 字段必须完整引用 TimePicker,不再用 Input + suffix 临时代替',
2871
+ ],
2872
+ examples: [
2873
+ { label: '时间(无值)', code: '<TimePicker type="time" />' },
2874
+ { label: '时间(展开面板)', code: '<TimePicker type="time" defaultOpen />' },
2875
+ { label: '时间(有值)', code: '<TimePicker type="time" value="12:15" />' },
2876
+ { label: '时间范围(有值)', code: '<TimePicker type="timerange" value={["09:30", "18:00"]} />' },
2877
+ { label: '禁用态', code: '<TimePicker disabled />' },
2878
+ { label: '❌ Bad(用 input type=time)', code: '/* 禁止!跨浏览器不一致、无 token */\n<input type="time" />' },
2879
+ { label: '✅ Good(用 TimePicker)', code: '<TimePicker type="time" />' },
2880
+ ],
2881
+ keywords: [
2882
+ 'TimePicker', 'time-picker', '时间选择', '时间选择器', '时分选择',
2883
+ 'time', 'timerange', '时间范围', 'HH:mm',
2884
+ // 错误信号词
2885
+ '<input type="time">', 'input type=time', 'native time input',
2886
+ ],
2887
+ },
2888
+ {
2889
+ id: 'modal',
2890
+ name: 'Modal',
2891
+ element: 'div',
2892
+ category: 'basic',
2893
+ description: '模态弹窗面板,含标题区、内容区和底部按钮。支持居中(center)和全屏弹窗(fullscreen)两种布局变体。',
2894
+ componentFile: './components/Modal.jsx',
2895
+ tokensFile: './components/Modal.tokens.js',
2896
+ props: [
2897
+ { name: 'layout', type: 'enum', options: ['center', 'fullscreen'], default: 'center' },
2898
+ { name: 'size', type: 'enum', options: ['sm', 'md', 'lg'], default: 'md' },
2899
+ { name: 'title', type: 'string', default: '模态标题' },
2900
+ { name: 'subtitle', type: 'string', default: '模态副标题' },
2901
+ { name: 'showFooterHint', type: 'boolean', default: true },
2902
+ { name: 'footerHint', type: 'string', default: '提示信息' },
2903
+ { name: 'cancelText', type: 'string', default: '取消' },
2904
+ { name: 'confirmText', type: 'string', default: '确定' },
2905
+ { name: 'onClose', type: 'function', default: null },
2906
+ { name: 'onCancel', type: 'function', default: null },
2907
+ { name: 'onConfirm', type: 'function', default: null },
2908
+ { name: 'footer', type: 'node', default: null },
2909
+ ],
2910
+ labels: {
2911
+ layout: { center: '居中', fullscreen: '全屏弹窗' },
2912
+ size: { sm: '小', md: '中', lg: '大' },
2913
+ },
2914
+ _preview: MODAL_PREVIEW,
2915
+ rules: [
2916
+ FONT_WEIGHT_RUNTIME_RULE,
2917
+ '【选型·vs Sheet】内容需要居中吸引注意力、或是需要全屏底部弹出 → Modal;内容是侧边补充信息、长表单、详情预览,用户保留上下文感知 → Sheet',
2918
+ '【选型·vs Tooltip】轻量 1-3 行说明 → Tooltip;需要用户确认或有表单交互 → Modal',
2919
+ '【场景·center sm】二次确认弹窗(删除、提交等不可撤销操作),size="sm"、showFooterHint={false}',
2920
+ '【场景·center md/lg】新建/编辑表单弹窗;复杂信息展示;跨步骤流程',
2921
+ '【场景·fullscreen】移动端风格从底部弹出的多步操作流程;在固定高度容器(如 iframe / 嵌套应用)内的沉浸式交互',
2922
+ '【禁忌】不要在 Modal 内再嵌套 Modal(层级混乱,用户无法感知关闭路径);不要用 Modal 显示纯提示信息(1-2 行)→ Tooltip 或 Toast;内容是侧边补充时用 Sheet 而非 Modal',
2923
+ '【范围】本组件只负责白色面板本体;半透明遮罩(`fixed inset-0 bg-black/40`)、定位、动画由页面或上层容器实现',
2924
+ '【center】居中浮层,受 size 控制最大宽度(sm 450px / md 684px / lg 920px),最大高度 85vh;内容溢出时给内容区加 overflow-y-auto',
2925
+ '【fullscreen】全屏弹窗,宽度撑满,高度 = 容器高度 - 24px,底部对齐,顶部圆角;外层须是 flex-col 定高容器(如 fixed inset-0),面板加 className="mt-auto" 停靠底部',
2926
+ '【size】仅在 layout="center" 时生效,fullscreen 布局会忽略 size',
2927
+ '【关闭】传入 onClose 才显示右上角关闭图标;不传则无关闭按钮(强制操作流程)',
2928
+ '【底部按钮】确定用 primary,取消用 outline-black;需要自定义按钮组时传入 footer prop(会覆盖默认底部)',
2929
+ '【无障碍】已设置 role="dialog"、aria-modal 与标题 aria-labelledby;打开时焦点应移入弹窗,关闭后返回触发元素',
2930
+ ],
2931
+ examples: [
2932
+ { label: '居中(默认)', code: '<Modal onClose={() => {}} onCancel={() => {}} onConfirm={() => {}} />' },
2933
+ { label: '居中小尺寸确认', code: '<Modal size="sm" title="确认删除?" subtitle={null} showFooterHint={false} onClose={() => {}} onCancel={() => {}} onConfirm={() => {}}><p>此操作不可撤销。</p></Modal>' },
2934
+ { label: '居中大尺寸表单', code: '<Modal size="lg" title="新建项目" onClose={() => {}} onCancel={() => {}} onConfirm={() => {}}><form>...</form></Modal>' },
2935
+ { label: '全屏弹窗', code: '<Modal layout="fullscreen" title="全屏弹窗标题" className="mt-auto" onClose={() => {}} onCancel={() => {}} onConfirm={() => {}} />' },
2936
+ { label: '隐藏底部提示', code: '<Modal showFooterHint={false} onClose={() => {}} onCancel={() => {}} onConfirm={() => {}} />' },
2937
+ { label: '❌ Bad(手写 fixed div 当弹窗)', code: '/* 禁止!手写 modal 缺 a11y、缺底部按钮、layout 错乱 */\n<div className="fixed inset-0 bg-black/40 flex items-center justify-center"><div className="bg-white rounded p-6">提示</div></div>' },
2938
+ { label: '✅ Good(用 Modal 组件)', code: '<Modal title="确认操作" subtitle={null} showFooterHint={false} onClose={() => {}} onCancel={() => {}} onConfirm={() => {}}><p>请确认是否继续?</p></Modal>' },
2939
+ { label: '❌ Bad(用 Modal 显示 1-2 行提示)', code: '/* 禁止!短提示用 Toast 或 Tooltip,Modal 太重 */\n<Modal title="保存成功" />' },
2940
+ { label: '✅ Good(短提示用 Toast)', code: '<Toast variant="success" title="保存成功" />' },
2941
+ ],
2942
+ keywords: [
2943
+ 'Modal', 'modal', '弹窗', '模态弹窗', '模态框', 'dialog', '对话框',
2944
+ 'center', 'fullscreen', '居中弹窗', '全屏弹窗',
2945
+ // 高频场景
2946
+ '二次确认', '确认删除', '删除确认', '提交确认', '不可撤销', '危险操作确认',
2947
+ '新建表单', '编辑表单', '创建对话框', '编辑对话框',
2948
+ '复杂信息展示', '跨步骤流程',
2949
+ // 英文
2950
+ 'confirm dialog', 'confirmation modal', 'delete confirm', 'modal dialog',
2951
+ // 错误信号词
2952
+ 'fixed inset-0 bg-black/40', '手写 modal', 'role=dialog 手写', '自制弹窗',
2953
+ ],
2954
+ },
2955
+ {
2956
+ id: 'sheet',
2957
+ name: 'Sheet',
2958
+ element: 'div',
2959
+ category: 'basic',
2960
+ description: '侧边抽屉面板,从屏幕右侧(或左侧)滑入,含标题区、内容区和底部按钮,3 档宽度,内容超高可滚动。',
2961
+ componentFile: './components/Sheet.jsx',
2962
+ tokensFile: './components/Sheet.tokens.js',
2963
+ props: [
2964
+ { name: 'size', type: 'enum', options: ['sm', 'md', 'lg'], default: 'md' },
2965
+ { name: 'showFooterHint', type: 'boolean', default: true },
2966
+ { name: 'title', type: 'string', default: '侧边标题' },
2967
+ { name: 'subtitle', type: 'string', default: '侧边副标题' },
2968
+ { name: 'footerHint', type: 'string', default: '提示信息' },
2969
+ { name: 'cancelText', type: 'string', default: '取消' },
2970
+ { name: 'confirmText', type: 'string', default: '确定' },
2971
+ { name: 'onClose', type: 'function', default: null },
2972
+ { name: 'onCancel', type: 'function', default: null },
2973
+ { name: 'onConfirm', type: 'function', default: null },
2974
+ { name: 'footer', type: 'node', default: null },
2975
+ ],
2976
+ labels: {
2977
+ size: { sm: '小', md: '中', lg: '大' },
2978
+ },
2979
+ _preview: SHEET_PREVIEW,
2980
+ rules: [
2981
+ FONT_WEIGHT_RUNTIME_RULE,
2982
+ '【范围】本组件只负责侧边白色面板;半透明遮罩、定位、滑入动画由页面或上层容器实现',
2983
+ '【方向】面板从屏幕右侧滑入,无圆角,贴合视口边缘',
2984
+ '【尺寸】sm(400px)适用于简短表单;md(560px)通用默认;lg(800px)适用于复杂内容或列表',
2985
+ '【高度】面板高度撑满视口(h-full),内容区域内部滚动,适合长表单或列表',
2986
+ '【关闭】传入 onClose 才显示右上角关闭图标(灰色无背景),悬浮变深',
2987
+ '【底部提示】showFooterHint=false 可隐藏底部左侧提示信息',
2988
+ '【主操作】确定用 primary,取消用 outline-black',
2989
+ '【自定义】需要复杂底部时传入 footer,将覆盖默认的提示文案与按钮行',
2990
+ '【无障碍】已设置 role="dialog"、aria-modal 与标题 aria-labelledby',
2991
+ ],
2992
+ examples: [
2993
+ { label: '默认(MD)', code: '<Sheet onClose={() => {}} onCancel={() => {}} onConfirm={() => {}} />' },
2994
+ { label: '小尺寸', code: '<Sheet size="sm" title="快速编辑" subtitle={null} showFooterHint={false} onClose={() => {}} onCancel={() => {}} onConfirm={() => {}}><form>...</form></Sheet>' },
2995
+ { label: '大尺寸复杂内容', code: '<Sheet size="lg" title="详情面板" onClose={() => {}} onCancel={() => {}} onConfirm={() => {}}><div>...</div></Sheet>' },
2996
+ { label: '❌ Bad(手写 fixed 抽屉)', code: '/* 禁止!手写抽屉缺 a11y、底部按钮、滑入动画 */\n<div className="fixed top-0 right-0 h-full w-[400px] bg-white p-6">详情</div>' },
2997
+ { label: '✅ Good(用 Sheet 组件)', code: '<Sheet size="md" title="详情面板" onClose={() => {}} onCancel={() => {}} onConfirm={() => {}}>详情内容</Sheet>' },
2998
+ { label: '❌ Bad(长表单用 Modal center)', code: '/* 禁止!长表单/详情预览应该用 Sheet 保留上下文 */\n<Modal layout="center" size="lg" title="编辑用户">…长表单</Modal>' },
2999
+ { label: '✅ Good(长表单用 Sheet)', code: '<Sheet size="md" title="编辑用户">…长表单</Sheet>' },
3000
+ ],
3001
+ keywords: [
3002
+ 'Sheet', 'sheet', '侧边抽屉', '抽屉面板', '右侧抽屉', '侧边栏弹层', 'drawer', 'side panel',
3003
+ 'detail panel', 'edit panel',
3004
+ '详情面板', '编辑面板', '快速编辑', '长表单', '侧边详情', '侧边长表单',
3005
+ // 错误信号词
3006
+ 'fixed top-0 right-0', '手写抽屉', '自制 drawer', 'right side fixed',
3007
+ ],
3008
+ },
3009
+ {
3010
+ id: 'tag-input',
3011
+ name: 'TagInput',
3012
+ element: 'div',
3013
+ category: 'basic',
3014
+ description: '标签输入框,严格复用 Tag 展示已选项与 +N 折叠项,宽度自适应折叠超出标签,并通过 Tooltip 展示被省略标签。',
3015
+ componentFile: './components/TagInput.jsx',
3016
+ tokensFile: './components/TagInput.tokens.js',
3017
+ props: [
3018
+ { name: 'status', type: 'enum', options: ['default', 'error'], default: 'default' },
3019
+ { name: 'disabled', type: 'boolean', default: false },
3020
+ { name: 'placeholder', type: 'string', default: '请输入' },
3021
+ { name: 'value', type: 'array' },
3022
+ { name: 'defaultValue', type: 'array', default: null },
3023
+ { name: 'onChange', type: 'function', default: null },
3024
+ { name: 'onRemove', type: 'function', default: null },
3025
+ { name: 'aiSuggestion', type: 'array | string | object', default: null },
3026
+ { name: 'aiSuggestions', type: 'array', default: null },
3027
+ { name: 'onAdoptSuggestion', type: 'function', default: null },
3028
+ { name: 'onRefreshAiSuggestions', type: 'function', default: null },
3029
+ { name: 'closable', type: 'boolean', default: true },
3030
+ { name: 'tagVariant', type: 'enum', options: ['brand', 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'pink', 'teal', 'grey', 'white'], default: 'grey' },
3031
+ { name: 'tagSize', type: 'enum', options: ['s', 'm', 'l'], default: 'm' },
3032
+ { name: 'className', type: 'string', default: '' },
3033
+ ],
3034
+ labels: {
3035
+ status: { default: '默认', error: '错误' },
3036
+ tagVariant: {
3037
+ brand: '品牌',
3038
+ red: '红色',
3039
+ orange: '橙色',
3040
+ yellow: '黄色',
3041
+ green: '绿色',
3042
+ cyan: '青色',
3043
+ blue: '蓝色',
3044
+ purple: '紫色',
3045
+ pink: '粉色',
3046
+ teal: '青绿',
3047
+ grey: '灰色',
3048
+ white: '白色',
3049
+ },
3050
+ tagSize: { s: 'S', m: 'M', l: 'L' },
3051
+ },
3052
+ _preview: TAGINPUT_PREVIEW,
3053
+ rules: [
3054
+ '【视觉】容器对齐 Figma taginput:默认宽度 300px、36px 高、白底、1px 默认描边、8px 圆角,空态显示“请输入”',
3055
+ '【Tag 复用】已选项、+N 折叠项、Tooltip 内省略项、隐藏测量项都必须使用平台现有 Tag 组件;TagInput 不自绘任何标签视觉',
3056
+ '【宽度自适应】通过 ResizeObserver 监听容器宽度,按真实 Tag 宽度计算可见数量;宽度变窄自动折叠,变宽自动展开',
3057
+ '【溢出折叠】可见区域放不下的标签折叠为 +N,+N 固定在末尾并以 Tag 渲染;悬浮 +N 使用白底 tone="light" Tooltip 展示被省略的所有 Tag,该白底样式仅用于 TagInput 更多标签场景',
3058
+ '【删除】closable=true 时每个可见 Tag 显示关闭按钮,删除后触发 onChange(nextTags, removedTag) 与 onRemove(removedTag)',
3059
+ '【受控】传 value 时为受控模式,非受控使用 defaultValue;value/defaultValue 支持字符串数组或 { value, label, variant } 对象数组',
3060
+ '【状态】status="error" 使用浅红底与红色聚焦描边;disabled 叠加禁用背景、透明度并关闭删除操作',
3061
+ '【AI推荐】支持推荐单个标签或推荐标签集合;推荐区展示在 TagInput 下方,默认支持真实点击采纳与刷新推荐,不允许只展示静态建议文案',
3062
+ '【AI推荐采纳】点击推荐后,TagInput 默认按“追加去重”把推荐标签写入当前标签列表,并触发 onAdoptSuggestion(suggestedTags);若传入 onRefreshAiSuggestions,应在本地轮换建议之外继续向外通知',
3063
+ '【Form】Form 的 type="tag-input" 字段必须完整引用 TagInput,不再用 Select 或原生结构临时代替',
3064
+ '【尺寸】仅保留 MD 一种尺寸,高度固定 36px;与 Input 保持一致',
3065
+ ],
3066
+ examples: [
3067
+ { label: '空态', code: '<TagInput placeholder="请输入" />' },
3068
+ { label: '有值', code: '<TagInput defaultValue={["标签", "标签", "标签"]} />' },
3069
+ { label: '窄宽度折叠', code: '<TagInput className="!w-[220px]" defaultValue={["标签", "标签", "标签", "标签", "标签"]} />' },
3070
+ { label: '错误态', code: '<TagInput status="error" defaultValue={["标签"]} />' },
3071
+ { label: '禁用', code: '<TagInput disabled defaultValue={["标签", "标签"]} />' },
3072
+ { label: '受控', code: '<TagInput value={tags} onChange={(nextTags) => setTags(nextTags)} />' },
3073
+ { label: '❌ Bad(手搓输入框 + 自渲染 Tag)', code: '/* 禁止!缺折叠、Tooltip、删除回调 */\n<div className="border rounded p-2"><Tag>A</Tag><Tag>B</Tag><input /></div>' },
3074
+ { label: '✅ Good(用 TagInput)', code: '<TagInput defaultValue={["标签 A", "标签 B"]} onChange={(next, removed) => {}} />' },
3075
+ ],
3076
+ keywords: [
3077
+ 'TagInput', 'tag-input', '标签输入', '多标签输入', '关键字输入', 'multi tag input',
3078
+ '已选标签', '+N 折叠', '可关闭标签输入',
3079
+ 'closable', '受控', '非受控',
3080
+ // 高频场景
3081
+ '关键词输入', '行业输入', '多标签字段', '人员选择',
3082
+ // 错误信号词
3083
+ '手搓 Tag 输入框', 'border rounded p-2 tag', '自渲染 Tag 列表',
3084
+ ],
3085
+ },
3086
+ {
3087
+ id: 'tag',
3088
+ name: 'Tag',
3089
+ element: 'span',
3090
+ category: 'basic',
3091
+ description: '标签组件,用于标记、分类或状态展示。支持 13 种颜色变体、2 档字体粗细、3 档尺寸、3 种圆角、可关闭功能。',
3092
+ componentFile: './components/Tag.jsx',
3093
+ tokensFile: './components/Tag.tokens.js',
3094
+ props: [
3095
+ { name: 'variant', type: 'enum', options: ['brand', 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'pink', 'teal', 'grey', 'white', 'ai'], default: 'brand' },
3096
+ { name: 'fontWeight', type: 'enum', options: ['regular', 'bold'], default: 'bold' },
3097
+ { name: 'size', type: 'enum', options: ['s', 'm', 'l'], default: 'm' },
3098
+ { name: 'radius', type: 'enum', options: ['md', 'full'], default: 'md' },
3099
+ { name: 'showIcon', type: 'boolean', default: false },
3100
+ { name: 'iconName', type: 'string', default: 'tag-01-stroked' },
3101
+ { name: 'closable', type: 'boolean', default: false },
3102
+ { name: 'onClose', type: 'function', default: null },
3103
+ ],
3104
+ labels: {
3105
+ variant: {
3106
+ brand: '全部',
3107
+ red: '红色',
3108
+ orange: '橙色',
3109
+ yellow: '黄色',
3110
+ green: '绿色',
3111
+ cyan: '青色',
3112
+ blue: '蓝色',
3113
+ purple: '紫色',
3114
+ pink: '粉色',
3115
+ teal: '青绿色',
3116
+ grey: '灰色',
3117
+ white: '白色',
3118
+ ai: 'AI标签',
3119
+ },
3120
+ fontWeight: { regular: '细体', bold: '粗体' },
3121
+ size: { s: 'S', m: 'M', l: 'L' },
3122
+ radius: { md: '圆角', full: '全圆' },
3123
+ },
3124
+ _preview: TAG_PREVIEW,
3125
+ rules: [
3126
+ '【选型·vs Button】Tag 是「展示型」(只读标记/状态),不可点击触发动作;需要用户点击触发操作 → Button;需要用户点击切换选中状态(chips 筛选)→ 可用 closable Tag 配合状态管理,但注意补键盘交互',
3127
+ '【选型·vs Badge】Tag 有文案(短词级标签,建议 2-4 字,尽量不超过 6 字)且横排展示;Badge 是数字角标(小红点/数字),叠加在图标或头像右上角',
3128
+ '【场景】表格行内状态标记(审核中/已通过/已拒绝);分类/行业标签列表;筛选条件 chip;TagInput 已选项;可关闭的多选结果展示',
3129
+ '【文案与换行】Tag 文案严禁换行,默认必须单行显示全部文案;因此内容只承载短标签,不承载长句或说明性文本。建议使用 2-4 字短文案,尽量不超过 6 字。',
3130
+ '【禁忌】不要把 Tag 当 Button 用(无 role="button"、无键盘触发语义);不要放超过 6 个字的长文案、整句或描述型文本(会拉宽标签且降低可读性);不要在同一区域混用超过 4 种颜色变体(视觉噪音)',
3131
+ '【语义色速查】red → 错误/危险;orange / yellow → 警告;green → 成功/通过;cyan / blue → 信息/进行中;purple / pink / teal → 中性状态分类;brand → 品牌/当前选中态;grey / white → 中性/禁用;ai → AI 功能标识',
3132
+ '【颜色变体】支持 13 种颜色(暖色调 → 冷色调 → 中性 → 品牌 → AI);全部使用浅背景 + 深文字;white 变体单独有描边,适合白底卡片场景',
3133
+ '【AI 标签】variant="ai" 使用 ai-fill-1 渐变背景 + ai-fill-text 文字,其余逻辑与普通 Tag 一致',
3134
+ '【尺寸选择】**默认 `size="m"`**(20px 高,与组件实现一致):列表父行、筛选 chip、卡片标签、状态标记等页面主区域优先用 **m**;**s(16px)** 仅用于表格行内、辅助元数据等极紧凑行;**l(24px)** 用于需要强调的少量标签块',
3135
+ '【禁止手搓】不要用 `span` + 圆角底 + 文字色手写「假 Tag」;状态/分类展示一律用 `Tag` + `variant` + `size`',
3136
+ '【圆角选择】md(8px,默认)→ 通用方形圆角;full(9999px)→ 全圆胶囊,常用于筛选 chip',
3137
+ '【字体粗细】bold(默认)→ 标签文字字重固定为 `700`(`--font-bold`);regular → 字重固定为 `400`,仅用于弱化展示,其余样式不变',
3138
+ '【可关闭】closable=true 时出现关闭按钮,点击触发 onClose;常用于已选标签列表或可移除筛选条件',
3139
+ '【图标】showIcon=true 时图标固定 12px(xs 规格),图标右侧间距 4px;`iconName` 必须为 B 端 Icon 库中的 **kebab-case** 名称',
3140
+ ],
3141
+ examples: [
3142
+ { label: '基础用法(默认粗体)', code: '<Tag>标签</Tag>' },
3143
+ { label: '品牌色', code: '<Tag variant="brand">品牌标签</Tag>' },
3144
+ { label: 'AI标签', code: '<Tag variant="ai">AI标签</Tag>' },
3145
+ { label: '细体标签', code: '<Tag fontWeight="regular">细体标签</Tag>' },
3146
+ { label: '纯白色', code: '<Tag variant="white">白色标签</Tag>' },
3147
+ { label: '多种颜色', code: '<Tag variant="red">红色</Tag><Tag variant="green">绿色</Tag><Tag variant="blue">蓝色</Tag>' },
3148
+ { label: 'S 尺寸(表格行内)', code: '<Tag size="s">S 标签</Tag>' },
3149
+ { label: 'M 尺寸', code: '<Tag size="m">M 标签</Tag>' },
3150
+ { label: 'L 尺寸', code: '<Tag size="l">L 标签</Tag>' },
3151
+ { label: '圆角(默认)', code: '<Tag radius="md">圆角标签</Tag>' },
3152
+ { label: '全圆', code: '<Tag radius="full">全圆标签</Tag>' },
3153
+ { label: '显示图标', code: '<Tag showIcon>带图标标签</Tag>' },
3154
+ { label: '自定义图标', code: '<Tag showIcon iconName="heart-stroked">带心形图标</Tag>' },
3155
+ { label: '可关闭', code: '<Tag closable onClose={() => {}}>可关闭标签</Tag>' },
3156
+ { label: '图标+可关闭', code: '<Tag showIcon closable onClose={() => {}}>带图标可关闭</Tag>' },
3157
+ { label: '❌ Bad(用 span+rounded 手搓 Tag)', code: '/* 禁止!状态/分类一律用 Tag 组件 */\n<span className="px-2 py-1 bg-red-100 text-red-700 rounded">已拒绝</span>' },
3158
+ { label: '✅ Good(用 Tag 表达状态)', code: '<Tag variant="red">已拒绝</Tag>' },
3159
+ { label: '❌ Bad(Tag 当面板切换)', code: '/* 禁止!面板切换用 Tabs,不用 Tag */\n<div className="flex gap-2"><Tag variant="brand">Input</Tag><Tag variant="grey">Output</Tag></div>' },
3160
+ { label: '✅ Good(面板切换用 Tabs)', code: '<Tabs variant="segment" items={[{label:"Input"},{label:"Output"}]} />' },
3161
+ ],
3162
+ keywords: [
3163
+ 'Tag', 'tag', '标签', '状态标签', '分类标签', '状态标记', 'badge tag', 'chip',
3164
+ 'brand', 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'pink', 'teal', 'grey', 'white', 'ai',
3165
+ // 状态语义
3166
+ '已通过', '已拒绝', '审核中', '进行中', '已完成', '失败', '成功', '警告', '错误', '草稿',
3167
+ 'success status', 'error status', 'warning status', 'info status',
3168
+ // 分类
3169
+ '行业标签', '类型标签', '业务标签', '场景标签', '类型分类',
3170
+ // chips / 筛选
3171
+ '筛选 chip', '筛选标签', '已选标签', 'filter chip', 'filter tag', 'pill chip',
3172
+ // 圆角
3173
+ 'closable', '可关闭', 'radius full', '全圆胶囊',
3174
+ // 错误信号词
3175
+ 'span rounded bg-', '手搓 Tag', '自制状态标签', 'px-2 py-1 bg-red-100',
3176
+ ],
3177
+ },
3178
+ {
3179
+ id: 'toast',
3180
+ name: 'Toast',
3181
+ element: 'div',
3182
+ category: 'basic',
3183
+ description:
3184
+ '轻量反馈条:信息/成功/警示/错误四态,左侧状态图标(相对行首额外 4px 左边距)+ 主文案 + 可选文字操作(绿色文字 Button)+ 可关闭;宽度随内容收缩(w-fit),最大不超过 min(100%,560px);圆角 12px、可选语义描边(bordered)、内距 12px、主文案 14px 半粗,行内纵向居中对齐。规范仅覆盖单条条目视觉;全局队列、停留时长与 Portal 由业务集成(对齐 HiUI Toast)。',
3185
+ componentFile: './components/Toast.jsx',
3186
+ tokensFile: './components/Toast.tokens.js',
3187
+ props: [
3188
+ {
3189
+ name: 'type',
3190
+ type: 'enum',
3191
+ options: ['info', 'success', 'warning', 'error'],
3192
+ default: 'info',
3193
+ },
3194
+ { name: 'message', type: 'string', default: '补充信息或状态切换' },
3195
+ { name: 'children', type: 'node', default: null },
3196
+ { name: 'actions', type: 'array', default: null },
3197
+ { name: 'bordered', type: 'boolean', default: true },
3198
+ { name: 'showClose', type: 'boolean', default: true },
3199
+ { name: 'onClose', type: 'function', default: null },
3200
+ { name: 'className', type: 'string', default: '' },
3201
+ ],
3202
+ labels: {
3203
+ type: {
3204
+ info: '信息',
3205
+ success: '成功',
3206
+ warning: '警示',
3207
+ error: '错误',
3208
+ },
3209
+ bordered: { true: '显示描边', false: '隐藏描边' },
3210
+ },
3211
+ _preview: TOAST_PREVIEW,
3212
+ rules: [
3213
+ FONT_WEIGHT_RUNTIME_RULE,
3214
+ '【语义】info 补充说明;success 正向结果;warning 可恢复风险;error 已发生失败',
3215
+ '【结构】左状态图标(20px,ml-1)+ 主文案(message 或 children)+ 可选 actions(1~2 项,Button text-brand 14px)+ 可选关闭;四段顺序排列,相邻水平间距均为 8px',
3216
+ '【关闭】showClose 且传入 onClose 时渲染;默认 Button ghost-black sm iconOnly(无色底、黑/前景字、纯图标 X),与主行垂直居中',
3217
+ '【无障碍】error 使用 role="alert" + aria-live="assertive";其余 role="status" + polite',
3218
+ '【集成】单组件不包含定时器与堆叠;页面级 Toast 容器请在业务中实现队列与自动消失',
3219
+ '【与 Tooltip】Tooltip 为悬浮说明无强语义色;Toast 为结果反馈且带状态色与可选操作',
3220
+ '【描边】bordered=false 时隐藏语义色描边(边框透明),圆角与阴影仍保留',
3221
+ ],
3222
+ examples: [
3223
+ { label: '信息', code: '<Toast type="info" message="补充信息或状态切换" onClose={() => {}} />' },
3224
+ { label: '成功', code: '<Toast type="success" message="表单保存成功" onClose={() => {}} />' },
3225
+ { label: '警示', code: '<Toast type="warning" message="可能出现的错误" onClose={() => {}} />' },
3226
+ { label: '错误', code: '<Toast type="error" message="已经出现的错误" onClose={() => {}} />' },
3227
+ {
3228
+ label: '单项操作',
3229
+ code: '<Toast type="info" message="补充信息或状态切换" actions={[{ label: "操作1", onClick: () => {} }]} onClose={() => {}} />',
3230
+ },
3231
+ {
3232
+ label: '两项操作',
3233
+ code:
3234
+ '<Toast type="info" message="补充信息或状态切换" actions={[{ label: "操作1", onClick: () => {} }, { label: "操作2", onClick: () => {}}]} onClose={() => {}} />',
3235
+ },
3236
+ { label: '无关闭', code: '<Toast type="success" message="保存成功" showClose={false} />' },
3237
+ { label: '无描边', code: '<Toast type="success" message="表单保存成功" bordered={false} onClose={() => {}} />' },
3238
+ { label: '❌ Bad(用 Modal 显示「保存成功」)', code: '/* 禁止!结果反馈短提示用 Toast */\n<Modal title="保存成功" onClose={() => {}} />' },
3239
+ { label: '✅ Good(用 Toast)', code: '<Toast type="success" message="保存成功" onClose={() => {}} />' },
3240
+ { label: '❌ Bad(手搓提示条)', code: '/* 禁止!缺图标、语义色、role=alert */\n<div className="p-3 bg-green-100 text-green-700 rounded">操作成功</div>' },
3241
+ { label: '✅ Good(用 Toast 语义反馈)', code: '<Toast type="success" message="操作成功" onClose={() => {}} />' },
3242
+ ],
3243
+ keywords: [
3244
+ 'Toast', 'toast', '轻提示', '反馈条', '提示条', '通知条', 'snackbar', 'notification',
3245
+ 'info', 'success', 'warning', 'error',
3246
+ '保存成功', '提交成功', '删除成功', '操作成功', '保存失败', '提交失败', '操作失败',
3247
+ '请求失败', '网络异常', '连接异常', '提示信息',
3248
+ // 错误信号词
3249
+ 'p-3 bg-green-100', 'bg-red-100 text-red-700', '手搓提示条', '自制反馈',
3250
+ ],
3251
+ },
3252
+ {
3253
+ id: 'tooltip',
3254
+ name: 'Tooltip',
3255
+ element: 'div',
3256
+ category: 'basic',
3257
+ description: '文字提示气泡,hover/focus 触发,支持 12 个方位与视口边缘自动翻转。默认深色变体(grey-800 背景 + 白字),light 白底变体仅用于 TagInput +N 更多标签场景;文本容器固定规格(圆角 8px、内距 8/12、字号 14px),通过 Portal 渲染到 body 以避开父容器裁切。',
3258
+ componentFile: './components/Tooltip.jsx',
3259
+ tokensFile: './components/Tooltip.tokens.js',
3260
+ props: [
3261
+ {
3262
+ name: 'placement',
3263
+ type: 'enum',
3264
+ options: [
3265
+ 'top', 'top-start', 'top-end',
3266
+ 'bottom', 'bottom-start', 'bottom-end',
3267
+ 'left', 'left-start', 'left-end',
3268
+ 'right', 'right-start', 'right-end',
3269
+ ],
3270
+ default: 'top',
3271
+ },
3272
+ { name: 'arrow', type: 'boolean', default: true },
3273
+ { name: 'autoFlip', type: 'boolean', default: true },
3274
+ { name: 'defaultOpen', type: 'boolean', default: false },
3275
+ { name: 'mouseEnterDelay', type: 'number', default: 100 },
3276
+ { name: 'mouseLeaveDelay', type: 'number', default: 100 },
3277
+ { name: 'content', type: 'node', default: null },
3278
+ { name: 'tone', type: 'enum', options: ['dark', 'light'], default: 'dark' },
3279
+ ],
3280
+ labels: {
3281
+ placement: {
3282
+ top: '上方',
3283
+ 'top-start': '上方·左对齐',
3284
+ 'top-end': '上方·右对齐',
3285
+ bottom: '下方',
3286
+ 'bottom-start': '下方·左对齐',
3287
+ 'bottom-end': '下方·右对齐',
3288
+ left: '左侧',
3289
+ 'left-start': '左侧·顶对齐',
3290
+ 'left-end': '左侧·底对齐',
3291
+ right: '右侧',
3292
+ 'right-start': '右侧·顶对齐',
3293
+ 'right-end': '右侧·底对齐',
3294
+ },
3295
+ },
3296
+ _preview: TOOLTIP_PREVIEW,
3297
+ rules: [
3298
+ '【选型·vs Modal】1-3 行补充说明、无需用户操作 → Tooltip;内容超 3 行、需要链接/按钮/表单交互 → Modal 或 Sheet',
3299
+ '【选型·vs description 文案】信息对所有用户都重要 → 直接显示为说明文案;信息只在用户疑惑时才需要 → Tooltip(不要依赖 hover 传达核心信息)',
3300
+ '【场景·必配】图标按钮(Button iconOnly)必须传 Button 自身的 `tooltip` prop 说明操作含义,否则无文案用户无法理解;仅在非 Button 触发器上才外层手动包 `<Tooltip>`',
3301
+ '【场景·表头】列标题含义模糊时加 Tooltip;placement 用 top,autoFlip=true',
3302
+ '【场景·表单说明】字段旁的补充说明(如"格式要求");placement 用 top-start,贴近字段左侧',
3303
+ '【场景·截断文本】文本超长截断时悬浮显示完整内容;content 传完整字符串,placement 用 top',
3304
+ '【禁忌】不要在 Tooltip 内放链接、按钮或任何可交互元素(用户悬浮才能触发,移动端完全不可用);不要用 Tooltip 传递核心操作路径(用户可能永远不会 hover);不要放超过 3 行的长内容',
3305
+ '【组合·图标按钮】`<Button variant="ghost-black" iconOnly tooltip="删除" icon={<Icon name="trash-01-stroked" />} />`;不要再外层重复包 Tooltip',
3306
+ '【触发方式】仅 hover/focus 触发;进入/离开各延迟 100ms,避免快速滑过频繁闪烁;mouseEnterDelay / mouseLeaveDelay 可调整',
3307
+ '【方位选择】top 为默认;表单字段说明用 top-start;表头说明用 top;行内 icon 说明用 top 或 right',
3308
+ '【文本规格】固定字号 14px、行高 20px、内距 8/12、圆角 8px;最大宽度 280px 自动换行',
3309
+ '【白底例外】tone="light" 纯白背景 + 深色文字,仅供 TagInput +N 更多标签场景;常规说明继续用默认深色',
3310
+ '【容器溢出】气泡通过 Portal 渲染到 document.body(position:fixed),不受父容器 overflow:hidden 裁切',
3311
+ '【无障碍】气泡设置 role="tooltip",触发器自动绑定 aria-describedby;defaultOpen 仅用于预览/调试,生产环境不要打开',
3312
+ ],
3313
+ examples: [
3314
+ { label: '默认(上方)', code: '<Tooltip content="这是一段提示"><Button>悬浮我</Button></Tooltip>' },
3315
+ { label: '指定方位', code: '<Tooltip content="右下提示" placement="right-end"><span>右下</span></Tooltip>' },
3316
+ { label: '多行内容', code: '<Tooltip content={`第一行说明\\n第二行补充`}><span>详情</span></Tooltip>' },
3317
+ { label: '关闭箭头', code: '<Tooltip content="无箭头" arrow={false}><span>无箭头</span></Tooltip>' },
3318
+ { label: '关闭自动翻转', code: '<Tooltip content="不翻转" autoFlip={false}><span>固定方向</span></Tooltip>' },
3319
+ { label: '自定义延迟', code: '<Tooltip content="慢出慢隐" mouseEnterDelay={300} mouseLeaveDelay={200}><span>延迟</span></Tooltip>' },
3320
+ { label: '❌ Bad(用 title 属性当 Tooltip)', code: '/* 禁止!原生 title 在不同浏览器样式不一致、无 placement、无延迟控制 */\n<button title="删除">🗑</button>' },
3321
+ { label: '✅ Good(Button tooltip + iconOnly)', code: '<Button variant="ghost-black" iconOnly tooltip="删除" icon={<Icon name="trash-01-stroked" />} />' },
3322
+ { label: '❌ Bad(图标按钮无 Tooltip)', code: '/* 禁止!iconOnly 必须配 Tooltip 解释含义 */\n<Button variant="ghost-black" iconOnly icon={<Icon name="settings-01-stroked" />} />' },
3323
+ { label: '✅ Good(图标按钮 + tooltip)', code: '<Button variant="ghost-black" iconOnly tooltip="设置" icon={<Icon name="settings-01-stroked" />} />' },
3324
+ ],
3325
+ keywords: [
3326
+ 'Tooltip', 'tooltip', '提示气泡', '悬浮提示', 'hover 提示', '悬停提示',
3327
+ 'placement', 'top', 'bottom', 'left', 'right', '上方提示', '下方提示',
3328
+ '图标按钮提示', 'icon button tooltip', 'iconOnly tooltip', 'Button tooltip',
3329
+ '表头提示', '字段说明', '截断提示', '溢出提示',
3330
+ // 错误信号词
3331
+ 'title 属性', 'native title', 'title="…"', '原生 title',
3332
+ ],
3333
+ },
3334
+ {
3335
+ id: 'empty',
3336
+ name: 'Empty',
3337
+ element: 'div',
3338
+ category: 'basic',
3339
+ description: '空状态占位组件,固定布局(插画 → 文案 → 操作),所有内容区域独立配置、按需组合。10 种内置插画对应常见场景;标题/描述/链接行/三种按钮均可独立传入或省略;buttonLayout 控制横排/竖排。',
3340
+ componentFile: './components/Empty.jsx',
3341
+ tokensFile: './components/Empty.tokens.js',
3342
+ props: [
3343
+ {
3344
+ name: 'illustrationType',
3345
+ type: 'enum',
3346
+ options: ['no-result', 'no-content', 'not-found', 'no-access', 'failure', 'idle', 'success', 'construction', 'administrator-1', 'administrator-2'],
3347
+ default: 'no-content',
3348
+ },
3349
+ {
3350
+ name: 'buttonLayout',
3351
+ type: 'enum',
3352
+ options: ['row', 'column'],
3353
+ default: 'row',
3354
+ },
3355
+ { name: 'title', type: 'string', default: '空状态标题' },
3356
+ { name: 'description', type: 'string', default: '这是一段对当前空状态的描述文本' },
3357
+ { name: 'primaryLabel', type: 'string', default: '一级操作' },
3358
+ { name: 'onPrimary', type: 'function', default: null },
3359
+ { name: 'secondaryLabel', type: 'string', default: '' },
3360
+ { name: 'onSecondary', type: 'function', default: null },
3361
+ { name: 'tertiaryLabel', type: 'string', default: '' },
3362
+ { name: 'onTertiary', type: 'function', default: null },
3363
+ { name: 'linkPrefix', type: 'string', default: '' },
3364
+ { name: 'linkText', type: 'string', default: '' },
3365
+ { name: 'onLinkClick', type: 'function', default: null },
3366
+ ],
3367
+ labels: {
3368
+ illustrationType: {
3369
+ 'no-result': '搜索无结果',
3370
+ 'no-content': '通用空状态',
3371
+ 'not-found': '页面不存在',
3372
+ 'no-access': '无权限',
3373
+ 'failure': '加载失败',
3374
+ 'idle': '空闲',
3375
+ 'success': '成功',
3376
+ 'construction': '建设中',
3377
+ 'administrator-1':'管理员 1',
3378
+ 'administrator-2':'管理员 2',
3379
+ },
3380
+ buttonLayout: {
3381
+ row: '横排',
3382
+ column: '竖排',
3383
+ },
3384
+ },
3385
+ _preview: {
3386
+ base: {
3387
+ illustrationSize: '200px',
3388
+ titleFontSize: '20px',
3389
+ titleFontWeight: '600',
3390
+ descFontSize: '14px',
3391
+ linkColor: '#129683',
3392
+ },
3393
+ },
3394
+ rules: [
3395
+ FONT_WEIGHT_RUNTIME_RULE,
3396
+ '【插画选型】no-content 首次使用/数据为空;no-result 搜索/筛选无结果;not-found 页面 404;no-access 无权限;failure 网络/加载出错;construction 功能建设中',
3397
+ '【标题与描述】标题建议 6-12 字,描述 1-2 句(≤ 30 字);二者均可单独省略,省略时对应行不渲染',
3398
+ '【链接行】linkText + linkPrefix 组合用于轻量错误提示(如"页面出错了,刷新试试"),可与标题/描述同时存在或单独使用',
3399
+ '【按钮数量】建议最多 2 个按钮;常用组合:一级 + 二级(横排)、一级 + 文字按钮(竖排);3 个按钮同时出现时竖排更清晰',
3400
+ '【横排 vs 竖排】buttonLayout="row" 适合页面级空状态(空间充裕);buttonLayout="column" 适合侧边栏/卡片等局部空间',
3401
+ '【仅说明时】不传任何按钮 label,组件仅展示插画 + 文案,不渲染操作区',
3402
+ '【搜索无结果】illustrationType="no-result" title="未找到搜索结果" description="请尝试更换关键词"',
3403
+ '【无权限】illustrationType="no-access" title="无权限查看该页面" description="如需查看,请联系管理员" primaryLabel="联系管理员" secondaryLabel="返回首页"',
3404
+ '【页面不存在】illustrationType="not-found" title="没有找到页面" description="页面不存在,请确认网址是否正确" primaryLabel="返回首页"',
3405
+ '【加载失败】illustrationType="failure" linkPrefix="页面显示失败," linkText="刷新重试" onLinkClick={handleRetry}',
3406
+ '【竖排多操作】buttonLayout="column" primaryLabel="立即创建" tertiaryLabel="查看帮助文档"',
3407
+ ],
3408
+ examples: [
3409
+ { label: '通用空状态', code: '<Empty title="暂无数据" description="当前没有可显示的内容" primaryLabel="立即创建" />' },
3410
+ { label: '搜索无结果', code: '<Empty illustrationType="no-result" title="未找到搜索结果" description="请尝试更换关键词或清除筛选条件" />' },
3411
+ { label: '无权限', code: '<Empty illustrationType="no-access" title="无权限查看该页面" description="如需查看,请联系管理员开通权限" primaryLabel="联系管理员" secondaryLabel="返回首页" />' },
3412
+ { label: '页面不存在', code: '<Empty illustrationType="not-found" title="没有找到页面" description="页面不存在,请确认网址是否正确" primaryLabel="返回首页" />' },
3413
+ { label: '加载失败(链接型)', code: '<Empty illustrationType="failure" linkPrefix="页面显示失败," linkText="刷新重试" onLinkClick={() => window.location.reload()} />' },
3414
+ { label: '竖排多操作', code: '<Empty illustrationType="idle" title="暂无我的处理中工单" description="是时候思考宇宙的奥秘了" buttonLayout="column" primaryLabel="查看全部工单" tertiaryLabel="了解工单流程" />' },
3415
+ { label: '建设中', code: '<Empty illustrationType="construction" title="功能建设中" description="该功能正在开发,敬请期待" />' },
3416
+ { label: '纯说明(无操作)', code: '<Empty illustrationType="no-result" title="未找到搜索结果" description="换个关键词试试" />' },
3417
+ { label: '❌ Bad(手搓空状态文案)', code: '/* 禁止!缺少插画、缺少操作引导、缺少 token */\n<div className="text-center text-gray-400 py-10">暂无数据</div>' },
3418
+ { label: '✅ Good(用 Empty 组件)', code: '<Empty title="暂无数据" description="当前没有可显示的内容" primaryLabel="立即创建" />' },
3419
+ { label: '❌ Bad(404 页面手搓)', code: '/* 禁止!404 页面应使用 not-found 插画 */\n<div><h1>404</h1><p>页面不存在</p></div>' },
3420
+ { label: '✅ Good(用 Empty not-found)', code: '<Empty illustrationType="not-found" title="没有找到页面" description="页面不存在" primaryLabel="返回首页" />' },
3421
+ ],
3422
+ keywords: [
3423
+ 'Empty', 'empty', '空状态', '空状态页', '占位空状态', 'empty state',
3424
+ 'no-result', 'no-content', 'not-found', 'no-access', 'failure', 'idle', 'success', 'construction',
3425
+ // 高频场景
3426
+ '搜索无结果', '无搜索结果', '没有结果', '暂无数据', '没有数据', '无数据',
3427
+ '404 页面', '页面不存在', 'Not Found',
3428
+ '无权限', '无访问权限', '权限不足', 'Forbidden', '403',
3429
+ '加载失败', '请求失败', '网络异常', '网络错误',
3430
+ '建设中', '功能建设中', '敬请期待', 'Coming Soon',
3431
+ // 错误信号词
3432
+ 'text-center text-gray-400', '手搓空状态', '404 页面手搓', '自制空状态文案',
3433
+ ],
3434
+ },
3435
+
3436
+ {
3437
+ id: 'full-screen-page',
3438
+ name: 'FullScreenPage',
3439
+ element: 'div',
3440
+ category: 'business',
3441
+ description: '全屏操作页容器,覆盖整个模版框架(含侧边导航),适合分步骤编辑、创建页面不同板块内容流程,以及内容量大、需沉浸长时间编辑的业务场景。支持白底(直铺/描边面板)和灰底(白卡面板)两种背景模式。',
3442
+ componentFile: './components/FullScreenPage.jsx',
3443
+ tokensFile: './components/FullScreenPage.tokens.js',
3444
+ props: [
3445
+ { name: 'bg', type: 'enum', options: ['white', 'grey'], default: 'white' },
3446
+ { name: 'title', type: 'string', default: '页面标题' },
3447
+ { name: 'onBack', type: 'function', default: null },
3448
+ { name: 'headerActions', type: 'node', default: null },
3449
+ ],
3450
+ labels: {
3451
+ bg: { white: '白色背景', grey: '灰色背景' },
3452
+ },
3453
+ _preview: {
3454
+ bg: {
3455
+ white: { bgColor: '#FFFFFF' },
3456
+ grey: { bgColor: '#F2F4F7' },
3457
+ },
3458
+ base: {
3459
+ headerHeight: '56px',
3460
+ headerBg: '#FFFFFF',
3461
+ borderColor: '#E4E7EC',
3462
+ titleFontSize: '16px',
3463
+ titleFontWeight: '600',
3464
+ },
3465
+ },
3466
+ rules: [
3467
+ FONT_WEIGHT_RUNTIME_RULE,
3468
+ '【选型链·四级体量】按内容体量从小到大依次升级:Modal(快速确认/小表单,最大 85vh)→ Sheet(侧边补充信息/中等表单,保留主页面上下文)→ Modal layout="fullscreen"(结构化多步表单,底部弹出沉浸)→ FullScreenPage(内容体量最大、需长时间专注编辑,完全覆盖所有界面)',
3469
+ '【组件归类】FullScreenPage 属于业务组件,不属于基础组件;它表达的是“覆盖式业务流程页面布局”,用于创建/编辑流程,而不是单个原子控件。',
3470
+ '【选型·何时用 FullScreenPage】满足以下任一条件即升级到 FullScreenPage:① 创建或编辑页面需要分步骤完成(如步骤一基础信息、步骤二内容配置、步骤三预览发布);② 编辑内容有多个独立区块(如基础信息 + 富文本 + 关联配置),用 Sheet/Modal 会让内容过度压缩;③ 操作预计耗时超过 3 分钟,需要沉浸式专注环境;④ 需要多栏布局或内嵌预览(如左侧编辑 + 右侧实时预览)',
3471
+ '【选型·不要用 FullScreenPage 的情况】字段少于 8 个的简单表单 → Sheet 或 Modal center;只需要二次确认 → Modal center sm;用户需要同时参考主页面数据 → Sheet(用户能看到背景内容)',
3472
+ '【场景·白底 bg="white"】创建知识库条目、填写工单详情、配置单一策略、分步骤创建 QA 对——表单结构相对扁平,页面可直铺在白底上;若存在步骤面板/信息块,优先用透明或白底直铺 + 1px 灰色描边,而不是再套整块白卡',
3473
+ '【场景·灰底 bg="grey"】复杂多区块编辑(如内容平台文章编辑器:基础信息卡 + 正文编辑卡 + SEO 设置卡)或分步骤编辑不同板块内容——灰底 + 白色卡片形成明显的区块分层,适合信息密度高的场景',
3474
+ '【面板样式·白底】bg="white" 时,步骤面板/表单分组应表现为“轻面板”:推荐 transparent 或 surface 底色 + border border-default + rounded-xl;重点是保持页面整体一体化,不要在白底里再堆叠大面积白卡',
3475
+ '【面板样式·灰底】bg="grey" 时,步骤面板/表单分组应表现为“白卡面板”:必须使用 bg-white + rounded-xl + 合理内边距(通常 p-6);通过灰底页面与白卡面板的对比拉开层次,避免灰底上直接裸露复杂表单内容',
3476
+ '【覆盖范围】使用 absolute inset-0,须挂在 relative 定位的父级容器内;会完全覆盖包括侧边导航在内的所有区域,用户进入后只能通过顶栏返回按钮退出',
3477
+ '【顶栏结构】左:返回箭头(传 onBack 才渲染)+ 标题(16px/600);右:headerActions 插槽,按钮组从左到右权重递增,最右侧放主操作(如"发布"用 primary,"保存草稿"用 outline-black)',
3478
+ '【灰底模式注意】bg="grey" 时页面背景为浅灰,内容区块必须在 children 内自行包白色卡片(rounded-xl + bg-white + p-6),组件本身不自动生成卡片',
3479
+ '【切换原则】不要只把背景色从 white 改成 grey 却沿用同一套面板样式;背景模式切换时,内部面板样式也必须同步切换为“白底描边面板”或“灰底白卡面板”这两套规范之一',
3480
+ '【禁忌】不要在 FullScreenPage 内再弹 FullScreenPage(层级混乱);不要把 FullScreenPage 当普通路由页面用(它的定位是覆盖在现有页面之上的操作层,不是新页面)',
3481
+ ],
3482
+ examples: [
3483
+ { label: '白底创建页(描边面板)', code: '<FullScreenPage title="创建QA对" onBack={() => handleBack()} headerActions={<><Button variant="outline-black">保存草稿</Button><Button variant="primary">发布</Button></>}><div className="flex gap-4 p-6 h-full"><section className="flex-1 rounded-xl border border-default p-6">基础信息面板</section><aside className="w-60 rounded-xl border border-default p-6">辅助配置面板</aside></div></FullScreenPage>' },
3484
+ { label: '灰底编辑页(白卡面板)', code: '<FullScreenPage bg="grey" title="编辑策略" onBack={() => handleBack()} headerActions={<Button variant="primary">保存</Button>}><div className="flex gap-4 p-6 h-full"><section className="flex-1 rounded-xl bg-white p-6">主编辑面板</section><aside className="w-60 rounded-xl bg-white p-6">配置侧栏面板</aside></div></FullScreenPage>' },
3485
+ { label: '❌ Bad(用 Modal fullscreen 当沉浸式编辑器)', code: '/* 禁止!长时间编辑应升级到 FullScreenPage,覆盖侧栏 */\n<Modal layout="fullscreen" title="编辑策略">…</Modal>' },
3486
+ { label: '✅ Good(用 FullScreenPage)', code: '<FullScreenPage title="编辑策略" onBack={() => {}} headerActions={<Button variant="primary">保存</Button>}>…</FullScreenPage>' },
3487
+ ],
3488
+ keywords: [
3489
+ 'FullScreenPage', 'full-screen-page', '全屏页', '全屏操作页', '全屏编辑器', '沉浸式页面',
3490
+ 'fullscreen page', 'immersive editor', 'create page', 'edit page',
3491
+ '创建页', '编辑页', '创建流程', '编辑流程', '分步骤编辑', '分步骤创建',
3492
+ '步骤编辑页', '多步骤创建页', '页面板块编辑', '不同板块内容流程',
3493
+ '富文本编辑器', '配置页', '详情编辑',
3494
+ '多区块编辑', '基础信息 富文本', '左编辑 右预览', '左右双栏',
3495
+ // 错误信号词
3496
+ '手搓全屏页', '自制 fullscreen', 'Modal fullscreen 当编辑器',
3497
+ ],
3498
+ },
3499
+ ];