@tfdesign/b-end 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tfdesign/b-end",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "TFDS B-end React components + Tailwind v4 theme.css; self-contained npm install (no monorepo clone required).",
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * TFDS 版本更新检测(只读,不修改用户项目)
4
+ *
5
+ * 在对方项目根目录执行:
6
+ * node node_modules/@tfdesign/b-end/scripts/check-tfds-update.mjs
7
+ *
8
+ * 也可以直接执行包内脚本:
9
+ * node /path/to/node_modules/@tfdesign/b-end/scripts/check-tfds-update.mjs
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = path.dirname(__filename);
17
+ const pkgRoot = path.resolve(__dirname, '..');
18
+ const packageJsonPath = path.join(pkgRoot, 'package.json');
19
+ const packageName = '@tfdesign/b-end';
20
+ const registryUrl = `https://registry.npmjs.org/${encodeURIComponent(packageName).replace('%2F', '%2f')}`;
21
+
22
+ function readPackageJson() {
23
+ try {
24
+ return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
25
+ } catch (error) {
26
+ console.log('\n[tfds-update-check] 无法读取本地 package.json');
27
+ console.log(` 路径:${packageJsonPath}`);
28
+ console.log(` 错误:${error?.message || error}`);
29
+ process.exit(1);
30
+ }
31
+ }
32
+
33
+ function formatDateToMinute(value) {
34
+ if (!value) return '未知';
35
+ const date = new Date(value);
36
+ if (Number.isNaN(date.getTime())) return '未知';
37
+
38
+ const parts = new Intl.DateTimeFormat('zh-CN', {
39
+ timeZone: 'Asia/Shanghai',
40
+ year: 'numeric',
41
+ month: '2-digit',
42
+ day: '2-digit',
43
+ hour: '2-digit',
44
+ minute: '2-digit',
45
+ hour12: false,
46
+ }).formatToParts(date);
47
+
48
+ const byType = Object.fromEntries(parts.map((part) => [part.type, part.value]));
49
+ return `${byType.year}-${byType.month}-${byType.day} ${byType.hour}:${byType.minute}`;
50
+ }
51
+
52
+ function parseVersion(version) {
53
+ return String(version || '')
54
+ .trim()
55
+ .replace(/^v/i, '')
56
+ .split('-')[0]
57
+ .split('.')
58
+ .map((part) => Number.parseInt(part, 10) || 0);
59
+ }
60
+
61
+ function compareVersions(a, b) {
62
+ const av = parseVersion(a);
63
+ const bv = parseVersion(b);
64
+ const len = Math.max(av.length, bv.length, 3);
65
+
66
+ for (let i = 0; i < len; i += 1) {
67
+ const left = av[i] || 0;
68
+ const right = bv[i] || 0;
69
+ if (left > right) return 1;
70
+ if (left < right) return -1;
71
+ }
72
+ return 0;
73
+ }
74
+
75
+ async function fetchNpmMetadata() {
76
+ const response = await fetch(registryUrl, {
77
+ headers: {
78
+ accept: 'application/json',
79
+ },
80
+ });
81
+
82
+ if (!response.ok) {
83
+ throw new Error(`npm registry 返回 ${response.status} ${response.statusText}`);
84
+ }
85
+
86
+ return response.json();
87
+ }
88
+
89
+ function printUpdateCommand() {
90
+ console.log('\n 建议更新命令:');
91
+ console.log(` npm install ${packageName}@latest --save`);
92
+ console.log(' 或重新执行一键安装:');
93
+ console.log(` npx -y -p ${packageName}@latest tfds-setup`);
94
+ }
95
+
96
+ async function main() {
97
+ const localPkg = readPackageJson();
98
+ const localVersion = localPkg.version;
99
+
100
+ console.log('\n╔══════════════════════════════════════════════════════╗');
101
+ console.log('║ TFDS NPM 版本更新检测 ║');
102
+ console.log('╚══════════════════════════════════════════════════════╝');
103
+
104
+ if (localPkg.name !== packageName || !localVersion) {
105
+ console.log('\n[tfds-update-check] 当前脚本不在有效的 @tfdesign/b-end 包内。');
106
+ console.log(` 读取到的包名:${localPkg.name || '未知'}`);
107
+ console.log(` 读取到的版本:${localVersion || '未知'}\n`);
108
+ process.exit(1);
109
+ }
110
+
111
+ let metadata;
112
+ try {
113
+ metadata = await fetchNpmMetadata();
114
+ } catch (error) {
115
+ console.log('\n⚠ 暂时无法连接 npm registry,已跳过线上版本检测。');
116
+ console.log(` 当前本地版本:v ${localVersion}`);
117
+ console.log(` 原因:${error?.message || error}`);
118
+ console.log('\n 不影响继续使用 TFDS。网络恢复后可重新执行:');
119
+ console.log(` node node_modules/${packageName}/scripts/check-tfds-update.mjs\n`);
120
+ process.exit(0);
121
+ }
122
+
123
+ const latestVersion = metadata?.['dist-tags']?.latest;
124
+ const localPublishedAt = metadata?.time?.[localVersion];
125
+ const latestPublishedAt = latestVersion ? metadata?.time?.[latestVersion] : metadata?.time?.modified;
126
+
127
+ if (!latestVersion) {
128
+ console.log('\n⚠ 已连接 npm registry,但没有读取到 latest 版本。');
129
+ console.log(` 当前本地版本:v ${localVersion}`);
130
+ console.log(' 请稍后重试,或打开 npm 页面手动确认:');
131
+ console.log(` https://www.npmjs.com/package/${packageName.replace('/', '%2F')}\n`);
132
+ process.exit(0);
133
+ }
134
+
135
+ const versionState = compareVersions(localVersion, latestVersion);
136
+
137
+ console.log(`\n 包名:${packageName}`);
138
+ console.log(` 当前版本:v ${localVersion}`);
139
+ console.log(` 当前版本更新时间:${formatDateToMinute(localPublishedAt)}`);
140
+ console.log(` 线上最新版本:v ${latestVersion}`);
141
+ console.log(` 最新版本更新时间:${formatDateToMinute(latestPublishedAt)}`);
142
+
143
+ if (versionState < 0) {
144
+ console.log('\n⚠ 检测到 TFDS 有新版本,可以选择更新后再生成页面。');
145
+ printUpdateCommand();
146
+ console.log('');
147
+ process.exit(0);
148
+ }
149
+
150
+ if (versionState > 0) {
151
+ console.log('\n✓ 当前本地版本高于 npm latest,可能是本地预发布或内部测试包。');
152
+ console.log(' 如果你不是在调试 TFDS 包,请确认安装来源是否符合预期。\n');
153
+ process.exit(0);
154
+ }
155
+
156
+ console.log('\n✓ 当前已是 npm latest,可以继续使用 TFDS 生成页面。\n');
157
+ }
158
+
159
+ main().catch((error) => {
160
+ console.log('\n[tfds-update-check] 更新检测脚本异常退出。');
161
+ console.log(` 错误:${error?.message || error}\n`);
162
+ process.exit(1);
163
+ });
@@ -1,4 +1,4 @@
1
- // 安装 @tfdesign/b-end 后自动执行三件事:
1
+ // 安装 @tfdesign/b-end 后自动执行四件事:
2
2
  //
3
3
  // 1. IDE Skill 自动写入(仅 opt-in 通道,不写 always-on)
4
4
  // → .cursor/skills/tfds/ (Cursor → /tfds 唤起)
@@ -21,11 +21,15 @@
21
21
  // 已有 theme.css import → 跳过;未找到 @import "tailwindcss" → 不改写,终端提示
22
22
  // 跳过:SKIP_TFDS_CSS_PATCH=1
23
23
  //
24
- // 3. 终端安装报告(已做了什么、还差什么、如何唤起规范)
24
+ // 3. npm 最新版本检测(只读提醒,不阻断安装)
25
+ // 跳过:SKIP_TFDS_UPDATE_CHECK=1
26
+ //
27
+ // 4. 终端安装报告(已做了什么、还差什么、如何唤起规范)
25
28
 
26
29
  import fs from 'node:fs';
27
30
  import os from 'node:os';
28
31
  import path from 'node:path';
32
+ import { spawnSync } from 'node:child_process';
29
33
  import { fileURLToPath } from 'node:url';
30
34
 
31
35
  const __filename = fileURLToPath(import.meta.url);
@@ -49,6 +53,7 @@ function readJson(p) {
49
53
  const projectPkg = readJson(path.join(projectRoot, 'package.json'));
50
54
  const isSelfPkg = projectPkg?.name === '@tfdesign/b-end';
51
55
  const hasNoPkg = !projectPkg;
56
+ const updateCheckScript = path.join(pkgRoot, 'scripts', 'check-tfds-update.mjs');
52
57
 
53
58
  if (hasNoPkg || isSelfPkg) {
54
59
  console.log('\n╔══════════════════════════════════════════════════════╗');
@@ -107,6 +112,33 @@ function readText(p) {
107
112
  }
108
113
  }
109
114
 
115
+ function runUpdateCheck() {
116
+ if (process.env.SKIP_TFDS_UPDATE_CHECK === '1') {
117
+ done.push('版本更新检测已跳过(SKIP_TFDS_UPDATE_CHECK=1)');
118
+ return;
119
+ }
120
+
121
+ if (!fs.existsSync(updateCheckScript)) {
122
+ todo.push('未找到版本更新检测脚本,已跳过 npm latest 检查。');
123
+ return;
124
+ }
125
+
126
+ const result = spawnSync(process.execPath, [updateCheckScript], {
127
+ cwd: projectRoot,
128
+ stdio: 'inherit',
129
+ env: process.env,
130
+ });
131
+
132
+ if (result.error) {
133
+ todo.push(`版本更新检测未完成:${result.error.message}`);
134
+ return;
135
+ }
136
+
137
+ if (result.status && result.status !== 0) {
138
+ todo.push('版本更新检测脚本返回异常,但不影响本次安装。可稍后手动执行:node node_modules/@tfdesign/b-end/scripts/check-tfds-update.mjs');
139
+ }
140
+ }
141
+
110
142
  function hasSourceSkillDiff(dest) {
111
143
  return REQUIRED_TFDS_SKILL_FILES.some((file) => {
112
144
  const sourceFile = path.join(skillSrc, file);
@@ -344,7 +376,13 @@ function patchEntryCss() {
344
376
  }
345
377
 
346
378
  // ─────────────────────────────────────────────────────────────────
347
- // Step 3: 终端安装报告
379
+ // Step 3: 版本更新检测(只读,不阻断安装)
380
+ // ─────────────────────────────────────────────────────────────────
381
+
382
+ runUpdateCheck();
383
+
384
+ // ─────────────────────────────────────────────────────────────────
385
+ // Step 4: 终端安装报告
348
386
  // ─────────────────────────────────────────────────────────────────
349
387
 
350
388
  console.log('\n╔══════════════════════════════════════════════════════╗');
@@ -375,7 +413,7 @@ console.log(
375
413
  ' 自检(只读):node node_modules/@tfdesign/b-end/scripts/check-tfds-integration.mjs',
376
414
  );
377
415
  console.log(
378
- ' 跳过 CSS 补丁:SKIP_TFDS_CSS_PATCH=1 npm i | 跳过 Skill:SKIP_TFDS_CURSOR_SKILL=1 npm i | 跳过 Codex 全局 Skill:SKIP_TFDS_CODEX_GLOBAL=1 npm i',
416
+ ' 跳过 CSS 补丁:SKIP_TFDS_CSS_PATCH=1 npm i | 跳过 Skill:SKIP_TFDS_CURSOR_SKILL=1 npm i | 跳过版本检测:SKIP_TFDS_UPDATE_CHECK=1 npm i',
379
417
  );
380
418
  console.log('');
381
419
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "system": "b-end",
3
3
  "skill": "tfds",
4
- "generatedAt": "2026-05-11T04:59:21.276Z",
4
+ "generatedAt": "2026-05-12T06:04:56.982Z",
5
5
  "summary": "B 端 COMPONENTS 36 条 + 列表页模板 4 + PATTERNS 6;import 一律来自 @tf-designsystem/b-end。",
6
6
  "components": [
7
7
  {
@@ -1430,7 +1430,7 @@
1430
1430
  "【信息密度】标题最多 1 行,主状态标签建议 1 个,辅助标签建议总数不超过 2 个;超过时优先保留最影响当前决策的信息。",
1431
1431
  "【辅助信息】用户名、单号、时间属于辅助识别信息;空间不足时允许截断,但应通过 Tooltip 提供完整信息。",
1432
1432
  "【标签语义】右侧状态标签只承载“当前处理状态”;待干预 / 异常用 red,托管中 / 正常托管用 green,挂起 / 等待用 orange,已处理 / 已完成用 blue;原因、规则、倒计时等信息作为辅助标签。",
1433
- "【收起态】仅当空间受限但仍需保留对象切换能力时收起为纯头像态;收起态只承担切换,不承担复杂状态判断,展开后应恢复用户上次工作宽度。",
1433
+ "【收起态】仅当空间受限但仍需保留对象切换能力时收起为纯头像态;收起态只承担切换,不承担复杂状态判断,展开后应恢复用户上次工作宽度。纯头像模式每个头像按钮行高固定为 68px,选中态与未选中态高度必须一致;选中态不得因只包裹头像内容而变成自适应高度,禁止用 h-auto、min-h 或按 Avatar 内容高度撑开替代固定 68px。",
1434
1434
  "【卡片列表限制】`variant=\"card\"` 时顶部 tabs 是唯一的分类切换入口;卡片列表不显示分组标题,也不支持分组展开/收起。选中“全部”时,卡片必须按状态分类顺序 `待干预 → 托管中 → 其它` 自上而下连续排列;禁止再引入独立 `cards` 数据源或把 card 形态拆成另一套列表。",
1435
1435
  "【卡片列表宽度】`variant=\"card\"` 时不进入纯头像收起态,但保留整体宽度拖拽能力;拖拽变宽后卡片宽度必须按实际可用宽度自适应扩展,拖窄时不得小于 333px。",
1436
1436
  "【卡片列表栅格】当 `variant=\"card\"` 时,列数必须基于组件实际可用宽度动态判断:`<=580px` 为 1 列,`>580px` 为 2 列,`>950px` 为 3 列;嵌入客服工作台等外层可拖拽容器时,也必须读取外层给到的实际宽度,而不是固定按默认 400px 判断。卡片横向与纵向间距都统一为 `16px`。",
@@ -2388,10 +2388,12 @@
2388
2388
  "【文本回复】title=\"\" + steps={null} + resultText=\"...\" 即可渲染纯文本 AI 回复(仅 14px/20px 段落 + 可选 actions),常用于短答、确认、致谢等非任务型回复",
2389
2389
  "【执行流·结构】title + steps[] 构成完整执行流;步骤左侧 16px 轨道槽位 + 12px 内容间距 + 14px/20px 正文;右侧 chevron 控制收起展开",
2390
2390
  "【执行流·状态】status=\"completed\" 绿色对勾;status=\"processing\" 旋转环形动效 + 最新一条操作卡片浅灰骨架屏扫光",
2391
- "【执行流·结果区】resultText 独立通栏文本;resultArtifacts[] 多张产物卡(网页/代码/表格,统一 32px 玻璃感图标 + 标题 + meta + 更多按钮 + 单行省略号);confirms[] 是结果区的另一种形态——人工确认卡(mode=\"text-card\" 文本+卡片,\"card-only\" 仅卡片)",
2391
+ "【执行流·结果区】resultText 独立通栏文本;resultArtifacts[] 多张产物卡(网页/代码/表格,统一 32px 玻璃感图标 + 标题 + meta + 更多按钮 + 单行省略号);confirms[] 是结果区的另一种形态——人工确认卡(mode=\"text-card\" 文本+卡片,\"card-only\" 仅卡片,\"option-card\" 澄清确认卡片1,\"form-card\" 澄清确认卡片2)",
2392
2392
  "【执行流·任务进行中徽章】taskBadge 传字符串启用 indigo 徽章 + 星标,位于结果区之后、消息操作之前,常用于\"任务进行中\"提示",
2393
2393
  "【卡片·任务规划】plan 传对象启用:灰底圆角 12 容器 + 头部图标 + 任务数徽章 + chevron;展开后白底内容卡内紫色编号方块(gradient violet)+ 任务标题 + 灰圆点子项;右下\"开始执行任务\"黑底主按钮",
2394
2394
  "【卡片·配置表单】配置表单类卡片复用执行流的 confirms(mode=\"text-card\" 或 \"card-only\"),需要\"次操作 + 主操作\"按钮组的人工确认场景",
2395
+ "【卡片·澄清确认卡片1】凡是涉及用户来确认/澄清以帮助 AI 确认内容的场景,统一使用 confirms mode=\"option-card\";内部必须复用基础组件 RadioGroup/Radio 的 styleType=\"pureCard\" + variant=\"brand\" 单选卡片,不允许手搓 option div;options 支持 { value, label, disabled },defaultSelectedValue 设置默认选项,onPrimaryAction 接收 { value, option };面板尺寸/底色完全对齐【配置表单】(mode=\"text-card\"/\"card-only\"),仅内容由文本换成单选组件",
2396
+ "【卡片·澄清确认卡片2】当 AI 需要用户通过结构化字段(多个选项 + 文本输入)来确认/澄清信息时,统一使用 confirms mode=\"form-card\";内部必须复用基础组件 Form + FormField,formItems[] 支持 select/input/input-number/textarea/radio/checkbox/switch/date-picker 等字段类型,每项 { id, label, type, placeholder, defaultValue, options?, fullWidth },不允许手搓 input/select;面板尺寸/底色完全对齐【配置表单】与【澄清确认卡片1】,仅内容由单选组件换成 Form 表单组件",
2395
2397
  "【深度思考·进行中】status=\"thinking\" + thinking={ state:\"thinking\", inProgressLabel:\"深度思考中 ...\" },仅显示头像 + 思考文案,不渲染其他内容",
2396
2398
  "【深度思考·已完成】thinking={ state:\"completed\", durationLabel, content, defaultExpanded } 渲染\"深度思考(用时 X 秒)\"+ chevron + 灰色引用线左侧 + 思考文本;位于 AI 头像之后、标题/步骤之前",
2397
2399
  "【操作栏】actions 传 true 启用复制/引用/赞/踩四件套;传对象可单独 showCopy/showQuote/showLike/showDislike + copyCount + onXxx 回调;时间戳由 timestamp 字段单独控制",
@@ -2439,6 +2441,14 @@
2439
2441
  "label": "卡片 · 配置表单(人工确认)",
2440
2442
  "code": "<ChatMessage header title=\"\" steps={null} resultText=\"已完成售后政策摘要整理,以下审核口径需要人工确认后再继续生成标准答复。\" confirms={[{ mode: \"text-card\", title: \"售后政策确认\", description: \"请确认退货退款、换货与仅退款的适用范围\", iconName: \"sticker-square-stroked\", secondaryActionLabel: \"返回修改\", primaryActionLabel: \"确认继续\" }]} />"
2441
2443
  },
2444
+ {
2445
+ "label": "卡片 · 澄清确认卡片1",
2446
+ "code": "<ChatMessage header title=\"\" steps={null} confirms={[{ mode: \"option-card\", title: \"澄清确认\", iconName: \"clipboard-check-stroked\", primaryActionLabel: \"确认\", defaultSelectedValue: \"handoff-guide\", options: [{ value: \"handoff-guide\", label: \"方案1: 用户请求转人工但系统未触发转接,持续引导自助操作\" }, { value: \"handoff\", label: \"方案2: 用户请求转人工\" }, { value: \"self-service\", label: \"方案3: 持续引导自助操作\" }], onPrimaryAction: ({ value, option }) => {} }]} />"
2447
+ },
2448
+ {
2449
+ "label": "卡片 · 澄清确认卡片2",
2450
+ "code": "<ChatMessage header title=\"\" steps={null} confirms={[{ mode: \"form-card\", title: \"澄清确认\", iconName: \"clipboard-check-stroked\", primaryActionLabel: \"确认\", formItems: [{ id: \"scene\", label: \"业务场景\", type: \"select\", placeholder: \"请选择业务场景\", defaultValue: \"after-sales\", options: [{ value: \"after-sales\", label: \"售后服务\" }, { value: \"pre-sales\", label: \"售前咨询\" }, { value: \"logistics\", label: \"物流配送\" }], fullWidth: true }, { id: \"channel\", label: \"处理渠道\", type: \"select\", placeholder: \"请选择处理渠道\", defaultValue: \"self-service\", options: [{ value: \"self-service\", label: \"自助引导\" }, { value: \"handoff\", label: \"转人工\" }], fullWidth: true }, { id: \"remark\", label: \"补充说明\", type: \"input\", placeholder: \"请输入补充说明(可选)\", fullWidth: true }] }]} />"
2451
+ },
2442
2452
  {
2443
2453
  "label": "深度思考 · 思考中",
2444
2454
  "code": "<ChatMessage header status=\"thinking\" steps={null} thinking={{ state: \"thinking\", inProgressLabel: \"深度思考中 ...\" }} />"
@@ -3265,7 +3275,10 @@
3265
3275
  "【组件定位】Form 是字段组合层,只管理 label、提示、错误反馈、top/left 布局和字段编排,不重新定义已有表单控件样式",
3266
3276
  "【控件复用】已有基础组件必须复用: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",
3267
3277
  "【布局】Form 在页面主内容区、白色卡片容器与纵向堆叠场景中默认 `w-full min-w-0 self-stretch`;labelPosition=\"top\" 时字段容器与控件区都默认全宽,label 与控件间距 4px;labelPosition=\"left\" 时标签列固定 96px、间距 24px、右侧控件区使用 `flex-1 min-w-0 w-full` 撑满剩余宽度",
3268
- "【表单宽度铁律】页面主表单、白卡表单、抽屉/弹窗正文表单、侧栏详情表单中,Input / Select / TextArea / InputNumber / DatePicker / TimePicker / TagInput 必须默认撑满字段内容列,且同一区域左右边界对齐;只有筛选栏、工具条、紧凑查询区允许固定宽 160/200/240px,禁止把 300px 示意宽或筛选栏固定宽套到业务表单正文",
3278
+ "【正文表单宽度铁律】页面主表单、白卡/卡片内配置表单、澄清确认卡片、抽屉/弹窗正文表单、侧栏详情表单中,Input / Select / TextArea / InputNumber / DatePicker / TimePicker / TagInput / Slider / InputGroup 这类字段型控件必须默认撑满字段内容列,且同一表单区域左右边界对齐、宽度一致。多个字段上下排列在同一卡片容器内时,每个字段都应随卡片内容区动态撑满,不受 240px / 200px / 320px 的筛选搜索宽度限制。",
3279
+ "【筛选工具条宽度例外】只有列表筛选栏、工具条、紧凑查询区、AI 首页模板搜索、Tabs 右侧搜索这类横向辅助查询入口,才允许固定中窄宽:搜索 Input 默认约 240px、最小 200px、最大 320px;Select / DatePicker / TimePicker / TagInput 等筛选控件常用 160-240px(日期时间范围可更宽)。禁止把筛选工具条固定宽套到业务表单正文。",
3280
+ "【筛选栏不是纵向表单】列表页顶部筛选栏、工具条、紧凑查询区不使用 Form 的纵向正文表单排布;搜索 Input、Select/DatePicker/TagInput 等筛选控件、重置/查询 Button 必须放在同一个横向 flex 行流中(推荐 `flex items-center flex-wrap gap-2`),禁止用 `Form layout=\"vertical\"`、`flex-col` 或 `space-y-*` 把筛选条件和按钮上下堆叠。只有真正的配置填写、详情编辑、弹窗正文才使用 Form 的纵向/网格字段布局。",
3281
+ "【紧凑/特殊控件宽度】Switch、普通 CheckboxGroup、普通 RadioGroup 的控件本体按语义保持自然宽度,但字段容器仍占满表单区域并与其它字段左边界对齐;卡片型 Radio/Checkbox 选项可按场景设置 `w-full` 形成等宽选项。Upload 字段区域占满表单区域,内部图片 tile 保持固定尺寸并自动换行,不强行拉伸 tile。",
3269
3282
  "【自定义控件宽度】使用 children 或 item.control 传入自定义字段时,外层必须补 `className=\"w-full min-w-0 max-w-full\"`;否则会绕过 Form 内置的 `!w-full` 控件宽度约束,导致 Input、Select、TextArea 等宽度不一致",
3270
3283
  "【详情页全集预览】组件详情页的“全集”分类按字段类型从上到下单列排列,每个组件间距 40px,超出预览画布时在画布内滚动查看",
3271
3284
  "【字段配置】items 数组每项支持 id、label、type、required、labelPosition、middleHelpText、helpText、error、errorText、disabled、value/defaultValue/onChange 等字段;当字段命中 AI 推荐语义时,可继续透传 aiSuggestion、aiSuggestions、onAdoptSuggestion、onRefreshAiSuggestions 到 Input / Select / TextArea",
@@ -3447,7 +3460,8 @@
3447
3460
  "【AI推荐误判边界】普通 placeholder、helpText、默认值、静态示例文案不等于 AI 推荐能力;只有明确存在“推荐/建议/AI辅助填写”语义时,才默认展示 Input 的 AI 推荐区。",
3448
3461
  "【刷新推荐】当存在 AI 推荐时,Input 默认应支持真实刷新;未接业务服务时,组件内部仍需轮换下一组建议,不允许点了刷新但内容不变。若传入 onRefreshAiSuggestions,应在本地刷新闭环之外继续向外通知;按钮位于清空按钮右侧,tooltip 固定为“刷新推荐”,icon 使用 `ai-fill-3` 渐变 token",
3449
3462
  "【采纳回调】点击任一推荐内容时都会触发 onAdoptSuggestion(suggestion),并默认把推荐值真实写入输入框;受控模式下需配合 value + onChange 完成外部值更新",
3450
- "【宽度】默认随父容器满宽(避免板块内输入框不撑满);需要固定宽(如筛选栏)时,用外层 style.width(240/200/160)或传 `style={{ \"--size-input-width\": \"240px\" }}` 控制",
3463
+ "【宽度·正文表单】Input 默认随父容器满宽;在 Form 正文、卡片内纵向表单、配置表单、详情编辑、抽屉/弹窗正文中,Input 必须跟随字段列动态撑满,并与同组 Select / TextArea / InputNumber 等字段宽度一致。不要把搜索框 240px 规则套到正文表单字段。",
3464
+ "【宽度·辅助查询】只有列表筛选栏、工具条、AI 首页模板搜索、Tabs 右侧搜索等横向辅助查询入口,才使用固定中等宽度:默认 240px,最小 200px,最大不超过 320px;可用外层 style.width 或 `style={{ \"--size-input-width\": \"240px\" }}` 控制。此类场景禁止写 `flex-1` / `w-full` 让搜索框撑满整行或撑满 Tabs 右侧全部剩余空间。",
3451
3465
  "【尺寸】仅保留 MD 一种尺寸,高度固定 36px;与 Button md 高度一致",
3452
3466
  "【页面默认】页面主表单、筛选栏、白卡内录入区、侧栏与弹窗正文中的单行 Input 默认使用 36px 高度,不再提供 size 配置"
3453
3467
  ],
@@ -3694,7 +3708,7 @@
3694
3708
  "【自动增高】resize=\"vertical\" 时高度随正文 scrollHeight 变化;禁止原生拖拽(resize-none)。resize=\"none\" 时高度锁定在 minRows 对应高度,超出部分框内滚动",
3695
3709
  "【撑满父容器】fillHeight=true 时,TextArea 外层、字段框和正文区都会进入 `flex-1 min-h-0` 撑满模式,并强制正文区内部滚动;父容器若缺 `flex-1 min-h-0 overflow-hidden`,撑满不会生效。",
3696
3710
  "【字数】maxLength 可选,不传则不限制字数、不施加原生 maxLength;showCount=true 且无 maxLength 时仅展示已输入字数;有 maxLength 时展示「当前/上限」,enforceMaxLength=false 可超出仅标红",
3697
- "【宽度】默认随父容器满宽;筛选栏/工具条等并排场景才使用固定宽(240/200/160)。主编辑区配合 fillHeight 可撑满高度,宽度仍默认满宽",
3711
+ "【宽度】默认随父容器满宽;在 Form 正文、卡片内纵向表单、配置表单、详情编辑、抽屉/弹窗正文中,TextArea 必须跟随字段列动态撑满,并与同组 Input / Select 等字段宽度一致。筛选栏/工具条等并排辅助查询场景才允许固定宽(240/200/160)。主编辑区配合 fillHeight 可撑满高度,宽度仍默认满宽",
3698
3712
  "【AI推荐】支持单条 aiSuggestion 和多条 aiSuggestions;推荐区固定展示在 TextArea 下方,并支持真实点击采纳与刷新推荐。未接业务服务时,组件内部也必须能够轮换下一组草稿建议,不能只展示静态文案",
3699
3713
  "【AI推荐采纳】点击推荐草稿后,TextArea 默认应真实写入推荐内容,并触发 onAdoptSuggestion(suggestion);若传入 onRefreshAiSuggestions,应在本地轮换草稿之外继续对外通知",
3700
3714
  "【详情预览·侧栏默认】与 _preview.panelDefaults 一致:内容「空」、超长计数「关」、可用性「默认可用」、默认高度「3 行」、校验态「默认」、高度适配「纵向拉伸」",
@@ -3948,7 +3962,7 @@
3948
3962
  ],
3949
3963
  "rules": [
3950
3964
  "【⛔ AI 强制使用场景】所有数字类参数字段必须使用 InputNumber,禁止用 `<input type=\"number\">` 或 Input 组件代替。典型场景:Temperature(0~2 精度 0.01)、Top-P(0~1 精度 0.01)、Max Tokens(整数步进)、学习率、权重、阈值等一切数值配置项。原生 input 会产生黑色边框并丢失步进、范围约束、精度控制能力",
3951
- "【组件定位】InputNumber 是数字输入原子组件,输入框视觉必须复用 Input:表单正文默认随字段列满宽,MD 高 36px、圆角 8px、白底灰边框、聚焦 1px 绿色描边;仅筛选栏/工具条/详情预览示意可由父容器限制为 160/200/240/300px",
3965
+ "【组件定位】InputNumber 是数字输入原子组件,输入框视觉必须复用 Input:表单正文、卡片内纵向表单、配置表单、详情编辑和抽屉/弹窗正文默认随字段列满宽,并与同组 Input / Select 等字段宽度一致;MD 高 36px、圆角 8px、白底灰边框、聚焦 1px 绿色描边。仅筛选栏/工具条/详情预览示意可由父容器限制为 160/200/240/300px",
3952
3966
  "【按钮布局】默认 outer 外置步进按钮:位于输入框右侧,宽 16px、高 36px,与输入框间距 4px,上下两个箭头图标均为 8×8px",
3953
3967
  "【内置按钮】innerButtons=true 时步进按钮内嵌在输入框右侧,默认隐藏,仅在容器 hover 或 focus-within 时显示,输入内容需预留右侧空间",
3954
3968
  "【禁用态】disabled 时输入框与步进按钮同步进入浅灰底、禁用文字和 0.6 透明度;按钮不可点击",
@@ -4157,7 +4171,8 @@
4157
4171
  "【关闭】点击触发器、选项或外部 mousedown(触发器与面板均排除)关闭",
4158
4172
  "【键盘】Enter/Space 展开或选中高亮项;↑/↓ 移动高亮;Esc 关闭;Tab 失焦关闭",
4159
4173
  "【无障碍】触发器 role=\"combobox\" aria-expanded;列表 role=\"listbox\" 与 role=\"option\"",
4160
- "【宽度】触发器为 w-full min-w-0 max-w-full,表单正文默认随字段列满宽;筛选栏、工具条或详情预览示意才允许由父容器限制宽度,禁止在业务表单正文把 Select 固定为 300px 导致与 Input / TextArea 不对齐",
4174
+ "【宽度·正文表单】触发器为 w-full min-w-0 max-w-full;在 Form 正文、卡片内纵向表单、配置表单、详情编辑、抽屉/弹窗正文中,Select 必须默认随字段列满宽,并与同组 Input / TextArea / DatePicker 等字段宽度一致。禁止在业务表单正文把 Select 固定为 240px / 300px 导致与其它字段不对齐。",
4175
+ "【宽度·辅助查询】仅筛选栏、工具条、紧凑查询区等横向辅助查询入口允许由父容器限制为中窄宽,常用 160-240px;该限制不作用于正文表单字段。",
4161
4176
  "【右侧图标】下拉箭头、清除按钮与自定义 indicator 均固定为 16×16px",
4162
4177
  "【尺寸】仅保留 MD 一种尺寸,触发器高度固定 36px;与 Input 保持一致"
4163
4178
  ],
@@ -4282,7 +4297,7 @@
4282
4297
  "kind": "component",
4283
4298
  "name": "Radio",
4284
4299
  "category": "basic",
4285
- "description": "互斥单选:同一组内只能选一个值。适合选项少、需要并排或纵列展示全部标签的表单场景。多选请用 CheckboxGroup;布尔即时开关请用 Switch;选项很多请用 Select。",
4300
+ "description": "互斥单选:同一组内只能选一个值。支持基础样式、带圆点单选卡片、单选卡片 3 类形态,适合选项少且需要平铺比较的表单场景。多选请用 CheckboxGroup;布尔即时开关请用 Switch;选项很多请用 Select。",
4286
4301
  "import": {
4287
4302
  "from": "@tf-designsystem/b-end",
4288
4303
  "default": "RadioGroup",
@@ -4300,6 +4315,16 @@
4300
4315
  ],
4301
4316
  "default": "brand"
4302
4317
  },
4318
+ {
4319
+ "name": "styleType",
4320
+ "type": "enum",
4321
+ "options": [
4322
+ "basic",
4323
+ "card",
4324
+ "pureCard"
4325
+ ],
4326
+ "default": "basic"
4327
+ },
4303
4328
  {
4304
4329
  "name": "layout",
4305
4330
  "type": "enum",
@@ -4341,7 +4366,11 @@
4341
4366
  "【受控】RadioGroup 使用 value + onChange(nextValue, event);非受控用 defaultValue",
4342
4367
  "【禁用】RadioGroup disabled 禁用整组;Radio 可单独 disabled",
4343
4368
  "【变体】brand 为选中实心主色 + 白点;black 为深灰主色 + 白点",
4344
- "【布局】vertical 组间距 12px;horizontal 组间距 16px(对齐 Semi radioGroup)",
4369
+ "【样式分类】styleType=basic 为基础样式;styleType=card 为带圆点单选卡片;styleType=pureCard 为单选卡片。3 类都支持 variant=brand/black",
4370
+ "【卡片样式】带圆点单选卡片保留左侧 16px 圆点指示器,适合需要强调“单选控件感”的配置项;单选卡片隐藏圆点,仅用卡片描边/浅底表达选中,适合更轻量的枚举切换",
4371
+ "【卡片文案】card / pureCard 的文案默认左对齐并向右撑满;当组件宽度受限时单行省略,hover 文案区域通过 Tooltip 展示完整内容",
4372
+ "【选中强调】card / pureCard 选中后文案字重自动变粗;未选中保持常规字重。基础样式不跟随这条规则变化",
4373
+ "【布局】vertical 组间距 12px;horizontal 组间距 16px,全部选项优先横向排列,整组容器宽度不够时自动换行到下一行",
4345
4374
  "【样式来源】HIUIpackage/es/components/radio-group 为 Semi 封装,视觉以 umd/view.css 为准",
4346
4375
  "【表单】未传 name 时自动用稳定 id,同一组内各 input 共享 name",
4347
4376
  "【无障碍】使用原生 type=\"radio\" 与 radiogroup 容器;聚焦可见环在指示圈上",
@@ -4352,6 +4381,14 @@
4352
4381
  "label": "基础",
4353
4382
  "code": "<RadioGroup defaultValue=\"b\" onChange={(v) => {}}><Radio value=\"a\">选项 A</Radio><Radio value=\"b\">选项 B</Radio></RadioGroup>"
4354
4383
  },
4384
+ {
4385
+ "label": "带圆点单选卡片",
4386
+ "code": "<RadioGroup styleType=\"card\" layout=\"horizontal\" defaultValue=\"a\"><Radio value=\"a\">单选框标题</Radio><Radio value=\"b\">单选框标题</Radio></RadioGroup>"
4387
+ },
4388
+ {
4389
+ "label": "单选卡片",
4390
+ "code": "<RadioGroup styleType=\"pureCard\" layout=\"horizontal\" defaultValue=\"a\"><Radio value=\"a\">单选框标题</Radio><Radio value=\"b\">单选框标题</Radio></RadioGroup>"
4391
+ },
4355
4392
  {
4356
4393
  "label": "横向",
4357
4394
  "code": "<RadioGroup layout=\"horizontal\" defaultValue=\"a\"><Radio value=\"a\">月付</Radio><Radio value=\"b\">年付</Radio></RadioGroup>"
@@ -5300,7 +5337,8 @@
5300
5337
  "【场景·daterange / datetimerange】表格/列表顶部时间范围筛选、统计报表数据周期选择、合同有效期区间",
5301
5338
  "【禁忌】不要用 type=\"date\" 代替 type=\"datetime\"(会丢失时分信息);不要把两个 DatePicker 手拼成范围选择(直接用 type=\"daterange\");不要在极紧凑行内(宽度 < 200px)使用,面板最小宽度 280px",
5302
5339
  "【类型说明】date / datetime → 单月面板单值选择;daterange / datetimerange → 双月面板范围选择,范围中间区间浅色连续高亮",
5303
- "【宽度】表单正文默认随字段列满宽;筛选栏、工具条或详情预览示意才允许由父容器限制宽度(date / datetime / daterange 常用 300px,datetimerange 常用 400px)。禁止在业务表单正文把 DatePicker 固定为 300px 导致与 Input / Select 不对齐",
5340
+ "【宽度·正文表单】表单正文、卡片内纵向表单、配置表单、详情编辑和抽屉/弹窗正文默认随字段列满宽,并与同组 Input / Select / TimePicker 等字段宽度一致;禁止在业务表单正文把 DatePicker 固定为 300px 导致字段不对齐。",
5341
+ "【宽度·辅助查询】仅筛选栏、工具条或详情预览示意允许由父容器限制宽度(date / datetime / daterange 常用 240-300px,datetimerange 常用 320-400px)。该限制不作用于正文表单字段。",
5304
5342
  "【面板行为】单值点击即选中并关闭;范围需点击两次确定起止后自动关闭",
5305
5343
  "【时间栏】datetime / datetimerange 面板底部固定显示 52px 日期/时间信息栏",
5306
5344
  "【组合·表单】配 Form 使用时包在 Form.Item(type=\"date-picker\")里;时间范围筛选常配合 Table 顶部工具栏 + \"查询\"Button 使用",
@@ -5438,7 +5476,8 @@
5438
5476
  "rules": [
5439
5477
  "【字重实现规范】涉及 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 已约定处。",
5440
5478
  "【类型】time 为单时间输入框;timerange 为时间范围输入框",
5441
- "【触发器】视觉对齐 Figma / DatePicker:表单正文默认随字段列满宽,详情预览示意可用 300px36px 高、白底、1px 默认描边、8px 圆角,内容默认左对齐,右侧时钟图标固定为 16×16px",
5479
+ "【触发器】视觉对齐 Figma / DatePicker:表单正文、卡片内纵向表单、配置表单、详情编辑和抽屉/弹窗正文默认随字段列满宽,并与同组 Input / Select / DatePicker 等字段宽度一致;详情预览示意可用 300px36px 高、白底、1px 默认描边、8px 圆角,内容默认左对齐,右侧时钟图标固定为 16×16px",
5480
+ "【宽度·辅助查询】仅筛选栏、工具条、紧凑查询区等横向辅助查询入口允许由父容器限制为中窄宽,time 常用 160-200px,timerange 常用 200-240px;该限制不作用于正文表单字段。",
5442
5481
  "【面板】点击 Trigger 展开浮层;面板默认与 Trigger 等宽,最小宽度 150px;单时间面板高 252px,无标题;时间范围面板高 304px,顶部显示开始/结束标题",
5443
5482
  "【固定选择区】面板中间固定 36px 高浅绿选择区,时/分列按面板宽度等分并居中对齐;timerange 使用同一条横跨四列的固定选择区,四列都支持真实滚动选择",
5444
5483
  "【选择】点击任一时间项会滚动到中间选择区;滚动时/分列时实时同步选择区内最近项,停止后自动吸附",
@@ -5887,7 +5926,8 @@
5887
5926
  }
5888
5927
  ],
5889
5928
  "rules": [
5890
- "【视觉】容器对齐 Figma taginput:表单正文默认随字段列满宽,详情预览示意可用 300px36px 高、白底、1px 默认描边、8px 圆角,空态显示“请输入”",
5929
+ "【视觉】容器对齐 Figma taginput:表单正文、卡片内纵向表单、配置表单、详情编辑和抽屉/弹窗正文默认随字段列满宽,并与同组 Input / Select 等字段宽度一致;详情预览示意可用 300px36px 高、白底、1px 默认描边、8px 圆角,空态显示“请输入”",
5930
+ "【宽度·辅助查询】仅筛选栏、工具条、紧凑查询区等横向辅助查询入口允许由父容器限制为中窄宽,常用 200-240px;该限制不作用于正文表单字段。",
5891
5931
  "【Tag 复用】已选项、+N 折叠项、Tooltip 内省略项、隐藏测量项都必须使用平台现有 Tag 组件;TagInput 不自绘任何标签视觉",
5892
5932
  "【宽度自适应】通过 ResizeObserver 监听容器宽度,按真实 Tag 宽度计算可见数量;宽度变窄自动折叠,变宽自动展开",
5893
5933
  "【溢出折叠】可见区域放不下的标签折叠为 +N,+N 固定在末尾并以 Tag 渲染;悬浮 +N 使用白底 tone=\"light\" Tooltip 展示被省略的所有 Tag,该白底样式仅用于 TagInput 更多标签场景",
@@ -6938,6 +6978,8 @@
6938
6978
  "【Figma 对齐】默认视觉对齐 Figma 节点 8279:67909「在线Agent」:根容器浅灰底 `--color-blueGrey-200`、16px 内边距、顶部 24px 状态条、下方左侧半透明板块 + 右侧主白卡;6 个可见圆角统一为 16px:左侧板块左上 / 左下,右侧主白卡四角。",
6939
6979
  "【结构】根节点必须直接作为页面主工作区或右侧主内容区的一级框架;内部顺序固定为 Header(客服名称 / 在线状态 / 指标工具 / 模式切换) + Workspace(左侧上下文占位 + 右侧主面板)。不要再用大白卡或 `bg-surface rounded-*` 包住整个模板。",
6940
6980
  "【顶部栏】左侧显示客服名称与在线时长;中部数据栏必须默认按页面可用宽度动态居中,不随左侧客服名称或右侧模式切换偏移;指标容器使用 `bg-white/60 + border-white + radius-md + px-4 py-1`;在线状态胶囊与工具入口这类按钮化页面元素必须复用基础 `Button`;右侧基础模式 / 托管模式属于同页模式切换,必须复用基础 `Tabs`,默认使用 `variant=\"segment\"`,不要手写 tab button 或用 NavBar 替代。",
6981
+ "【顶部数据】指标区左侧数据仅用于展示当前页面相关的高优关注数据,形式固定为 Icon + 数字值;最多展示 4 类数据,超出时应按业务优先级裁切;每一类数据都必须支持 hover/focus 通过 Tooltip 查看文案解释,tooltip 文案优先来自 `tooltip` / `description`,否则回退到 `label`。",
6982
+ "【顶部工具】指标区右侧按钮仅用于平台框架级入口,例如全局设置、平台公告、平台数据统计等;最多展示 3 个按钮,超出时应按平台级优先级裁切;按钮必须复用 `Button iconOnly` 并提供 `tooltip` / `aria-label`,不要放置页面内局部操作或业务表单操作。",
6941
6983
  "【工作区层级】Workspace 外层只负责横向布局、覆盖层级与拖拽,不承担可见圆角;左侧板块自身为 `bg-white/50 + border-white + rounded-xl`,右侧主面板自身为白色 `surface + border-white + rounded-xl`,两者高度一致、宽度不同,并通过主面板 `margin-left: -32px` 形成“右侧白板覆盖在左侧灰板上”的叠压视觉,而不是中间留缝的并排关系。",
6942
6984
  "【可见圆角】最终可见的 6 个角统一为 16px:左侧板块左上 / 左下两个角露出;右侧主白卡四角露出;左侧板块右上 / 右下两个角必须被右侧主白卡完整覆盖。主面板覆盖量必须大于圆角半径(推荐 32px = 2 × 16px),防止主面板左上 / 左下圆角切角处露出左侧板块的右边界或描边,避免出现“残缺一块”的视觉。",
6943
6985
  "【左右拖拽】左侧区域与右侧主白卡之间必须提供纵向拖拽热区,默认热区 8px;拖拽热区覆盖在主面板左边缘上,不占用布局宽度、不制造中间空隙;拖动时调整左侧区域宽度并让右侧主面板 `flex-1 min-w-0` 自动占满剩余空间;左侧默认宽度 432px(400px 可视会话列表 + 32px 主面板覆盖区);拖拽最小宽度优先由左侧业务组件最小可用宽度决定,例如 ConversationList 默认列表纯头像锁定 88px 时,左侧板块最小宽度为 `max(100px 框架兜底, 88px + 32px 覆盖区) = 120px`;ConversationList 卡片列表最小内容宽度为 333px,左侧板块最小宽度为 365px;右侧纯白主容器最小宽度 380px,左侧最大可拖宽度需按 `工作区宽度 + 32px 覆盖量 - 380px` 动态计算;拖拽条需支持键盘左右方向键微调。",
@@ -7014,6 +7056,7 @@
7014
7056
  "【布局 Recipe】必须按 `LAYOUT_RECIPES.md` 选择 `base-management`:灰底 AppShell + `main p-4 gap-2` + 一张或多张白色 WorkSurface;页面 header / 顶部 Tabs / 主次操作直接坐灰底。",
7015
7057
  "【五模板定位】本模板只解决“管理一批结构化对象”的 B 端工作区:变量、MCP、策略、知识库、数据资产、审核任务、配置列表等。用户第一步必须是筛选 / 搜索 / 浏览对象 / 批量操作 / 查看详情;如果第一步是“输入一句需求开始 AI 任务”,改用 `ChatHomePagePattern`;如果第一步是“继续追问 AI 任务”,改用 `ChatConversationPattern`;如果右侧主角是对象/产物编辑且 AI 只是侧助,改用 `CopilotPagePattern`;如果是真实客服/私信线程,改用 `IMConversationPattern`。",
7016
7058
  "【组件选型门禁】白卡标题必须用 `FormTitle`;筛选字段按语义用 `Input` / `Select` / `DatePicker` / `TagInput` / `Checkbox`;结构化列表必须用 `Table`;状态/类型/数量徽标必须用 `Tag`;所有操作必须用 `Button`;左侧分类维度必须用 `TagBar`;弹层按复杂度用 `Modal` 或 `Sheet`;空状态用 `Empty`。禁止手写标题、状态徽标、表格、筛选胶囊或按钮。",
7059
+ "【筛选栏同一行硬约束】列表页白卡顶部筛选栏是查询工具条,不是业务正文表单;搜索框、下拉框 / 日期 / 标签等筛选控件、重置与查询按钮必须处在同一行流内,默认使用 `flex items-center flex-wrap gap-2`。搜索框建议宽 240px,下拉筛选建议 160-200px,按钮 `shrink-0`;仅当容器宽度不足时允许在同一个 flex 行流中自然换行,禁止每个控件独占一行、禁止用 `flex-col` / `space-y-*` / `Form layout=\"vertical\"` 把搜索框、下拉框和按钮上下堆叠。",
7017
7060
  "【页面范式选型总则】TFDS 页面框架不只有一种白卡管理页范式。生成页面前必须先判断“用户当前要完成什么任务”,再在 `BasePageFramePattern`、`ChatHomePagePattern`、`ChatConversationPattern`(必要时 `CopilotPagePattern`)之间选最合适的页面模板,禁止把所有页面默认都生成成白卡单栏/双栏/多栏管理页。",
7018
7061
  "【判断标准】用“用户进入页面后的第一步动作”决定布局:第一步是“发起一个 AI 任务 / 输入一句需求 / 从模板开始”→ 优先 `ChatHomePagePattern`(AI 入口页);第一步是“围绕一个已开始的任务持续追问、查看消息流、继续与 AI 协作”→ 优先 `ChatConversationPattern`(AI 对话页);第一步是“筛选、搜索、浏览对象列表、查看详情、批量操作结构化信息”→ 优先 `BasePageFramePattern`(白卡管理页)。",
7019
7062
  "【AI入口页适用场景】当 `ChatInput` 本身就是页面的核心主功能,且页面目标是“帮助用户开始一件事”而不是“先管理一批对象”时,必须优先使用 `ChatHomePagePattern`。典型场景:AI 工作台首页、智能分析入口、模板广场、报告生成入口、用户尚未进入具体上下文的助手首页。此时核心输入区与推荐内容应直接坐落在浅灰大背景上,不要强行套成典型白卡后台页。",
@@ -7116,13 +7159,13 @@
7116
7159
  "【组件选型门禁】主输入必须用 `ChatInput`,不可用原生 textarea 或 Input 冒充 AI 输入;模板/助手/案例推荐必须用 `Card`;分类切换必须用 `Tabs`;搜索框用 `Input` + `Icon` 前缀;无结果用 `Empty`。禁止用 Button/Tag 排一行冒充 Tabs,禁止手写卡片。",
7117
7160
  "【整体背景】页面以浅灰大背景(`--color-blueGrey-200`)为主:Hero、筛选行、卡片网格**直接坐灰底**,不要再额外包一整张“右侧白色大卡片容器”(白卡只用于推荐卡片本身)。",
7118
7161
  "【反例扫描】如果同一右侧首页函数里同时出现 `ChatInput`、模板 `Card` 网格,并且外层存在 `background: var(--color-surface)` / `bg-surface rounded-*` 包住 Hero + 输入框 + Tabs + 网格,说明 AI 把入口页误生成成白卡管理页,必须拆掉这层 wrapper。",
7119
- "【框架不动】外框灰底 + 左侧 `NavBar` 固定不变;右侧内容区必须 `flex-1 min-w-0 min-h-0 overflow-hidden`,由内部区域控制滚动。",
7120
- "【Hero 区】居中欢迎标题 + 最大宽 680px 的 ChatInput 直接坐浅灰底;默认使用 `ChatInput variant=\"default\"` 作为入口主输入;标准节奏为 `padding: 120px 40px 80px`。AI 渐变只用于输入框内部 / AI 标识,不作为整块 Hero 白底。",
7121
- "【筛选与分类】分类切换必须用 `Tabs size=\"sm\"`;筛选行 `padding: 16px 40px`,左 Tabs、右搜索,搜索框约 240px,空间不足允许换行。",
7122
- "【卡片列表】使用自适应 grid(优先 `repeat(auto-fit, minmax(min(320px, 100%), 1fr))`),网格 `gap-4`,禁止固定列宽导致窄屏横向溢出;卡片使用 `Card color=\"white\"`;列表区域 `flex-1 min-h-0 overflow-y-auto` 独立滚动,不挤压上方区域。",
7162
+ "【框架不动】外框灰底 + 左侧 `NavBar` 固定不变;右侧内容区必须 `flex-1 min-w-0 min-h-0 overflow-y-auto overflow-x-hidden`,由右侧内容区作为唯一滚动容器承载整体页面滚动;Hero、ChatInput、筛选行、卡片网格要一起被滑动,禁止只让下方卡片列表单独 `overflow-y-auto`。",
7163
+ "【Hero 区】居中欢迎标题 + 最大宽 680px 的 ChatInput 直接坐浅灰底;Hero 主标题使用 `text-4xl leading-10`;默认使用 `ChatInput variant=\"default\"` 作为入口主输入;标准节奏为 `padding: 84px 40px 80px`,标题区较旧版整体上移 36px。标题区副标题与 ChatInput 之间必须保留 48px 间距(比普通标题说明节奏额外增加 24px),保证输入框不贴近标题区。AI 渐变只用于输入框内部 / AI 标识,不作为整块 Hero 白底。",
7164
+ "【筛选与分类】分类切换必须用 `Tabs size=\"sm\"`;筛选行 `padding: 16px 40px`,左 Tabs、右搜索。模板/助手搜索框是辅助筛选入口,不是页面主输入,必须固定宽度并自动对齐到页面右侧:用外层 `<div className=\"ml-auto shrink-0\" style={{ flex: \"0 0 240px\", width: \"240px\", minWidth: \"240px\", maxWidth: \"240px\" }}>` 包裹 `Input`。注意 Input 组件的 `style` 会透传到原生 input,不要把固定宽直接写在 Input 上;Tabs 外层使用 `flex shrink-0` 保持左侧胶囊 Tabs 可见。筛选行容器禁止使用 `flex-wrap`、`justify-between`、`space-y-*`、`flex-col`;禁止让筛选搜索框 `flex-1`、`w-full`、撑满 Tabs 右侧剩余空间,或换到 Tabs 下方单独占一行。",
7165
+ "【卡片列表】使用自适应 grid(优先 `repeat(auto-fit, minmax(min(320px, 100%), 1fr))`),网格 `gap-4`,禁止固定列宽导致窄屏横向溢出;卡片使用 `Card color=\"white\"`;列表区域作为普通内容块跟随右侧页面整体滚动,使用 `shrink-0` + `padding: 0 40px 40px`,禁止在卡片列表自身设置 `overflow-y-auto` 造成只有卡片区滚动。",
7123
7166
  "【内容可扩展】Tab 项数量和卡片数据替换为业务真实数据;卡片 stats/tags 按业务维度自定义"
7124
7167
  ],
7125
- "code": "import NavBar from './components/NavBar';\nimport Tabs from './components/Tabs';\nimport ChatInput from './components/ChatInput';\nimport Card from './components/Card';\nimport Icon from './components/Icon';\nimport Input from './components/Input';\n\nconst CATEGORY_TABS = [\n { label: '全部' },\n { label: '推荐', icon: <Icon name=\"star-01-stroked\" /> },\n { label: '对话助手', icon: <Icon name=\"message-chat-square-stroked\" /> },\n { label: '数据分析', icon: <Icon name=\"bar-chart-01-stroked\" /> },\n];\n\nexport default function ChatHomePage() {\n return (\n <div\n className=\"flex w-full items-stretch overflow-hidden\"\n style={{\n flex: '1 0 auto', alignSelf: 'stretch', minHeight: '720px',\n background: 'var(--color-blueGrey-200)',\n borderRadius: 0,\n }}\n >\n <div className=\"flex shrink-0\">\n <NavBar platform=\"ola\" selectedItemId=\"knowledge\" />\n </div>\n\n <div className=\"flex flex-1 min-w-0 min-h-0 flex-col overflow-hidden\">\n {/* Hero 区:欢迎语 + ChatInput */}\n <div\n className=\"flex flex-col items-center shrink-0\"\n style={{\n padding: '120px 40px 80px',\n }}\n >\n <div className=\"flex flex-col items-center gap-2 w-full text-center\">\n <h1 className=\"m-0 text-3xl [font-weight:var(--font-semibold)] leading-9\" style={{ color: 'var(--foreground)' }}>\n 今天想做什么?\n </h1>\n <p className=\"m-0 text-sm\" style={{ color: 'var(--foreground-muted)' }}>\n 从下方搜索助手,或直接输入你的需求,AI 帮你快速搞定\n </p>\n </div>\n <div className=\"w-full\" style={{ maxWidth: '680px' }}>\n <ChatInput variant=\"default\" placeholder=\"描述你想完成的任务…\" />\n </div>\n </div>\n\n {/* 筛选行:Tabs + 搜索,直接坐灰底 */}\n <div\n className=\"flex min-w-0 shrink-0 flex-wrap items-center justify-between gap-4\"\n style={{ padding: '16px 40px' }}\n >\n <Tabs variant=\"pill\" size=\"sm\" items={CATEGORY_TABS} defaultIndex={0} />\n <Input\n placeholder=\"搜索标题、描述\"\n prefix={<Icon name=\"search-md-stroked\" size=\"sm\" />}\n allowClear\n className=\"max-w-full\"\n style={{ flex: '0 1 240px', width: '240px', minWidth: '200px', '--size-input-width': '100%' }}\n />\n </div>\n\n {/* 卡片列表(可滚动):卡片自身是白卡,外层不包白卡 */}\n <div className=\"flex-1 min-h-0 overflow-y-auto\" style={{ padding: '0 40px 40px' }}>\n <div className=\"grid gap-4\" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))' }}>\n {/* 👉 替换为业务卡片数据 */}\n <Card color=\"white\" title=\"智能客服助手\" description=\"基于大模型的全渠道客服对话助手\" tags={['客服', '对话']} />\n <Card color=\"white\" title=\"数据洞察报告\" description=\"上传数据,自动输出可视化洞察报告\" tags={['数据', '分析']} />\n <Card color=\"white\" title=\"知识库 QA 问答\" description=\"基于私有知识文档,快速搭建专属问答\" tags={['知识库', 'RAG']} />\n </div>\n </div>\n </div>\n </div>\n );\n}",
7168
+ "code": "import NavBar from './components/NavBar';\nimport Tabs from './components/Tabs';\nimport ChatInput from './components/ChatInput';\nimport Card from './components/Card';\nimport Icon from './components/Icon';\nimport Input from './components/Input';\n\nconst CATEGORY_TABS = [\n { label: '全部' },\n { label: '推荐', icon: <Icon name=\"star-01-stroked\" /> },\n { label: '对话助手', icon: <Icon name=\"message-chat-square-stroked\" /> },\n { label: '数据分析', icon: <Icon name=\"bar-chart-01-stroked\" /> },\n];\n\nconst TEMPLATE_SEARCH_WRAP_STYLE = {\n flex: '0 0 240px',\n width: '240px',\n minWidth: '240px',\n maxWidth: '240px',\n};\n\nexport default function ChatHomePage() {\n return (\n <div\n className=\"flex w-full items-stretch overflow-hidden\"\n style={{\n flex: '1 0 auto', alignSelf: 'stretch', minHeight: '720px',\n background: 'var(--color-blueGrey-200)',\n borderRadius: 0,\n }}\n >\n <div className=\"flex shrink-0\">\n <NavBar platform=\"ola\" selectedItemId=\"knowledge\" />\n </div>\n\n <div className=\"flex flex-1 min-w-0 min-h-0 flex-col overflow-y-auto overflow-x-hidden\">\n {/* Hero 区:欢迎语 + ChatInput */}\n <div\n className=\"flex flex-col items-center shrink-0\"\n style={{\n padding: '84px 40px 80px',\n }}\n >\n <div className=\"flex flex-col items-center gap-2 w-full text-center\">\n <h1 className=\"m-0 text-4xl [font-weight:var(--font-semibold)] leading-10\" style={{ color: 'var(--foreground)' }}>\n 今天想做什么?\n </h1>\n <p className=\"m-0 text-sm\" style={{ color: 'var(--foreground-muted)' }}>\n 从下方搜索助手,或直接输入你的需求,AI 帮你快速搞定\n </p>\n </div>\n <div className=\"w-full\" style={{ maxWidth: '680px', marginTop: '48px' }}>\n <ChatInput variant=\"default\" placeholder=\"描述你想完成的任务…\" />\n </div>\n </div>\n\n {/* 筛选行:Tabs + 固定宽搜索,直接坐灰底 */}\n <div\n className=\"flex min-w-0 shrink-0 items-center gap-4\"\n style={{ padding: '16px 40px' }}\n >\n <div className=\"flex shrink-0\">\n <Tabs variant=\"pill\" size=\"sm\" items={CATEGORY_TABS} defaultIndex={0} />\n </div>\n <div className=\"ml-auto shrink-0\" style={TEMPLATE_SEARCH_WRAP_STYLE}>\n <Input\n placeholder=\"搜索标题、描述\"\n prefix={<Icon name=\"search-md-stroked\" size=\"sm\" />}\n allowClear\n />\n </div>\n </div>\n\n {/* 卡片列表:随右侧页面整体滚动,卡片自身是白卡,外层不包白卡 */}\n <div className=\"shrink-0\" style={{ padding: '0 40px 40px' }}>\n <div className=\"grid gap-4\" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))' }}>\n {/* 👉 替换为业务卡片数据 */}\n <Card color=\"white\" title=\"智能客服助手\" description=\"基于大模型的全渠道客服对话助手\" tags={['客服', '对话']} />\n <Card color=\"white\" title=\"数据洞察报告\" description=\"上传数据,自动输出可视化洞察报告\" tags={['数据', '分析']} />\n <Card color=\"white\" title=\"知识库 QA 问答\" description=\"基于私有知识文档,快速搭建专属问答\" tags={['知识库', 'RAG']} />\n </div>\n </div>\n </div>\n </div>\n );\n}",
7126
7169
  "keywords": [
7127
7170
  "ChatHomePagePattern",
7128
7171
  "AI 入口页",