deerflow2/.planning/phases/06-/06-02-PLAN.md

164 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
phase: 06-
plan: 02
type: execute
wave: 2
depends_on:
- 06-01
files_modified:
- frontend/src/components/workspace/input-box.tsx
- frontend/src/components/ai-elements/prompt-input.tsx
- frontend/src/core/uploads/hooks.ts
- frontend/src/components/ui/dropdown-menu.tsx
autonomous: true
requirements:
- ATREF-01
- ATREF-02
must_haves:
truths:
- "用户在输入框输入 @ 后可立即看到当前线程文件候选,并可继续输入过滤。"
- "用户选择候选后在输入区看到可删除 chip而不是纯文本 @文件名。"
- "同名文件可通过类型徽标和路径尾段区分,且超过 10 个引用会被阻止。"
artifacts:
- path: "frontend/src/components/workspace/input-box.tsx"
provides: "@候选收集、过滤、dropdown 展示、chip 管理"
- path: "frontend/src/components/ai-elements/prompt-input.tsx"
provides: "textarea 键盘事件和 chip 删除协同"
key_links:
- from: "frontend/src/components/workspace/input-box.tsx"
to: "frontend/src/core/uploads/hooks.ts"
via: "useUploadedFiles(threadId)"
pattern: "useUploadedFiles"
- from: "frontend/src/components/workspace/input-box.tsx"
to: "frontend/src/components/ui/dropdown-menu.tsx"
via: "DropdownMenu 候选面板"
pattern: "DropdownMenuContent"
---
<objective>
实现输入态 `@` 引用交互覆盖候选展示、过滤、选择、chip、上限与键盘操作。
Purpose: 把 D-01/D-02/D-03/D-04/D-08/D-09 直接转成可见交互,且不突破线程边界。
Output: 输入框引用交互闭环dropdown + chip + 限制策略)。
</objective>
<execution_context>
@/home/mt/.codex/get-shit-done/workflows/execute-plan.md
@/home/mt/.codex/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/06-/06-CONTEXT.md
@.planning/phases/06-/06-RESEARCH.md
@frontend/src/components/workspace/input-box.tsx
@frontend/src/components/ai-elements/prompt-input.tsx
@frontend/src/core/uploads/hooks.ts
@frontend/src/components/workspace/chats/chat-box.tsx
@frontend/src/components/ui/dropdown-menu.tsx
</context>
<interfaces>
From `frontend/src/core/uploads/hooks.ts`:
```typescript
export function useUploadedFiles(threadId: string)
```
From `frontend/src/components/ui/dropdown-menu.tsx`:
```typescript
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem
}
```
From `frontend/src/components/workspace/chats/chat-box.tsx`:
```typescript
const { thread } = useThread();
// artifacts 来源thread.values.artifacts
```
</interfaces>
<tasks>
<task type="auto">
<name>Task 1: 构建 thread-scoped @ 候选聚合与 dropdown 触发过滤</name>
<files>frontend/src/components/workspace/input-box.tsx, frontend/src/core/uploads/hooks.ts</files>
<read_first>
- frontend/src/components/workspace/input-box.tsx
- frontend/src/components/workspace/chats/chat-box.tsx
- frontend/src/core/uploads/hooks.ts
- frontend/src/components/ui/dropdown-menu.tsx
- .planning/phases/06-/06-CONTEXT.md
</read_first>
<action>`InputBox` 增加 `referenceCandidates``mentionQuery` 状态;候选源固定为当前 `threadId``artifacts + uploads`(按 D-01检测 textarea 输入中最后一个 `@` token输入 `@` 立即打开 dropdown按 D-02继续输入做前缀过滤候选项渲染包含“文件名 + 类型徽标 + 路径尾段”(按 D-04面板必须使用 `DropdownMenu*` 组件(按 D-09禁止自定义绝对定位浮层。</action>
<acceptance_criteria>
- `rg -n "useUploadedFiles\\(" frontend/src/components/workspace/input-box.tsx` 命中候选上传源。
- `rg -n "thread\\.values\\.artifacts|artifacts" frontend/src/components/workspace/input-box.tsx` 命中 artifact 源。
- `rg -n "DropdownMenu|DropdownMenuContent|DropdownMenuItem" frontend/src/components/workspace/input-box.tsx` 命中 dropdown 实现。
- `rg -n "mentionQuery|@\"|lastIndexOf\\(\"@\"" frontend/src/components/workspace/input-box.tsx` 命中触发过滤逻辑。
</acceptance_criteria>
<verify>
<automated>cd frontend && pnpm -s typecheck</automated>
</verify>
<done>输入 `@` 可见 thread 内候选,过滤生效,且候选 UI 满足去歧义展示。</done>
</task>
<task type="auto">
<name>Task 2: 实现 chip 选择/删除、上限控制与键盘行为</name>
<files>frontend/src/components/workspace/input-box.tsx, frontend/src/components/ai-elements/prompt-input.tsx</files>
<read_first>
- frontend/src/components/workspace/input-box.tsx
- frontend/src/components/ai-elements/prompt-input.tsx
- .planning/phases/06-/06-CONTEXT.md
- .planning/phases/06-/06-RESEARCH.md
</read_first>
<action>选中候选后写入 `references` 状态并在输入区展示可删除 chip按 D-03不把引用作为纯文本提交`source+path` 去重;引用数量达到 10 时用 `toast.error` 提示并阻止新增(按 D-08实现键盘交互`ArrowUp/ArrowDown` 切换候选、`Enter` 选中、`Escape` 关闭、空输入时 `Backspace` 删除最后一个 chip与 IME 组合输入状态兼容(`isComposing` 时不触发选择提交)。</action>
<acceptance_criteria>
- `rg -n "references|chip|Tag" frontend/src/components/workspace/input-box.tsx` 命中 chip 渲染与状态。
- `rg -n "10|MAX_.*REFERENCE|超限|toast\\.error" frontend/src/components/workspace/input-box.tsx` 命中上限控制。
- `rg -n "ArrowDown|ArrowUp|Escape|Backspace|isComposing" frontend/src/components/workspace/input-box.tsx frontend/src/components/ai-elements/prompt-input.tsx` 命中键盘实现。
</acceptance_criteria>
<verify>
<automated>cd frontend && pnpm -s typecheck</automated>
</verify>
<done>chip 交互、上限、键盘行为与 IME 保护均实现并可编译。</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| textarea 输入→候选匹配 | 用户输入内容驱动候选过滤 |
| 候选列表→引用状态 | 可展示文件元数据进入可提交状态 |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-06-02-01 | I | `input-box.tsx` 候选聚合 | mitigate | 候选严格绑定当前 `threadId` 的 artifacts/uploads禁止全局池ASVS V4per D-01。 |
| T-06-02-02 | T | `@` 查询与选择 | mitigate | 选择仅可来自候选对象提交不信任自由文本路径ASVS V5。 |
| T-06-02-03 | D | 引用数量控制 | mitigate | 强制 10 个上限并阻止继续添加,降低前端/提交膨胀风险per D-08。 |
</threat_model>
<verification>
- `cd frontend && pnpm -s typecheck`
- `cd frontend && pnpm -s lint -- src/components/workspace/input-box.tsx src/components/ai-elements/prompt-input.tsx`
</verification>
<success_criteria>
- `@` 触发、过滤、选择、关闭行为完整可用。
- 引用展示为 chip支持删除、去重、键盘操作。
- 候选来源与组件实现满足 D-01/D-09 的硬约束。
</success_criteria>
<output>
After completion, create `.planning/phases/06-/06-02-SUMMARY.md`
</output>