fix: restore essential UI guidance
Settings navigation and several workspace surfaces had lost context that helps users scan, understand, and operate them. Restore concise bilingual descriptions, workflow cues, and visual hierarchy for the welcome message, composer, activity history, knowledge workflows, Magic Notes, assistant workbar, Runtime settings, and channel projects while keeping decorative or duplicate copy removed. Release note: 恢复设置菜单副标题、新对话引导及知识库、运行记录、魔法笔记和助手工作栏中的必要说明,提升功能可发现性与页面信息层级。
This commit is contained in:
@@ -110,10 +110,10 @@ describe('ActivityPanel', () => {
|
||||
).toBeInTheDocument()
|
||||
expect(screen.queryByText('RUN HISTORY')).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByText(
|
||||
screen.getByText(
|
||||
'Review execution details by project, task, and conversation, or browse the activity timeline and model usage.'
|
||||
)
|
||||
).not.toBeInTheDocument()
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByRole('tab', { name: 'Tasks and conversations' })
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
@@ -616,10 +616,10 @@ describe('ActivityPanel', () => {
|
||||
).toBeInTheDocument()
|
||||
expect(within(timeline).getByText('项目:项目甲')).toBeInTheDocument()
|
||||
expect(
|
||||
within(timeline).queryByText(
|
||||
'所有轨道共享同一执行顺序,节点按发生时间依次展开,点击节点查看身份和活动详情。'
|
||||
within(timeline).getByText(
|
||||
'所有轨道共享同一执行顺序,节点按发生时间依次展开;点击节点可查看身份和活动详情。'
|
||||
)
|
||||
).not.toBeInTheDocument()
|
||||
).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', {
|
||||
|
||||
@@ -674,6 +674,7 @@ export function ActivityPanel({
|
||||
className="activity-panel"
|
||||
>
|
||||
<PageHeader
|
||||
description={t('header.description')}
|
||||
headingId="activity-panel-title"
|
||||
icon={<Activity size={20} />}
|
||||
scope={{ kind: 'all-projects' }}
|
||||
@@ -801,6 +802,9 @@ export function ActivityPanel({
|
||||
aria-label={t('timeline.ariaLabel')}
|
||||
className="activity-tracks"
|
||||
>
|
||||
<p className="activity-tracks__description">
|
||||
{t('timeline.description')}
|
||||
</p>
|
||||
<ul
|
||||
aria-label={t('timeline.legendAriaLabel')}
|
||||
className="activity-tracks__legend"
|
||||
|
||||
@@ -1074,7 +1074,11 @@ describe('App', () => {
|
||||
expect(screen.getByText('Project: Default project')).toHaveClass(
|
||||
'scope-badge'
|
||||
)
|
||||
expect(screen.getByText('Hi, I’m GoodBuddy.')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Hi, I’m GoodBuddy\. Ask me a question, add local files/u
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByLabelText('Message GoodBuddy')
|
||||
).toHaveAttribute(
|
||||
@@ -1085,7 +1089,7 @@ describe('App', () => {
|
||||
screen.getByLabelText('Message GoodBuddy')
|
||||
).toHaveAttribute(
|
||||
'title',
|
||||
'Enter to send, Shift+Enter for a new line'
|
||||
'Enter to send, Shift+Enter for a new line, Ctrl+V to paste an image or text'
|
||||
)
|
||||
} finally {
|
||||
cleanup()
|
||||
@@ -1243,7 +1247,11 @@ describe('App', () => {
|
||||
await screen.findByRole('heading', {
|
||||
name: '今天想一起完成什么?'
|
||||
})
|
||||
expect(screen.getByText('你好,我是 GoodBuddy。')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText(
|
||||
/你好,我是 GoodBuddy。你可以直接向我提问、添加本地文件/u
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
expect(screen.queryByText('桌面工作区')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('GOODBUDDY 工作台')).not.toBeInTheDocument()
|
||||
expect(
|
||||
@@ -5395,7 +5403,7 @@ describe('App', () => {
|
||||
).toHaveAttribute('title', '添加附件')
|
||||
expect(
|
||||
within(contentTools).getByRole('button', { name: '语音输入' })
|
||||
).not.toHaveAttribute('title')
|
||||
).toHaveAttribute('title', '语音转文字,转写后可编辑再发送')
|
||||
|
||||
const conversationSettings = within(composer!).getByRole(
|
||||
'group',
|
||||
@@ -8136,6 +8144,12 @@ describe('App', () => {
|
||||
screen.queryByRole('tab', { name: '上下文' })
|
||||
).not.toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole('tab', { name: '工作区' }))
|
||||
expect(
|
||||
screen.getByRole('tab', { name: '工作区' })
|
||||
).toHaveAttribute('title', '浏览项目文件、Git 变更与文件内容')
|
||||
expect(
|
||||
screen.getByRole('heading', { name: '项目工作区' })
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByRole('button', { name: '刷新工作区文件' })
|
||||
).toBeInTheDocument()
|
||||
@@ -8143,6 +8157,7 @@ describe('App', () => {
|
||||
screen.queryByText(/选择文件后在当前工作区内预览/)
|
||||
).not.toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole('tab', { name: '浏览器' }))
|
||||
expect(screen.getByText('实时浏览器')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText(/Agent 打开网页后/)
|
||||
).toBeInTheDocument()
|
||||
@@ -8150,7 +8165,9 @@ describe('App', () => {
|
||||
expect(
|
||||
screen.getByRole('button', { name: '导入 PDF、图片或网页' })
|
||||
).toBeInTheDocument()
|
||||
expect(screen.queryByText('生成与导入成果')).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByRole('heading', { name: '生成与导入成果' })
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByRole('tab', { name: '预览' })
|
||||
).not.toBeInTheDocument()
|
||||
|
||||
@@ -646,7 +646,8 @@ const chatBottomProximity = 96
|
||||
function createConversation(
|
||||
projectId?: string,
|
||||
runtimeSelection?: AgentRuntimeSelection,
|
||||
greeting = '你好,我是 GoodBuddy。'
|
||||
greeting =
|
||||
'你好,我是 GoodBuddy。你可以直接向我提问、添加本地文件,或使用知识库整理和检索信息。需要我操作文件或调用工具时,请选择合适的 Agent Runtime 和工作模式。'
|
||||
): Conversation {
|
||||
const now = Date.now()
|
||||
return {
|
||||
@@ -8541,7 +8542,7 @@ function App(): React.JSX.Element {
|
||||
? t('composer.voice.stopAndRecognize')
|
||||
: voiceListening
|
||||
? t('composer.voice.cancel')
|
||||
: undefined
|
||||
: t('composer.voice.description')
|
||||
}
|
||||
type="button"
|
||||
>
|
||||
|
||||
@@ -628,8 +628,8 @@ describe('ChannelSettingsSection', () => {
|
||||
expect(dingtalkTab).toHaveAttribute('tabindex', '-1')
|
||||
expect(screen.getByText('项目设置')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByText('与左上角当前通道项目的设置保持同步。')
|
||||
).not.toBeInTheDocument()
|
||||
screen.getByText('与左上角当前通道项目的设置保持同步。')
|
||||
).toBeInTheDocument()
|
||||
expect(screen.queryByText('通道项目')).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByRole('switch', { name: '启用企业微信通道' })
|
||||
|
||||
@@ -211,6 +211,7 @@ function ChannelProjectCard({
|
||||
<div className="capability-card__header">
|
||||
<div>
|
||||
<strong>{t('channels.project.cardTitle')}</strong>
|
||||
<small>{t('channels.project.cardDescription')}</small>
|
||||
</div>
|
||||
</div>
|
||||
<ChannelProjectSettingsFields
|
||||
|
||||
@@ -270,7 +270,9 @@ describe('KnowledgeRetrievalWorkbench', () => {
|
||||
).toHaveAttribute('aria-pressed', 'true')
|
||||
expect(
|
||||
document.querySelector('.knowledge-retrieval-pipeline')
|
||||
).not.toBeInTheDocument()
|
||||
).toBeInTheDocument()
|
||||
expect(screen.getByText('召回候选')).toBeInTheDocument()
|
||||
expect(screen.getByText('重排与上下文')).toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole('button', { name: '测试检索' }))
|
||||
|
||||
expect(onTest).toHaveBeenCalledWith({
|
||||
|
||||
@@ -459,6 +459,7 @@ export function KnowledgeRetrievalWorkbench({
|
||||
<div className="knowledge-workbench-section__heading">
|
||||
<div>
|
||||
<h3 id={`${titleId}-query`}>{t('retrieval.query.title')}</h3>
|
||||
<p>{t('retrieval.query.help')}</p>
|
||||
</div>
|
||||
<span className="knowledge-character-count">
|
||||
{t('retrieval.query.count', { count: query.length })}
|
||||
@@ -506,6 +507,71 @@ export function KnowledgeRetrievalWorkbench({
|
||||
{validationErrors.weights}
|
||||
</p>
|
||||
)}
|
||||
<div className="knowledge-retrieval-pipeline">
|
||||
<div className="knowledge-retrieval-pipeline__step">
|
||||
<span>1</span>
|
||||
<div>
|
||||
<strong>{t('retrieval.pipeline.recall.title')}</strong>
|
||||
<small>
|
||||
{candidateLimit === undefined
|
||||
? t('retrieval.pipeline.recall.pending')
|
||||
: t('retrieval.pipeline.recall.summary', {
|
||||
count: candidateLimit
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="knowledge-retrieval-pipeline__connector" />
|
||||
<div
|
||||
className={`knowledge-retrieval-pipeline__step${
|
||||
draftSettings.rerankMode !== 'none'
|
||||
? ''
|
||||
: ' knowledge-retrieval-pipeline__step--muted'
|
||||
}`}
|
||||
>
|
||||
<span>2</span>
|
||||
<div>
|
||||
<strong>{t('retrieval.pipeline.rerank.title')}</strong>
|
||||
<small>
|
||||
{draftSettings.rerankMode !== 'none'
|
||||
? t('retrieval.pipeline.rerank.enabled', {
|
||||
count: candidateLimit ?? '—'
|
||||
})
|
||||
: t('retrieval.pipeline.rerank.disabled')}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="knowledge-retrieval-pipeline__connector" />
|
||||
<div className="knowledge-retrieval-pipeline__step">
|
||||
<span>3</span>
|
||||
<div>
|
||||
<strong>{t('retrieval.pipeline.select.title')}</strong>
|
||||
<small>
|
||||
{t('retrieval.pipeline.select.summary', {
|
||||
count: Number.isSafeInteger(draftSettings.topK)
|
||||
? draftSettings.topK
|
||||
: '—'
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="knowledge-retrieval-pipeline__connector" />
|
||||
<div className="knowledge-retrieval-pipeline__step">
|
||||
<span>4</span>
|
||||
<div>
|
||||
<strong>{t('retrieval.pipeline.context.title')}</strong>
|
||||
<small>
|
||||
{t('retrieval.pipeline.context.summary', {
|
||||
count: Number.isSafeInteger(
|
||||
draftSettings.contextMaxCharacters
|
||||
)
|
||||
? draftSettings.contextMaxCharacters.toLocaleString()
|
||||
: '—'
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-describedby={
|
||||
showSettingsValidation && validationErrors.weights
|
||||
@@ -524,6 +590,7 @@ export function KnowledgeRetrievalWorkbench({
|
||||
<h4 id={`${titleId}-recall-settings`}>
|
||||
{t('retrieval.settings.groups.recall.title')}
|
||||
</h4>
|
||||
<p>{t('retrieval.settings.groups.recall.description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="knowledge-workbench-settings__grid knowledge-workbench-settings__grid--three">
|
||||
@@ -637,6 +704,7 @@ export function KnowledgeRetrievalWorkbench({
|
||||
<h4 id={`${titleId}-output-settings`}>
|
||||
{t('retrieval.settings.groups.output.title')}
|
||||
</h4>
|
||||
<p>{t('retrieval.settings.groups.output.description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
|
||||
@@ -318,8 +318,8 @@ describe('KnowledgeWorkspace', () => {
|
||||
expect(screen.getByText('架构说明.md')).toBeInTheDocument()
|
||||
expect(screen.getByText('Local file · 架构说明.md')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByText(/Imported content is parsed/u)
|
||||
).not.toBeInTheDocument()
|
||||
screen.getByText(/Imported content is parsed/u)
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('imports an HTTP URL into the selected library', async () => {
|
||||
|
||||
@@ -1269,6 +1269,13 @@ function DocumentsView({
|
||||
<h3 id="sources-title">
|
||||
{t('documents.sources.title')}
|
||||
</h3>
|
||||
<p className="knowledge-section-description">
|
||||
{t(
|
||||
library.graphEnabled
|
||||
? 'documents.sources.descriptionWithGraph'
|
||||
: 'documents.sources.description'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="knowledge-documents__import-actions">
|
||||
<button
|
||||
@@ -2257,6 +2264,9 @@ function KnowledgeSettingsView({
|
||||
<h3 id="knowledge-graph-settings-title">
|
||||
{t('settings.graphConfiguration.title')}
|
||||
</h3>
|
||||
<p className="knowledge-section-description">
|
||||
{t('settings.graphConfiguration.description')}
|
||||
</p>
|
||||
</div>
|
||||
<label className="knowledge-field">
|
||||
{t('fields.graphExtractionStrategy')}
|
||||
|
||||
@@ -423,6 +423,7 @@ describe('MagicNotesWorkspace', () => {
|
||||
expect(
|
||||
screen.queryByRole('button', { name: '创建待办' })
|
||||
).not.toBeInTheDocument()
|
||||
expect(screen.getByText('全部笔记')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByRole('tab', { name: '待办' }))
|
||||
expect(
|
||||
|
||||
@@ -1537,6 +1537,10 @@ export function MagicNotesWorkspace({
|
||||
id="magic-library-panel-notes"
|
||||
role="tabpanel"
|
||||
>
|
||||
<div className="magic-notes-pane-heading">
|
||||
<strong>{t('notes.heading')}</strong>
|
||||
<span>{notes.length}</span>
|
||||
</div>
|
||||
<label className="magic-notes-search">
|
||||
<span className="sr-only">{t('notes.searchLabel')}</span>
|
||||
<input
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
CircleAlert,
|
||||
ExternalLink,
|
||||
FileText,
|
||||
FolderTree,
|
||||
ClockFading,
|
||||
Monitor,
|
||||
Plus,
|
||||
@@ -200,7 +201,8 @@ export function RightAssistantSidebar({
|
||||
() =>
|
||||
tabIds.map((id) => ({
|
||||
id,
|
||||
label: t(`sidebar.tabs.${id}.label`)
|
||||
label: t(`sidebar.tabs.${id}.label`),
|
||||
description: t(`sidebar.tabs.${id}.description`)
|
||||
})),
|
||||
[t]
|
||||
)
|
||||
@@ -594,6 +596,7 @@ export function RightAssistantSidebar({
|
||||
onKeyDown={(event) => moveTabFocus(event, item.id)}
|
||||
role="tab"
|
||||
tabIndex={tab === item.id ? 0 : -1}
|
||||
title={item.description}
|
||||
type="button"
|
||||
>
|
||||
{item.label}
|
||||
@@ -857,22 +860,26 @@ export function RightAssistantSidebar({
|
||||
</section>
|
||||
) : (
|
||||
<section className="assistant-sidebar__section">
|
||||
<button
|
||||
aria-label={t('sidebar.workspace.refreshAriaLabel')}
|
||||
className="icon-button"
|
||||
disabled={!workspaceProjectId}
|
||||
onClick={() => {
|
||||
setWorkspaceRefreshVersion((current) => current + 1)
|
||||
runAction(
|
||||
onRefreshChanges,
|
||||
t('sidebar.errors.refreshWorkspace')
|
||||
)
|
||||
}}
|
||||
title={t('sidebar.workspace.refresh')}
|
||||
type="button"
|
||||
>
|
||||
<RefreshCw size={14} />
|
||||
</button>
|
||||
<h3>
|
||||
<FolderTree size={15} />
|
||||
{t('sidebar.workspace.projectTitle')}
|
||||
<button
|
||||
aria-label={t('sidebar.workspace.refreshAriaLabel')}
|
||||
className="icon-button"
|
||||
disabled={!workspaceProjectId}
|
||||
onClick={() => {
|
||||
setWorkspaceRefreshVersion((current) => current + 1)
|
||||
runAction(
|
||||
onRefreshChanges,
|
||||
t('sidebar.errors.refreshWorkspace')
|
||||
)
|
||||
}}
|
||||
title={t('sidebar.workspace.refresh')}
|
||||
type="button"
|
||||
>
|
||||
<RefreshCw size={14} />
|
||||
</button>
|
||||
</h3>
|
||||
<WorkspaceFilesPanel
|
||||
changedFiles={workspaceChanges?.files ?? emptyChangedFiles}
|
||||
key={`${workspaceProjectId ?? 'none'}:${workspaceRefreshVersion}`}
|
||||
@@ -959,6 +966,10 @@ export function RightAssistantSidebar({
|
||||
</section>
|
||||
) : (
|
||||
<section className="assistant-sidebar__section">
|
||||
<h3>
|
||||
<FileText size={15} />
|
||||
{t('sidebar.results.sectionTitle')}
|
||||
</h3>
|
||||
<button
|
||||
className="secondary-button assistant-sidebar__import"
|
||||
onClick={() =>
|
||||
@@ -1011,6 +1022,10 @@ export function RightAssistantSidebar({
|
||||
{tab === 'browser' && (
|
||||
<section className="assistant-sidebar__browser">
|
||||
<header>
|
||||
<span>
|
||||
<Monitor size={15} />
|
||||
<strong>{t('sidebar.browser.title')}</strong>
|
||||
</span>
|
||||
{browserState &&
|
||||
browserState.status !== 'stopped' && (
|
||||
<div className="assistant-sidebar__browser-actions">
|
||||
|
||||
@@ -860,8 +860,8 @@ describe('SettingsPanel runtime files', () => {
|
||||
})
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByText('直连模型的历史压缩与原文保留')
|
||||
).not.toBeInTheDocument()
|
||||
screen.getByRole('tab', { name: '上下文控制' })
|
||||
).toHaveTextContent('直连模型的历史压缩与原文保留')
|
||||
const enabled = screen.getByRole('switch', {
|
||||
name: '自动压缩较早的对话'
|
||||
})
|
||||
@@ -1435,8 +1435,15 @@ describe('SettingsPanel runtime files', () => {
|
||||
expect(content.parentElement).toBe(navigation.parentElement)
|
||||
expect(content).toHaveClass('settings-panel__content')
|
||||
expect(
|
||||
screen.getByRole('tab', { name: 'Agent Runtime' })
|
||||
).toHaveTextContent(/^Agent Runtime$/u)
|
||||
screen.getByText('管理模型、Runtime、平台能力、消息通道与本地数据。')
|
||||
).toBeInTheDocument()
|
||||
const runtimeTab = screen.getByRole('tab', {
|
||||
name: 'Agent Runtime'
|
||||
})
|
||||
expect(runtimeTab).toHaveTextContent('Agent Runtime')
|
||||
expect(runtimeTab).toHaveTextContent(
|
||||
'配置 Agent Runtime、默认工作区与原生能力'
|
||||
)
|
||||
})
|
||||
|
||||
it('omits the redundant close-only footer on passive settings pages', () => {
|
||||
@@ -2179,6 +2186,11 @@ describe('SettingsPanel runtime files', () => {
|
||||
screen.queryByRole('button', { name: '重新检测 Continue' })
|
||||
).not.toBeVisible()
|
||||
fireEvent.click(screen.getByText('高级设置'))
|
||||
expect(
|
||||
screen.getByText(
|
||||
'普通使用无需修改。这里可以切换模型来源、复用 Runtime 自有配置,或覆盖内置程序。'
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', { name: '重新检测 Continue' })
|
||||
)
|
||||
@@ -2461,6 +2473,11 @@ describe('SettingsPanel runtime files', () => {
|
||||
).not.toBeVisible()
|
||||
|
||||
fireEvent.click(screen.getByText('高级设置'))
|
||||
expect(
|
||||
screen.getByText(
|
||||
'普通使用无需修改。这里可以切换模型来源、复用 Runtime 自有配置,或覆盖内置程序与服务。'
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByLabelText('OpenCode 可执行文件路径')
|
||||
).toBeInTheDocument()
|
||||
@@ -2489,6 +2506,12 @@ describe('SettingsPanel runtime files', () => {
|
||||
expect(screen.getByText('1.5.47')).toBeInTheDocument()
|
||||
expect(screen.getByText('高级设置').closest('details'))
|
||||
.not.toHaveAttribute('open')
|
||||
fireEvent.click(screen.getByText('高级设置'))
|
||||
expect(
|
||||
screen.getByText(
|
||||
'普通使用无需修改。这里可以切换模型来源、复用 Runtime 自有配置,或覆盖内置程序。'
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('selects a native OpenCode Agent and excludes GoodBuddy assignments from inventory', async () => {
|
||||
|
||||
@@ -1795,6 +1795,7 @@ export function SettingsPanel({
|
||||
<X aria-hidden="true" size={19} />
|
||||
</button>
|
||||
}
|
||||
description={t('center.description')}
|
||||
headingId="settings-title"
|
||||
title={t('center.title')}
|
||||
/>
|
||||
@@ -1830,6 +1831,11 @@ export function SettingsPanel({
|
||||
<strong>
|
||||
{t(`categories.${category.translationKey}.label`)}
|
||||
</strong>
|
||||
<small>
|
||||
{t(
|
||||
`categories.${category.translationKey}.navigationDescription`
|
||||
)}
|
||||
</small>
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
@@ -2129,6 +2135,9 @@ export function SettingsPanel({
|
||||
</div>
|
||||
<details className="settings-section">
|
||||
<summary>{t('runtime.advanced')}</summary>
|
||||
<p className="settings-panel__description">
|
||||
{t('runtime.opencode.advancedDescription')}
|
||||
</p>
|
||||
<fieldset className="runtime-source-options">
|
||||
<legend>{t('runtime.sourceLegend')}</legend>
|
||||
<label>
|
||||
@@ -2357,6 +2366,9 @@ export function SettingsPanel({
|
||||
</div>
|
||||
<details className="settings-section">
|
||||
<summary>{t('runtime.advanced')}</summary>
|
||||
<p className="settings-panel__description">
|
||||
{t('runtime.continue.advancedDescription')}
|
||||
</p>
|
||||
<fieldset className="runtime-source-options">
|
||||
<legend>{t('runtime.sourceLegend')}</legend>
|
||||
<label>
|
||||
|
||||
@@ -3,7 +3,9 @@ import type { activity as chineseActivity } from '../zh-CN/activity'
|
||||
|
||||
export const activity = {
|
||||
header: {
|
||||
title: 'Run history'
|
||||
title: 'Run history',
|
||||
description:
|
||||
'Review execution details by project, task, and conversation, or browse the activity timeline and model usage.'
|
||||
},
|
||||
tabs: {
|
||||
ariaLabel: 'Run history views',
|
||||
@@ -78,6 +80,8 @@ export const activity = {
|
||||
},
|
||||
timeline: {
|
||||
ariaLabel: 'Parallel activity tracks grouped by project and conversation',
|
||||
description:
|
||||
'All tracks share the same execution order. Nodes unfold by time; select one to inspect its identity and activity details.',
|
||||
lanes: 'Project / conversation',
|
||||
laneAriaLabel: 'Activity track for conversation {{title}}',
|
||||
nodeAriaLabel: '{{actor}}, {{kind}}, {{status}}, {{title}}, {{time}}',
|
||||
|
||||
@@ -72,7 +72,8 @@ export const app = {
|
||||
conversation: {
|
||||
defaultTitle: 'New conversation',
|
||||
remoteTitle: 'Remote conversation',
|
||||
greeting: 'Hi, I’m GoodBuddy.',
|
||||
greeting:
|
||||
'Hi, I’m GoodBuddy. Ask me a question, add local files, or use your knowledge base to organize and retrieve information. When you want me to operate on files or use tools, choose the appropriate Agent Runtime and work mode.',
|
||||
interrupted: 'The previous run stopped unexpectedly. You can resend your question.',
|
||||
active: 'Conversation is active',
|
||||
unread: 'Unread',
|
||||
@@ -377,7 +378,8 @@ export const app = {
|
||||
inputLabel: 'Message GoodBuddy',
|
||||
placeholder: 'Message GoodBuddy…',
|
||||
imagePlaceholder: 'Describe the image you want to generate…',
|
||||
keyboardHint: 'Enter to send, Shift+Enter for a new line',
|
||||
keyboardHint:
|
||||
'Enter to send, Shift+Enter for a new line, Ctrl+V to paste an image or text',
|
||||
addContent: 'Add content',
|
||||
addAttachment: 'Add attachment',
|
||||
attachmentProgress: {
|
||||
@@ -479,6 +481,7 @@ export const app = {
|
||||
cancel: 'Cancel speech recognition',
|
||||
input: 'Voice input',
|
||||
stopAndRecognize: 'Stop recording and recognize speech',
|
||||
description: 'Convert speech to editable text before sending',
|
||||
unsupported:
|
||||
'Built-in speech recognition is unavailable. You can keep typing.',
|
||||
downloadingPack:
|
||||
|
||||
@@ -18,6 +18,8 @@ export const integrations = {
|
||||
project: {
|
||||
sectionAriaLabel: '{{name}} channel project settings',
|
||||
cardTitle: 'Project settings',
|
||||
cardDescription:
|
||||
'Stays synchronized with the selected channel project in the top-left project switcher.',
|
||||
descriptionLabel: 'Project description',
|
||||
descriptionAriaLabel: '{{name}} project description',
|
||||
rootLabel: 'Default working directory',
|
||||
|
||||
@@ -164,20 +164,45 @@ export const knowledge = {
|
||||
close: 'Close retrieval test',
|
||||
query: {
|
||||
title: 'Test question',
|
||||
help: 'Enter a real question to inspect channels, ranking, and final context.',
|
||||
label: 'Retrieval question',
|
||||
placeholder: 'For example: How do I configure document parsing offline?',
|
||||
count: '{{count}} / 4000 characters'
|
||||
},
|
||||
pipeline: {
|
||||
recall: {
|
||||
title: 'Recall candidates',
|
||||
summary: 'Up to {{count}} fused candidates',
|
||||
pending: 'Enter valid settings to calculate'
|
||||
},
|
||||
rerank: {
|
||||
title: 'Rerank candidates',
|
||||
enabled: 'Rerank up to {{count}} candidates',
|
||||
disabled: 'Off; keep fused ranking'
|
||||
},
|
||||
select: {
|
||||
title: 'Final results',
|
||||
summary: 'Keep the top {{count}} chunks'
|
||||
},
|
||||
context: {
|
||||
title: 'Assemble context',
|
||||
summary: '{{count}} character budget'
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
title: 'Settings for this test',
|
||||
temporary:
|
||||
'These changes apply only to this test. Save them as the current library defaults to keep using them.',
|
||||
groups: {
|
||||
recall: {
|
||||
title: 'Candidate recall'
|
||||
title: 'Candidate recall',
|
||||
description:
|
||||
'Control the initial search pool, filtering threshold, and each channel’s influence on fused ranking.'
|
||||
},
|
||||
output: {
|
||||
title: 'Reranking and context'
|
||||
title: 'Reranking and context',
|
||||
description:
|
||||
'Control candidate ordering, final result count, and the context sent to the model.'
|
||||
}
|
||||
},
|
||||
candidateMultiplier: 'Recall multiplier',
|
||||
@@ -474,6 +499,10 @@ export const knowledge = {
|
||||
documents: {
|
||||
sources: {
|
||||
title: 'Content sources',
|
||||
description:
|
||||
'Imported content is parsed and added to the retrieval index automatically.',
|
||||
descriptionWithGraph:
|
||||
'Imported content is parsed, indexed for retrieval, and added to the knowledge graph with the current strategy.',
|
||||
emptyTitle: 'No content sources connected',
|
||||
emptyDescription:
|
||||
'Choose files, a folder, or a URL, or drag files into the area above.',
|
||||
@@ -540,7 +569,9 @@ export const knowledge = {
|
||||
askDescription:
|
||||
'“Ask when needed” does not generate a graph automatically and cannot run re-extraction.',
|
||||
graphConfiguration: {
|
||||
title: 'Extraction method'
|
||||
title: 'Extraction method',
|
||||
description:
|
||||
'Control how new imports, resyncs, and explicit rebuilds generate entities, relations, and evidence.'
|
||||
},
|
||||
chunking: {
|
||||
title: 'Chunking strategy',
|
||||
|
||||
@@ -73,6 +73,7 @@ export const magicNotes = {
|
||||
notes: {
|
||||
listLabel: 'Notes list',
|
||||
streamLabel: 'Note entries',
|
||||
heading: 'All notes',
|
||||
searchLabel: 'Search notes in the current scope',
|
||||
searchPlaceholder: 'Search notes',
|
||||
titleLabel: 'Note title',
|
||||
|
||||
@@ -8,6 +8,8 @@ export const settings = {
|
||||
},
|
||||
center: {
|
||||
title: 'Settings',
|
||||
description:
|
||||
'Manage models, Runtimes, platform capabilities, message channels, and local data.',
|
||||
close: 'Close settings',
|
||||
categoriesAriaLabel: 'Settings categories'
|
||||
},
|
||||
@@ -345,6 +347,8 @@ export const settings = {
|
||||
title: 'OpenCode Agent',
|
||||
recommendation:
|
||||
'Following the GoodBuddy model is recommended. Use OpenCode configuration only when you need its native models, plugins, or MCP configuration.',
|
||||
advancedDescription:
|
||||
'Most users do not need to change these options. Use them to switch model sources, reuse Runtime-owned configuration, or override the bundled program and service.',
|
||||
followDescription:
|
||||
'Generates a secure runtime configuration automatically, with no Runtime configuration file to maintain.',
|
||||
ownDescription:
|
||||
@@ -363,6 +367,8 @@ export const settings = {
|
||||
title: 'Continue CLI',
|
||||
recommendation:
|
||||
'Following the GoodBuddy model is recommended. Use Continue configuration only when you need its native models, rules, or MCP configuration.',
|
||||
advancedDescription:
|
||||
'Most users do not need to change these options. Use them to switch model sources, reuse Runtime-owned configuration, or override the bundled program.',
|
||||
followDescription:
|
||||
'Generates a secure temporary runtime configuration and deletes it when the task ends.',
|
||||
ownDescription:
|
||||
|
||||
@@ -75,16 +75,20 @@ export const workspace = {
|
||||
categoriesAriaLabel: 'Workspace categories',
|
||||
tabs: {
|
||||
tasks: {
|
||||
label: 'Task center'
|
||||
label: 'Task center',
|
||||
description: 'Review approvals and manage automations'
|
||||
},
|
||||
workspace: {
|
||||
label: 'Files'
|
||||
label: 'Files',
|
||||
description: 'Browse project files, Git changes, and file contents'
|
||||
},
|
||||
browser: {
|
||||
label: 'Browser'
|
||||
label: 'Browser',
|
||||
description: 'View the live browser while the Agent works'
|
||||
},
|
||||
results: {
|
||||
label: 'Results'
|
||||
label: 'Results',
|
||||
description: 'View generated or imported standalone results'
|
||||
}
|
||||
},
|
||||
tasks: {
|
||||
@@ -126,6 +130,7 @@ export const workspace = {
|
||||
workspace: {
|
||||
back: 'Back to workspace',
|
||||
title: 'Files',
|
||||
projectTitle: 'Project workspace',
|
||||
fileSize: '{{formattedSize}} bytes',
|
||||
fileFallback: 'Project workspace file',
|
||||
reading: 'Reading file…',
|
||||
@@ -138,12 +143,14 @@ export const workspace = {
|
||||
results: {
|
||||
back: 'Back to results',
|
||||
title: 'Results',
|
||||
sectionTitle: 'Generated and imported results',
|
||||
loadingImage: 'Loading image…',
|
||||
import: 'Import PDF, image, or web page',
|
||||
empty:
|
||||
'Generated files, images, reports, and manually imported content will appear here.'
|
||||
},
|
||||
browser: {
|
||||
title: 'Live browser',
|
||||
interact: 'Interact',
|
||||
interacting: 'Interacting',
|
||||
stop: 'Stop browser',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export const activity = {
|
||||
header: {
|
||||
title: '运行记录'
|
||||
title: '运行记录',
|
||||
description:
|
||||
'按项目、任务与会话查看执行详情,或浏览活动时间线和模型用量。'
|
||||
},
|
||||
tabs: {
|
||||
ariaLabel: '运行记录视图',
|
||||
@@ -75,6 +77,8 @@ export const activity = {
|
||||
},
|
||||
timeline: {
|
||||
ariaLabel: '按项目和会话分组的并行活动轨道',
|
||||
description:
|
||||
'所有轨道共享同一执行顺序,节点按发生时间依次展开;点击节点可查看身份和活动详情。',
|
||||
lanes: '项目 / 会话',
|
||||
laneAriaLabel: '会话 {{title}} 的活动轨道',
|
||||
nodeAriaLabel: '{{actor}},{{kind}},{{status}},{{title}},{{time}}',
|
||||
|
||||
@@ -69,7 +69,8 @@ export const app = {
|
||||
conversation: {
|
||||
defaultTitle: '新对话',
|
||||
remoteTitle: '远程会话',
|
||||
greeting: '你好,我是 GoodBuddy。',
|
||||
greeting:
|
||||
'你好,我是 GoodBuddy。你可以直接向我提问、添加本地文件,或使用知识库整理和检索信息。需要我操作文件或调用工具时,请选择合适的 Agent Runtime 和工作模式。',
|
||||
interrupted: '上次运行意外中断,可以重新发送问题',
|
||||
active: '会话正在活动',
|
||||
unread: '未读',
|
||||
@@ -363,7 +364,8 @@ export const app = {
|
||||
inputLabel: '向 GoodBuddy 提问',
|
||||
placeholder: '给 GoodBuddy 发消息…',
|
||||
imagePlaceholder: '描述你想生成的图片…',
|
||||
keyboardHint: 'Enter 发送,Shift+Enter 换行',
|
||||
keyboardHint:
|
||||
'Enter 发送,Shift+Enter 换行,Ctrl+V 粘贴图片或文本',
|
||||
addContent: '添加内容',
|
||||
addAttachment: '添加附件',
|
||||
attachmentProgress: {
|
||||
@@ -460,6 +462,7 @@ export const app = {
|
||||
cancel: '取消语音识别',
|
||||
input: '语音输入',
|
||||
stopAndRecognize: '停止录音并开始识别',
|
||||
description: '语音转文字,转写后可编辑再发送',
|
||||
unsupported: '当前系统不支持内置语音识别,可继续使用键盘输入',
|
||||
downloadingPack: '正在下载中文离线语音包,完成后将自动开始听写',
|
||||
transcribed: '语音已转为文字,可编辑后发送',
|
||||
|
||||
@@ -15,6 +15,7 @@ export const integrations = {
|
||||
project: {
|
||||
sectionAriaLabel: '{{name}} 通道项目设置',
|
||||
cardTitle: '项目设置',
|
||||
cardDescription: '与左上角当前通道项目的设置保持同步。',
|
||||
descriptionLabel: '项目说明',
|
||||
descriptionAriaLabel: '{{name}} 项目说明',
|
||||
rootLabel: '默认工作目录',
|
||||
|
||||
@@ -159,19 +159,42 @@ export const knowledge = {
|
||||
close: '关闭检索测试',
|
||||
query: {
|
||||
title: '测试问题',
|
||||
help: '输入一个真实问题,查看各检索通道、排名和最终上下文。',
|
||||
label: '检索问题',
|
||||
placeholder: '例如:如何为离线环境配置文档解析?',
|
||||
count: '{{count}} / 4000 字'
|
||||
},
|
||||
pipeline: {
|
||||
recall: {
|
||||
title: '召回候选',
|
||||
summary: '最多 {{count}} 个融合候选',
|
||||
pending: '设置有效参数后计算'
|
||||
},
|
||||
rerank: {
|
||||
title: '候选重排',
|
||||
enabled: '重排最多 {{count}} 个候选',
|
||||
disabled: '已关闭,保留融合排名'
|
||||
},
|
||||
select: {
|
||||
title: '最终结果',
|
||||
summary: '保留前 {{count}} 个分块'
|
||||
},
|
||||
context: {
|
||||
title: '组装上下文',
|
||||
summary: '预算 {{count}} 字符'
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
title: '本次测试参数',
|
||||
temporary: '这些调整只影响本次测试。需要长期使用时,请保存为当前知识库默认值。',
|
||||
groups: {
|
||||
recall: {
|
||||
title: '候选召回'
|
||||
title: '候选召回',
|
||||
description: '控制首轮检索范围、过滤阈值和各通道对融合排名的影响。'
|
||||
},
|
||||
output: {
|
||||
title: '重排与上下文'
|
||||
title: '重排与上下文',
|
||||
description: '控制候选排序、最终保留数量,以及实际送入模型的上下文范围。'
|
||||
}
|
||||
},
|
||||
candidateMultiplier: '召回倍数',
|
||||
@@ -443,6 +466,9 @@ export const knowledge = {
|
||||
documents: {
|
||||
sources: {
|
||||
title: '内容来源',
|
||||
description: '导入内容后会自动解析并建立检索索引。',
|
||||
descriptionWithGraph:
|
||||
'导入内容后会自动解析、建立检索索引,并按当前策略更新知识图谱。',
|
||||
emptyTitle: '尚未连接内容来源',
|
||||
emptyDescription:
|
||||
'可选择文件、目录或 URL;也可以直接将文件拖入上方区域。',
|
||||
@@ -505,7 +531,8 @@ export const knowledge = {
|
||||
strategyAriaLabel: '知识图谱抽取策略',
|
||||
askDescription: '“按需询问”不会自动生成图谱,也不能执行重新抽取。',
|
||||
graphConfiguration: {
|
||||
title: '抽取方式'
|
||||
title: '抽取方式',
|
||||
description: '控制新导入、重新同步和显式重建时如何生成实体、关系与证据。'
|
||||
},
|
||||
chunking: {
|
||||
title: '分块策略',
|
||||
|
||||
@@ -68,6 +68,7 @@ export const magicNotes = {
|
||||
notes: {
|
||||
listLabel: '笔记列表',
|
||||
streamLabel: '笔记记录',
|
||||
heading: '全部笔记',
|
||||
searchLabel: '搜索当前范围的笔记',
|
||||
searchPlaceholder: '搜索笔记',
|
||||
titleLabel: '笔记标题',
|
||||
|
||||
@@ -5,6 +5,7 @@ export const settings = {
|
||||
},
|
||||
center: {
|
||||
title: '设置中心',
|
||||
description: '管理模型、Runtime、平台能力、消息通道与本地数据。',
|
||||
close: '关闭设置',
|
||||
categoriesAriaLabel: '设置分类'
|
||||
},
|
||||
@@ -315,6 +316,8 @@ export const settings = {
|
||||
title: 'OpenCode Agent',
|
||||
recommendation:
|
||||
'推荐直接跟随 GoodBuddy 模型。只有需要复用 OpenCode 原生模型、插件或 MCP 配置时,才切换到 Runtime 自有配置。',
|
||||
advancedDescription:
|
||||
'普通使用无需修改。这里可以切换模型来源、复用 Runtime 自有配置,或覆盖内置程序与服务。',
|
||||
followDescription:
|
||||
'自动生成安全的运行期配置,无需维护 Runtime 配置文件。',
|
||||
ownDescription: '适合需要原生模型、插件或 MCP 配置的专业用户。',
|
||||
@@ -332,6 +335,8 @@ export const settings = {
|
||||
title: 'Continue CLI',
|
||||
recommendation:
|
||||
'推荐直接跟随 GoodBuddy 模型。只有需要复用 Continue 原生模型、规则或 MCP 配置时,才切换到 Runtime 自有配置。',
|
||||
advancedDescription:
|
||||
'普通使用无需修改。这里可以切换模型来源、复用 Runtime 自有配置,或覆盖内置程序。',
|
||||
followDescription:
|
||||
'自动生成安全的临时运行期配置,任务结束后立即删除。',
|
||||
ownDescription: '适合需要原生模型、规则或 MCP 配置的专业用户。',
|
||||
|
||||
@@ -72,16 +72,20 @@ export const workspace = {
|
||||
categoriesAriaLabel: '工作栏分类',
|
||||
tabs: {
|
||||
tasks: {
|
||||
label: '任务中心'
|
||||
label: '任务中心',
|
||||
description: '处理待审批操作并管理自动化'
|
||||
},
|
||||
workspace: {
|
||||
label: '工作区'
|
||||
label: '工作区',
|
||||
description: '浏览项目文件、Git 变更与文件内容'
|
||||
},
|
||||
browser: {
|
||||
label: '浏览器'
|
||||
label: '浏览器',
|
||||
description: '查看 Agent 操作网页时的实时画面'
|
||||
},
|
||||
results: {
|
||||
label: '成果'
|
||||
label: '成果',
|
||||
description: '查看生成或手动导入的独立成果'
|
||||
}
|
||||
},
|
||||
tasks: {
|
||||
@@ -122,6 +126,7 @@ export const workspace = {
|
||||
workspace: {
|
||||
back: '返回工作区',
|
||||
title: '工作区',
|
||||
projectTitle: '项目工作区',
|
||||
fileSize: '{{formattedSize}} 字节',
|
||||
fileFallback: '项目工作区文件',
|
||||
reading: '正在读取文件…',
|
||||
@@ -134,11 +139,13 @@ export const workspace = {
|
||||
results: {
|
||||
back: '返回成果列表',
|
||||
title: '成果',
|
||||
sectionTitle: '生成与导入成果',
|
||||
loadingImage: '正在加载图片…',
|
||||
import: '导入 PDF、图片或网页',
|
||||
empty: '生成的文件、图片、报告和手动导入内容会显示在这里。'
|
||||
},
|
||||
browser: {
|
||||
title: '实时浏览器',
|
||||
interact: '交互',
|
||||
interacting: '交互中',
|
||||
stop: '停止浏览器',
|
||||
|
||||
Reference in New Issue
Block a user