Merge remote-tracking branch 'origin/master' into codex/web-queue-actions

# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
This commit is contained in:
kingwl
2026-07-30 11:38:54 +08:00
12 changed files with 43 additions and 42 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-conversation/README.md
README.md: a017de28e612474dd4ad94b75385e6320e682955
README.zh.md: b352f6f745a1458d9f0555a5d7d269fe67b8e307
README.md: 75c181f2e1240f753d1f8c30152d2978151b059f
README.zh.md: 6dc167c63af27dfbc37c51b9a55087cf2c50dc7f

View File

@@ -36,7 +36,7 @@ None; this package neither assembles nor sends a provider request.
- **The stats line has no duration segment** — assistant `usage` carries token accounting only; elapsed-time needs a host data source.
- **Details panel is the minimal form and currently has no entry point** — selected call args/result raw display; the Input/Output/Metadata switch, Prev/Next stepping, and See-in-trajectory deep link are deferred. Tool rows stopped being details-panel click targets and nothing replaced that gesture, so `ChatViewInjected.openDetails` is implemented but uncalled and the panel (including its terminal card) is unreachable in the assembled application; its rendering stays covered by mounting it with a selection directly.
- **Assistant per-message paging is a reserved slot** — drawn in the design, not implemented. The finalized IconActions row (copy / branch / clock) ships; branch remains a chrome stub.
- **Assistant per-message paging is a reserved slot** — drawn in the design, not implemented. The finalized content IconActions row (copy / branch / clock) ships under text output only; branch remains a chrome stub.
- **The sparkle icon for the others tool row is a hand-drawn approximation** — the design glyph's vector geometry is not exportable locally; promotion into ui-primitives waits on an exact export.
- **The approval panel's "Always allow this type" is deferred** — durable grants need a grant-storage design; only allow-once/reject answer today.
- **TodoPanel truncates long item text to one ellipsized line** — the figma strip has no wrap or expand affordance; full text is not readable inline.

View File

@@ -36,7 +36,7 @@ todo 两个面就是在该形状上的两个注册项,都是普通注册方插
- **统计行没有耗时区段**assistant `usage` 只携带 token 计数;耗时需要主机数据源。
- **详情面板是最小形态,且当前没有入口**以原始形式显示已选择调用的参数结果Input/Output/Metadata 切换、Prev/Next 步进与 See-in-trajectory 深链接暂缓实现。工具行已不再是详情面板的点击目标,且没有任何手势接替它,因此 `ChatViewInjected.openDetails` 虽已实现却无人调用,该面板(含其终端卡片)在组装后的应用中不可达;其渲染仍由直接以选中态挂载它来覆盖。
- **assistant 逐消息分页是预留 slot**:设计中已有图稿,尚未实现。已定稿的 IconActions 行(复制/分支/时钟)已落地;分支仍是 chrome stub。
- **assistant 逐消息分页是预留 slot**:设计中已有图稿,尚未实现。已定稿的内容 IconActions 行(复制/分支/时钟)只挂在 text 输出下;分支仍是 chrome stub。
- **others 工具行的闪光图标是手绘近似版本**:无法在本地导出设计字形的矢量几何;等到存在精确导出后再将其提升到 ui-primitives。
- **审批面板的「始终允许此类」暂缓**:持久授权需要授权存储设计;今天只能回答允许一次/拒绝。
- **TodoPanel 将过长条目截成单行省略号**figma 条没有换行或展开入口,完整文本无法在行内读完。

View File

@@ -34,11 +34,3 @@
/* Optical align with 28px icon hit targets that pad 6px past the glyph. */
margin-left: -6px;
}
/* Hover-capable pointers: reveal shared actions on root hover/focus. */
@media (hover: hover) {
.root:hover .actions,
.root:focus-within .actions {
opacity: 1;
}
}

View File

@@ -4,7 +4,8 @@
// view groups them into tool rows through its keyed toolview slot (figma
// step-summary flow). Shared by finalized nodes and the streaming partial;
// the turn-level loading dots live in the chat view's tail, not here.
// Finalized nodes append IconActions (copy / branch / clock) once streaming ends.
// Finalized content (text) nodes append IconActions once streaming ends;
// Think / tool-head-only nodes stay chrome-free.
import { memo } from 'react'
import type { AssistantBlock } from '@deepseek-ai/dsh-client-runtime/client'
@@ -38,6 +39,11 @@ function copyText(blocks: readonly AssistantBlock[]): string {
return parts.join('')
}
/** True when the node has model-visible text content worth chrome under. */
function hasContentText(blocks: readonly AssistantBlock[]): boolean {
return blocks.some(block => block.kind === 'text' && block.text.trim() !== '')
}
/** Reasoning block as the Think variant summary row (figma 39:28304). */
function ThinkRow({ text, running }: { text: string; running: boolean }) {
return (
@@ -64,8 +70,8 @@ export const AssistantMarkdown = memo(function AssistantMarkdown({
|| interrupted === true
|| blocks.some(block => block.kind !== 'tool-call')
if (!hasVisible) return null
// Footer only after the turn settles with a known event time; streaming omits it.
const showActions = !streaming && time !== undefined
// Footer only under settled content text; Think-only / streaming omit it.
const showActions = !streaming && time !== undefined && hasContentText(blocks)
return (
<div className={css.root} data-streaming={streaming || undefined}>
<div className={css.body}>

View File

@@ -1,5 +1,5 @@
/* Shared message IconActions row (user + assistant). Parent modules own
hover-reveal selectors and layout offsets via the composed className. */
layout offsets via the composed className. Always visible when mounted. */
.actions {
display: flex;
@@ -25,14 +25,6 @@
white-space: nowrap;
}
/* Hover-capable pointers: hide until a parent hover/focus rule reveals. */
@media (hover: hover) {
.actions {
opacity: 0;
transition: opacity var(--ds-transition-duration) var(--ds-ease-in-out);
}
}
.action {
display: inline-flex;
align-items: center;

View File

@@ -18,7 +18,7 @@ export interface MessageIconActionsProps {
clock: 'start' | 'end'
/** When true, append the stub edit control (user bubble). */
edit?: boolean | undefined
/** Parent layout / hover-reveal class composed onto the actions row. */
/** Parent layout class composed onto the actions row. */
className?: string | undefined
}

View File

@@ -20,14 +20,6 @@
color: var(--dsw-alias-label-primary);
}
/* Hover-capable pointers: reveal shared MessageIconActions on row hover/focus. */
@media (hover: hover) {
.userRow:hover .actions,
.userRow:focus-within .actions {
opacity: 1;
}
}
.badge {
display: inline-block;
margin-bottom: 4px;

View File

@@ -177,7 +177,7 @@ describe('small branch tails', () => {
expect(view.getByText('one-liner')).toBeTruthy()
})
it('finalized assistant messages expose copy / branch / clock after the body; streaming omits them', () => {
it('finalized content messages expose copy / branch / clock; Think-only and streaming omit them', () => {
const writeText = vi.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
@@ -199,6 +199,17 @@ describe('small branch tails', () => {
expect(writeText).toHaveBeenCalledWith('answer body')
settled.unmount()
const thinkOnly = render(
<AssistantMarkdown
blocks={[{ kind: 'reasoning', text: 'only thinking' }]}
streaming={false}
time={time}
/>,
)
expect(thinkOnly.queryByRole('button', { name: '复制' })).toBeNull()
expect(thinkOnly.queryByText('14:24')).toBeNull()
thinkOnly.unmount()
const streaming = render(
<AssistantMarkdown blocks={[{ kind: 'text', text: 'partial' }]} streaming time={time} />,
)