FinClaw V1 UI Prototype and Design System
状态:Accepted Initial Design / B-4 工程蓝图(Wave 2) 日期:2026-05-16 项目:FinClaw 文档级别:项目级前端工程契约(design tokens + 组件清单 + 关键交互原型) 上游文档:v1-ui-ux-interaction-design.md(UX 流程母本)、v1-tech-stack-and-architecture-design.md §4(前端栈锁定)、v1-api-contract-design.md §9(前端适配契约)、v1-product-object-and-schema-design.md §3 / §4 / §5 / §8 / §9(对象 schema) 配套引入:../../reference-experience/sharedocs-finclaw-ui-mvp-design.md(视觉基线)
1. Purpose
本文是 FinClaw V1 前端 sub-packet(特别是 v1-eng-impl-sub-4-refresh-diff-view / sub-5-save-thread-sheet / sub-6-feedback-review-queue)直接 consume 的工程契约。
它在 v1-ui-ux-interaction-design.md 已锁定的 UX 流程 / 屏幕逻辑 之上,down-pour 三件可工程化的事:
- Design Tokens(颜色、字体、间距、圆角、阴影、动效)的工程化定义,与 Tailwind config + shadcn/ui CSS variables 1:1 对齐;
- 组件清单(基于 shadcn/ui 二次封装)+ 每个组件的 prop 边界与禁用项;
- 关键交互原型(ASCII / mermaid,无需 Figma),覆盖 6 条核心路径。
本文不重复 UX 流程描述(→ v1-ui-ux-interaction-design.md)、不重新选型前端栈(已锁定,→ v1-tech-stack-and-architecture-design.md §4)、不定义 API client(→ v1-api-contract-design.md §9)。
2. Design Source and Visual Baseline
2.1 视觉基线来源
| 来源 | 角色 | 锁定状态 |
|---|---|---|
| reference-experience/sharedocs-finclaw-ui-mvp-design.md | 团队成员 ShareDocs FinClaw MVP UI 设计已逐字导入治理库;本文继承其全部色彩 / 字体 / 圆角 / 间距规范作为 V1 视觉基线 | 已锁定 |
reference-experience/sharedocs-finclaw-ui-demo.jpg | New Task 页面实际渲染参照 | 视觉参照 |
| v1-ui-ux-interaction-design.md | UX 流程 / 屏幕逻辑 / 可读性优先级 | 已锁定 |
2.2 与 ShareDocs 视觉基线的边界
| 项 | 沿用 | 扩展 / 修正 |
|---|---|---|
| 色板(深色主题、紫色 accent、bullish/bearish 双向色) | 全量沿用 | 增补 boundary / sensitive_reject / degradation 三组语义色 |
| 字体(Inter + tabular numerics) | 全量沿用 | 增补字号阶梯 token |
| 圆角 / 间距 / 卡片密度 | 全量沿用 | 增补 motion / shadow tokens |
| 信息密度(彭博终端式紧凑感) | 全量沿用 | Refresh Diff / Checkpoint UI 在 desktop 三列模式下尤其遵守 |
| Widget × Dashboard 形态 | 沿用概念 | V1 dashboard 的「认知卡片」由 SnapshotCard / ThreadCard / CheckpointCard 三套构成(§4) |
| ShareDocs 的「行动建议」区块 | 修正:V1 把行动建议拆成独立 PreExecutionCheckpoint 对象,并在 UI 上明确标注「非执行边界」(§5.4) | 见 reference §10 标注 ⚠️ 项 |
| 「了解付费 / 升级」按钮 | 引入 trial pricing intent dummy 入口 | 必须标注「意愿调查、不扣款」(与 v1-commercial-signal-instrumentation-design.md §4.6 对齐) |
2.3 不引入的视觉资产
| 项 | 不引入理由 |
|---|---|
| K-line / 实时撮合行情图 widget | v1-tech-stack-and-architecture-design.md §4 禁止依赖;与边界冲突 |
| Wallet connect / Web3 button 视觉资产 | mvp-product-definition.md §3 边界 |
| 高保真 Figma 源 | V1 trial 阶段以本文 ASCII 原型 + shadcn/ui 默认视觉为准;高保真 Figma 留待 trial 退出 / 公开扩展前再补(v1-ui-ux-interaction-design.md §11 Open Item) |
3. Design Tokens
3.1 颜色(与 Tailwind theme.extend.colors 1:1)
// web/tailwind.config.ts (excerpt)
colors: {
bg: {
base: "#0F1119", // sharedocs §4.1 deep navy-black
surface: "#1A1D2E", // 卡片背景
surface_alt: "#13162A", // 内嵌区块(drawer / sheet inner)
overlay: "rgba(15,17,25,0.72)", // dialog backdrop
},
border: {
subtle: "#2A2D3E", // 卡片边框 / 分隔线
strong: "#3A3F55", // 聚焦 / 选中态
},
fg: {
primary: "#E8E6DF", // 主文本
secondary: "#98968E", // 次文本 / metadata
muted: "#6F6E68", // 占位符 / disabled
inverse: "#0F1119", // 在亮色 chip 上
},
accent: {
DEFAULT: "#7F77DD", // 主紫色 accent
hover: "#9089E5",
pressed: "#6E66C8",
bg_soft: "#251F4D", // 软背景
},
bull: { DEFAULT: "#1D9E75", soft: "#13352A" }, // 看多
bear: { DEFAULT: "#E24B4A", soft: "#3A1A1B" }, // 看空
warn: { DEFAULT: "#EF9F27", soft: "#352618" }, // 数据质量 / risk badge
// V1 新增的语义色:
boundary: { DEFAULT: "#4A6FE3", soft: "#1A2244" }, // 边界提示(非执行)
reject: { DEFAULT: "#C0392B", soft: "#3A1715" }, // 凭证 / 私钥拒收
degrade: { DEFAULT: "#A1887F", soft: "#2A211E" }, // degradation_notice
evidence: {
backed: "#1D9E75", // source-backed
inferred: "#7F77DD", // model-inferred
user: "#E8E6DF", // user-supplied
delayed: "#EF9F27",
unavailable: "#C0392B",
conflicting: "#E24B4A",
simulated: "#4A6FE3",
},
}
全部 evidence / quality state 颜色与 v1-ui-ux-interaction-design.md §6B Evidence Drawer badge 表 1:1 对应;前端实现
<EvidencePill status={...}>时 token 来源只能从colors.evidence.*取,禁止 inline 颜色。
3.2 字体
fontFamily: {
sans: ["Inter", "ui-sans-serif", "system-ui"],
mono: ["JetBrains Mono", "ui-monospace", "monospace"],
numeric: ['"Inter var"', "Inter", "system-ui"], // tabular-nums
},
fontSize: {
// (size, lineHeight, letterSpacing)
xs: ["12px", { lineHeight: "16px", letterSpacing: "0.01em" }], // metadata / badge
sm: ["13px", { lineHeight: "18px" }], // 次文本
base: ["14px", { lineHeight: "20px" }], // 正文
md: ["15px", { lineHeight: "22px" }], // 强调正文
lg: ["16px", { lineHeight: "24px" }], // section header
xl: ["18px", { lineHeight: "26px", letterSpacing: "-0.005em" }], // page header
"2xl":["20px", { lineHeight: "28px", letterSpacing: "-0.01em" }], // hero / dialog title
},
fontWeight: { regular: "400", medium: "500", semibold: "600", bold: "700" },
数字字段(confidence score、价格、token usage、change count)必须挂 font-numeric tabular-nums。
3.3 间距 / 圆角 / 阴影
spacing: { ... 默认 Tailwind ..., card_pad: "16px", card_gap: "12px", section_y: "24px" },
borderRadius: { sm: "4px", md: "8px", lg: "12px", pill: "9999px" },
boxShadow: {
card: "0 1px 2px rgba(0,0,0,0.4), 0 1px 1px rgba(0,0,0,0.16)",
raised: "0 4px 12px rgba(0,0,0,0.45)",
sheet: "0 -8px 28px rgba(0,0,0,0.55)",
focus: "0 0 0 2px #7F77DD",
},
| Token | 用途 |
|---|---|
card_pad (16px) | 所有 Card / Sheet 内 padding |
card_gap (12px) | 同一 Card 内子区块间距 |
section_y (24px) | 主区块之间垂直间距 |
radius.lg (12px) | Card / Drawer / Dialog |
radius.md (8px) | Button / Input / Select |
radius.pill | Badge / EvidencePill / chip |
3.4 动效(Motion Tokens)
transitionDuration: { fast: "120ms", base: "180ms", slow: "260ms" },
transitionTimingFunction: { standard: "cubic-bezier(.2,.8,.2,1)" },
约束:
- Refresh Diff 中的 changed inference 高亮入场用
base+standard; - Sheet / Drawer 入场用
slow+standard; - 任何 ≥ 400ms 的入场动效禁止(高密度信息场景中视觉延迟会被解读为系统延迟);
prefers-reduced-motion: reduce时所有 transition 退化到 0ms,仅保留静态 hover / focus 样式。
3.5 Token 来源约定(守门)
- 所有颜色 / 字号 / 间距 / 圆角 / 阴影必须通过 Tailwind utility class 引用 token,禁止 inline
style={{ color: "#xxx" }}; - 任何 hex / rgb 字面量只允许出现在
web/tailwind.config.ts与web/src/foundation/tokens.css; - shadcn/ui CSS variables(
--background/--foreground/--primary)由tokens.css重新映射到本文 token,禁止 shadcn/ui 默认值「直通」组件。
4. Component Inventory
V1 前端组件分 3 层:Foundation(shadcn/ui 直接复用 / 二次封装)、Domain(FinClaw 业务组件)、Page Shell(页面级布局)。
4.1 Foundation 组件(基于 shadcn/ui 二次封装)
| 组件 | shadcn 来源 | V1 必备 props / 边界 |
|---|---|---|
Button | button | variant: primary / secondary / ghost / destructive / boundary;size: sm / md / lg;禁止 variant="trade" / variant="execute" |
Input | input | 受 SensitiveInputClassifier 包裹(§6.2) |
Textarea | textarea | 同 Input;额外 maxLength=4000(与 v1-api-contract-design.md §4.2.2 question 上限对齐) |
Select | select | 用于 hint_task_type / research_depth |
Dialog | dialog | 模态确认;ProfileConsentDialog 必须用此 |
Sheet | sheet | 右侧 / 底部 sheet;CheckpointSheet / SaveThreadSheet 用此 |
Drawer | drawer | 与 Sheet 区别:底部抽屉 + 高度自适应;EvidenceDrawer 用此 |
Card | card | 主信息容器 |
Badge | badge | 状态标识(active / refresh_due / paused 等) |
Tabs | tabs | Snapshot 内分区 / Thread vs Snapshot 视图切换 |
Tooltip | tooltip | 字段解释;hover 延迟 500ms |
Toast | sonner | 短反馈 + sensitive rejection 短文案 |
Separator | separator | 分区分隔 |
Skeleton | skeleton | Loading 占位 |
ScrollArea | scroll-area | 长 thread history / evidence 列表 |
Switch | switch | ProfileConsent 各 scope 开关 |
Checkbox | checkbox | Pricing intent feature_kind 多选 |
RadioGroup | radio-group | Trial cohort 单选 |
禁止引入的 shadcn 组件:Calendar(V1 不做日期选择)、Carousel(信息密度场景不适合)、Progress(进度条易被误读为系统延迟)。
4.2 Domain 组件(FinClaw 业务组件)
| 组件 | 来源对象 | 必备 props | 必备行为 |
|---|---|---|---|
EvidencePill | EvidenceItem.evidence_status / DataQualityNote.quality_state | status: enum, claimRef: string, onOpen?: () => void | 点击展开 EvidenceDrawer;颜色映射强制走 §3.1 evidence token |
BoundaryBanner | Snapshot.execution_boundary / Checkpoint.non_execution_statement | kind: "execution" | "sensitive" | "degradation", message: string | 不可关闭;颜色 = colors.boundary |
RefreshDiffPanel | RefreshChange | previous: SnapshotRef, current: SnapshotRef, change: RefreshChange, mode: "desktop3col" | "mobileTimeline" | 见 §5.3 |
AdvisorAvatar | AdvisorOutput.advisor_role | role: enum, provider: "gpt-5.5" | "kimi-k2.6" | "byom:<name>", coordinationMode?: enum | provider 标签必须 visible(与 v1-model-and-provider-policy §6.3 model_name 对齐) |
SnapshotCard | MarketCognitionSnapshot | snapshot: Snapshot, density: "list" | "detail" | 必须渲染 ui_state badge + execution_boundary |
ThreadHeader | MarketCognitionThread | thread: Thread | 状态 bar + last_refreshed_at + refresh CTA |
CheckpointSheet | PreExecutionCheckpoint | checkpoint: Checkpoint | 强制渲染 §6.1 8 步顺序;强制顶部 BoundaryBanner |
ProfileConsentDialog | ProfileConsent | currentConsent?: ProfileConsent, onSubmit: (req) => Promise<void>, onRevoke: (id) => Promise<void> | 4 类 scope switch:save_to_profile / save_to_thread / training_use_allowed / consent_for_trial_data |
SensitiveInputRejectionToast | SensitiveInputHandling.classification = credential_or_permission | inputSegmentIndex: number | 来自 HTTP 451 触发;短文案 ≤ 80 字符;不展示原文 |
ClarificationPrompt | ui_state = needs_clarification | question: string, onSubmitAnswer / onContinueLowConfidence | 单一关键问题;不滚屏 |
WatchQuestionList | Thread.watch_questions | questions: WatchQuestion[], onResolve | 已解决 / 新增 / 拆分 / 失效四态 |
InvalidatorList | Thread.invalidators | invalidators: Invalidator[] | 不可一键启用为 alert(边界) |
DegradationNoticeBanner | SSE degradation_notice | kind: enum, affectedField: string, message: string | 持续可见直到下一次 refresh |
PricingIntentCard | CommercialSignalEvent pricing_intent_* | features: FeatureKind[], onSubmit | 强制 disclaimer「意愿调查、不扣款」 |
FeedbackComposer | Feedback request schema | relatedRef: string, onSubmit | 4 类 feedback_kind |
KillSwitchBanner | API 503 kill_switch_active | 全局顶部条;不可关闭 | 仅 admin token 看到 reason |
4.3 Page Shell
| 组件 | 用途 |
|---|---|
AppShell | 左侧 menu (2/10) + 主内容 (8/10)(与 sharedocs §6 双栏布局一致) |
LeftMenu | Dashboard / New Task / History / Threads |
TopBar | 当前用户 / 通知 / 帮助;admin 模式增 Kill Switch 控件 |
MobileShell | 单列 + 底部 navigation;Drawer 从底部弹出 |
4.4 显式禁止存在的组件
| 名称 | 禁止理由 |
|---|---|
BuySellButton / LongShortToggle | v1-tech-stack-and-architecture-design.md §10.1 边界 |
LeverageSlider | 同上 |
WalletConnect / WalletAddressInput | 同上 |
OrderForm / PositionSizeInput | 同上 |
AutoExecuteToggle / AlertToExecutionBridge | 同上 |
KlineInteractiveTrading | 同上 |
BrokerConnectButton | 同上 |
§8 frontend lint 守门把这些字符串写入禁止清单,CI 拦截。
5. Key Interaction Prototypes
全部原型用 ASCII / mermaid。无需高保真截图(v1-ui-ux-interaction-design.md §11 Source File Strategy 已声明 V1 不强制 Figma)。
5.1 Home → Snapshot 撰写
┌─[ AppShell · Home ]──────────────────────────────────────────────┐
│ LeftMenu │ TopBar (user · notifications · help) │
│ • Dashboard ├────────────────────────────────────────────────┤
│ • New Task ◀ │ │
│ • Threads │ [ BoundaryBanner kind="execution" ] │
│ • History │ "FinClaw 输出认知与判断;不下单 / 不连账户" │
│ │ │
│ │ ┌── Recent Threads ──────────────────┐ │
│ │ │ ThreadCard · BTC 周内主判断 (refresh_due)│ │
│ │ │ ThreadCard · ETH L2 narrative (active)│ │
│ │ └─────────────────────────────────────┘ │
│ │ │
│ │ ┌── 主输入区 ────────────────────────┐ │
│ │ │ <Textarea maxLength=4000> │ │
│ │ │ 占位:"帮我分析 ETH 最近为什么弱..." │ │
│ │ │ </Textarea> │ │
│ │ │ [Select hint_task_type] │ │
│ │ │ [Pill chips: depth · risk · lang] │ │
│ │ │ [ Run FinClaw (accent) ] │ │
│ │ └─────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
提交后立即进入 SSE 流(v1-api-contract-design.md §5.3):
sequenceDiagram
participant User
participant Home as Home / NewTask
participant Client as FinclawClient
participant API as POST /api/tasks
participant SSE as GET /api/tasks/{id}/stream
participant Snap as SnapshotView
User->>Home: 输入 question + 提交
Home->>Client: createTask({question, hint_task_type})
Client->>API: POST + Idempotency-Key
API-->>Client: 202 { task_id, sse_url }
Client->>SSE: subscribe
SSE-->>Home: task_started → cognition_step (task_routed)
SSE-->>Home: cognition_step (advisor_planned)
SSE-->>Home: advisor_invoked (Asset Research, gpt-5.5)
SSE-->>Home: evidence_updated × N
SSE-->>Home: snapshot_completed { snapshot_id }
Home->>Snap: navigate(/snapshots/{snapshot_id})
SSE-->>Home: task_completed
5.2 Snapshot View → Save Thread
┌─[ SnapshotView ]─────────────────────────────────────────────────┐
│ ⟵ Back Snapshot · BTC 周内主判断 [ui_state: complete] │
├──────────────────────────────────────────────────────────────────┤
│ Header: Object | Time range | DataQuality summary │
│ │
│ Main Thesis ★ EvidencePill · source-backed │
│ "BTC 周内倾向震荡偏弱,关注 60K 关口..." │
│ │
│ Supporting Reasons │
│ 1. ETF 净流入连续两日转负 [evi_xxx · live] │
│ 2. ... │
│ │
│ Counter-Thesis & Risks │
│ • 美元指数走弱可能反转 [model-inferred] │
│ │
│ Uncertainties / Watch Questions │
│ • SEC 下周决议是否影响 ETF 资金? │
│ │
│ Advisor Outputs (collapsed) │
│ AdvisorAvatar · Asset Research · gpt-5.5 │
│ AdvisorAvatar · Counter-Thesis · kimi-k2.6 │
│ │
│ ──────────────────────────────────────────────────────────────── │
│ [ Save as Thread ] [ Open Evidence Drawer ] [ Feedback ] │
└──────────────────────────────────────────────────────────────────┘
点击 Save as Thread 触发 SaveThreadSheet(右侧 Sheet 弹出):
┌─[ SaveThreadSheet ]──────────────┐
│ Title [BTC 周内主判断 ____] │
│ Object BTC │
│ User focus reason [Textarea] │
│ Initial main thesis (auto-filled) │
│ Counter / risks (auto-filled) │
│ Watch questions (editable list) │
│ Refresh conditions (editable) │
│ Invalidators (editable) │
│ │
│ Save user context? │
│ ☐ save_to_thread │
│ ☐ save_to_profile │
│ ☐ training_use_allowed │
│ (require ProfileConsent) │
│ │
│ [ Cancel ] [ Save Snapshot Only ]│
│ [ Save as Thread ] │
└───────────────────────────────────┘
若任一 save_to_* / training_use_allowed 切换为 true 且当前用户无对应 ProfileConsent,必须先弹 ProfileConsentDialog(详 §6.1);否则 API 返回 403 unauthorized(v1-api-contract-design.md §4.2.6)。
5.3 Refresh Diff Panel(desktop 三列 + mobile timeline)
Desktop:
┌─[ ThreadView · Refresh Diff ]────────────────────────────────────┐
│ ThreadHeader · BTC 周内主判断 · refreshed @ 2026-05-18 10:23 │
│ trigger: condition (ETF flow turned negative) │
│ Summary: +2 facts · 1 inference changed · 1 risk ↑ · 0 evidence │
├──────────────┬───────────────────────┬───────────────────────────┤
│ Previous │ Current │ Change Annotation │
├──────────────┼───────────────────────┼───────────────────────────┤
│ Main thesis: │ Main thesis: │ ↻ inference_changed │
│ "震荡偏弱" │ "短期承压,关注 58K" │ reason: ETF outflow ↑ │
├──────────────┼───────────────────────┼───────────────────────────┤
│ Risks: ETF │ Risks: ETF outflow │ ↑ risk_intensified │
│ flow neutral │ confirmed │ │
├──────────────┼───────────────────────┼───────────────────────────┤
│ Watch Qs: │ Watch Qs: │ + new question added │
│ • SEC 决议 │ • SEC 决议 │ • 矿工卖压是否回升? │
│ │ • 矿工卖压是否回升? │ │
└──────────────┴───────────────────────┴───────────────────────────┘
Mobile(单列时间线,每变化一卡片):
┌─[ Refresh Diff · timeline ]──┐
│ ↻ inference_changed │
│ "短期承压,关注 58K" │
│ reason: ETF outflow ↑ │
├──────────────────────────────┤
│ ↑ risk_intensified │
│ ETF outflow confirmed │
├──────────────────────────────┤
│ + watch_question added │
│ "矿工卖压是否回升?" │
└──────────────────────────────┘
无实质变化时 RefreshDiffPanel 必须渲染:
┌──────────────────────────────────────────┐
│ ✓ 本次刷新未发现实质性变化 │
│ 原因:来源未更新 / 无新事件 / 原判断维持 │
│ [ Open Evidence Drawer ] [ Re-run later ]│
└──────────────────────────────────────────┘
(与 v1-ui-ux-interaction-design.md §6A 「无实质变化」要求一致;前端不应在零变化时生成新文案。)
5.4 Pre-Execution Checkpoint
行动邻近问题命中后 SSE checkpoint_completed 触发,导航到 CheckpointSheet:
┌─[ CheckpointSheet ]──────────────────────────────────────────────┐
│ [ BoundaryBanner kind="execution" ] │
│ "你提出的是行动邻近问题。FinClaw 不下单 / 不连账户 / 不执行。" │
├──────────────────────────────────────────────────────────────────┤
│ 1. 降级后的认知任务 │
│ "评估 BTC 短线突破 60K 后的持续性,识别条件与失效信号" │
│ │
│ 2. 条件化策略假设 │
│ "若 ETF 资金日净流入 > $200M 持续 ≥ 2d,则主线倾向延续" │
│ │
│ 3. 必须确认的前提(user_confirmation_needed) │
│ ☐ ETF 数据是否覆盖到最新交易日 │
│ ☐ 主导市场情绪是否已反映在期货持仓 │
│ │
│ 4. 风险约束(risk_constraints) │
│ • 宏观突发事件可一夜失效 │
│ │
│ 5. 失效条件(invalidators) │
│ • ETF 净流入翻负且持续 1d │
│ │
│ 6. 数据质量与缺口(data_quality_notes) │
│ • 链上数据 delayed 6h │
│ │
│ 7. 非可执行边界(non_execution_statement) │
│ "本 Checkpoint 不构成下单 / 仓位 / 杠杆指令;不可被外部交易系统直接消费。"│
│ │
│ forbidden_execution_fields (debug only): [] │
└──────────────────────────────────────────────────────────────────┘
禁止渲染:buy / sell button、position size input、leverage control、wallet field、auto execute toggle(v1-ui-ux-interaction-design.md §7 禁用清单)。
5.5 Boundary Rejection Flow(sensitive_input_rejected)
用户在主输入框输入疑似私钥 / API key:
sequenceDiagram
participant User
participant Home
participant Client as FinclawClient
participant API as POST /api/tasks
participant Toast as SensitiveInputRejectionToast
User->>Home: 输入 "我的 Binance API key 是 sk_live_..."
Home->>Client: createTask({question})
Client->>API: POST
API-->>Client: 451 sensitive_input_rejected
Client->>Toast: show({inputSegmentIndex})
Note over Toast: "FinClaw 不接受凭证、私钥、交易所 API Key 或助记词。"
Toast->>Home: 自动清空疑似命中的 input segment
Toast 文案:
SensitiveInputRejectionToast (≤ 80 chars):
"FinClaw 不接受凭证、私钥、交易所 API Key 或助记词。已拒收并清除该段。"
[ 了解为什么 ] ← 弹小 popover 解释,不展示原文
5.6 ProfileConsent 引导
首次触发任何「保存」类操作或商业信号采集前:
┌─[ ProfileConsentDialog ]─────────────────────────────────────────┐
│ FinClaw 想为你保存什么? │
│ │
│ ☐ save_to_thread 把你的关注理由 / 持仓上下文存到当前线程 │
│ ☐ save_to_profile 跨线程长期记住你的偏好(语言 / 深度 / 风险)│
│ ☐ training_use_allowed 允许去识别后用于改进 FinClaw(V1 不进入实际训练)│
│ ☐ consent_for_trial_data 允许采集行为信号供 trial 评测使用 │
│ │
│ • 你随时可以撤回;撤回后 48 小时内删除商业信号事件。 │
│ • 凭证 / 私钥 / 助记词永远不被保存(与本同意书无关,硬约束)。 │
│ • 详细见隐私边界。 │
│ │
│ [ 跳过 ] [ 确认(生效本次 + 后续) ] │
└──────────────────────────────────────────────────────────────────┘
Dialog 退出条件:用户提交(生成 csn_<user_id>_v<version>,v1-data-and-persistence-design.md §5.1)或显式跳过。跳过状态下:
save_*都为 false → 不创建 saved-context;consent_for_trial_data为 false → 后端POST /api/events/cs收到accepted: false(v1-api-contract-design.md §4.2.16);- 用户仍可使用全部认知功能。
6. Boundary and Rejection UX Patterns
6.1 Pattern:Execution Boundary(持续可见)
| 出现位置 | 组件 | 文案锚点 |
|---|---|---|
| Home 顶部 | BoundaryBanner kind="execution" | "FinClaw 输出认知与判断;不下单 / 不连账户。" |
| SnapshotView 底部 | <p class="text-fg-muted">snapshot.execution_boundary</p> | 来自 schema |
| CheckpointSheet 顶部 | BoundaryBanner kind="execution" + non_execution_statement | 强约束 |
任何 pricing_intent_* 卡片 | inline <small> | "意愿调查,不会扣款" |
颜色统一 colors.boundary(深蓝),与 accent(紫色 CTA)刻意区分,避免 boundary 提示被误读为 CTA。
6.2 Pattern:Sensitive Input
Input 组件包装层(client 端预检):
┌──────────────────────────────────┐
│ <Textarea> ◀ wrapped by │
│ <SensitiveInputClassifierClient/> │
└──────────────────────────────────┘
↓ 用户键入
↓ debounce 300ms
↓ regex 预检:API key / private key / seed phrase 模式
↓ 命中 → 不阻塞输入,但触发 inline warning:
"检测到疑似凭证;FinClaw 不会保存或使用这一段。"
↓ 用户提交 → backend Sensitive Input Classifier 终判
↓ 后端命中 → HTTP 451 → SensitiveInputRejectionToast
| Sensitive class | UX 强度 |
|---|---|
ordinary_preference | 无提示 |
financial_context | inline hint:「将仅用于本次任务,保存前会确认」 |
sensitive_personal_financial | inline warning:「不会保存到 Profile,除非你显式同意」 |
credential_or_permission | inline warning + 提交时 toast 拒收 |
前端不做最终判定(仅 UX hint);最终拒收由后端 SensitiveInputClassifier 与 BoundaryGuard 决定(v1-tech-stack-and-architecture-design.md §6.1)。
6.3 Pattern:Degradation Notice
SSE degradation_notice 触发 DegradationNoticeBanner:
┌─ DegradationNoticeBanner ────────────────────────────────────┐
│ ⚠ 部分输出已降级 │
│ kind: missing_advisor_view · affected: counter_thesis │
│ "本次未能获得 Counter-Thesis Advisor 视角;下次刷新会重试。" │
└───────────────────────────────────────────────────────────────┘
颜色 colors.degrade(中性褐色),不使用 warn 色(避免与数据质量 warning 混淆)。
6.4 Pattern:Kill Switch
API 任意端点返回 503 kill_switch_active(v1-api-contract-design.md §7.2):
┌─ KillSwitchBanner (顶部全宽,sticky) ──────────────────────┐
│ ⛔ FinClaw trial 已暂停 │
│ 现有 thread 与 snapshot 仍可阅读;新 task / refresh 已停用。│
│ 如有疑问请联系项目发起人。 │
└─────────────────────────────────────────────────────────────┘
非 admin 用户不看到 reason(reason 仅 admin 可见,与 v1-api-contract-design.md §4.2.17 对齐)。
7. Accessibility Baseline
V1 不通过完整 WCAG 2.2 AA 认证(trial 内测无外部审计),但实施以下基线:
| 项 | 要求 | 实施 |
|---|---|---|
| 颜色对比度 | 主文本 / 主背景 ≥ 4.5:1;次文本 ≥ 3:1 | fg.primary #E8E6DF on bg.base #0F1119 ≈ 12.4:1 ✅;fg.secondary on bg.surface ≈ 4.7:1 ✅ |
| 不依赖颜色单一信号 | bullish/bearish 必须同时用图标(↑/↓)+ 文本("+2.3%"),不仅靠颜色 | <TrendBadge> 组件强制三要素 |
| 键盘可达 | 所有 Button / Input / Sheet / Dialog 全键盘可操作;Tab 顺序可预测 | shadcn/ui radix primitives 默认满足;本文不重写 |
| Focus visible | 全部 focusable 元素显示 boxShadow.focus(紫色 2px 描边) | tokens.css 中 :focus-visible 全局规则 |
| 屏幕阅读器 | 所有 EvidencePill / BoundaryBanner / DegradationNoticeBanner 必须有 aria-label 或 aria-describedby | 组件内置;prop aria-label 必填 |
| Motion | 尊重 prefers-reduced-motion(§3.4) | 全局 CSS 守门 |
| 字号 | 用户浏览器缩放至 200% 后主流程不破版 | layout 用 rem 而非 px;tokens.css 中 html { font-size: 16px } 仅作 base |
| 中英文混排 | 中英标点 / 数字 tabular-nums 兼容 | font-numeric token |
V1 阶段不承诺:屏幕阅读器完整 narration、高对比度模式、自定义字号 UI、多语言切换(trial 仅中英)。
8. Frontend Lint Guardrails
8.1 与 Wave 1 一致性约束
本节与 v1-tech-stack-and-architecture-design.md §10.1 边界硬约束 + v1-api-contract-design.md §9.6 Frontend lint 守门 完全一致。本文不重新讨论清单,只负责前端代码层落地。
8.2 禁止字符串清单(CI lint 拦截)
| 禁止字符串 | 出现处 | 例外 |
|---|---|---|
BuySellButton / LongShortToggle / LeverageSlider / WalletConnect / OrderForm / PositionSizeInput / AutoExecuteToggle / BrokerConnectButton | 任何 web/src/**/*.{ts,tsx} | 无 |
order_side / order_type / quantity / leverage / wallet_address / private_key / seed_phrase / auto_execute / alert_trigger_to_execute | 任何 web/src/**/*.{ts,tsx} | web/src/api/types.ts 中作为 forbidden_execution_fields 字段值的字符串字面量(受 v1-api-contract-design §4.2.12 Pydantic schema 约束) |
inline style={{ color: "#... | 任何 web/src/** | web/tailwind.config.ts 与 web/src/foundation/tokens.css |
8.3 Lint 实现
// web/.eslintrc.cjs (excerpt)
module.exports = {
rules: {
"no-restricted-syntax": [
"error",
{
selector: "Identifier[name=/^(BuySellButton|LongShortToggle|LeverageSlider|WalletConnect|OrderForm|PositionSizeInput|AutoExecuteToggle|BrokerConnectButton)$/]",
message: "FinClaw V1 禁止此组件名(边界硬约束)。见 v1-ui-prototype-and-design-system §8。",
},
],
},
};
并在 web/scripts/check-forbidden-strings.mjs 中跑一遍 grep(兜底捕获 ESLint AST 漏掉的字符串字面量),通过 package.json "lint:strings" 接入 pnpm lint。
CI workflow(.github/workflows/ci.yml)lint job 任一拦截失败 = PR 阻断。
8.4 守门测试
web/src/__tests__/forbidden-strings.test.ts:
import { execSync } from "node:child_process";
test("no forbidden execution-control strings in source", () => {
const out = execSync("node scripts/check-forbidden-strings.mjs", { encoding: "utf8" });
expect(out).toMatch(/0 violations/);
});
9. Component Library Folder Layout
与 v1-tech-stack-and-architecture-design.md §7 Repository Skeleton web/src/ 骨架对齐,本文细化组件库布局:
web/src/
├── foundation/ ← Design tokens + 工具
│ ├── tokens.css ← :root CSS variables (映射到 §3 token)
│ ├── theme.ts ← 主题切换(V1 仅深色)
│ ├── motion.ts ← framer-motion 预设(与 §3.4 对齐)
│ └── icons.ts ← lucide-react re-exports
│
├── components/ ← Foundation 组件(shadcn/ui 二次封装)
│ ├── ui/ ← shadcn 生成的原始组件(极少改动)
│ │ ├── button.tsx
│ │ ├── input.tsx
│ │ ├── dialog.tsx
│ │ ├── sheet.tsx
│ │ ├── drawer.tsx
│ │ ├── card.tsx
│ │ ├── badge.tsx
│ │ ├── tabs.tsx
│ │ ├── tooltip.tsx
│ │ ├── select.tsx
│ │ ├── switch.tsx
│ │ ├── checkbox.tsx
│ │ ├── radio-group.tsx
│ │ ├── separator.tsx
│ │ ├── skeleton.tsx
│ │ └── scroll-area.tsx
│ ├── Toast.tsx ← sonner 包装
│ └── index.ts ← barrel export
│
├── domains/ ← 业务域切片(与 B-1 §7 对齐)
│ ├── analysis/ ← Snapshot / Checkpoint 阅读层
│ │ ├── components/
│ │ │ ├── SnapshotCard.tsx
│ │ │ ├── EvidencePill.tsx
│ │ │ ├── BoundaryBanner.tsx
│ │ │ ├── AdvisorAvatar.tsx
│ │ │ ├── DegradationNoticeBanner.tsx
│ │ │ └── ClarificationPrompt.tsx
│ │ └── pages/
│ │ └── SnapshotView.tsx
│ │
│ ├── cognition/ ← Thread / Refresh
│ │ ├── components/
│ │ │ ├── ThreadHeader.tsx
│ │ │ ├── ThreadCard.tsx
│ │ │ ├── RefreshDiffPanel.tsx
│ │ │ ├── WatchQuestionList.tsx
│ │ │ ├── InvalidatorList.tsx
│ │ │ └── SaveThreadSheet.tsx
│ │ └── pages/
│ │ ├── ThreadView.tsx
│ │ └── ThreadList.tsx
│ │
│ ├── checkpoint/
│ │ ├── components/CheckpointSheet.tsx
│ │ └── pages/CheckpointView.tsx
│ │
│ ├── onboarding/
│ │ └── components/ProfileConsentDialog.tsx
│ │
│ ├── sensitive/
│ │ ├── components/SensitiveInputClassifierClient.tsx
│ │ └── components/SensitiveInputRejectionToast.tsx
│ │
│ ├── feedback/
│ │ ├── components/FeedbackComposer.tsx
│ │ └── pages/FeedbackQueue.tsx (admin only)
│ │
│ ├── pricing/
│ │ └── components/PricingIntentCard.tsx
│ │
│ └── task/ ← New Task 入口与 SSE 编排
│ ├── components/
│ │ ├── TaskInputComposer.tsx
│ │ └── TaskStreamView.tsx
│ └── pages/HomePage.tsx
│
├── layout/ ← Page Shell
│ ├── AppShell.tsx
│ ├── LeftMenu.tsx
│ ├── TopBar.tsx
│ ├── KillSwitchBanner.tsx
│ └── MobileShell.tsx
│
├── api/ ← REST + SSE client(B-3 §9)
│ ├── client.ts
│ ├── sse.ts
│ ├── types.ts ← zod schemas, forbidden_execution_fields 字面量许可
│ └── errors.ts
│
├── lib/ ← utils / hooks
│ ├── hooks/
│ │ ├── useSnapshot.ts
│ │ ├── useThread.ts
│ │ ├── useTaskStream.ts
│ │ └── useProfileConsent.ts
│ └── utils/
│ ├── classify-sensitive.ts ← client 端 regex 预检
│ ├── token-est.ts ← 与 B-7 §10 cost telemetry 对齐
│ └── analytics.ts ← reportEvent 包装
│
├── pages/ ← 路由根节点(React Router 6)
│ ├── home/
│ ├── analysis/
│ ├── cognition/
│ └── dashboard/
│
└── __tests__/
├── forbidden-strings.test.ts
└── tokens.test.ts ← 验证 §3 tokens 与 tailwind config 一致
9.1 边界约定
components/ui/*不导入domains/*;domains/<X>/*可导入components/ui/*+foundation/*+api/*+lib/*,但不能跨 domain 导入彼此的components/;跨 domain 共享需上提到components/或lib/;layout/*可导入任何层;api/*不导入任何 React 组件(保持 framework-agnostic,便于 Vitest 单测)。
9.2 命名约定
| 文件 | 命名 |
|---|---|
| 组件 | PascalCase.tsx |
| Hook | useXxx.ts |
| util | kebab-case.ts |
| zod schema | <Object>Schema (e.g. SnapshotSchema) |
10. Acceptance
本文满足 V1 工程化 B-4 任务的接收条件:
| 项 | 状态 |
|---|---|
| 视觉基线明确指向 reference-experience/sharedocs-finclaw-ui-mvp-design.md §4 + §7(§2) | 是 |
| Design tokens(color / typography / spacing / radius / shadow / motion)工程化定义,与 Tailwind config 1:1(§3) | 是 |
| 组件清单覆盖 Foundation(≥ 17 个 shadcn 包装)+ Domain(≥ 16 个业务组件)+ Page Shell(5 个)+ 显式禁止清单(§4) | 是 |
| 关键交互原型覆盖 Home → Snapshot → Save Thread → Refresh Diff → Pre-Execution Checkpoint → Boundary Rejection → ProfileConsent 7 条路径,全部 ASCII / mermaid(§5 + §6) | 是 |
| Boundary / Sensitive / Degradation / Kill Switch UX pattern 与 v1-ui-ux-interaction-design.md §7 / §8 / §10 对齐(§6) | 是 |
| Accessibility 基线 7 项可工程化检查(§7) | 是 |
| Frontend lint 守门清单与 v1-tech-stack-and-architecture-design.md §10.1 + v1-api-contract-design.md §9.6 完全一致(§8) | 是 |
组件库目录与 v1-tech-stack-and-architecture-design.md §7 web/src/ 骨架对齐 + 边界约定(§9) | 是 |
| YAML frontmatter + section_navigation + 跨文档相对路径精确到 § | 是 |
| 全文不出现订单 / 仓位 / 私钥 / 钱包字段(仅作为禁止清单出现) | 是 |
11. Open Items
- O-1:高保真 Figma 源是否在 trial 退出前补齐 — V1 trial 期间以本文 ASCII 原型为准(与 v1-ui-ux-interaction-design.md §11 Open Item 一致);trial 退出 / 公开扩展前与项目发起人对齐;
- O-2:浏览器可运行原型(Storybook 或 Ladle)是否在 V1 落地 — 本 Wave 不强制;如 sub-4 / sub-5 / sub-6 owner 选择 Storybook 接入,需补一行
pnpm storybook到web/package.jsonscripts,但不写入 trial-prod docker image; - O-3:色板是否在 trial 期收集到对比度 / 可读性的负反馈 — 由 v1-trial-sub-2-trial-execution 收集;如 D7 内出现 ≥ 1 条对比度抱怨,由 PM 决议是否调整;
- O-4:MobileShell 在 ≤ 360px 屏宽下的折叠策略尚未压力测试 — 由 sub-4 / sub-5 实施时跑
pnpm vitest --run mobile-layout验证; - O-5:是否在 V1 引入
@storybook/test-runner/ Playwright 视觉回归 — 推迟到 trial 退出前评估。