fix: hide inactive browser profile settings

Managed browser profile controls suggested that login state and cookies would be reused, but named profiles do not yet affect browser execution. The settings UI now hides profile management while preserving the built-in browser switch, diagnostics, stored profile metadata, and compatibility APIs.

Release note: 暂时隐藏尚未实际生效的托管浏览器配置,避免误以为登录状态和 Cookie 会被复用;已有底层数据不会删除。
This commit is contained in:
mesalogo
2026-08-18 12:36:18 +08:00
parent 7a46422fc1
commit 08884c127c
9 changed files with 20 additions and 348 deletions
+1 -1
View File
@@ -733,7 +733,7 @@ export class CapabilityService {
await this.browserProfiles.getSnapshot()
return {
status: 'available',
summary: '隔离的托管浏览器配置存储可用。'
summary: '内置浏览器隔离存储可用。'
}
}
}
@@ -36,7 +36,7 @@ export const computerCapabilityCatalog: readonly ComputerCapabilityCatalogEntry[
id: 'host-browser-control',
name: '内置浏览器',
description:
'使用 GoodBuddy 内置的临时隔离浏览器执行网页操作,不会控制客户端已安装的浏览器;命名配置当前仅保存未来托管隔离所需的元数据。',
'使用 GoodBuddy 内置的临时隔离浏览器执行网页操作,不会控制客户端已安装的浏览器。',
enabledByDefault: false,
implementationKind: 'managed-browser-driver',
supportedTargets: Object.freeze([
-204
View File
@@ -3,7 +3,6 @@ import {
CircleAlert,
Database,
FlaskConical,
Globe2,
MonitorCog,
Network,
Pencil,
@@ -136,10 +135,6 @@ export function McpSettingsSection({
const [diagnostics, setDiagnostics] = useState<
Partial<Record<ComputerCapabilityId, CapabilityDiagnosticReport>>
>({})
const [newProfileName, setNewProfileName] = useState('')
const [profileNames, setProfileNames] = useState<Record<string, string>>(
{}
)
const [activeTab, setActiveTab] =
useState<McpSettingsTab>('builtin')
const editorDialogRef = useRef<HTMLDivElement>(null)
@@ -231,21 +226,6 @@ export function McpSettingsSection({
}
}
const createProfile = async (): Promise<void> => {
const name = newProfileName.trim()
if (!name) {
return
}
if (
await run('profile:create', () =>
window.goodbuddy.capabilities.createBrowserProfile?.({ name }) ??
Promise.reject(new Error(t('mcp.errors.unsupportedProfiles')))
)
) {
setNewProfileName('')
}
}
const save = async (): Promise<void> => {
if (!editor) {
return
@@ -405,10 +385,6 @@ export function McpSettingsSection({
const builtinMcpStates = new Map(
snapshot?.builtinMcpServers?.map((server) => [server.id, server])
)
const browserProfiles = snapshot?.browserProfiles ?? {
profiles: [],
defaultProfileId: null
}
const webSearch = snapshot?.webSearch ?? {
provider: 'exa' as const,
enabled: true,
@@ -601,154 +577,6 @@ export function McpSettingsSection({
</section>
)}
{activeTab === 'model-tools' && browserCapability && (
<section
aria-labelledby="browser-profiles-heading"
className="mcp-tool-section"
>
<div className="mcp-subsection-heading">
<div>
<Globe2 size={15} />
<strong id="browser-profiles-heading">
{t('mcp.profiles.title')}
</strong>
</div>
<small>
{t('mcp.profiles.count', {
count: browserProfiles.profiles.length
})}
</small>
</div>
<p className="settings-notice">
{t('mcp.profiles.notice')}
</p>
<div className="browser-profile-create">
<label className="field">
<span>{t('mcp.profiles.newName')}</span>
<input
onChange={(event) => setNewProfileName(event.target.value)}
placeholder={t('mcp.profiles.placeholder')}
value={newProfileName}
/>
</label>
<button
className="secondary-button"
disabled={Boolean(busy) || !newProfileName.trim()}
onClick={() => void createProfile()}
type="button"
>
<Plus size={13} />
{t('mcp.profiles.create')}
</button>
</div>
<div className="browser-profile-list">
{browserProfiles.profiles.length === 0 && (
<p className="settings-empty">{t('mcp.profiles.empty')}</p>
)}
{browserProfiles.profiles.map((profile) => {
const referenced = computerCapabilities.some(
(capability) =>
capability.browserProfileId === profile.id
)
return (
<article className="browser-profile-row" key={profile.id}>
<label className="field">
<span>{t('mcp.profiles.name')}</span>
<input
aria-label={t('mcp.profiles.nameAriaLabel', {
name: profile.name
})}
onChange={(event) =>
setProfileNames((current) => ({
...current,
[profile.id]: event.target.value
}))
}
value={profileNames[profile.id] ?? profile.name}
/>
</label>
<label className="browser-profile-default">
<input
aria-label={t(
'mcp.profiles.setDefaultAriaLabel',
{ name: profile.name }
)}
checked={
browserProfiles.defaultProfileId === profile.id
}
disabled={Boolean(busy)}
name="default-browser-profile"
onChange={() =>
void run(`profile:default:${profile.id}`, () =>
window.goodbuddy.capabilities.setDefaultBrowserProfile?.(
profile.id
) ??
Promise.reject(
new Error(t('mcp.errors.unsupportedProfiles'))
)
)
}
type="radio"
/>
{t('mcp.profiles.default')}
</label>
<button
aria-label={t('mcp.profiles.renameAriaLabel', {
name: profile.name
})}
className="secondary-button"
disabled={
Boolean(busy) ||
!(profileNames[profile.id] ?? '').trim() ||
profileNames[profile.id] === profile.name
}
onClick={() =>
void run(`profile:rename:${profile.id}`, () =>
window.goodbuddy.capabilities.renameBrowserProfile?.({
profileId: profile.id,
name: profileNames[profile.id] ?? profile.name
}) ??
Promise.reject(
new Error(t('mcp.errors.unsupportedProfiles'))
)
)
}
type="button"
>
<Pencil size={13} />
{t('mcp.profiles.rename')}
</button>
<button
aria-label={t('mcp.profiles.deleteAriaLabel', {
name: profile.name
})}
className="danger-ghost"
disabled={Boolean(busy) || referenced}
onClick={() =>
void run(`profile:remove:${profile.id}`, () =>
window.goodbuddy.capabilities.removeBrowserProfile?.(
profile.id
) ??
Promise.reject(
new Error(t('mcp.errors.unsupportedProfiles'))
)
)
}
title={
referenced ? t('mcp.profiles.inUse') : undefined
}
type="button"
>
<Trash2 size={13} />
{t('mcp.profiles.delete')}
</button>
</article>
)
})}
</div>
</section>
)}
{activeTab === 'builtin' && (
<section
aria-labelledby="builtin-mcp-heading"
@@ -1219,38 +1047,6 @@ export function McpSettingsSection({
<CircleAlert aria-hidden="true" size={13} />
{t('mcp.browser.control')}
</p>
<label className="field computer-capability-profile">
<span>{t('mcp.browser.profile')}</span>
<select
aria-label={t('mcp.browser.profileAriaLabel')}
disabled={Boolean(busy)}
onChange={(event) =>
void run('browser:profile', () =>
window.goodbuddy.capabilities.setComputerCapabilityBrowserProfile?.(
browserCapability.id,
event.target.value || null
) ??
Promise.reject(
new Error(
t('mcp.errors.unsupportedProfiles')
)
)
)
}
value={
browserCapability.browserProfileId ?? ''
}
>
<option value="">
{t('mcp.browser.defaultProfile')}
</option>
{browserProfiles.profiles.map((profile) => (
<option key={profile.id} value={profile.id}>
{profile.name}
</option>
))}
</select>
</label>
<div className="capability-diagnostic">
<button
aria-label={t(
+7 -33
View File
@@ -234,7 +234,7 @@ const capabilitySnapshot = {
{
id: 'host-browser-control' as const,
name: '内置浏览器',
description: '使用隔离的托管浏览器配置执行网页操作。',
description: '使用 GoodBuddy 内置的临时隔离浏览器执行网页操作。',
enabled: false,
supported: true,
browserProfileId: null,
@@ -321,10 +321,10 @@ const diagnoseComputerCapability = vi.fn(async () => ({
checkedAt: '2026-08-05T12:00:00.000Z',
checks: [
{
id: 'managed-profile-root',
id: 'browser-executable',
status: 'degraded' as const,
summary: '托管配置可用,但尚未选择默认网站。',
remedy: '先创建并选择托管配置。'
summary: '内置浏览器核心可用,但当前未启动会话。',
remedy: '开始一次浏览器任务后重试。'
}
]
}))
@@ -3656,35 +3656,9 @@ describe('SettingsPanel runtime files', () => {
expect(diagnoseComputerCapability).toHaveBeenCalledWith(
'host-browser-control'
)
fireEvent.change(screen.getByLabelText('新配置名称'), {
target: { value: '购物网站' }
})
fireEvent.click(
screen.getByRole('button', { name: '创建托管配置' })
)
await waitFor(() =>
expect(createBrowserProfile).toHaveBeenCalledWith({
name: '购物网站'
})
)
fireEvent.change(screen.getByLabelText('配置名称 工作网站'), {
target: { value: '工作站点' }
})
fireEvent.click(
screen.getByRole('button', { name: '重命名配置 工作网站' })
)
await waitFor(() =>
expect(renameBrowserProfile).toHaveBeenCalledWith({
profileId: browserProfileId,
name: '工作站点'
})
)
fireEvent.click(
screen.getByRole('button', { name: '删除配置 工作网站' })
)
await waitFor(() =>
expect(removeBrowserProfile).toHaveBeenCalledWith(browserProfileId)
)
expect(screen.queryByText('托管浏览器配置')).not.toBeInTheDocument()
expect(screen.queryByText('内置浏览器配置')).not.toBeInTheDocument()
expect(screen.queryByLabelText('新配置名称')).not.toBeInTheDocument()
expect(
screen.queryByRole('switch', {
name: '启用 Linux 桌面控制'
@@ -164,8 +164,6 @@ export const integrations = {
unsupportedDiagnostics:
'Computer control diagnostics are unavailable in this version',
diagnostics: 'Capability diagnostics failed',
unsupportedProfiles:
'Managed browser profiles are unavailable in this version',
test: 'MCP connection test failed',
unsupportedBuiltinBrowser:
'The built-in browser is unavailable in this version',
@@ -200,23 +198,7 @@ export const integrations = {
remedy: ' Suggested action: {{remedy}}'
},
profiles: {
title: 'Managed browser profiles',
count: '{{count}}',
notice:
'Each profile uses isolated storage managed by GoodBuddy. The interface never receives or displays executable paths, command arguments, or environment variables.',
newName: 'New profile name',
placeholder: 'For example: Work sites',
create: 'Create managed profile',
empty: 'No managed browser profiles',
name: 'Profile name',
nameAriaLabel: 'Profile name {{name}}',
setDefaultAriaLabel: 'Set {{name}} as the default profile',
default: 'Default',
renameAriaLabel: 'Rename profile {{name}}',
rename: 'Rename',
deleteAriaLabel: 'Delete profile {{name}}',
delete: 'Delete',
inUse: 'This profile is used by the built-in browser'
count: '{{count}}'
},
builtin: {
title: 'Built-in GoodBuddy MCP',
@@ -266,9 +248,6 @@ export const integrations = {
enableAriaLabel: 'Enable the direct-model built-in browser',
enabled: 'Enabled',
disabled: 'Disabled',
profile: 'Built-in browser profile',
profileAriaLabel: 'Managed profile used by the built-in browser',
defaultProfile: 'Use the default managed profile',
diagnoseAriaLabel: 'Diagnose the built-in browser',
toolsAriaLabel: 'Direct-model built-in browser tools'
},
@@ -153,7 +153,6 @@ export const integrations = {
operation: '能力设置操作失败',
unsupportedDiagnostics: '当前版本不支持电脑控制能力诊断',
diagnostics: '能力诊断失败',
unsupportedProfiles: '当前版本不支持托管浏览器配置',
test: 'MCP 连接测试失败',
unsupportedBuiltinBrowser: '当前版本不支持内置浏览器',
unsupportedComputerControl: '当前版本不支持电脑控制能力'
@@ -186,23 +185,7 @@ export const integrations = {
remedy: ' 处理建议:{{remedy}}'
},
profiles: {
title: '托管浏览器配置',
count: '{{count}} 个',
notice:
'每个配置使用 GoodBuddy 管理的隔离存储;界面不会接收或显示可执行路径、命令参数与环境变量。',
newName: '新配置名称',
placeholder: '例如:工作网站',
create: '创建托管配置',
empty: '尚未创建托管浏览器配置',
name: '配置名称',
nameAriaLabel: '配置名称 {{name}}',
setDefaultAriaLabel: '设为默认配置 {{name}}',
default: '默认',
renameAriaLabel: '重命名配置 {{name}}',
rename: '重命名',
deleteAriaLabel: '删除配置 {{name}}',
delete: '删除',
inUse: '此配置正被内置浏览器使用'
count: '{{count}} 个'
},
builtin: {
title: 'GoodBuddy 内置 MCP',
@@ -248,9 +231,6 @@ export const integrations = {
enableAriaLabel: '启用直连模型内置浏览器',
enabled: '已启用',
disabled: '已停用',
profile: '内置浏览器配置',
profileAriaLabel: '内置浏览器使用的托管配置',
defaultProfile: '使用默认托管配置',
diagnoseAriaLabel: '诊断内置浏览器',
toolsAriaLabel: '直连模型内置浏览器工具'
},
-60
View File
@@ -7145,10 +7145,6 @@ details.settings-section > :not(summary) + :not(summary) {
margin-top: 1px;
}
.computer-capability-profile {
max-width: 360px;
}
.capability-diagnostic {
display: flex;
align-items: flex-start;
@@ -7278,64 +7274,8 @@ details.settings-section > :not(summary) + :not(summary) {
}
}
.browser-profile-create,
.browser-profile-row {
display: flex;
align-items: flex-end;
gap: var(--space-2);
}
.browser-profile-create .field,
.browser-profile-row .field {
flex: 1;
margin: 0;
}
.browser-profile-list {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.browser-profile-row {
padding: var(--space-3);
border: 1px solid var(--border-default);
border-radius: var(--radius-card);
background: var(--surface-raised);
}
.browser-profile-default {
display: flex;
align-items: center;
min-height: 32px;
color: var(--text-secondary);
font-size: var(--font-caption);
gap: var(--space-1);
}
.browser-profile-row .danger-ghost {
display: flex;
align-items: center;
min-height: 32px;
padding: 0 var(--space-2);
border: 1px solid var(--danger-border);
border-radius: var(--radius-control);
background: transparent;
color: var(--danger);
cursor: pointer;
font-size: var(--font-caption);
gap: var(--space-1);
}
.browser-profile-row .danger-ghost:disabled {
cursor: not-allowed;
opacity: 0.55;
}
@media (max-width: 720px) {
.capability-diagnostic,
.browser-profile-create,
.browser-profile-row,
.mcp-server-card__header {
align-items: stretch;
flex-direction: column;