fix(forge-panel): round reviewer avatars in every engine and share the menu search keys

The reviewer avatar pairs its full-round radius with `corner-shape:
round`, as the theme requires of every full-round radius. The base
branch menu and the reviewer search share one handler for the keys of
their search field instead of two copies.
This commit is contained in:
Yichen Jiang committed 2026-11-09 21:27:06 +08:00
1 parent ef22fbe7d3
commit 8da1df0775
7 files changed
+25 -14

No files matched your search

@@ -18,8 +18,8 @@
en: c3fa707a638717e4
zh: dfeba8a57c58a8aa
/deepseek-ai-dsh-experimental-client-ui-forge/understand-the-implementation:
en: 34e9adf3a843f969
zh: 7a04974cf01fa2e7
en: c9541603ea971f46
zh: 15c41326e3fb29e4
/deepseek-ai-dsh-experimental-client-ui-forge/further-exploration:
en: fa4d8c579ffc49ff
zh: 5856ed7469e4585b
@@ -106,6 +106,7 @@ The browser entry mounts the Remote, then the UI. One stream per Session feeds t
| [`src/client/thread-state.ts`](src/client/thread-state.ts) | Thread reads and their places in the shown changes |
| [`src/review-cache.ts`](src/review-cache.ts) | Review threads kept in memory |
| [`src/client/host-viewed.ts`](src/client/host-viewed.ts) | Viewed marks the code host keeps, with marks being saved |
| [`src/client/menu-search.ts`](src/client/menu-search.ts) | Keys of the search field at the top of a menu |
| [`src/client/FileLabel.tsx`](src/client/FileLabel.tsx) | Changed file label: name, then directory |
| [`src/client/WorktreeToggle.tsx`](src/client/WorktreeToggle.tsx) | New Session worktree option |
| [`src/client/WorktreeProgress.tsx`](src/client/WorktreeProgress.tsx) | Worktree creation and removed-worktree card |
@@ -106,6 +106,7 @@ Session 在所创建的 worktree 中工作时,`localDiff` 以它的 `baseCommi
| [`src/client/thread-state.ts`](src/client/thread-state.ts) | 读取讨论并确定它们在所显示变更中的位置 |
| [`src/review-cache.ts`](src/review-cache.ts) | 保存在内存中的评审讨论 |
| [`src/client/host-viewed.ts`](src/client/host-viewed.ts) | 代码托管平台保存的已审阅标记,以及正在保存的标记 |
| [`src/client/menu-search.ts`](src/client/menu-search.ts) | 菜单顶部搜索框的按键处理 |
| [`src/client/FileLabel.tsx`](src/client/FileLabel.tsx) | 变更文件的标签:文件名在前,目录在后 |
| [`src/client/WorktreeToggle.tsx`](src/client/WorktreeToggle.tsx) | 新会话的工作树选项 |
| [`src/client/WorktreeProgress.tsx`](src/client/WorktreeProgress.tsx) | worktree 创建卡片与 worktree 缺失卡片 |
@@ -10,6 +10,7 @@ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol'
import type { ForgeBaseBranch, ForgeBaseBranches } from '../types.ts'
import type { NS } from './locales.ts'
import { searchFieldKeyDown } from './menu-search.ts'
import css from './BaseMenu.module.css'
/** Milliseconds between the last keystroke of a search and the listing it reads. */
@@ -169,12 +170,7 @@ export function BaseMenu({ chosen, label, missing, visible, list, choose, t }: B
<Input ref={search} className={clsx(css.searchInput)} icon={<IconSearchOutlineRegular size={14} />} value={query}
aria-label={t('diff.base.search')} placeholder={t('diff.base.search')}
onChange={(event) => { setQuery(event.target.value) }}
onKeyDown={(event) => {
if (['Home', 'End', 'ArrowLeft', 'ArrowRight'].includes(event.key)) event.stopPropagation()
if (event.key !== 'Enter' || event.nativeEvent.isComposing || first === undefined) return
event.preventDefault()
pick(first.base)
}} />
onKeyDown={(event) => { searchFieldKeyDown(event, first === undefined ? undefined : () => { pick(first.base) }) }} />
</div>
<div className={css.rows}>{body}</div>
<div className={css.note}>{t('diff.base.note')}</div>
@@ -35,6 +35,7 @@
justify-content: center;
box-sizing: border-box;
border-radius: 50%;
corner-shape: round;
background: var(--dsw-alias-interactive-bg-active);
color: var(--dsw-alias-label-secondary);
font-size: 10px;
@@ -22,6 +22,7 @@ import { formatDuration } from './format.ts'
import { CheckGlyph } from './glyphs.tsx'
import type { ForgePanelKey, NS } from './locales.ts'
import { checkCounts, checkDuration } from './status.ts'
import { searchFieldKeyDown } from './menu-search.ts'
import css from './ForgeTab.module.css'
import overview from './ChangeRequestOverview.module.css'
@@ -470,12 +471,7 @@ function ReviewerSearch({ url, list, choose, t }: {
<div className={overview.menuSearch}>
<Input ref={search} icon={<IconSearchOutlineRegular size={14} />} value={query} aria-label={t('reviewers.search')}
placeholder={t('reviewers.search')} onChange={(event) => { setQuery(event.target.value) }}
onKeyDown={(event) => {
if (['Home', 'End', 'ArrowLeft', 'ArrowRight'].includes(event.key)) event.stopPropagation()
if (event.key !== 'Enter' || event.nativeEvent.isComposing || first === undefined) return
event.preventDefault()
choose(first.login)
}} />
onKeyDown={(event) => { searchFieldKeyDown(event, first === undefined ? undefined : () => { choose(first.login) }) }} />
</div>
<div className={overview.menuRows}>{body}</div>
</>
@@ -0,0 +1,16 @@
/** Keys of the search field at the top of a menu that lists what the search matches. */
import type { KeyboardEvent } from 'react'
/**
* Keep the edge and arrow keys in a menu's search field, so they move the caret instead of the menu's focus, and choose
* the first listed row with Enter outside an input method composition.
* @param event - the key event of the search field.
* @param chooseFirst - chooses the first listed row, or undefined while none is listed.
*/
export function searchFieldKeyDown(event: KeyboardEvent<HTMLInputElement>, chooseFirst: (() => void) | undefined): void {
if (['Home', 'End', 'ArrowLeft', 'ArrowRight'].includes(event.key)) event.stopPropagation()
if (event.key !== 'Enter' || event.nativeEvent.isComposing || chooseFirst === undefined) return
event.preventDefault()
chooseFirst()
}