cleanup(web): drop the unimplemented user-message edit control

The user bubble's IconActions row carried an edit button with no click
handler, no client mutation, and no host operation behind it, advertising
an affordance the product cannot honor. The row now renders clock / copy /
branch only for both user and assistant chrome, and MessageIconActions
loses the `edit` prop with the button.

The package README records the missing capability under Known Limitations,
the web aria goldens pin the row without the control, and the Agent Note
states what must exist before it comes back.
This commit is contained in:
creatixchu
2026-07-31 16:23:36 +08:00
parent 9e11f438f3
commit 171c18b9ca
29 changed files with 73 additions and 60 deletions
@@ -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: b183aab56a10c3a22eeab31f8b1b0e6b5219beba
README.zh.md: af6427774a048de324d57bc9856f77547e9a03cc
README.md: 3ee0f5efa653ade99c4e5c94dcc94377244346ba
README.zh.md: d8740fb81b0b464c8002119a5080486432200b33
@@ -47,6 +47,7 @@ None; this package neither assembles nor sends a provider request.
- **Stats-line durations cover the in-window flow only** — LLM and tool wall times fold the snapshot's assistant `timing` and tool call/result pairs, so nodes outside the loaded event window (older history) are not counted.
- **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 content IconActions row (copy / branch / clock) ships under the last content-text assistant of each turn only; mid-turn narration and Think-only nodes stay chrome-free. Branch forks through the turn containing that message, increments the inherited title on the client, and then opens the child, while a fork or rename failure leaves the source selected.
- **Sent user messages cannot be edited** — the user bubble's IconActions row carries clock / copy / branch only. The design's edit control shipped as a stub that no click handler backed, and it was removed until resending an edited message exists end to end ([decision](../../../.agents/notes/implemented/simplification/2026-07-31-drop-user-message-edit-stub.md)).
- **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.
@@ -47,6 +47,7 @@ todo 两个面就是在该形状上的两个注册项,都是普通注册方插
- **统计行的耗时只覆盖窗口内消息流**:LLM(大语言模型)与工具墙钟时间由快照的 assistant `timing` 与工具 call/result 配对折算,落在已加载事件窗口之外的节点(更早的历史)不计入。
- **详情面板是最小形态,且当前没有入口**:以原始形式显示已选择调用的参数/结果;Input/Output/Metadata 切换、Prev/Next 步进与 See-in-trajectory 深链接暂缓实现。工具行已不再是详情面板的点击目标,且没有任何手势接替它,因此 `ChatViewInjected.openDetails` 虽已实现却无人调用,该面板(含其终端卡片)在组装后的应用中不可达;其渲染仍由直接以选中态挂载它来覆盖。
- **assistant 逐消息分页是预留 slot**:设计中已有图稿,尚未实现。已定稿的内容 IconActions 行(复制/分支/时钟)只挂在每个轮次中最后一条带 text 内容的 assistant 下;轮次中间的叙述与纯 Think 节点不带 chrome。分支会 fork 到包含该消息的轮次末尾,在 client 端递增继承标题后打开子会话,而 fork 或改名失败时源会话保持选中。
- **已发送的 user 消息无法编辑**:user 气泡的 IconActions 行只有时钟/复制/分支。设计中的编辑控件曾以存根形式上线,没有任何点击处理挂在其上,现已移除,等到编辑后重新发送这条完整链路存在后再提供([决策](../../../.agents/notes/implemented/simplification/2026-07-31-drop-user-message-edit-stub.md))。
- **others 工具行的闪光图标是手绘近似版本**:无法在本地导出设计字形的矢量几何;等到存在精确导出后再将其提升到 ui-primitives。
- **审批面板的「始终允许此类」暂缓**:持久授权需要授权存储设计;今天只能回答允许一次/拒绝。
- **TodoPanel 将过长条目截成单行省略号**:figma 条没有换行或展开入口,完整文本无法在行内读完。
@@ -1,10 +1,9 @@
// Shared IconActions chrome for user and assistant messages: copy live,
// branch wired through onBranch, date-aware clock,
// optional edit stub.
// branch wired through onBranch, date-aware clock.
import { useCallback } from 'react'
import {
IconBranchOutline16, IconCopyOutline16, IconEditOutline16, Tooltip,
IconBranchOutline16, IconCopyOutline16, Tooltip,
} from '@deepseek-ai/dsh-client-ui-primitives'
import type { ChatViewSlotProps } from '../contract/slots.ts'
import { formatMessageClock, writeClipboard } from './message-chrome.ts'
@@ -18,8 +17,6 @@ export interface MessageIconActionsProps {
time: number
/** Clock before icons (user) or after (assistant). */
clock: 'start' | 'end'
/** When true, append the stub edit control (user bubble). */
edit?: boolean | undefined
/** Fork the session at this message. */
onBranch?: (() => void) | undefined
/** Parent layout class composed onto the actions row. */
@@ -30,11 +27,11 @@ export interface MessageIconActionsProps {
/**
* Copy / branch (/ clock) IconActions row shared by user and assistant chrome.
* @param props - Copy text, event time, clock side, optional edit, branch callback, className.
* @param props - Copy text, event time, clock side, branch callback, className.
* @returns The actions row element.
*/
export function MessageIconActions({
text, time, clock, edit, onBranch, className, t,
text, time, clock, onBranch, className, t,
}: MessageIconActionsProps) {
const day = useCalendarDay()
const onCopy = useCallback(() => {
@@ -58,13 +55,6 @@ export function MessageIconActions({
<IconBranchOutline16 />
</button>
</Tooltip>
{edit === true && (
<Tooltip label={t('edit')} side="bottom">
<button type="button" className={css.action} aria-label={t('edit')}>
<IconEditOutline16 />
</button>
</Tooltip>
)}
{clock === 'end' ? clockEl : null}
</div>
)
@@ -156,7 +156,6 @@ export const MessageItem = memo(function MessageItem({
text={text}
time={node.time}
clock="start"
edit
onBranch={onFork === undefined ? undefined : () => { onFork(node.seq) }}
className={css.actions}
t={t}
@@ -27,7 +27,7 @@ afterEach(() => {
const t: MessageItemProps['t'] = makeTranslate(zh, commonZh)
describe('MessageItem arms', () => {
it('user bubbles expose clock / copy / branch / edit; copy writes the text', () => {
it('user bubbles expose clock / copy / branch and no edit; copy writes the text', () => {
const writeText = vi.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
@@ -47,7 +47,7 @@ describe('MessageItem arms', () => {
expect(screen.getByText('14:24')).toBeTruthy()
expect(screen.getByRole('button', { name: '复制' })).toBeTruthy()
expect(screen.getByRole('button', { name: '在新对话中分支' })).toBeTruthy()
expect(screen.getByRole('button', { name: '编辑' })).toBeTruthy()
expect(screen.queryByRole('button', { name: '编辑' })).toBeNull()
fireEvent.click(screen.getByRole('button', { name: '复制' }))
expect(writeText).toHaveBeenCalledWith('hello bubble')
})