@devflow-tools/plugin-tailwind 0.12.6 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1177 @@
1
+ const structuredOutputSchema = {
2
+ type: "object",
3
+ additionalProperties: true,
4
+ };
5
+ const readOnlyAnnotations = {
6
+ readOnlyHint: true,
7
+ idempotentHint: true,
8
+ };
9
+ export const tailwindPlugin = {
10
+ meta: {
11
+ name: "tailwind",
12
+ version: "0.1.0",
13
+ description: "Tailwind 专家 — 响应式设计、主题定制、Purge 检查、Utility 审查",
14
+ icon: "🌊",
15
+ tags: ["frontend", "css", "tailwind", "utility-first", "responsive"],
16
+ },
17
+ knowledge: {
18
+ sources: [
19
+ {
20
+ name: "tailwind-docs",
21
+ urls: [
22
+ "https://tailwindcss.com/docs",
23
+ "https://tailwindcss.com/docs/configuration",
24
+ "https://tailwindcss.com/docs/customizing-colors",
25
+ ],
26
+ pluginName: "tailwind",
27
+ version: "3.4.0",
28
+ },
29
+ ],
30
+ },
31
+ memory: {
32
+ project: {
33
+ stack: ["Tailwind CSS 3", "PostCSS", "autoprefixer"],
34
+ conventions: [
35
+ { key: "styling", value: "优先使用 Tailwind utility classes,避免自定义 CSS", source: "manual", confidence: 0.9 },
36
+ { key: "responsive", value: "使用 sm:/md:/lg:/xl:/2xl: 断点前缀", source: "manual", confidence: 0.9 },
37
+ { key: "theme", value: "自定义颜色和间距通过 tailwind.config.ts", source: "manual", confidence: 0.85 },
38
+ { key: "dark-mode", value: "使用 dark: 前缀和 class 策略实现暗色模式", source: "manual", confidence: 0.8 },
39
+ ],
40
+ },
41
+ global: [
42
+ {
43
+ id: "tailwind:utility-first",
44
+ source: "tailwind",
45
+ title: "Utility-First 设计理念",
46
+ content: "直接在 HTML 中使用预定义的 utility class 构建界面。优点:无需命名 CSS 类,样式不会级联,体积通过 PurgeCSS 优化。复杂样式抽取为 @apply 指令。",
47
+ embedding: [],
48
+ },
49
+ {
50
+ id: "tailwind:responsive",
51
+ source: "tailwind",
52
+ title: "响应式设计断点",
53
+ content: "Tailwind 默认断点:sm(640px)、md(768px)、lg(1024px)、xl(1280px)、2xl(1536px)。移动优先:unprefixed class 作用于小屏。",
54
+ embedding: [],
55
+ },
56
+ {
57
+ id: "tailwind:theme-customization",
58
+ source: "tailwind",
59
+ title: "主题定制方法",
60
+ content: "tailwind.config.ts 中 extend 字段扩展默认主题。colors、spacing、fontSize 通过 extend 追加。使用 CSS 变量配合 theme() 函数实现动态主题。",
61
+ embedding: [],
62
+ },
63
+ {
64
+ id: "tailwind:performance",
65
+ source: "tailwind",
66
+ title: "Tailwind 性能优化",
67
+ content: "content 路径精确匹配避免扫描无关文件。safelist 只包含动态类名。使用 @layer 管理样式优先级。避免 @apply 过度使用。生产构建自动移除未使用样式。",
68
+ embedding: [],
69
+ },
70
+ ],
71
+ },
72
+ workflow: {
73
+ steps: [],
74
+ templates: [
75
+ // ── 新建 Tailwind 组件 ──
76
+ {
77
+ name: "new-component",
78
+ description: "新建 Tailwind 组件:语义结构 → 样式 → 响应式 → 暗色模式",
79
+ version: "2.0.0",
80
+ steps: [
81
+ {
82
+ id: "design-structure",
83
+ title: "设计组件语义结构",
84
+ instruction: [
85
+ "## 设计组件语义结构(HTML + ARIA)",
86
+ "",
87
+ "先确定组件的语义化 HTML 结构,再考虑样式。结构正确后 Tailwind classes 才有意义。",
88
+ "",
89
+ "**执行动作**:",
90
+ "1. 明确组件职责:是展示型(display)还是交互型(interactive)",
91
+ "2. 选择最合适的 HTML 语义标签:",
92
+ " - 导航 → `<nav>` + `<ul>` + `<li>` + `<a>`",
93
+ " - 表单 → `<form>` + `<label>` + `<input>` / `<select>` / `<textarea>`",
94
+ " - 列表 → `<ul>` / `<ol>` + `<li>`",
95
+ " - 卡片容器 → `<article>` 或 `<section>`",
96
+ " - 按钮 → `<button>`(不要用 `<div>` 模拟)",
97
+ "3. 添加 ARIA 属性(必要时):",
98
+ " - `aria-label` / `aria-labelledby` 给无文本图标按钮",
99
+ " - `role=\"dialog\"` / `aria-modal=\"true\"` 给弹窗",
100
+ " - `aria-expanded` 给可折叠区域",
101
+ "4. 确定组件的 props 接口(如用 React/Vue):类型、默认值、是否必填",
102
+ "",
103
+ "**产出格式**:",
104
+ "```tsx",
105
+ "// 伪代码,仅结构示意",
106
+ "<nav aria-label=\"Main navigation\">",
107
+ " <ul className=\"...\">",
108
+ " <li><a href=\"...\">...</a></li>",
109
+ " </ul>",
110
+ "</nav>",
111
+ "```",
112
+ "component_props: { label: string; items: MenuItem[]; variant?: 'default' | 'compact' }",
113
+ ].join("\n"),
114
+ suggestedTools: ["Read"],
115
+ output: "语义化 HTML 骨架和 props 接口定义",
116
+ },
117
+ {
118
+ id: "apply-base-styles",
119
+ title: "应用基础样式",
120
+ instruction: [
121
+ "## 应用基础 Tailwind 样式(spacing、typography、colors)",
122
+ "",
123
+ "在语义结构上叠加基础 utility class。所有值优先从 `tailwind.config.ts` 的 theme 中取。",
124
+ "",
125
+ "**执行动作**:",
126
+ "1. **Spacing(间距)**:使用 `p-*`、`m-*`、`gap-*`,值从 theme.spacing 取",
127
+ " ```",
128
+ " p-4 = padding: 1rem (theme.spacing[4])",
129
+ " px-6 = padding-left/right: 1.5rem",
130
+ " gap-3 = gap: 0.75rem",
131
+ " ```",
132
+ "2. **Typography(字体)**:使用 `text-*`、`font-*`、`leading-*`",
133
+ " ```",
134
+ " text-sm = font-size: 0.875rem; line-height: 1.25rem",
135
+ " font-semibold = font-weight: 600",
136
+ " leading-tight = line-height: 1.25",
137
+ " ```",
138
+ "3. **Colors(颜色)**:使用 `text-{color}-{shade}`、`bg-{color}-{shade}`、`border-{color}-{shade}`",
139
+ " - 优先使用语义色:`text-primary`、`bg-surface`(如已在 theme.extend.colors 定义)",
140
+ " - 或直接用 Tailwind 默认色板:`text-gray-700`、`bg-blue-500`",
141
+ "4. **布局**:`flex`、`grid`、`items-center`、`justify-between` 等",
142
+ "5. **圆角 / 阴影**:`rounded-lg`、`shadow-sm` 等",
143
+ "",
144
+ "**约束**:",
145
+ "- 不要在此步加响应式前缀(下一步)",
146
+ "- 不要加 dark: 前缀(下一步)",
147
+ "- 不要加 hover/focus 等状态前缀(下一步)",
148
+ "- 不要写自定义 CSS 或 @apply",
149
+ "",
150
+ "**产出格式**:",
151
+ "```tsx",
152
+ "<nav className=\"flex items-center gap-4 px-6 py-3 bg-white text-gray-800 shadow-sm\">",
153
+ " ...",
154
+ "</nav>",
155
+ "```",
156
+ ].join("\n"),
157
+ suggestedTools: ["Read"],
158
+ output: "带基础 utility class 的组件代码(无响应式/暗色/状态前缀)",
159
+ depends: ["design-structure"],
160
+ },
161
+ {
162
+ id: "add-responsive",
163
+ title: "添加响应式前缀",
164
+ instruction: [
165
+ "## 添加响应式前缀(sm / md / lg / xl / 2xl)",
166
+ "",
167
+ "Tailwind 移动优先:unprefixed class 作用于最小屏,通过断点前缀覆盖。",
168
+ "",
169
+ "**断点对照表**:",
170
+ "| 前缀 | 最小宽度 | 典型设备 |",
171
+ "|------|----------|----------|",
172
+ "| sm | 640px | 大手机横屏 |",
173
+ "| md | 768px | 平板 |",
174
+ "| lg | 1024px | 小笔记本 |",
175
+ "| xl | 1280px | 桌面 |",
176
+ "| 2xl | 1536px | 大桌面 |",
177
+ "",
178
+ "**执行动作**:",
179
+ "1. 找出需要响应式调整的 class(通常是布局、字号、间距、显隐)",
180
+ "2. 从最小屏开始(无 prefix),逐步叠加断点前缀:",
181
+ " ```",
182
+ " text-sm md:text-base lg:text-lg // 字号随屏幕变大",
183
+ " flex-col md:flex-row // 小屏竖排,平板以上横排",
184
+ " hidden md:block // 小屏隐藏,平板以上显示",
185
+ " px-4 md:px-6 lg:px-8 // 间距逐级增大",
186
+ " grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 // 网格列数",
187
+ " ```",
188
+ "3. 检查每个断点下的视觉效果是否符合设计稿",
189
+ "4. 避免给所有 class 加响应式前缀——只改需要变的",
190
+ "",
191
+ "**产出格式**:",
192
+ "```tsx",
193
+ "<div className=\"flex flex-col md:flex-row gap-4 md:gap-6 px-4 md:px-8\">",
194
+ " ...",
195
+ "</div>",
196
+ "```",
197
+ "responsive_breakpoints_used: [\"sm\", \"md\", \"lg\"]",
198
+ ].join("\n"),
199
+ suggestedTools: ["Read"],
200
+ output: "添加了响应式断点前缀的组件代码",
201
+ depends: ["apply-base-styles"],
202
+ },
203
+ {
204
+ id: "add-dark-mode",
205
+ title: "添加暗色模式样式",
206
+ instruction: [
207
+ "## 添加 dark: 前缀暗色模式样式",
208
+ "",
209
+ "使用 Tailwind 的 class 策略暗色模式。暗色 class 在 `<html class=\"dark\">` 时生效。",
210
+ "",
211
+ "**执行动作**:",
212
+ "1. 对所有涉及颜色的 class 添加 `dark:` 对应版本:",
213
+ " ```",
214
+ " bg-white dark:bg-gray-900",
215
+ " text-gray-800 dark:text-gray-100",
216
+ " border-gray-200 dark:border-gray-700",
217
+ " ```",
218
+ "2. 对图片/Logo 在暗色下可能需要反色:",
219
+ " ```",
220
+ " <img className=\"dark:invert\" src=\"/logo.svg\" />",
221
+ " ```",
222
+ "3. 阴影在暗色下通常更重或更淡:",
223
+ " ```",
224
+ " shadow-sm dark:shadow-lg",
225
+ " shadow-gray-200 dark:shadow-black/50",
226
+ " ```",
227
+ "4. 检查透明度和叠加效果:暗色下背景叠加可能需要调整 opacity",
228
+ "5. 确认 `tailwind.config.ts` 中 `darkMode: 'class'` 已配置",
229
+ "",
230
+ "**约束**:",
231
+ "- 暗色 class 紧跟对应亮色 class,不要散落各处",
232
+ "- 背景色、文本色、边框色、阴影色都需要 dark: 版本",
233
+ "- 不要漏掉 hover/focus 状态中的颜色(下一步处理)",
234
+ "",
235
+ "**产出格式**:",
236
+ "```tsx",
237
+ "<div className=\"bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-100\">",
238
+ " ...",
239
+ "</div>",
240
+ "```",
241
+ "dark_mode_config: tailwind.config.ts → darkMode: 'class'",
242
+ ].join("\n"),
243
+ suggestedTools: ["Read"],
244
+ output: "同时支持亮色和暗色模式的组件代码",
245
+ depends: ["apply-base-styles"],
246
+ },
247
+ {
248
+ id: "add-interactive",
249
+ title: "添加交互状态样式",
250
+ instruction: [
251
+ "## 添加交互状态(hover / focus / active / disabled)",
252
+ "",
253
+ "为可交互元素(按钮、链接、输入框等)添加状态样式,提升可访问性和体验。",
254
+ "",
255
+ "**状态前缀对照**:",
256
+ "| 前缀 | 触发条件 | 典型用途 |",
257
+ "|------------|----------------------------|----------|",
258
+ "| hover: | 鼠标悬停 | 颜色变化、阴影加深 |",
259
+ "| focus: | 键盘聚焦 | outline、ring |",
260
+ "| focus-visible: | 仅键盘聚焦(推荐) | 比 focus 更精准 |",
261
+ "| active: | 鼠标按下 | 颜色加深、scale |",
262
+ "| disabled: | 元素禁用 | 灰度、降低透明度 |",
263
+ "| group-hover: | 父元素 hover 时子元素变化 | 卡片 hover 时标题变色 |",
264
+ "",
265
+ "**执行动作**:",
266
+ "1. **按钮/链接**:",
267
+ " ```",
268
+ " hover:bg-blue-600 focus-visible:ring-2 focus-visible:ring-blue-500",
269
+ " active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed",
270
+ " ```",
271
+ "2. **输入框**:",
272
+ " ```",
273
+ " focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20",
274
+ " disabled:bg-gray-100 disabled:cursor-not-allowed",
275
+ " ```",
276
+ "3. **卡片/列表项(使用 group-hover)**:",
277
+ " ```",
278
+ " <div className=\"group\">",
279
+ " <h3 className=\"group-hover:text-blue-500\">...</h3>",
280
+ " </div>",
281
+ " ```",
282
+ "4. **过渡动画**:给 hover/focus 变化加 `transition-colors` 或 `transition-all`",
283
+ "5. 暗色模式下对应状态也要加:`dark:hover:bg-gray-800`",
284
+ "",
285
+ "**约束**:",
286
+ "- 所有交互元素必须有 focus-visible 样式(键盘可访问性)",
287
+ "- disabled 状态不要只靠颜色区分,还要有 cursor 和 opacity",
288
+ "- 不要给非交互元素(div、span)加 hover",
289
+ "",
290
+ "**产出格式**:",
291
+ "```tsx",
292
+ "<button className=\"bg-blue-500 hover:bg-blue-600 focus-visible:ring-2",
293
+ " focus-visible:ring-blue-500 active:scale-95 transition-colors",
294
+ " disabled:opacity-50 disabled:cursor-not-allowed\">",
295
+ " Submit",
296
+ "</button>",
297
+ "```",
298
+ ].join("\n"),
299
+ suggestedTools: ["Read"],
300
+ output: "完整交互状态的组件代码(含 hover/focus/active/disabled)",
301
+ depends: ["apply-base-styles"],
302
+ },
303
+ {
304
+ id: "review-classes",
305
+ title: "审查 class 列表",
306
+ instruction: [
307
+ "## 审查 class 列表(消除自定义 CSS,统一为 utility)",
308
+ "",
309
+ "最后审查一遍所有 class,确保没有遗留的自定义 CSS 或可优化的地方。",
310
+ "",
311
+ "**检查项**:",
312
+ "",
313
+ "**1. 查找自定义 CSS**",
314
+ "```bash",
315
+ "# 检查组件中是否有 style 属性",
316
+ "grep -n 'style={{' src/components/YourComponent.tsx",
317
+ "",
318
+ "# 检查同目录 CSS 文件",
319
+ "ls src/components/YourComponent.css 2>/dev/null",
320
+ "```",
321
+ "",
322
+ "**2. 查找 @apply 指令**",
323
+ "```bash",
324
+ "grep -rn '@apply' src/components/ --include='*.css'",
325
+ "```",
326
+ "如果有 @apply,评估是否能拆为多个 utility class。",
327
+ "",
328
+ "**3. 查找硬编码颜色/尺寸**",
329
+ "```bash",
330
+ "grep -nE '(#[0-9a-fA-F]{3,8}|rgb[a]?\\(|[0-9]+px)' src/components/YourComponent.tsx",
331
+ "```",
332
+ "",
333
+ "**4. 审查重复 class 组合**",
334
+ "如果多个元素有完全相同的 5+ class 组合,考虑抽取为组件或 @apply。",
335
+ "",
336
+ "**5. 检查 class 顺序**",
337
+ "建议按 Tailwind 官方推荐顺序排列(便于阅读和维护):",
338
+ "1. Layout: `flex`, `grid`, `block`",
339
+ "2. Spacing: `p-*`, `m-*`, `gap-*`",
340
+ "3. Sizing: `w-*`, `h-*`, `max-w-*`",
341
+ "4. Typography: `text-*`, `font-*`, `leading-*`",
342
+ "5. Colors: `text-*`, `bg-*`, `border-*`",
343
+ "6. Effects: `shadow-*`, `opacity-*`",
344
+ "7. States: `hover:*`, `focus:*`, `dark:*`",
345
+ "",
346
+ "**产出格式**:",
347
+ "```",
348
+ "custom_css_found: false",
349
+ "inline_styles_found: false",
350
+ "hardcoded_values: []",
351
+ "apply_directives: []",
352
+ "suggestions: [\"class 顺序调整\"] | []",
353
+ "```",
354
+ ].join("\n"),
355
+ suggestedTools: ["Bash", "Read"],
356
+ output: "class 审查报告(自定义 CSS、硬编码值、重复组合)",
357
+ depends: ["add-responsive", "add-dark-mode", "add-interactive"],
358
+ },
359
+ {
360
+ id: "check-purge-safelist",
361
+ title: "确认动态 class 在 safelist 中",
362
+ instruction: [
363
+ "## 确认动态 class 在 safelist 中",
364
+ "",
365
+ "PurgeCSS 通过静态扫描移除非使用 class。动态拼接的 class 必须加到 safelist。",
366
+ "",
367
+ "**执行动作**:",
368
+ "",
369
+ "**1. 查找动态 class 拼接**",
370
+ "```bash",
371
+ "# 查找 class 拼接、模板字符串、变量引用",
372
+ "grep -rnE '(className=\\{.*\\+|`.*\\$\\{|classes\\[)' src/components/YourComponent.tsx",
373
+ "",
374
+ "# 查找条件 class 工具(clsx, classnames, tailwind-merge)",
375
+ "grep -rnE '(clsx|classnames|cn|twMerge)\\(' src/components/YourComponent.tsx",
376
+ "```",
377
+ "",
378
+ "**2. 列出组件中所有动态 class**",
379
+ "例如:",
380
+ "```tsx",
381
+ "const variantClasses = {",
382
+ " primary: 'bg-blue-500 text-white',",
383
+ " secondary: 'bg-gray-200 text-gray-800',",
384
+ " danger: 'bg-red-500 text-white',",
385
+ "};",
386
+ "```",
387
+ "这些 class 都可能在运行时才出现,PurgeCSS 扫描不到。",
388
+ "",
389
+ "**3. 检查 tailwind.config.ts 的 safelist**",
390
+ "```bash",
391
+ "grep -A 10 'safelist' tailwind.config.ts",
392
+ "```",
393
+ "如果未配置 safelist 或 safelist 中缺少上述 class,需补充:",
394
+ "```typescript",
395
+ "// tailwind.config.ts",
396
+ "export default {",
397
+ " safelist: [",
398
+ " 'bg-blue-500', 'text-white',",
399
+ " 'bg-gray-200', 'text-gray-800',",
400
+ " 'bg-red-500',",
401
+ " ],",
402
+ "};",
403
+ "```",
404
+ "",
405
+ "**4. 使用模式匹配 safelist(推荐)**",
406
+ "```typescript",
407
+ "safelist: [",
408
+ " { pattern: /^bg-(blue|gray|red)-\\d+$/ },",
409
+ " { pattern: /^text-(white|gray-\\d+)$/ },",
410
+ "],",
411
+ "```",
412
+ "",
413
+ "**产出格式**:",
414
+ "```",
415
+ "dynamic_classes_found: ['bg-blue-500', 'bg-red-500', ...]",
416
+ "safelist_configured: true | false",
417
+ "safelist_entries: ['bg-blue-500', ...]",
418
+ "missing_from_safelist: [] | ['bg-red-500']",
419
+ "```",
420
+ ].join("\n"),
421
+ suggestedTools: ["Bash", "Read"],
422
+ output: "动态 class 列表和 safelist 配置检查报告",
423
+ depends: ["review-classes"],
424
+ },
425
+ ],
426
+ triggers: [{ type: "cli", command: "new:component" }, { type: "mcp", tool: "new_component" }],
427
+ },
428
+ // ── Utility 审查 ──
429
+ {
430
+ name: "utility-audit",
431
+ description: "Utility 审查:清除自定义 CSS → 统一为 Tailwind utility",
432
+ version: "2.0.0",
433
+ steps: [
434
+ {
435
+ id: "scan-custom-css",
436
+ stepTemplate: "scan-styles",
437
+ overrides: {
438
+ title: "扫描项目中的自定义 CSS 和 @apply 指令",
439
+ instruction: [
440
+ "## 扫描项目中的自定义 CSS 和 @apply 指令",
441
+ "",
442
+ "找出所有可被 Tailwind utility 替代的自定义样式。",
443
+ "",
444
+ "**执行动作**:",
445
+ "```bash",
446
+ "# 1. 找到所有样式文件",
447
+ "find src/ \\( -name '*.css' -o -name '*.scss' -o -name '*.less' -o -name '*.module.css' \\) | head -50",
448
+ "",
449
+ "# 2. 扫描 @apply 指令(可被直接替换为 utility)",
450
+ "grep -rn '@apply' src/ --include='*.css' --include='*.scss' --include='*.vue' -l",
451
+ "",
452
+ "# 3. 统计 @apply 使用次数",
453
+ "grep -rn '@apply' src/ | wc -l",
454
+ "",
455
+ "# 4. 查找可能用 utility 替代的属性",
456
+ "grep -rnE '(display:\\s*flex|justify-content|align-items|margin:\\s*0|padding:\\s*0|color:\\s*#|background(-color)?:\\s*#|font-size:\\s*[0-9]+px)' src/ --include='*.css' --include='*.scss' --include='*.tsx' --include='*.vue' | head -50",
457
+ "",
458
+ "# 5. 查找 inline style",
459
+ "grep -rn 'style={{' src/ --include='*.tsx' | head -20",
460
+ "```",
461
+ "",
462
+ "**产出格式**:",
463
+ "```",
464
+ "style_files: [list of files]",
465
+ "apply_directives_count: <N>",
466
+ "inline_styles_count: <N>",
467
+ "replaceable_properties: [list]",
468
+ "```",
469
+ ].join("\n"),
470
+ },
471
+ suggestedTools: ["Bash", "Read"],
472
+ output: "自定义 CSS 和可替换样式清单",
473
+ },
474
+ {
475
+ id: "match-utility",
476
+ title: "匹配 Tailwind utility",
477
+ instruction: [
478
+ "## 匹配每个自定义样式到对应的 Tailwind utility",
479
+ "",
480
+ "对上一步发现的自定义 CSS 属性,逐一匹配 Tailwind utility class。",
481
+ "",
482
+ "**常见映射表**:",
483
+ "| CSS 属性 | Tailwind utility |",
484
+ "|-------------------------------|---------------------------|",
485
+ "| `display: flex` | `flex` |",
486
+ "| `display: grid` | `grid` |",
487
+ "| `justify-content: center` | `justify-center` |",
488
+ "| `align-items: center` | `items-center` |",
489
+ "| `margin: 0 auto` | `mx-auto` |",
490
+ "| `padding: 1rem` | `p-4` |",
491
+ "| `padding: 0.5rem 1rem` | `py-2 px-4` |",
492
+ "| `font-size: 14px` | `text-sm` |",
493
+ "| `font-weight: 600` | `font-semibold` |",
494
+ "| `color: #1a1a1a` | `text-gray-900` |",
495
+ "| `background: #fff` | `bg-white` |",
496
+ "| `border-radius: 0.5rem` | `rounded-lg` |",
497
+ "| `box-shadow: 0 1px 2px ...` | `shadow-sm` |",
498
+ "| `width: 100%` | `w-full` |",
499
+ "| `height: 100vh` | `h-screen` |",
500
+ "| `position: absolute` | `absolute` |",
501
+ "| `top: 0; right: 0` | `top-0 right-0` |",
502
+ "| `z-index: 50` | `z-50` |",
503
+ "| `opacity: 0.5` | `opacity-50` |",
504
+ "| `transition: all 0.2s` | `transition-all duration-200` |",
505
+ "",
506
+ "**执行动作**:",
507
+ "1. 对每个自定义 CSS 属性,查上表找对应 utility",
508
+ "2. 如果属性值不在 Tailwind 默认 theme 中(如 `width: 300px`),使用任意值语法:`w-[300px]`",
509
+ "3. 如果是多次使用的任意值,应提取到 theme(参考 theme-migrate 模板)",
510
+ "4. 组合多个属性时,注意 Tailwind 简写形式:",
511
+ " - `margin-top: 1rem; margin-bottom: 1rem` → `my-4`(不是 `mt-4 mb-4`)",
512
+ " - `padding-left: 1rem; padding-right: 1rem` → `px-4`",
513
+ "",
514
+ "**产出格式**:",
515
+ "```",
516
+ "mappings: [",
517
+ " { file: 'Button.css', line: 10-15, custom: 'display: flex; gap: 0.5rem', utility: 'flex gap-2' },",
518
+ " { file: 'Card.css', line: 5, custom: 'padding: 1.5rem', utility: 'p-6' },",
519
+ " ...",
520
+ "]",
521
+ "```",
522
+ ].join("\n"),
523
+ suggestedTools: ["Read"],
524
+ output: "CSS 属性 → Tailwind utility 的映射清单",
525
+ depends: ["scan-custom-css"],
526
+ },
527
+ {
528
+ id: "detect-deprecated",
529
+ title: "检测已弃用的 Tailwind class",
530
+ instruction: [
531
+ "## 检测已弃用的 Tailwind class(v2 → v3 迁移)",
532
+ "",
533
+ "Tailwind v3 废弃了一批 v2 的 class。扫描项目并替换为新 class。",
534
+ "",
535
+ "**执行动作**:",
536
+ "```bash",
537
+ "# 1. 搜索已弃用的 class",
538
+ "grep -rnE '(flex-shrink-0|flex-grow-0|flex-shrink|flex-grow|overflow-scroll|decoration-slice|decoration-clone|decoration-clone|rounded-sm)' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.html' | head -30",
539
+ "",
540
+ "# 2. 统计每种弃用 class 的出现次数",
541
+ "grep -rhoE '(flex-shrink-[0-9]+|flex-grow-[0-9]+|flex-shrink|flex-grow|overflow-scroll)' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.html' | sort | uniq -c | sort -rn",
542
+ "```",
543
+ "",
544
+ "**弃用对照表**:",
545
+ "| 弃用 class | 替代 class | 说明 |",
546
+ "|-------------------------|-------------------|------|",
547
+ "| `flex-shrink-0` | `shrink-0` | 简写 |",
548
+ "| `flex-shrink` | `shrink` | 简写 |",
549
+ "| `flex-grow-0` | `grow-0` | 简写 |",
550
+ "| `flex-grow` | `grow` | 简写 |",
551
+ "| `overflow-scroll` | `overflow-auto` 或 `overflow-y-scroll` | 语义更明确 |",
552
+ "| `decoration-slice` | `box-decoration-slice` | 重命名 |",
553
+ "| `decoration-clone` | `box-decoration-clone` | 重命名 |",
554
+ "| `rounded-sm` | `rounded-sm` 仍有效,但语义不清 | 建议改为明确值 |",
555
+ "| `ring` 无宽度 | `ring-3` | v3 默认 ring 宽度变为 3px |",
556
+ "",
557
+ "**执行替换**:",
558
+ "```bash",
559
+ "# 批量替换(建议先 dry-run)",
560
+ "find src/ -type f \\( -name '*.tsx' -o -name '*.jsx' -o -name '*.vue' -o -name '*.html' \\) \\",
561
+ " -exec sed -i '' \\",
562
+ " -e 's/\\bflex-shrink-0\\b/shrink-0/g' \\",
563
+ " -e 's/\\bflex-shrink\\b/shrink/g' \\",
564
+ " -e 's/\\bflex-grow-0\\b/grow-0/g' \\",
565
+ " -e 's/\\bflex-grow\\b/grow/g' \\",
566
+ " {} +",
567
+ "```",
568
+ "",
569
+ "**产出格式**:",
570
+ "```",
571
+ "deprecated_classes_found: { 'flex-shrink-0': 15, 'flex-grow': 3 }",
572
+ "replacements_applied: ['shrink-0', 'grow']",
573
+ "files_modified: [list]",
574
+ "```",
575
+ ].join("\n"),
576
+ suggestedTools: ["Bash", "Read"],
577
+ output: "已弃用 class 清单和替换建议",
578
+ depends: ["scan-custom-css"],
579
+ },
580
+ {
581
+ id: "check-arbitrary-values",
582
+ title: "审查任意值是否应提取为 theme",
583
+ instruction: [
584
+ "## 审查任意值 `[...]` 是否应提取为 theme token",
585
+ "",
586
+ "Tailwind 任意值(如 `w-[300px]`、`bg-[#1da1f2]`)方便但不利于设计一致性。",
587
+ "出现 3 次以上的任意值应提取到 theme。",
588
+ "",
589
+ "**执行动作**:",
590
+ "```bash",
591
+ "# 1. 查找所有任意值用法",
592
+ "grep -rhoE '[a-z]+-\\[[^]]+\\]' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.html' | sort | uniq -c | sort -rn | head -30",
593
+ "",
594
+ "# 2. 按类别分组统计",
595
+ "grep -rhoE '[a-z]+-\\[[^]]+\\]' src/ | sed -E 's/^([a-z]+)-.*/\\1/' | sort | uniq -c | sort -rn",
596
+ "```",
597
+ "",
598
+ "**判定标准**:",
599
+ "- 出现 ≥ 3 次 → 必须提取到 theme",
600
+ "- 出现 2 次 → 建议提取(看是否为语义 token)",
601
+ "- 出现 1 次 → 保留任意值 OK",
602
+ "",
603
+ "**提取示例**:",
604
+ "",
605
+ "**颜色(`bg-[#1da1f2]` 出现 5 次)**:",
606
+ "```typescript",
607
+ "// tailwind.config.ts",
608
+ "theme: {",
609
+ " extend: {",
610
+ " colors: {",
611
+ " 'twitter-blue': '#1da1f2',",
612
+ " },",
613
+ " },",
614
+ "},",
615
+ "```",
616
+ "替换:`bg-[#1da1f2]` → `bg-twitter-blue`",
617
+ "",
618
+ "**间距(`p-[18px]` 出现 4 次)**:",
619
+ "```typescript",
620
+ "theme: {",
621
+ " extend: {",
622
+ " spacing: {",
623
+ " '18': '18px',",
624
+ " },",
625
+ " },",
626
+ "},",
627
+ "```",
628
+ "替换:`p-[18px]` → `p-18`",
629
+ "",
630
+ "**尺寸(`w-[320px]` 出现 3 次)**:",
631
+ "```typescript",
632
+ "theme: {",
633
+ " extend: {",
634
+ " width: {",
635
+ " 'sidebar': '320px',",
636
+ " },",
637
+ " },",
638
+ "},",
639
+ "```",
640
+ "替换:`w-[320px]` → `w-sidebar`",
641
+ "",
642
+ "**产出格式**:",
643
+ "```",
644
+ "arbitrary_values: {",
645
+ " 'bg-[#1da1f2]': { count: 5, suggest: 'bg-twitter-blue', theme_key: 'colors.twitter-blue' },",
646
+ " 'p-[18px]': { count: 4, suggest: 'p-18', theme_key: 'spacing.18' },",
647
+ " 'w-[320px]': { count: 3, suggest: 'w-sidebar', theme_key: 'width.sidebar' },",
648
+ "}",
649
+ "```",
650
+ ].join("\n"),
651
+ suggestedTools: ["Bash", "Read"],
652
+ output: "任意值使用统计和提取到 theme 的建议",
653
+ depends: ["scan-custom-css"],
654
+ },
655
+ {
656
+ id: "replace-custom",
657
+ title: "用 Tailwind utility 替换自定义 CSS",
658
+ instruction: [
659
+ "## 用 Tailwind utility 替换自定义 CSS",
660
+ "",
661
+ "根据 `match-utility` 步骤产出的映射表,逐一替换。",
662
+ "",
663
+ "**执行动作**:",
664
+ "",
665
+ "**1. 替换 CSS 文件中的 @apply**",
666
+ "原代码:",
667
+ "```css",
668
+ ".card {",
669
+ " @apply bg-white p-6 rounded-lg shadow-sm;",
670
+ "}",
671
+ "```",
672
+ "替换为直接使用 utility:",
673
+ "```tsx",
674
+ "<div className=\"bg-white p-6 rounded-lg shadow-sm\">",
675
+ " ...",
676
+ "</div>",
677
+ "```",
678
+ "",
679
+ "**2. 替换 inline style**",
680
+ "原代码:",
681
+ "```tsx",
682
+ "<div style={{ padding: '1rem', color: '#1a1a1a' }}>",
683
+ "```",
684
+ "替换为:",
685
+ "```tsx",
686
+ "<div className=\"p-4 text-gray-900\">",
687
+ "```",
688
+ "",
689
+ "**3. 删除已清空的 CSS 文件**",
690
+ "```bash",
691
+ "# 找出只剩 import 或空的 CSS 文件",
692
+ "find src/ -name '*.css' -o -name '*.scss' | while read f; do",
693
+ " lines=$(grep -cvE '^\\s*$|^\\s*//' \"$f\")",
694
+ " [ $lines -lt 3 ] && echo \"$f (可删除)\"",
695
+ "done",
696
+ "```",
697
+ "",
698
+ "**4. 替换后验证 class 拼写正确**",
699
+ "```bash",
700
+ "# 查找拼写错误的 class(例如 text-gary 而不是 text-gray)",
701
+ "grep -rhoE 'text-[a-z]+-\\d+|bg-[a-z]+-\\d+|border-[a-z]+-\\d+' src/ | sort -u > /tmp/used_classes.txt",
702
+ "# 与 Tailwind 默认色板对比",
703
+ "```",
704
+ "",
705
+ "**约束**:",
706
+ "- 一次只替换一个文件,便于 git diff 审查",
707
+ "- 不要改变视觉外观——如果替换后样式变了,说明映射错了",
708
+ "- 对于复杂选择器(`.btn > .icon`)可能需要保留部分 CSS",
709
+ "",
710
+ "**产出格式**:",
711
+ "```",
712
+ "replacements_applied: {",
713
+ " 'Button.tsx': 5,",
714
+ " 'Card.tsx': 3,",
715
+ "}",
716
+ "files_modified: ['src/components/Button.tsx', 'src/components/Card.tsx']",
717
+ "files_deleted: ['src/styles/legacy.css']",
718
+ "```",
719
+ ].join("\n"),
720
+ suggestedTools: ["Bash", "Read"],
721
+ output: "替换应用清单:修改/删除的文件列表",
722
+ depends: ["match-utility"],
723
+ },
724
+ {
725
+ id: "build-verify",
726
+ stepTemplate: "build-verify",
727
+ overrides: {
728
+ title: "构建验证(确认 PurgeCSS 正确生成)",
729
+ instruction: [
730
+ "## 构建验证(确认 PurgeCSS 正确生成)",
731
+ "",
732
+ "替换自定义 CSS 后,运行完整构建,确保 Tailwind 正确生成产物。",
733
+ "",
734
+ "**执行动作**:",
735
+ "1. 运行构建:",
736
+ " ```bash",
737
+ " npm run build",
738
+ " ```",
739
+ "2. 检查构建输出中的 CSS 文件体积:",
740
+ " ```bash",
741
+ " ls -lh dist/assets/*.css",
742
+ " ```",
743
+ " - 体积应在合理范围(通常 < 50KB gzip)",
744
+ " - 如果体积异常大,可能是 PurgeCSS 没正确工作",
745
+ "3. 抽样检查生成的 CSS,确认:",
746
+ " - 已替换的 class 在产物中",
747
+ " - 旧自定义 CSS 类名不在产物中",
748
+ " - 无重复或冗余样式",
749
+ "4. 检查 `tailwind.config.ts` 的 `content` 路径是否覆盖了所有源文件目录:",
750
+ " ```typescript",
751
+ " content: ['./src/**/*.{js,ts,jsx,tsx,vue,html}'],",
752
+ " ```",
753
+ "5. 如果构建失败,记录错误信息,返回前面的步骤修复",
754
+ "",
755
+ "**产出格式**:",
756
+ "```",
757
+ "build_success: true | false",
758
+ "css_bundle_size: '32.5 KB (gzip: 8.2 KB)'",
759
+ "purge_working: true | false",
760
+ "errors: [] | [list]",
761
+ "```",
762
+ ].join("\n"),
763
+ },
764
+ depends: ["replace-custom"],
765
+ },
766
+ ],
767
+ triggers: [{ type: "cli", command: "audit:utility" }],
768
+ },
769
+ // ── 主题迁移 ──
770
+ {
771
+ name: "theme-migrate",
772
+ description: "主题迁移:分析现有设计 → 提取 Design Token → 生成配置",
773
+ version: "2.0.0",
774
+ steps: [
775
+ {
776
+ id: "extract-tokens",
777
+ title: "提取现有设计中的 Design Token",
778
+ instruction: [
779
+ "## 从现有代码/设计稿提取颜色、间距、字体",
780
+ "",
781
+ "扫描项目源代码,找出所有硬编码的设计值。",
782
+ "",
783
+ "**执行动作**:",
784
+ "```bash",
785
+ "# 1. 提取颜色(HEX、RGB、HSL)",
786
+ "grep -rhoE '(#[0-9a-fA-F]{3,8}|rgb\\([0-9, ]+\\)|rgba?\\([0-9., ]+\\)|hsl\\([0-9.,%° ]+\\))' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.css' --include='*.scss' | sort | uniq -c | sort -rn | head -30",
787
+ "",
788
+ "# 2. 提取字体大小",
789
+ "grep -rhoE 'font-size:\\s*[0-9]+(px|rem|em)|fontSize:\\s*[\"\\'`]?[0-9]+(px|rem|em)' src/ | sort | uniq -c | sort -rn",
790
+ "",
791
+ "# 3. 提取间距(padding/margin/gap)",
792
+ "grep -rhoE '(padding|margin|gap|top|bottom|left|right):\\s*[0-9]+(px|rem|em)' src/ | sort | uniq -c | sort -rn | head -20",
793
+ "",
794
+ "# 4. 提取圆角",
795
+ "grep -rhoE 'border-radius:\\s*[0-9]+(px|%|rem)' src/ | sort | uniq -c | sort -rn",
796
+ "",
797
+ "# 5. 提取阴影",
798
+ "grep -rhoE 'box-shadow:\\s*[^;]+' src/ | sort | uniq -c | sort -rn | head -10",
799
+ "",
800
+ "# 6. 提取字号和行高",
801
+ "grep -rhoE 'font-size:\\s*[0-9]+(px|rem)|line-height:\\s*[0-9.]+(px|rem|em|%)' src/ | sort | uniq -c | sort -rn | head -20",
802
+ "```",
803
+ "",
804
+ "**产出格式**:",
805
+ "```",
806
+ "colors: { '#1da1f2': 15, '#14171a': 42, '#657786': 28, ... }",
807
+ "spacing: { '16px': 35, '24px': 22, '8px': 18, ... }",
808
+ "font_sizes: { '14px': 30, '16px': 25, '12px': 15, ... }",
809
+ "border_radii: { '4px': 20, '8px': 12, '50%': 8 }",
810
+ "shadows: { '0 2px 4px rgba(0,0,0,0.1)': 10, ... }",
811
+ "```",
812
+ ].join("\n"),
813
+ suggestedTools: ["Bash", "Read"],
814
+ output: "项目中所有硬编码设计值的统计数据",
815
+ },
816
+ {
817
+ id: "categorize-tokens",
818
+ title: "分类 Design Token",
819
+ instruction: [
820
+ "## 分类 Design Token(color / spacing / typography / shadow / radius)",
821
+ "",
822
+ "将提取的原始值按语义分组,决定哪些应成为 theme token。",
823
+ "",
824
+ "**分类规则**:",
825
+ "",
826
+ "**1. 颜色(colors)**",
827
+ "按用途分组:",
828
+ "- **品牌色**:Logo、主按钮、链接(如 Twitter blue)",
829
+ "- **文本色**:主文本、次要文本、禁用文本",
830
+ "- **背景色**:页面背景、卡片背景、悬浮背景",
831
+ "- **边框色**:分割线、输入框边框",
832
+ "- **状态色**:成功(绿)、警告(黄)、错误(红)、信息(蓝)",
833
+ "",
834
+ "**2. 间距(spacing)**",
835
+ "- 找出基础单位(通常 4px 或 8px)",
836
+ "- 其他值应为整数倍:4、8、12、16、24、32、48",
837
+ "- 非整数倍的(如 13px)要问为什么",
838
+ "",
839
+ "**3. 字体(typography)**",
840
+ "- `fontFamily`:标题字体、正文字体、等宽字体",
841
+ "- `fontSize`:xs / sm / base / lg / xl / 2xl / 3xl / 4xl",
842
+ "- `fontWeight`:normal / medium / semibold / bold",
843
+ "- `lineHeight`:tight / snug / normal / relaxed / loose",
844
+ "",
845
+ "**4. 阴影(boxShadow)**",
846
+ "- sm / md / lg / xl 多级",
847
+ "- 命名:`shadow-card`、`shadow-modal` 等语义化",
848
+ "",
849
+ "**5. 圆角(borderRadius)**",
850
+ "- sm / md / lg / xl / full",
851
+ "- 或语义化:`rounded-button`、`rounded-card`",
852
+ "",
853
+ "**判定标准**:",
854
+ "- 出现 ≥ 3 次的值 → 必须成为 token",
855
+ "- 出现 2 次但语义清晰 → 建议成为 token",
856
+ "- 出现 1 次 → 保留为任意值或 Tailwind 默认",
857
+ "",
858
+ "**产出格式**:",
859
+ "```typescript",
860
+ "// 初步的 token 规划",
861
+ "tokens = {",
862
+ " colors: {",
863
+ " brand: { primary: '#1da1f2', secondary: '#14171a' },",
864
+ " text: { primary: '#14171a', secondary: '#657786', disabled: '#aab8c2' },",
865
+ " bg: { default: '#ffffff', muted: '#f5f8fa', elevated: '#ffffff' },",
866
+ " border: { default: '#e1e8ed', strong: '#657786' },",
867
+ " },",
868
+ " spacing: { base: '4px', xs: '4px', sm: '8px', md: '16px', lg: '24px', xl: '32px' },",
869
+ " fontSize: { xs: '12px', sm: '14px', base: '16px', lg: '18px', xl: '20px', '2xl': '24px' },",
870
+ " borderRadius: { sm: '4px', md: '8px', lg: '12px', full: '9999px' },",
871
+ " boxShadow: {",
872
+ " card: '0 2px 4px rgba(0,0,0,0.1)',",
873
+ " modal: '0 8px 16px rgba(0,0,0,0.15)',",
874
+ " },",
875
+ "};",
876
+ "```",
877
+ ].join("\n"),
878
+ suggestedTools: ["Read"],
879
+ output: "按语义分类的 Design Token 结构",
880
+ depends: ["extract-tokens"],
881
+ },
882
+ {
883
+ id: "generate-config",
884
+ title: "生成 tailwind.config.ts 配置",
885
+ instruction: [
886
+ "## 生成 tailwind.config.ts 配置",
887
+ "",
888
+ "将分类好的 Design Token 转换为 `tailwind.config.ts`。",
889
+ "",
890
+ "**执行动作**:",
891
+ "",
892
+ "**1. 创建或更新 tailwind.config.ts**",
893
+ "```typescript",
894
+ "import type { Config } from 'tailwindcss';",
895
+ "",
896
+ "export default {",
897
+ " content: ['./src/**/*.{js,ts,jsx,tsx,vue,html}'],",
898
+ " darkMode: 'class',",
899
+ " theme: {",
900
+ " extend: {",
901
+ " colors: {",
902
+ " brand: {",
903
+ " primary: '#1da1f2',",
904
+ " secondary: '#14171a',",
905
+ " },",
906
+ " text: {",
907
+ " primary: '#14171a',",
908
+ " secondary: '#657786',",
909
+ " disabled: '#aab8c2',",
910
+ " },",
911
+ " bg: {",
912
+ " default: '#ffffff',",
913
+ " muted: '#f5f8fa',",
914
+ " elevated: '#ffffff',",
915
+ " },",
916
+ " border: {",
917
+ " default: '#e1e8ed',",
918
+ " strong: '#657786',",
919
+ " },",
920
+ " },",
921
+ " spacing: {",
922
+ " xs: '0.25rem', // 4px",
923
+ " sm: '0.5rem', // 8px",
924
+ " md: '1rem', // 16px",
925
+ " lg: '1.5rem', // 24px",
926
+ " xl: '2rem', // 32px",
927
+ " '2xl': '3rem', // 48px",
928
+ " },",
929
+ " fontSize: {",
930
+ " xs: ['0.75rem', { lineHeight: '1rem' }],",
931
+ " sm: ['0.875rem', { lineHeight: '1.25rem' }],",
932
+ " base: ['1rem', { lineHeight: '1.5rem' }],",
933
+ " lg: ['1.125rem', { lineHeight: '1.75rem' }],",
934
+ " xl: ['1.25rem', { lineHeight: '1.75rem' }],",
935
+ " '2xl': ['1.5rem', { lineHeight: '2rem' }],",
936
+ " },",
937
+ " borderRadius: {",
938
+ " sm: '0.25rem', // 4px",
939
+ " md: '0.5rem', // 8px",
940
+ " lg: '0.75rem', // 12px",
941
+ " },",
942
+ " boxShadow: {",
943
+ " card: '0 2px 4px rgba(0,0,0,0.1)',",
944
+ " modal: '0 8px 16px rgba(0,0,0,0.15)',",
945
+ " },",
946
+ " },",
947
+ " },",
948
+ " plugins: [],",
949
+ "} satisfies Config;",
950
+ "```",
951
+ "",
952
+ "**2. 验证配置文件语法**",
953
+ "```bash",
954
+ "npx tsc --noEmit tailwind.config.ts",
955
+ "# 或",
956
+ "node -e \"require('./tailwind.config.ts')\"",
957
+ "```",
958
+ "",
959
+ "**3. 运行 Tailwind 测试**",
960
+ "```bash",
961
+ "npx tailwindcss -i src/input.css -o /tmp/test-output.css --minify",
962
+ "```",
963
+ "确认没有报错,且新 token 对应的 utility 能生成。",
964
+ "",
965
+ "**产出格式**:",
966
+ "```",
967
+ "config_file: 'tailwind.config.ts'",
968
+ "new_tokens_count: { colors: 12, spacing: 6, fontSize: 6, borderRadius: 3, boxShadow: 2 }",
969
+ "content_paths: ['./src/**/*.{js,ts,jsx,tsx,vue,html}']",
970
+ "dark_mode: 'class'",
971
+ "```",
972
+ ].join("\n"),
973
+ suggestedTools: ["Read", "Bash"],
974
+ output: "tailwind.config.ts 配置文件",
975
+ depends: ["categorize-tokens"],
976
+ },
977
+ {
978
+ id: "replace-hardcoded",
979
+ title: "替换硬编码值为 Tailwind class",
980
+ instruction: [
981
+ "## 替换代码中的硬编码颜色/尺寸为 Tailwind class",
982
+ "",
983
+ "根据新生成的 `tailwind.config.ts`,将源代码中的硬编码值替换为语义化 class。",
984
+ "",
985
+ "**执行动作**:",
986
+ "",
987
+ "**1. 替换颜色**",
988
+ "```bash",
989
+ "# 查找所有硬编码颜色",
990
+ "grep -rnE '(#[0-9a-fA-F]{3,8}|rgb\\([0-9, ]+\\))' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.css' | head -50",
991
+ "```",
992
+ "替换示例:",
993
+ "```",
994
+ "color: #1da1f2 → text-brand-primary",
995
+ "background: #fff → bg-bg-default",
996
+ "border-color: #e1e8ed → border-border-default",
997
+ "```",
998
+ "",
999
+ "**2. 替换间距**",
1000
+ "```bash",
1001
+ "grep -rnE '(padding|margin|gap|top|left|right|bottom):\\s*[0-9]+px' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.css' | head -50",
1002
+ "```",
1003
+ "替换示例:",
1004
+ "```",
1005
+ "padding: 16px → p-md (或 p-4 如果用默认 spacing)",
1006
+ "margin-top: 8px → mt-sm (或 mt-2)",
1007
+ "gap: 24px → gap-lg (或 gap-6)",
1008
+ "```",
1009
+ "",
1010
+ "**3. 替换字体大小**",
1011
+ "```bash",
1012
+ "grep -rnE 'font-size:\\s*[0-9]+(px|rem)' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.css' | head -30",
1013
+ "```",
1014
+ "替换示例:",
1015
+ "```",
1016
+ "font-size: 14px → text-sm",
1017
+ "font-size: 16px → text-base",
1018
+ "font-size: 24px → text-2xl",
1019
+ "```",
1020
+ "",
1021
+ "**4. 替换圆角和阴影**",
1022
+ "```",
1023
+ "border-radius: 8px → rounded-md",
1024
+ "box-shadow: 0 2px 4px rgba(0,0,0,0.1) → shadow-card",
1025
+ "```",
1026
+ "",
1027
+ "**5. 替换后检查**",
1028
+ "```bash",
1029
+ "# 确认没有遗漏的硬编码值",
1030
+ "grep -rnE '(#[0-9a-fA-F]{3,8}|[0-9]+px)' src/ --include='*.tsx' --include='*.jsx' --include='*.vue' --include='*.css' | grep -vE '(node_modules|dist|.test.)' | head -20",
1031
+ "```",
1032
+ "",
1033
+ "**约束**:",
1034
+ "- 每次替换后检查视觉外观是否一致",
1035
+ "- 不要替换测试文件和 mock 数据中的值",
1036
+ "- 保留 Tailwind 默认 scale 能覆盖的值(如 `16px` → `p-4`),不要硬用自定义 spacing",
1037
+ "",
1038
+ "**产出格式**:",
1039
+ "```",
1040
+ "replacements: {",
1041
+ " colors: { count: 42, files: 15 },",
1042
+ " spacing: { count: 28, files: 12 },",
1043
+ " fontSize: { count: 18, files: 10 },",
1044
+ " borderRadius: { count: 8, files: 5 },",
1045
+ " boxShadow: { count: 4, files: 3 },",
1046
+ "}",
1047
+ "remaining_hardcoded: [list of files and values not replaced]",
1048
+ "```",
1049
+ ].join("\n"),
1050
+ suggestedTools: ["Bash", "Read"],
1051
+ output: "硬编码值替换统计和剩余未替换清单",
1052
+ depends: ["generate-config"],
1053
+ },
1054
+ {
1055
+ id: "visual-regression",
1056
+ stepTemplate: "run-tests",
1057
+ overrides: {
1058
+ title: "视觉回归测试确认外观一致",
1059
+ instruction: [
1060
+ "## 视觉回归测试确认外观一致",
1061
+ "",
1062
+ "主题迁移后必须确认视觉外观未变。",
1063
+ "",
1064
+ "**执行动作**:",
1065
+ "",
1066
+ "**1. 运行现有视觉测试**",
1067
+ "```bash",
1068
+ "# 如果有 Playwright / Cypress / Percy 等视觉回归测试",
1069
+ "npm run test:visual",
1070
+ "```",
1071
+ "",
1072
+ "**2. 运行单元测试和 E2E 测试**",
1073
+ "```bash",
1074
+ "npm run test",
1075
+ "npm run test:e2e",
1076
+ "```",
1077
+ "确认功能未被破坏。",
1078
+ "",
1079
+ "**3. 手动对比截图**",
1080
+ "如果没有自动化视觉测试,启动开发服务器手动检查:",
1081
+ "```bash",
1082
+ "npm run dev",
1083
+ "```",
1084
+ "在浏览器中打开主要页面,对比迁移前后截图:",
1085
+ "- 颜色是否一致(特别是主题色、背景色、文本色)",
1086
+ "- 间距是否一致(组件之间的留白)",
1087
+ "- 字号和行高是否一致",
1088
+ "- 圆角和阴影是否一致",
1089
+ "- 暗色模式是否正确应用",
1090
+ "- 响应式断点下是否正确",
1091
+ "",
1092
+ "**4. 检查 Tailwind 生成的 CSS**",
1093
+ "```bash",
1094
+ "# 构建并检查 CSS 体积",
1095
+ "npm run build",
1096
+ "ls -lh dist/assets/*.css",
1097
+ "",
1098
+ "# 抽样检查 CSS 中是否包含新 token 对应的 utility",
1099
+ "grep -E '(brand-primary|text-secondary|bg-muted)' dist/assets/*.css | head -10",
1100
+ "```",
1101
+ "",
1102
+ "**5. 常见差异排查**",
1103
+ "- 颜色略有色差:检查 HEX 大小写、RGB 值是否精确",
1104
+ "- 间距变大/变小:可能 Tailwind 默认 scale 与原值不同,使用 `theme.extend.spacing` 覆盖",
1105
+ "- 字体渲染不同:检查 `fontFamily` 是否正确包含 fallback",
1106
+ "- 阴影缺失:检查 `boxShadow` token 的透明度值",
1107
+ "",
1108
+ "**产出格式**:",
1109
+ "```",
1110
+ "visual_tests_passed: true | false",
1111
+ "unit_tests_passed: true | false",
1112
+ "e2e_tests_passed: true | false",
1113
+ "css_bundle_size: '32.5 KB (gzip: 8.2 KB)'",
1114
+ "visual_diff_found: [] | [list of issues]",
1115
+ "issues_fixed: [list] | 'all'",
1116
+ "```",
1117
+ ].join("\n"),
1118
+ },
1119
+ depends: ["replace-hardcoded"],
1120
+ },
1121
+ ],
1122
+ triggers: [{ type: "cli", command: "migrate:theme" }],
1123
+ },
1124
+ ],
1125
+ },
1126
+ prompt: {
1127
+ system: "你是一个 Tailwind CSS 专家。使用 utility-first 方法构建界面。遇到问题先查 Tailwind 官方文档。",
1128
+ rules: [
1129
+ "优先使用 Tailwind utility classes,避免自定义 CSS",
1130
+ "使用 Tailwind 响应式断点(sm/md/lg/xl/2xl)",
1131
+ "颜色和间距从 theme 配置中引用",
1132
+ "复杂样式使用 @apply 抽取",
1133
+ "使用 class 策略实现暗色模式",
1134
+ "避免内联 style 和自定义 CSS",
1135
+ "不确定 class 名称时先查 Tailwind 官方文档",
1136
+ ],
1137
+ },
1138
+ tools: [
1139
+ {
1140
+ name: 'tailwind_new_component',
1141
+ description: '新建 Tailwind 组件:语义结构 → 样式 → 响应式 → 暗色模式',
1142
+ inputSchema: {
1143
+ type: 'object',
1144
+ properties: {
1145
+ componentName: { type: 'string', description: '组件名称' },
1146
+ variant: { type: 'string', enum: ['flex', 'grid', 'card'], description: '布局变体' },
1147
+ },
1148
+ required: ['componentName'],
1149
+ },
1150
+ outputSchema: structuredOutputSchema,
1151
+ annotations: readOnlyAnnotations,
1152
+ handler: async (input) => {
1153
+ const { generateComponent } = await import('./analyzers/new-component.js');
1154
+ return generateComponent(input);
1155
+ },
1156
+ },
1157
+ {
1158
+ name: 'tailwind_optimize_classes',
1159
+ description: '优化 Tailwind 类名:检测冗余、冲突、可提取的模式',
1160
+ inputSchema: {
1161
+ type: 'object',
1162
+ properties: {
1163
+ filePath: { type: 'string', description: '组件文件路径' },
1164
+ projectRoot: { type: 'string', description: '项目根目录' },
1165
+ },
1166
+ required: ['filePath'],
1167
+ },
1168
+ outputSchema: structuredOutputSchema,
1169
+ annotations: readOnlyAnnotations,
1170
+ handler: async (input) => {
1171
+ const { optimizeClasses } = await import('./analyzers/optimize-classes.js');
1172
+ return optimizeClasses(input);
1173
+ },
1174
+ },
1175
+ ],
1176
+ };
1177
+ //# sourceMappingURL=index.js.map