feat(tasks-ui): one task window everywhere, cards are their own detail entry

Round-three review feedback:

- NO PER-CARD DETAIL BUTTON: the ellipsis control is gone from graph cards
  and tree rows — the card IS the affordance, and clicking it opens the
  detail window (the transcript jump moved inside as the primary button).
  Fold aggregates keep click-to-expand.
- ONE TASK WINDOW: every task surface (board row, agent node task line, the
  node detail list, and creating a task) now opens the same non-fullscreen
  DRAGGABLE window (TaskWindow.tsx + its TaskPopover shell): markdown
  description by default, 编辑 switches the same card to multi-line editing,
  plus owner reassignment, complete/reopen and a two-step delete. The board
  lost its overflow menu and its Modal entirely; create uses the same window
  in create mode.
- REUSE: TaskPopover (4 call sites), MultilineField (the plugin's own
  multi-line input — the host primitive set has none), OwnerPicker,
  TaskCreateButton, and the shared glyph/TaskLine helpers across both modes.
- 3 new copy keys across zh/en/ja and the 18 third-language dictionaries.

Tests: the node-click spec now asserts the detail window (and that the jump
button inside it navigates), a new spec pins 'no nested control inside a
card', and two more cover the shared window: markdown-first view then
multi-line edit posting an 'edit' with the current revision, and immediate
owner reassignment. Full suite 1406 passed / 133 files.
This commit is contained in:
Menghuan1918
2026-09-27 22:18:19 +08:00
parent e41f10afc9
commit 7cbfc766a1
31 changed files with 799 additions and 347 deletions
+16 -1
View File
@@ -143,14 +143,29 @@ export function primaryTask(tasks: readonly TasksNodeTask[]): TasksNodeTask | un
* status word, and a `+N` tail when the agent owns more. Renders nothing
* without tasks, so non-team nodes keep the three-line shape.
*/
export function TaskLine(props: { tasks: readonly TasksNodeTask[] | undefined }): ReactNode {
export function TaskLine(props: {
tasks: readonly TasksNodeTask[] | undefined
onOpenTask(taskId: string, anchor: HTMLElement): void
}): ReactNode {
const tasks = props.tasks ?? []
const primary = primaryTask(tasks)
if (primary === undefined) return null
return (
<span
role="button"
tabIndex={0}
className={css.taskLine}
title={tasks.map(task => `${task.subject}(${t(taskStatusKey(task.status))})`).join('\n')}
onClick={(event) => {
event.stopPropagation()
props.onOpenTask(primary.id, event.currentTarget)
}}
onKeyDown={(event) => {
if (event.key !== 'Enter' && event.key !== ' ') return
event.preventDefault()
event.stopPropagation()
props.onOpenTask(primary.id, event.currentTarget)
}}
>
<span className={css.taskGlyph} aria-hidden="true"><IconChecklistOutline14 size={9} /></span>
<span className={css.taskSubject}>{primary.subject}</span>