@bovinphang/frontend-craft 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +61 -0
- package/.claude-plugin/plugin.json +32 -0
- package/.mcp.json +44 -0
- package/CHANGELOG.md +229 -0
- package/CHANGELOG.zh-CN.md +228 -0
- package/CODE_OF_CONDUCT.md +34 -0
- package/CODE_OF_CONDUCT.zh-CN.md +34 -0
- package/CONTRIBUTING.md +356 -0
- package/CONTRIBUTING.zh-CN.md +356 -0
- package/LICENSE +21 -0
- package/README.md +467 -0
- package/README.openclaw.md +262 -0
- package/README.openclaw.zh-CN.md +262 -0
- package/README.zh-CN.md +467 -0
- package/SECURITY.md +44 -0
- package/SECURITY.zh-CN.md +44 -0
- package/agents/fec-architect.md +125 -0
- package/agents/fec-code-reviewer.md +152 -0
- package/agents/fec-debugger.md +44 -0
- package/agents/fec-design-token-mapper.md +75 -0
- package/agents/fec-doc-updater.md +30 -0
- package/agents/fec-e2e-runner.md +107 -0
- package/agents/fec-figma-implementer.md +104 -0
- package/agents/fec-performance-optimizer.md +293 -0
- package/agents/fec-refactor-cleaner.md +32 -0
- package/agents/fec-security-reviewer.md +116 -0
- package/agents/fec-test-planner.md +50 -0
- package/agents/fec-typescript-reviewer.md +142 -0
- package/agents/fec-ui-checker.md +120 -0
- package/commands/fec-debug.md +30 -0
- package/commands/fec-doc-sync.md +18 -0
- package/commands/fec-init.md +70 -0
- package/commands/fec-plan.md +94 -0
- package/commands/fec-refactor-clean.md +19 -0
- package/commands/fec-review.md +66 -0
- package/commands/fec-scaffold.md +205 -0
- package/commands/fec-tdd.md +21 -0
- package/dist/bin/frontend-craft.js +82 -0
- package/dist/hooks/fec-cleanup-claude-cache.js +4 -0
- package/dist/hooks/fec-format-changed-file.js +1 -0
- package/dist/hooks/fec-notify.js +1 -0
- package/dist/hooks/fec-run-tests.js +1 -0
- package/dist/hooks/fec-security-check.js +2 -0
- package/dist/hooks/fec-session-start.js +1 -0
- package/docs/example-prompts.md +124 -0
- package/docs/ja-JP/README.md +467 -0
- package/docs/ko-KR/README.md +467 -0
- package/docs/project-structure.md +119 -0
- package/docs/runtimes/antigravity.md +10 -0
- package/docs/runtimes/augment.md +10 -0
- package/docs/runtimes/claude.md +171 -0
- package/docs/runtimes/claude.zh-CN.md +169 -0
- package/docs/runtimes/cline.md +10 -0
- package/docs/runtimes/codebuddy.md +10 -0
- package/docs/runtimes/codex.md +10 -0
- package/docs/runtimes/copilot.md +10 -0
- package/docs/runtimes/cursor.md +10 -0
- package/docs/runtimes/gemini.md +10 -0
- package/docs/runtimes/kilo.md +10 -0
- package/docs/runtimes/openclaw.md +10 -0
- package/docs/runtimes/opencode.md +10 -0
- package/docs/runtimes/qoder.md +19 -0
- package/docs/runtimes/trae.md +10 -0
- package/docs/runtimes/windsurf.md +10 -0
- package/docs/superpowers/plans/2026-05-29-fec-debug.md +491 -0
- package/docs/superpowers/specs/2026-05-29-fec-debug-design.md +340 -0
- package/docs/zh-CN/example-prompts.md +124 -0
- package/docs/zh-CN/project-structure.md +119 -0
- package/docs/zh-TW/README.md +467 -0
- package/hooks/hooks.json +71 -0
- package/openclaw.plugin.json +26 -0
- package/package.json +101 -0
- package/skills/TEMPLATE.md +155 -0
- package/skills/eval_queries.json +890 -0
- package/skills/fec-accessibility-check/SKILL.md +39 -0
- package/skills/fec-accessibility-check/references/report-template.md +23 -0
- package/skills/fec-accessibility-check/references/screen-reader-testing.md +33 -0
- package/skills/fec-api-integration/SKILL.md +78 -0
- package/skills/fec-api-integration/references/integration-patterns.md +80 -0
- package/skills/fec-browser-storage/SKILL.md +34 -0
- package/skills/fec-browser-storage/references/storage-patterns.md +65 -0
- package/skills/fec-canvas-threejs/SKILL.md +66 -0
- package/skills/fec-canvas-threejs/references/rendering-patterns.md +67 -0
- package/skills/fec-canvas-threejs/references/shader-webgl-patterns.md +67 -0
- package/skills/fec-code-review/SKILL.md +135 -0
- package/skills/fec-code-review/references/report-template.md +29 -0
- package/skills/fec-component-testing/SKILL.md +119 -0
- package/skills/fec-data-fetching/SKILL.md +62 -0
- package/skills/fec-data-fetching/references/query-patterns.md +77 -0
- package/skills/fec-debug-framework/SKILL.md +162 -0
- package/skills/fec-dependency-upgrade/SKILL.md +34 -0
- package/skills/fec-dependency-upgrade/references/dependency-upgrade-workflow.md +56 -0
- package/skills/fec-doc-sync/SKILL.md +37 -0
- package/skills/fec-e2e-testing/SKILL.md +40 -0
- package/skills/fec-e2e-testing/references/e2e-advanced.md +176 -0
- package/skills/fec-e2e-testing/references/playwright-patterns.md +95 -0
- package/skills/fec-form-handling/SKILL.md +76 -0
- package/skills/fec-form-handling/references/advanced-form-patterns.md +72 -0
- package/skills/fec-implement-from-design/SKILL.md +122 -0
- package/skills/fec-implement-from-design/references/design-plan-template.md +36 -0
- package/skills/fec-legacy-to-modern-migration/SKILL.md +180 -0
- package/skills/fec-legacy-to-modern-migration/references/migration-report-template.md +41 -0
- package/skills/fec-legacy-web-standard/SKILL.md +35 -0
- package/skills/fec-legacy-web-standard/references/legacy-patterns.md +85 -0
- package/skills/fec-list-virtualization/SKILL.md +34 -0
- package/skills/fec-list-virtualization/references/virtualization-patterns.md +52 -0
- package/skills/fec-monorepo-project-standard/SKILL.md +142 -0
- package/skills/fec-motion-interaction/SKILL.md +66 -0
- package/skills/fec-motion-interaction/references/motion-patterns.md +112 -0
- package/skills/fec-nextjs-project-standard/SKILL.md +117 -0
- package/skills/fec-nuxt-project-standard/SKILL.md +122 -0
- package/skills/fec-performance-optimization/SKILL.md +72 -0
- package/skills/fec-pwa-implementation/SKILL.md +36 -0
- package/skills/fec-pwa-implementation/references/manifest-and-icons.md +53 -0
- package/skills/fec-pwa-implementation/references/service-worker-workbox.md +68 -0
- package/skills/fec-react-project-standard/SKILL.md +83 -0
- package/skills/fec-react-project-standard/references/react-project-details.md +642 -0
- package/skills/fec-refactor-clean/SKILL.md +48 -0
- package/skills/fec-responsive-layout/SKILL.md +34 -0
- package/skills/fec-responsive-layout/references/responsive-layout-patterns.md +59 -0
- package/skills/fec-route-protection/SKILL.md +135 -0
- package/skills/fec-security-review/SKILL.md +38 -0
- package/skills/fec-security-review/references/report-template.md +27 -0
- package/skills/fec-security-review/references/security-checklist.md +64 -0
- package/skills/fec-source-driven-development/SKILL.md +61 -0
- package/skills/fec-state-management/SKILL.md +140 -0
- package/skills/fec-state-management/references/state-patterns.md +109 -0
- package/skills/fec-storybook-component-doc/SKILL.md +37 -0
- package/skills/fec-storybook-component-doc/references/story-patterns.md +118 -0
- package/skills/fec-svg-animation/SKILL.md +148 -0
- package/skills/fec-tailwind-design-system/SKILL.md +34 -0
- package/skills/fec-tailwind-design-system/references/tailwind-system-patterns.md +55 -0
- package/skills/fec-tdd-workflow/SKILL.md +52 -0
- package/skills/fec-testing-strategy/SKILL.md +45 -0
- package/skills/fec-typescript-type-safety/SKILL.md +157 -0
- package/skills/fec-typescript-type-safety/references/type-safety-patterns.md +102 -0
- package/skills/fec-ui-design/SKILL.md +90 -0
- package/skills/fec-ui-design/data/product-rules.json +110 -0
- package/skills/fec-ui-design/data/stack-ui-rules.json +14 -0
- package/skills/fec-ui-design/data/style-archetypes.json +74 -0
- package/skills/fec-ui-design/data/ux-quality-rules.json +56 -0
- package/skills/fec-ui-design/references/design-intelligence.md +37 -0
- package/skills/fec-ui-design/references/master-page-overrides.md +30 -0
- package/skills/fec-ui-design/references/pre-delivery-checklist.md +33 -0
- package/skills/fec-ui-design/scripts/design-system.mjs +530 -0
- package/skills/fec-validation-fix/SKILL.md +48 -0
- package/skills/fec-vite-project-standard/SKILL.md +83 -0
- package/skills/fec-vue3-project-standard/SKILL.md +49 -0
- package/skills/fec-vue3-project-standard/references/vue3-project-details.md +547 -0
- package/skills/fec-web-workers/SKILL.md +36 -0
- package/skills/fec-web-workers/references/advanced-workers.md +54 -0
- package/skills/fec-web-workers/references/basic-worker.md +67 -0
- package/skills/metadata.json +1422 -0
- package/skills/relations.json +644 -0
- package/templates/claude/CLAUDE.md +106 -0
- package/templates/claude/settings.json +62 -0
- package/templates/codex/AGENTS.md +105 -0
- package/templates/codex/config.toml +32 -0
- package/templates/openclaw/AGENTS.md +101 -0
- package/templates/openclaw/OPENCLAW-CONFIG.md +12 -0
- package/templates/shared/rules/fec-agent-workflow.md +54 -0
- package/templates/shared/rules/fec-api-layer.md +99 -0
- package/templates/shared/rules/fec-ci-cd.md +128 -0
- package/templates/shared/rules/fec-code-comments.md +33 -0
- package/templates/shared/rules/fec-design-system.md +248 -0
- package/templates/shared/rules/fec-error-handling.md +200 -0
- package/templates/shared/rules/fec-git-conventions.md +73 -0
- package/templates/shared/rules/fec-i18n.md +112 -0
- package/templates/shared/rules/fec-naming-conventions.md +120 -0
- package/templates/shared/rules/fec-performance.md +101 -0
- package/templates/shared/rules/fec-react.md +82 -0
- package/templates/shared/rules/fec-refactoring.md +43 -0
- package/templates/shared/rules/fec-rendering-patterns.md +183 -0
- package/templates/shared/rules/fec-responsive-design.md +227 -0
- package/templates/shared/rules/fec-source-driven-development.md +31 -0
- package/templates/shared/rules/fec-state-management.md +163 -0
- package/templates/shared/rules/fec-testing.md +98 -0
- package/templates/shared/rules/fec-typescript.md +355 -0
- package/templates/shared/rules/fec-vue.md +64 -0
- package/templates/shared/rules/fec-working-modes.md +50 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fec-storybook-component-doc
|
|
3
|
+
description: Use when setting up or reviewing Storybook component documentation, design-system presentation, isolated component state previews, stories, addons, decorators, MDX docs, component-state interaction checks, visual baselines, or Chromatic. For real-browser cross-page journeys or broader coverage planning, choose the matching testing workflow first; Chinese triggers include Storybook, 组件文档, Design System, 视觉回归.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Storybook 组件文档化
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
为 UI 组件建立文档化、设计系统展示和隔离状态预览环境;交互与视觉测试只覆盖 Storybook 场景内的组件状态。
|
|
11
|
+
|
|
12
|
+
## Procedure
|
|
13
|
+
|
|
14
|
+
1. 初始化或识别现有 Storybook 配置,沿用项目框架适配器、stories glob、主题和构建命令。
|
|
15
|
+
2. 每个组件的 Story 覆盖默认态、主要变体、尺寸、语义 tone、disabled、loading、error、empty、selected、invalid 和关键边缘状态。
|
|
16
|
+
3. 依赖 Router、Store、i18n、ThemeProvider 的组件用 decorators 包裹,不在 Story 中复制应用入口。
|
|
17
|
+
4. 复杂组件用 MDX 补充使用说明、Props、slots/children、variant 矩阵、状态示例、可访问性要求和源码展示。
|
|
18
|
+
5. 需要自动化时接入 interaction tests、addon-a11y、Chromatic 或 Storybook Test Runner;这些能力服务于组件文档和设计系统展示,不承接跨页业务旅程。
|
|
19
|
+
6. 设计系统组件应记录 token 来源、Tailwind/class 变体、暗色模式、响应式尺寸和不支持的组合,避免消费者靠猜测拼装。
|
|
20
|
+
|
|
21
|
+
## Detailed References
|
|
22
|
+
|
|
23
|
+
- Load [references/story-patterns.md](references/story-patterns.md) for `main.ts`, `preview.ts`, stories, decorators, MDX, interaction tests, and visual regression examples.
|
|
24
|
+
|
|
25
|
+
## Constraints
|
|
26
|
+
|
|
27
|
+
- Story 需随组件 prop 变更同步更新。
|
|
28
|
+
- Provider 依赖必须通过 decorator 补齐。
|
|
29
|
+
- Storybook 构建产物 `storybook-static/` 不应提交到 Git。
|
|
30
|
+
- Storybook 用于开发和组件文档,不替代生产页面 SEO。
|
|
31
|
+
- Storybook 视觉基线用于组件状态;跨页面截图和真实路由流程分流到 E2E workflow。
|
|
32
|
+
- 大型组件库需控制 stories glob 和 addon 开销。
|
|
33
|
+
- 不用 Storybook 隐藏组件 API 问题;若变体组合爆炸,应先收敛组件职责或拆分组件。
|
|
34
|
+
|
|
35
|
+
## Expected Output
|
|
36
|
+
|
|
37
|
+
可交互的组件文档和设计系统展示站点,每个组件状态都有隔离预览,Props 表格可生成,关键 Storybook 场景有可选 play 测试或视觉回归基线。
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Storybook 配置与示例
|
|
2
|
+
|
|
3
|
+
## 初始化
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx storybook@latest init
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## main.ts
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import type { StorybookConfig } from "@storybook/react-vite";
|
|
13
|
+
|
|
14
|
+
const config: StorybookConfig = {
|
|
15
|
+
stories: ["../src/**/*.stories.@(ts|tsx)"],
|
|
16
|
+
addons: [
|
|
17
|
+
"@storybook/addon-links",
|
|
18
|
+
"@storybook/addon-essentials",
|
|
19
|
+
"@storybook/addon-interactions",
|
|
20
|
+
"@storybook/addon-a11y",
|
|
21
|
+
],
|
|
22
|
+
framework: { name: "@storybook/react-vite", options: {} },
|
|
23
|
+
docs: { autodocs: "tag" },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default config;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Story
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
33
|
+
import { Button } from "./Button";
|
|
34
|
+
|
|
35
|
+
const meta: Meta<typeof Button> = {
|
|
36
|
+
title: "Components/Button",
|
|
37
|
+
component: Button,
|
|
38
|
+
tags: ["autodocs"],
|
|
39
|
+
argTypes: {
|
|
40
|
+
variant: { control: "select", options: ["primary", "secondary", "ghost"] },
|
|
41
|
+
onClick: { action: "clicked" },
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default meta;
|
|
46
|
+
type Story = StoryObj<typeof Button>;
|
|
47
|
+
|
|
48
|
+
export const Primary: Story = {
|
|
49
|
+
args: { variant: "primary", children: "Primary Button" },
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Decorators
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import type { Preview } from "@storybook/react";
|
|
57
|
+
import { MemoryRouter } from "react-router-dom";
|
|
58
|
+
import { ThemeProvider } from "../src/app/providers";
|
|
59
|
+
|
|
60
|
+
const preview: Preview = {
|
|
61
|
+
decorators: [
|
|
62
|
+
(Story) => <ThemeProvider>{Story()}</ThemeProvider>,
|
|
63
|
+
(Story) => <MemoryRouter initialEntries={["/"]}>{Story()}</MemoryRouter>,
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default preview;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## MDX
|
|
71
|
+
|
|
72
|
+
```mdx
|
|
73
|
+
import { Meta, Canvas, Controls, Source } from "@storybook/blocks";
|
|
74
|
+
import * as InputStories from "./Input.stories";
|
|
75
|
+
|
|
76
|
+
<Meta of={InputStories} />
|
|
77
|
+
|
|
78
|
+
# Input 输入框
|
|
79
|
+
|
|
80
|
+
用于用户文本输入,支持校验提示、前后缀图标、字数统计等。
|
|
81
|
+
|
|
82
|
+
### 何时使用
|
|
83
|
+
|
|
84
|
+
- 单行文本输入
|
|
85
|
+
- 需要实时校验反馈的表单字段
|
|
86
|
+
|
|
87
|
+
<Canvas of={InputStories.Default} />
|
|
88
|
+
|
|
89
|
+
### 属性
|
|
90
|
+
|
|
91
|
+
<Controls />
|
|
92
|
+
|
|
93
|
+
### 源码
|
|
94
|
+
|
|
95
|
+
<Source of={InputStories.Default} />
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Interaction Test
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { expect, userEvent, within } from "@storybook/test";
|
|
102
|
+
|
|
103
|
+
export const FormSubmission: Story = {
|
|
104
|
+
play: async ({ canvasElement }) => {
|
|
105
|
+
const canvas = within(canvasElement);
|
|
106
|
+
await userEvent.type(canvas.getByLabelText("用户名"), "admin");
|
|
107
|
+
await userEvent.click(canvas.getByRole("button", { name: /登录/ }));
|
|
108
|
+
await expect(canvas.findByText("登录成功")).resolves.toBeInTheDocument();
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Visual Regression
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx chromatic --project-token=<token>
|
|
117
|
+
npx test-storybook
|
|
118
|
+
```
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fec-svg-animation
|
|
3
|
+
description: Use when implementing or reviewing SVG animation, path drawing, icon motion, logo animation, illustration motion, micro-interactions, CSS/SMIL/Framer Motion/GSAP choices, motion accessibility, or fallback behavior; Chinese triggers include SVG 动画, 路径描边, 动效.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SVG 动画实现
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
为 SVG 图标、插画和数据可视化提供可维护、可访问、性能可控的动画方案。
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- 需要实现 SVG 图标、Logo、插画或路径描边动画。
|
|
15
|
+
- 需要在 React/Vue 组件中接入 Framer Motion、GSAP 或 CSS 动画。
|
|
16
|
+
- 需要根据 `prefers-reduced-motion` 提供动效降级。
|
|
17
|
+
- 不用于复杂 3D/WebGL 场景;此类场景优先使用 Canvas / Three.js workflow。
|
|
18
|
+
|
|
19
|
+
## Procedure
|
|
20
|
+
|
|
21
|
+
### 1. 选择动画方式
|
|
22
|
+
|
|
23
|
+
按复杂度和运行时需求选型:
|
|
24
|
+
|
|
25
|
+
| 场景 | 推荐 |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| hover/focus、opacity、transform | CSS animation/transition |
|
|
28
|
+
| 路径描边、简单重复动效 | CSS + `stroke-dasharray` |
|
|
29
|
+
| React 组件状态驱动动画 | Framer Motion |
|
|
30
|
+
| 时间轴、复杂编排、滚动触发 | GSAP |
|
|
31
|
+
| 需要保持零依赖的静态 SVG | CSS 或 SMIL |
|
|
32
|
+
|
|
33
|
+
如果动效跨越页面转场、滚动叙事或复杂组件编排,应先按交互动效 workflow 判断强度、懒加载和 reduced-motion 策略;本 skill 只负责 SVG 图形本身的路径、形变和图标 motion。
|
|
34
|
+
|
|
35
|
+
### 2. 使用 CSS 实现路径描边
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
export function CheckmarkIcon() {
|
|
39
|
+
return (
|
|
40
|
+
<svg viewBox="0 0 24 24" className="checkmark" aria-hidden="true">
|
|
41
|
+
<path
|
|
42
|
+
className="checkmark-path"
|
|
43
|
+
d="M5 12.5l4.5 4.5L19 7"
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke="currentColor"
|
|
46
|
+
strokeWidth="2.5"
|
|
47
|
+
strokeLinecap="round"
|
|
48
|
+
strokeLinejoin="round"
|
|
49
|
+
/>
|
|
50
|
+
</svg>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```css
|
|
56
|
+
.checkmark-path {
|
|
57
|
+
stroke-dasharray: 24;
|
|
58
|
+
stroke-dashoffset: 24;
|
|
59
|
+
animation: draw-checkmark 420ms ease-out forwards;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@keyframes draw-checkmark {
|
|
63
|
+
to {
|
|
64
|
+
stroke-dashoffset: 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (prefers-reduced-motion: reduce) {
|
|
69
|
+
.checkmark-path {
|
|
70
|
+
animation: none;
|
|
71
|
+
stroke-dashoffset: 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. 在 React 中使用 Framer Motion
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { motion, useReducedMotion } from "framer-motion";
|
|
80
|
+
|
|
81
|
+
export function AnimatedSparkline({ path }: { path: string }) {
|
|
82
|
+
const reduceMotion = useReducedMotion();
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<svg viewBox="0 0 120 40" role="img" aria-label="趋势图">
|
|
86
|
+
<motion.path
|
|
87
|
+
d={path}
|
|
88
|
+
fill="none"
|
|
89
|
+
stroke="currentColor"
|
|
90
|
+
strokeWidth="2"
|
|
91
|
+
initial={reduceMotion ? false : { pathLength: 0, opacity: 0 }}
|
|
92
|
+
animate={{ pathLength: 1, opacity: 1 }}
|
|
93
|
+
transition={{ duration: 0.7, ease: "easeOut" }}
|
|
94
|
+
/>
|
|
95
|
+
</svg>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 4. 使用 GSAP 编排复杂时间轴
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import { useLayoutEffect, useRef } from "react";
|
|
104
|
+
import gsap from "gsap";
|
|
105
|
+
|
|
106
|
+
export function LogoReveal() {
|
|
107
|
+
const rootRef = useRef<SVGSVGElement>(null);
|
|
108
|
+
|
|
109
|
+
useLayoutEffect(() => {
|
|
110
|
+
if (!rootRef.current) return;
|
|
111
|
+
const ctx = gsap.context(() => {
|
|
112
|
+
gsap.timeline({ defaults: { ease: "power2.out" } })
|
|
113
|
+
.from(".logo-mark", { scale: 0.9, opacity: 0, duration: 0.25 })
|
|
114
|
+
.from(".logo-line", { strokeDashoffset: 80, duration: 0.55 }, "-=0.1");
|
|
115
|
+
}, rootRef);
|
|
116
|
+
|
|
117
|
+
return () => ctx.revert();
|
|
118
|
+
}, []);
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<svg ref={rootRef} viewBox="0 0 120 32" aria-hidden="true">
|
|
122
|
+
<circle className="logo-mark" cx="16" cy="16" r="10" />
|
|
123
|
+
<path className="logo-line" d="M36 16h68" stroke="currentColor" />
|
|
124
|
+
</svg>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 5. 控制运行成本
|
|
130
|
+
|
|
131
|
+
- 大型 SVG 拆成静态底图和少量可动画节点,不要让每个 path 都参与时间轴。
|
|
132
|
+
- 图标库或 Lottie 替代方案必须按需加载,不进入所有页面的基础包。
|
|
133
|
+
- 循环动画只在可见区域运行,页面隐藏或组件卸载时停止。
|
|
134
|
+
- 移动端减少滤镜、mask、clipPath 和大量渐变叠加。
|
|
135
|
+
|
|
136
|
+
## Constraints
|
|
137
|
+
|
|
138
|
+
- 优先动画 `transform`、`opacity` 和 SVG 路径属性;避免高频修改 layout 相关属性。
|
|
139
|
+
- 交互控件必须保留可见 focus 状态,动画不能替代状态表达。
|
|
140
|
+
- 所有非装饰性 SVG 必须有可访问名称;装饰性 SVG 使用 `aria-hidden="true"`。
|
|
141
|
+
- 默认尊重 `prefers-reduced-motion`,复杂动效必须提供静态或弱动效降级。
|
|
142
|
+
- 不要把大型 SVG 内联到频繁重渲染的组件中;提取为 memoized 组件或外部资源。
|
|
143
|
+
- 不用动画改变可点击区域、阅读顺序或焦点位置。
|
|
144
|
+
- 不让 SVG 滤镜、模糊、mask 或 clipPath 成为移动端掉帧主因;复杂效果需要设备降级。
|
|
145
|
+
|
|
146
|
+
## Expected Output
|
|
147
|
+
|
|
148
|
+
产出一个可复用的 SVG 动画组件或样式方案,包含选型理由、动效降级和可访问性处理。验证时检查动画流畅、无布局抖动、键盘与 reduced-motion 场景可用。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fec-tailwind-design-system
|
|
3
|
+
description: Use when designing, implementing, or reviewing Tailwind CSS design systems, token mapping, theme extension, utility class governance, component variants, dark mode, responsive utilities, safelists, or maintainable class composition; Chinese triggers include Tailwind, 设计 Token, 组件变体, 暗色模式, class 管理.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tailwind 设计系统
|
|
7
|
+
|
|
8
|
+
适用于用 Tailwind CSS 承载产品设计系统、组件变体和主题规则的前端任务。需要具体配置、variant、class 组织和迁移细节时加载 [references/tailwind-system-patterns.md](references/tailwind-system-patterns.md)。
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
让 Tailwind 项目中的 token、主题、组件变体和响应式样式可维护,而不是把一次性 utility class 散落到业务代码里。
|
|
13
|
+
|
|
14
|
+
## Procedure
|
|
15
|
+
|
|
16
|
+
1. 识别现状:确认 Tailwind 版本、配置位置、暗色模式策略、组件库、CSS 变量、设计 token 和 class 合并工具。
|
|
17
|
+
2. 建立 token 边界:颜色、间距、圆角、阴影、字体、z-index 和断点优先从项目 token 或 `theme.extend` 派生。
|
|
18
|
+
3. 设计组件变体:按钮、输入框、卡片、弹窗、表格等基础组件用集中 variant API 表达尺寸、语义、状态和密度。
|
|
19
|
+
4. 控制 class 复杂度:重复组合沉淀为组件、slot、variant 或局部 helper;不要把长 class 字符串复制到多个页面。
|
|
20
|
+
5. 处理主题和暗色模式:明确 `class`、`data-theme` 或 CSS 变量方案,避免首次渲染闪烁和对比度回归。
|
|
21
|
+
6. 接入响应式:移动优先组织 utility;复杂布局交给响应式布局工作流,不要只靠堆叠断点前缀碰运气。
|
|
22
|
+
7. 验证样式结果:检查 hover、active、focus-visible、disabled、loading、selected、invalid、dark 和不同断点。
|
|
23
|
+
|
|
24
|
+
## Constraints
|
|
25
|
+
|
|
26
|
+
- 不把 Tailwind class 当作设计系统本身;真正的系统是 token、组件 API、状态矩阵和使用约束。
|
|
27
|
+
- 不为单个页面随意扩展全局 token;新增 token 必须有语义名称和复用场景。
|
|
28
|
+
- 不使用拼接的动态 class 破坏构建扫描;必要时使用 safelist、映射表或 variant 工具。
|
|
29
|
+
- 不在组件外部覆盖基础组件内部结构来实现变体。
|
|
30
|
+
- 不让暗色模式只靠反色;状态色、边框、阴影、图表和图片都要重新检查。
|
|
31
|
+
|
|
32
|
+
## Expected Output
|
|
33
|
+
|
|
34
|
+
输出 Tailwind token 映射、主题配置边界、组件 variant 设计、class 复用策略、暗色模式和响应式验证结果。完成后样式应能被项目组件复用、可搜索、可测试,并与设计系统规则一致。
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Tailwind 系统模式
|
|
2
|
+
|
|
3
|
+
## Token 映射
|
|
4
|
+
|
|
5
|
+
优先使用语义化名称:
|
|
6
|
+
|
|
7
|
+
| 需求 | 推荐 | 避免 |
|
|
8
|
+
| ---- | ---- | ---- |
|
|
9
|
+
| 品牌色 | `brand.600`, `--color-brand-primary` | 到处复制 `blue-600` |
|
|
10
|
+
| 表面层级 | `surface.canvas`, `surface.panel` | 一次性灰色值 |
|
|
11
|
+
| 圆角 | `radius.control`, `radius.panel` | 随意重复的具体值 |
|
|
12
|
+
| 阴影 | `shadow.floating`, `shadow.focus` | 没有状态含义的装饰阴影 |
|
|
13
|
+
|
|
14
|
+
如果项目已有 CSS 变量,把 Tailwind token 映射到这些变量,而不是重复维护字面量。
|
|
15
|
+
|
|
16
|
+
## Variant 归属
|
|
17
|
+
|
|
18
|
+
长 class 组合应收敛到以下位置之一:
|
|
19
|
+
|
|
20
|
+
- 带 `variant`、`size`、`tone`、`density`、`state` props 的共享组件。
|
|
21
|
+
- 复合组件的 slot 映射表。
|
|
22
|
+
- CVA、tailwind-variants 或本地 `classNames` 映射等类型化 helper。
|
|
23
|
+
- 只在一个模块内复用时,使用局部常量。
|
|
24
|
+
|
|
25
|
+
不要根据不受控的用户输入拼接 class 名。使用显式映射:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const toneClass = {
|
|
29
|
+
neutral: "bg-slate-100 text-slate-900",
|
|
30
|
+
danger: "bg-red-600 text-white",
|
|
31
|
+
success: "bg-emerald-600 text-white",
|
|
32
|
+
} as const;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 暗色模式
|
|
36
|
+
|
|
37
|
+
- 优先选择单一的项目级策略:`class`、`data-theme` 或 CSS 变量。
|
|
38
|
+
- 如果应用支持手动切换主题,在 body 绘制前初始化已选主题。
|
|
39
|
+
- 在亮色和暗色主题下都测试 disabled、focus、selected、invalid、chart 和 skeleton 状态。
|
|
40
|
+
- 不要把降低透明度作为 disabled 的唯一提示;要保留对比度和可感知性。
|
|
41
|
+
|
|
42
|
+
## 响应式工具类
|
|
43
|
+
|
|
44
|
+
- 默认 class 列表从移动端布局开始。
|
|
45
|
+
- 只有信息架构变化时,才增加更大断点前缀。
|
|
46
|
+
- 重复出现的响应式区块应沉淀为布局组件或 CSS 容器查询,不要反复复制同一串断点。
|
|
47
|
+
- 控件、标签页、看板和数据单元格使用稳定尺寸,避免 hover 和 loading 状态造成布局跳动。
|
|
48
|
+
|
|
49
|
+
## 评审清单
|
|
50
|
+
|
|
51
|
+
- Token 值有语义角色,并且没有分叉既有设计规则。
|
|
52
|
+
- 组件 variants 覆盖 loading、disabled、selected、invalid、focus-visible 和暗色模式。
|
|
53
|
+
- 动态 class 可被静态扫描识别,或已安全加入 safelist。
|
|
54
|
+
- Tailwind 工具类没有破坏可访问性、点击目标或 reduced-motion 要求。
|
|
55
|
+
- 最终 UI 已在移动端、平板和桌面宽度检查过。
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fec-tdd-workflow
|
|
3
|
+
description: Use when implementing new frontend behavior, fixing bugs, or refactoring logic where tests can describe the expected behavior first. Apply to components, hooks/composables, utilities, API clients, route guards, or user workflows; Chinese triggers include TDD, 测试驱动, 先写测试, 红绿重构, 回归测试.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 前端 TDD 工作流
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
用“先写失败测试,再实现最小代码,再重构”的节奏交付前端功能,避免只在实现后补覆盖率。
|
|
11
|
+
|
|
12
|
+
## Procedure
|
|
13
|
+
|
|
14
|
+
1. 识别可观察行为:用户能看到的 UI、组件契约、hook/composable 返回值、路由守卫结果、API client 输出或错误状态。
|
|
15
|
+
2. 先写一个最小失败测试:
|
|
16
|
+
- 纯逻辑优先单元测试。
|
|
17
|
+
- 组件交互优先 Testing Library / Vue Test Utils。
|
|
18
|
+
- 跨页关键流程优先 Playwright / Cypress。
|
|
19
|
+
3. 运行该测试并确认失败原因正确,失败应来自行为尚未实现,而不是语法、导入或测试环境错误。
|
|
20
|
+
4. 写刚好能通过测试的最小实现,不顺手扩大范围。
|
|
21
|
+
5. 重新运行测试,确认变绿。
|
|
22
|
+
6. 在测试保持通过的前提下重构命名、边界和重复逻辑。
|
|
23
|
+
7. 对 bug 修复,保留能复现问题的回归测试。
|
|
24
|
+
8. 每轮只扩大一个可观察行为;新需求、新边界和新异常路径各自进入下一轮。
|
|
25
|
+
|
|
26
|
+
## Prove-It Pattern
|
|
27
|
+
|
|
28
|
+
修复缺陷时先让测试失败,再让它通过。若无法先失败,说明测试没有覆盖原始问题,需要收窄输入、断言或测试层级。
|
|
29
|
+
|
|
30
|
+
## Frontend Test Selection
|
|
31
|
+
|
|
32
|
+
| Risk | Preferred Test |
|
|
33
|
+
| ---- | -------------- |
|
|
34
|
+
| utils、schema、状态计算 | 单元测试 |
|
|
35
|
+
| hooks / composables | 单元或轻量集成测试 |
|
|
36
|
+
| 组件 props、emits、交互、状态 | 组件测试 |
|
|
37
|
+
| Router、Provider、Store 协作 | 轻量集成测试 |
|
|
38
|
+
| 登录、支付、权限、关键 CRUD | E2E 测试 |
|
|
39
|
+
|
|
40
|
+
## Constraints
|
|
41
|
+
|
|
42
|
+
- 不为了 TDD 引入项目没有使用且收益不清晰的新测试框架。
|
|
43
|
+
- 不测试实现细节、私有状态或脆弱 DOM 结构。
|
|
44
|
+
- 不把 E2E 当作所有风险的默认答案;优先选择离风险最近的测试层。
|
|
45
|
+
- 如果现有仓库没有测试基础设施,先输出最小测试落地建议,再请求用户确认是否引入。
|
|
46
|
+
- 不在红灯阶段同时重构;先证明问题,再最小修复,再整理结构。
|
|
47
|
+
|
|
48
|
+
## Expected Output
|
|
49
|
+
|
|
50
|
+
- 至少一个先失败后通过的测试覆盖新增或修复行为。
|
|
51
|
+
- 实现保持最小范围,重构只在测试通过后进行。
|
|
52
|
+
- 总结运行过的测试命令、覆盖的行为和未覆盖风险。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fec-testing-strategy
|
|
3
|
+
description: Use when planning or reviewing a frontend testing strategy, selecting the right test layer by risk, mapping coverage across static checks, unit tests, component tests, integration tests, E2E, Storybook/visual regression, a11y, security, performance, or CI gates. Do not use to write individual component/E2E tests or merely run existing validation commands; Chinese triggers include 测试策略, 测试分层, 测试计划, 覆盖矩阵.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 前端测试策略
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
按“离代码多近 / 覆盖风险层级”选择测试方法,避免把所有风险都塞进组件测试或 E2E。
|
|
11
|
+
|
|
12
|
+
## Procedure
|
|
13
|
+
|
|
14
|
+
1. 识别改动类型:纯逻辑、UI 组件、跨模块流程、浏览器能力、视觉稳定性、专项质量风险或发布门禁。
|
|
15
|
+
2. 建立测试层级矩阵,至少区分:
|
|
16
|
+
- 静态检查:TypeScript、ESLint、格式、依赖安全、构建。
|
|
17
|
+
- 单元测试:utils、hooks/composables、状态逻辑、schema、纯函数。
|
|
18
|
+
- 组件测试:props/emits、用户交互、loading/error/empty、mock 边界。
|
|
19
|
+
- 轻量集成测试:表单 + API mock + 路由/Store/Provider 上下文。
|
|
20
|
+
- E2E:真实浏览器、跨页面关键旅程、鉴权、支付、权限、CI artifacts。
|
|
21
|
+
- 视觉/交互文档:Storybook interaction、Chromatic、视觉回归基线。
|
|
22
|
+
- 专项质量:a11y、安全、性能、兼容性。
|
|
23
|
+
3. 按风险分配覆盖:高频核心路径优先 E2E,复杂组件状态优先组件测试,纯逻辑优先单元测试,跨 provider 协作用轻量集成测试。
|
|
24
|
+
4. 用测试金字塔控制维护成本:
|
|
25
|
+
- 越靠近纯逻辑,测试越多、越快、越稳定。
|
|
26
|
+
- E2E 只覆盖用户关键旅程和真实浏览器风险,不覆盖每个分支。
|
|
27
|
+
- 视觉、a11y、安全和性能属于专项质量层,不塞进普通组件测试。
|
|
28
|
+
5. 检查是否已有命令、测试框架、目录约定和 CI 门禁;沿用项目现状,不为策略文档引入不必要工具。
|
|
29
|
+
6. 规划失败证据:每个高风险项要说明失败时如何定位,例如断言、截图、trace、coverage、日志或报告。
|
|
30
|
+
7. 设计测试数据与 mock 策略:共享 fixture 表达业务场景,测试数据 builder 只封装噪声字段,避免每个测试手写随机对象。
|
|
31
|
+
8. 管理 flaky 风险:把时间、网络、动画、随机数、并发和外部服务标为不稳定来源,并指定隔离、重试和证据产物。
|
|
32
|
+
9. 输出可执行的最小测试计划:每层覆盖什么、不覆盖什么、优先级、建议命令和责任 skill。
|
|
33
|
+
|
|
34
|
+
## Constraints
|
|
35
|
+
|
|
36
|
+
- 不把测试策略写成泛泛的“多写测试”;每个建议必须对应具体风险。
|
|
37
|
+
- 不要求所有项目都补齐完整测试金字塔;按业务风险和团队维护能力裁剪。
|
|
38
|
+
- 不把 E2E 当作单元/组件测试的替代品;不把组件测试当作真实浏览器兼容保证。
|
|
39
|
+
- 不为小改动强制引入新框架;优先复用仓库已有工具和脚本。
|
|
40
|
+
- 不追求没有风险说明的覆盖率数字;覆盖率只能辅助判断,不能替代场景覆盖。
|
|
41
|
+
- 不把随机 fixture、共享全局状态或依赖执行顺序的测试纳入主干门禁。
|
|
42
|
+
|
|
43
|
+
## Expected Output
|
|
44
|
+
|
|
45
|
+
输出测试分层建议、风险到测试层的映射、优先级、建议命令、需新增或调整的测试文件范围,以及明确分流到哪些专项 skill 或 agent。
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fec-typescript-type-safety
|
|
3
|
+
description: Use when designing, implementing, or reviewing TypeScript type contracts, advanced generics, discriminated unions, type guards, API DTOs, component props, public utility types, or type-level regressions in frontend projects. Prefer code review for broad PR review; Chinese triggers include TypeScript 类型安全, 类型建模, 泛型, 判别联合, 类型收窄.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript 类型安全
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
为前端代码建立可演进的类型契约,减少 `any`、断言和运行时形状漂移。
|
|
11
|
+
|
|
12
|
+
## Procedure
|
|
13
|
+
|
|
14
|
+
### 1. 先确定类型边界
|
|
15
|
+
|
|
16
|
+
把类型分为外部输入、领域模型、UI view model、组件 props、工具函数 API。不要让后端 DTO、表单模型和 UI 展示模型互相冒充。
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
interface UserDto {
|
|
20
|
+
id: string;
|
|
21
|
+
display_name: string;
|
|
22
|
+
status: "ACTIVE" | "DISABLED";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface UserViewModel {
|
|
26
|
+
id: string;
|
|
27
|
+
displayName: string;
|
|
28
|
+
status: "active" | "disabled";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function mapUserDto(dto: UserDto): UserViewModel {
|
|
32
|
+
return {
|
|
33
|
+
id: dto.id,
|
|
34
|
+
displayName: dto.display_name,
|
|
35
|
+
status: dto.status === "ACTIVE" ? "active" : "disabled",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. 用 `unknown` 和收窄处理不可信数据
|
|
41
|
+
|
|
42
|
+
外部输入先校验再使用。不要用 `as` 让编译器闭嘴。
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
interface ApiErrorBody {
|
|
46
|
+
message: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isApiErrorBody(value: unknown): value is ApiErrorBody {
|
|
50
|
+
return (
|
|
51
|
+
typeof value === "object" &&
|
|
52
|
+
value !== null &&
|
|
53
|
+
"message" in value &&
|
|
54
|
+
typeof value.message === "string"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getApiErrorMessage(value: unknown): string {
|
|
59
|
+
return isApiErrorBody(value) ? value.message : "Unexpected error";
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. 用判别联合表达状态机
|
|
64
|
+
|
|
65
|
+
异步状态、权限分支、支付状态等有限状态,用判别字段让新增分支在编译期暴露。
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
type Loadable<T> =
|
|
69
|
+
| { state: "idle" }
|
|
70
|
+
| { state: "loading" }
|
|
71
|
+
| { state: "success"; data: T }
|
|
72
|
+
| { state: "error"; error: Error };
|
|
73
|
+
|
|
74
|
+
function assertNever(value: never): never {
|
|
75
|
+
throw new Error(`Unhandled state: ${JSON.stringify(value)}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function renderUserState(user: Loadable<{ name: string }>): string {
|
|
79
|
+
switch (user.state) {
|
|
80
|
+
case "idle":
|
|
81
|
+
return "Ready";
|
|
82
|
+
case "loading":
|
|
83
|
+
return "Loading";
|
|
84
|
+
case "success":
|
|
85
|
+
return user.data.name;
|
|
86
|
+
case "error":
|
|
87
|
+
return user.error.message;
|
|
88
|
+
default:
|
|
89
|
+
return assertNever(user);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 4. 让泛型服务调用方,而不是炫技
|
|
95
|
+
|
|
96
|
+
泛型应减少重复并保持调用方推断清晰。复杂类型超过阅读成本时,优先拆具名类型。
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
interface SelectOption<TValue extends string | number> {
|
|
100
|
+
label: string;
|
|
101
|
+
value: TValue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface SelectProps<TValue extends string | number> {
|
|
105
|
+
value: TValue;
|
|
106
|
+
options: Array<SelectOption<TValue>>;
|
|
107
|
+
onChange: (value: TValue) => void;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function Select<TValue extends string | number>({
|
|
111
|
+
value,
|
|
112
|
+
options,
|
|
113
|
+
onChange,
|
|
114
|
+
}: SelectProps<TValue>) {
|
|
115
|
+
return (
|
|
116
|
+
<select value={value} onChange={(event) => onChange(event.target.value as TValue)}>
|
|
117
|
+
{options.map((option) => (
|
|
118
|
+
<option key={option.value} value={option.value}>
|
|
119
|
+
{option.label}
|
|
120
|
+
</option>
|
|
121
|
+
))}
|
|
122
|
+
</select>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 5. 用类型测试保护公共类型
|
|
128
|
+
|
|
129
|
+
公共工具类型、组件库类型和 SDK 类型,应有轻量类型测试或编译期断言。
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
type Equal<TLeft, TRight> =
|
|
133
|
+
(<T>() => T extends TLeft ? 1 : 2) extends <T>() => T extends TRight ? 1 : 2
|
|
134
|
+
? true
|
|
135
|
+
: false;
|
|
136
|
+
|
|
137
|
+
type Expect<T extends true> = T;
|
|
138
|
+
|
|
139
|
+
type UserStatus = UserViewModel["status"];
|
|
140
|
+
type _UserStatusTest = Expect<Equal<UserStatus, "active" | "disabled">>;
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Detailed References
|
|
144
|
+
|
|
145
|
+
涉及高级泛型约束、映射类型、模板字面量类型、类型测试、DTO 映射或类型审查清单时,加载 [references/type-safety-patterns.md](references/type-safety-patterns.md)。
|
|
146
|
+
|
|
147
|
+
## Constraints
|
|
148
|
+
|
|
149
|
+
- 避免 `any`、无守卫的非空断言和不相关类型之间的强制 `as`。
|
|
150
|
+
- 不把大型匿名类型堆在函数参数或 JSX props 上;提取具名类型表达业务含义。
|
|
151
|
+
- 不为简单业务过度设计递归条件类型;类型复杂度应服务可维护性。
|
|
152
|
+
- 公共 API 的输入输出必须显式标注,局部变量可依赖推断。
|
|
153
|
+
- 类型建模不能替代运行时校验;外部数据仍需 schema 或 type guard。
|
|
154
|
+
|
|
155
|
+
## Expected Output
|
|
156
|
+
|
|
157
|
+
产出清晰的类型边界、可收窄的数据模型、必要的类型测试和验证命令。评审时应列出类型风险、运行时影响、推荐建模方式和是否需要专项修复。
|