fix(web): diff card review — TUI parity, path-header overlap, double-resolve
Bring the TUI diff footer onto the same terminator rule and distinct-path count the Web DiffBlock uses (a trailing newline terminates its line; two hunks in one file read as 1 file), so the two front ends' `+A -R · N file(s)` footers agree. Reserve space in the diff path header for the floating copy button so a long path no longer scrolls under it. Pass the tool's raw path to the injected openFile (which already resolves against cwd) instead of resolving twice. Rename the shared block-body CSS class to a card-neutral cardBody so a terminal-spacing tweak cannot silently move the diff card. Add a same-file two-hunk TUI unit test and an assembled built-boot assertion that the write turn renders +1 -0 · 1 file end to end.
This commit is contained in:
@@ -113,14 +113,15 @@
|
||||
color: var(--dsw-alias-label-tertiary);
|
||||
}
|
||||
|
||||
/* The two block-shaped expanded bodies: the code variant's run_code program
|
||||
through CodeBlock (shiki-highlighted TypeScript) and a terminal card's
|
||||
command output through TerminalBlock. Both are drawn by the shared
|
||||
primitive, so only the row's indentation is this file's concern — the margin
|
||||
also replaces each primitive's own standalone vertical spacing with the
|
||||
flow's row rhythm. */
|
||||
/* The block-shaped expanded bodies: the code variant's run_code program through
|
||||
CodeBlock (shiki-highlighted TypeScript), a terminal card's command output
|
||||
through TerminalBlock, and a write/edit diff through DiffBlock. All are drawn
|
||||
by a shared primitive, so only the row's indentation is this file's concern —
|
||||
the margin also replaces each primitive's own standalone vertical spacing with
|
||||
the flow's row rhythm. Card-neutral: it carries no terminal- or diff-specific
|
||||
value, so it fits every block body. */
|
||||
.codeBody,
|
||||
.terminalBody {
|
||||
.cardBody {
|
||||
margin: 4px 0 4px 22px;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,10 @@ export function ToolRow({
|
||||
// A row that names a single file keeps one interaction (open that path);
|
||||
// args expand is off whether or not the open callback is wired yet. A card
|
||||
// body (terminal or diff) still expands: only the file variants carry a
|
||||
// path. A write/edit row carries both a file path and a diff card, so its
|
||||
// path link and its expandable card coexist — the card expands, the summary
|
||||
// stays a link.
|
||||
// path. A write/edit row carries both a file path and a diff card, so both
|
||||
// the path link and the expandable card are offered — the collapsed row shows
|
||||
// the path link, and expanding swaps it for the card body (DisclosureRow
|
||||
// renders collapsedContent only while closed).
|
||||
const singleFile = filePath !== undefined
|
||||
const fileLink = singleFile && onOpenFile !== undefined
|
||||
const cardBody = terminalBody !== null || diffBody !== null
|
||||
@@ -138,9 +139,9 @@ export function ToolRow({
|
||||
<div className={css.terminalDescription}>{terminalBody.description}</div>
|
||||
)}
|
||||
{terminalBody !== null
|
||||
? <TerminalBlock {...terminalBody.card} maxLines={CHAT_TERMINAL_MAX_LINES} className={css.terminalBody} />
|
||||
? <TerminalBlock {...terminalBody.card} maxLines={CHAT_TERMINAL_MAX_LINES} className={css.cardBody} />
|
||||
: diffBody !== null
|
||||
? <DiffBlock {...diffBody.card} maxLines={CHAT_DIFF_MAX_LINES} className={css.terminalBody} />
|
||||
? <DiffBlock {...diffBody.card} maxLines={CHAT_DIFF_MAX_LINES} className={css.cardBody} />
|
||||
: variant === 'code'
|
||||
? <CodeBlock code={text} lang="typescript" className={css.codeBody} />
|
||||
: <div className={css.body}>{text}</div>}
|
||||
|
||||
@@ -101,8 +101,9 @@
|
||||
font: var(--dsw-font-xs-13);
|
||||
}
|
||||
|
||||
/* The terminal card sits directly under its section label, so it drops the
|
||||
primitive's standalone vertical margin; the section owns the spacing. */
|
||||
.terminal {
|
||||
/* A card body (terminal or diff) sits directly under its section label, so it
|
||||
drops the primitive's standalone vertical margin; the section owns the
|
||||
spacing. Card-neutral: no terminal- or diff-specific value. */
|
||||
.cardBody {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -146,12 +146,12 @@ function OutputBody({ material, cwd }: { material: CallMaterial; cwd: string | u
|
||||
{terminal.description !== undefined && (
|
||||
<div className={css.terminalDescription}>{terminal.description}</div>
|
||||
)}
|
||||
<TerminalBlock {...terminal.card} className={css.terminal} />
|
||||
<TerminalBlock {...terminal.card} className={css.cardBody} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
const diff = diffCardModel(material.block)
|
||||
if (diff !== null) return <DiffBlock {...diff.card} className={css.terminal} />
|
||||
if (diff !== null) return <DiffBlock {...diff.card} className={css.cardBody} />
|
||||
// A settled call always carries the result node the flattened form needs;
|
||||
// the running shape has no result to flatten.
|
||||
if (!('kind' in material.block)) return <div className={css.empty}>运行中…</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { Context } from 'cordis'
|
||||
import { DiffBlock, IconEditOutline16, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { ToolRowProps } from '../contract/slots.ts'
|
||||
import { CHAT_DIFF_MAX_LINES, diffCardModel } from '../contract/diff-card-model.ts'
|
||||
import { resolveToolPath, toolRowModel, type ToolRowState } from '../contract/tool-call-model.ts'
|
||||
import { toolRowModel, type ToolRowState } from '../contract/tool-call-model.ts'
|
||||
import css from './file-mutation-row.module.css'
|
||||
|
||||
function leadingFor(state: ToolRowState) {
|
||||
@@ -63,8 +63,9 @@ function errorText(block: ToolRowProps['block']): string | null {
|
||||
/**
|
||||
* File-mutation row: icon + {Edit,Write} · {path} in the shared ToolRow chrome,
|
||||
* with the applied diff resident below it. The summary is a path link (a file
|
||||
* tool's interaction) resolved against the session cwd and opened through the
|
||||
* host; the card's copy and expand controls are the row's only other actions.
|
||||
* tool's interaction); the host's `openFile` resolves it against the session
|
||||
* cwd, so this passes the tool's own path verbatim. The card's copy and expand
|
||||
* controls are the row's only other actions.
|
||||
*/
|
||||
export function FileMutationRow({ toolName, block, cwd, openFile }: ToolRowProps) {
|
||||
const model = toolRowModel(toolName, block, cwd)
|
||||
@@ -85,7 +86,7 @@ export function FileMutationRow({ toolName, block, cwd, openFile }: ToolRowProps
|
||||
<button
|
||||
type="button"
|
||||
className={css.fileLink}
|
||||
onClick={() => { openFile(resolveToolPath(cwd, filePath)) }}
|
||||
onClick={() => { openFile(filePath) }}
|
||||
>
|
||||
{model.summary}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user