feat(settings): replicate the WorkBuddy agents panel and persist team-management preference (TODO 15)

This commit is contained in:
xiaohan.yuan committed 2026-10-17 12:58:25 +08:00
1 parent b3be45715e
commit 143516b3a8
33 files changed
+1071 -21

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
/**
* 设置 → 智能体(TODO 15):只持久化「智能体团队管理」偏好。
*
* REA 依据(WorkBuddy 5.8.3,设置窗口 900×680):
* - ev_bd0f4b5dac7d4e2b89b1b050db1896c65e03b4fd1c3d3d986a8958fd9c1a9883(浅色顶部 DOM/layout/computed style + CSS 源)
* - ev_3384aa03af8a1ccdffb8e025dbd4a354636d8d6b0c207abd21e12fc5eb2ae719(深色同页)
* - ev_01940b7925326ee8e7f7e4aba661be2a17a2f876d634d4d370d01b6616a4a7fd(团队管理下拉展开:开启 / 禁用)
* - ev_1cc896e04c0cc9ceab39b0a252f4982c019305609195ad44804ae9b5fb7379a8(最终浅色顶部)
*
* 用户限定:云端服务与子代理调度本轮不处理。因此这里只保存偏好(默认开启,与原版一致),
* 多智能体自动拆分的执行器尚未接入;面板用 title 明确说明,不把偏好伪装成已生效能力。
*/
import type { AgentSettings, AgentSettingsPatch } from '@shared/types'
import { getStoredAgentSettings, setStoredAgentSettings } from './config'
export const DEFAULT_AGENT_SETTINGS: AgentSettings = { teamManagement: true }
export function normalizeAgentSettings(value: Partial<AgentSettings> | null | undefined): AgentSettings {
return { teamManagement: value?.teamManagement !== false }
}
export async function getAgentSettings(): Promise<AgentSettings> {
return normalizeAgentSettings(await getStoredAgentSettings())
}
export async function updateAgentSettings(patch: AgentSettingsPatch): Promise<AgentSettings> {
const next = normalizeAgentSettings({ ...await getAgentSettings(), ...patch })
await setStoredAgentSettings(next)
return next
}
+13 -1
View File
@@ -10,7 +10,7 @@
import { app, safeStorage } from 'electron'
import { promises as fs } from 'node:fs'
import { join } from 'node:path'
import type { GatewayConfig, GatewayConfigView, PersonalizationSettings } from '@shared/types'
import type { AgentSettings, GatewayConfig, GatewayConfigView, PersonalizationSettings } from '@shared/types'
interface StoredConfig {
speechBaseUrl?: string
@@ -28,6 +28,8 @@ interface StoredConfig {
autoLaunch?: boolean
/** 设置 → 个性化(身份、回复风格、指令、人设、长期记忆)。 */
personalization?: Partial<PersonalizationSettings>
/** 设置 → 智能体(智能体团队管理偏好)。 */
agentSettings?: Partial<AgentSettings>
}
const DEFAULTS: StoredConfig = {
@@ -131,6 +133,16 @@ export async function setStoredPersonalization(value: Partial<PersonalizationSet
await persist({ ...stored, personalization: { ...(stored.personalization ?? {}), ...value } })
}
export async function getStoredAgentSettings(): Promise<Partial<AgentSettings>> {
const stored = await load()
return stored.agentSettings ?? {}
}
export async function setStoredAgentSettings(value: Partial<AgentSettings>): Promise<void> {
const stored = await load()
await persist({ ...stored, agentSettings: { ...(stored.agentSettings ?? {}), ...value } })
}
/** 供渲染进程使用:脱敏视图 */
export async function getConfigView(): Promise<GatewayConfigView> {
const stored = await load()
+4 -1
View File
@@ -11,7 +11,7 @@ import { BrowserWindow, app, dialog, ipcMain, shell, type OpenDialogOptions } fr
import { randomUUID } from 'node:crypto'
import { unlink, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import type { ChatEvent, ChatMessage, GitBranchInfo, PersonalizationPatch, WindowState } from '@shared/types'
import type { AgentSettingsPatch, ChatEvent, ChatMessage, GitBranchInfo, PersonalizationPatch, WindowState } from '@shared/types'
import { freezeAssistantModelMetadata } from '@shared/assistant-model'
import type { ConfigPatchLike, EditAction, SendPayload, WorkspaceAttachResult } from '@shared/ipc'
import { applyAssistantEvent } from '@shared/message-parts'
@@ -35,6 +35,7 @@ import * as models from './models'
import * as workspaces from './workspaces'
import * as generalSettings from './general-settings'
import * as personalization from './personalization'
import * as agentSettings from './agent-settings'
import { prepareOmpModel } from './omp-model'
import type { CustomModelInput, ResolvedModel } from '@shared/models'
import type { SessionContext } from '@shared/workspaces'
@@ -411,6 +412,8 @@ export function registerIpc(getWindow: () => BrowserWindow | null): void {
ipcMain.handle('general:get', (_event, options?: { refreshUsage?: boolean }) => generalView(options?.refreshUsage === true))
ipcMain.handle('personalization:get', () => personalization.getPersonalization())
ipcMain.handle('personalization:update', (_event, patch: PersonalizationPatch) => personalization.updatePersonalization(patch))
ipcMain.handle('agents:get', () => agentSettings.getAgentSettings())
ipcMain.handle('agents:update', (_event, patch: AgentSettingsPatch) => agentSettings.updateAgentSettings(patch))
ipcMain.handle('general:update', async (_event, patch: { autoLaunch?: boolean; preventSleep?: boolean }) => {
if (patch.preventSleep !== undefined) {
await config.setPreventSleep(patch.preventSleep)
+4
View File
@@ -117,6 +117,10 @@ const api: YCodeApi = {
get: () => ipcRenderer.invoke('personalization:get'),
update: (patch) => ipcRenderer.invoke('personalization:update', patch)
},
agents: {
get: () => ipcRenderer.invoke('agents:get'),
update: (patch) => ipcRenderer.invoke('agents:update', patch)
},
workspace: {
get: () => ipcRenderer.invoke('workspace:get'),
choose: () => ipcRenderer.invoke('workspace:choose'),
@@ -0,0 +1,182 @@
/**
* 设置 → 智能体:WorkBuddy 5.8.3「智能体」页的 1:1 复刻(TODO 15)。
*
* REA 依据(设置窗口 900×680):
* - ev_bd0f4b5dac7d4e2b89b1b050db1896c65e03b4fd1c3d3d986a8958fd9c1a9883(浅色顶部 DOM/layout/computed style + CSS 源)
* - ev_3384aa03af8a1ccdffb8e025dbd4a354636d8d6b0c207abd21e12fc5eb2ae719(深色同页)
* - ev_01940b7925326ee8e7f7e4aba661be2a17a2f876d634d4d370d01b6616a4a7fd(团队管理下拉:开启 / 禁用)
* - ev_1cc896e04c0cc9ceab39b0a252f4982c019305609195ad44804ae9b5fb7379a8(最终浅色顶部)
*
* 页面只有三个区块:智能体团队管理(下拉)、经济模式(开关)、浏览器操控(Beta,三行扩展安装)。
* 用户限定:云端服务与子代理调度暂不处理。本轮只把「智能体团队管理」做成真实持久化偏好,
* 并在控件 title 说明多智能体执行器尚未接入;经济模式与浏览器操控保持原版布局、禁用并标注。
*/
import { useEffect, useRef, useState } from 'react'
import type { ReactNode } from 'react'
import type { AgentSettings } from '@shared/types'
import { IconSelectCaret } from './general-settings-icons'
import browserChromeIcon from './agents-assets/workbuddy-browser-chrome.svg'
import browserEdgeIcon from './agents-assets/workbuddy-browser-edge.svg'
import browserQqIcon from './agents-assets/workbuddy-browser-qq.png'
import './source-agents-panels.css'
const UNAVAILABLE_HINT = '暂未接入'
const UNSET_HINT = '尚未配置'
const BROWSER_ROWS: ReadonlyArray<{ key: string; icon: string; title: string; desc: string }> = [
{ key: 'chrome', icon: browserChromeIcon, title: 'YCode Chrome 扩展程序', desc: '在 Chrome 中安装此扩展程序,将 YCode 连接到你的浏览器。' },
{ key: 'edge', icon: browserEdgeIcon, title: 'YCode Edge 扩展程序', desc: '在 Edge 中安装此扩展程序,将 YCode 连接到你的浏览器。' },
{ key: 'qq', icon: browserQqIcon, title: 'YCode QQ 浏览器扩展程序', desc: '在 QQ 浏览器中安装此扩展程序,将 YCode 连接到你的浏览器。' }
]
function classNames(...values: Array<string | false | undefined>): string {
return values.filter(Boolean).join(' ')
}
function ExternalArrow(): React.JSX.Element {
return <svg className="wb-icon" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="none" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"><path d="M5.25 3.75h7v7M12 4 4.25 11.75" /></svg>
}
function Section({ title, desc, action, stacked }: { title: string; desc?: ReactNode; action: ReactNode; stacked?: boolean }): React.JSX.Element {
return (
<section className={classNames('general-settings-panel__section', 'general-settings-panel__section--plain',
stacked ? 'general-settings-panel__section--stacked' : 'general-settings-panel__section--inline')}>
<div className="general-settings-panel__section-main">
<div className="general-settings-panel__section-copy">
<h3 className="general-settings-panel__section-title">{title}</h3>
{desc ? <p className="general-settings-panel__section-desc">{desc}</p> : null}
</div>
<div className="general-settings-panel__section-action">{action}</div>
</div>
</section>
)
}
function Switch({ checked }: { checked: boolean }): React.JSX.Element {
return (
<div className="general-settings-panel__switch-wrap extensions-settings-panel__unavailable-control"
data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
<label className="wb-switch wb-switch--large">
<span className="wb-switch__control">
<input className="wb-switch__input" type="checkbox" role="switch" aria-label="经济模式" checked={checked} disabled
data-settings-unavailable="true" />
<span className="wb-switch__track"><span className="wb-switch__thumb" /></span>
</span>
</label>
</div>
)
}
function UnavailableButton({ label, width, arrow = false }: { label: string; width: number; arrow?: boolean }): React.JSX.Element {
return (
<button type="button" className="wb-button wb-button--secondary wb-button--medium extensions-settings-panel__action-button"
style={{ minWidth: width }} aria-disabled="true" title={UNSET_HINT}
onClick={(event) => { event.preventDefault(); event.stopPropagation() }}>
<span className="wb-button__content">{label}</span>
{arrow ? <span className="wb-button__icon"><ExternalArrow /></span> : null}
</button>
)
}
function TeamSelect({ value, open, onToggle, onChoose }: {
value: boolean
open: boolean
onToggle(): void
onChoose(value: boolean): void
}): React.JSX.Element {
return (
<div className="extensions-settings-panel__team-dropdown">
<button type="button" className="wb-select wb-select--medium" aria-label="智能体团队管理" aria-haspopup="listbox"
aria-expanded={open} title="仅保存偏好;多智能体协作执行尚未接入" onClick={onToggle}>
<span className="wb-select__value">{value ? '开启' : '禁用'}</span>
<IconSelectCaret className="wb-icon wb-select__caret" />
</button>
{open ? (
<div className="wb-popover extensions-settings-panel__team-popover" role="listbox" aria-label="智能体团队管理">
{[true, false].map((option) => (
<button type="button" key={String(option)} role="option" aria-selected={option === value}
className={classNames('wb-select__option', option === value && 'wb-select__option--single-selected')}
onClick={() => onChoose(option)}>
<span className="extensions-settings-panel__option-text">{option ? '开启' : '禁用'}</span>
{option === value ? <span className="wb-select__check"><svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"><path d="M3.25 8.4 6.4 11.5 12.75 4.75" /></svg></span> : null}
</button>
))}
</div>
) : null}
</div>
)
}
export default function AgentsSettingsPanel(): React.JSX.Element {
const [view, setView] = useState<AgentSettings | null>(null)
const [error, setError] = useState('')
const [teamOpen, setTeamOpen] = useState(false)
const root = useRef<HTMLDivElement>(null)
useEffect(() => {
let active = true
void window.ycode.agents.get()
.then((next) => { if (active) setView(next) })
.catch((reason) => { if (active) setError(String((reason as Error)?.message ?? reason)) })
return () => { active = false }
}, [])
useEffect(() => {
if (!teamOpen) return
const outside = (event: PointerEvent): void => { if (!root.current?.contains(event.target as Node)) setTeamOpen(false) }
const escape = (event: KeyboardEvent): void => {
if (event.key !== 'Escape') return
event.preventDefault(); event.stopPropagation(); setTeamOpen(false)
}
document.addEventListener('pointerdown', outside, true)
document.addEventListener('keydown', escape, true)
return () => { document.removeEventListener('pointerdown', outside, true); document.removeEventListener('keydown', escape, true) }
}, [teamOpen])
const update = async (teamManagement: boolean): Promise<void> => {
try {
setView(await window.ycode.agents.update({ teamManagement }))
setError('')
} catch (reason) {
setError(String((reason as Error)?.message ?? reason))
}
}
return (
<div ref={root} className="general-settings-panel extensions-settings-panel">
{error ? <p role="alert">{error}</p> : null}
<div className="general-settings-panel__group-card">
<Section title="智能体团队管理"
desc="开启后,复杂任务会自动拆分多个智能体协作完成。专家团队任务不受此设置影响。"
action={<TeamSelect value={view?.teamManagement ?? true} open={teamOpen} onToggle={() => setTeamOpen((open) => !open)}
onChoose={(teamManagement) => { setTeamOpen(false); void update(teamManagement) }} />} />
</div>
<div className="general-settings-panel__group-card">
<Section title="经济模式"
desc="自动在合适时机触发上下文压缩,减少长对话中重复传输的上下文,降低积分消耗"
action={<Switch checked />} />
</div>
<div className="general-settings-panel__group extensions-settings-panel__browser-group">
<p className="general-settings-panel__group-label extensions-settings-panel__browser-label">浏览器操控<span className="extensions-settings-panel__beta">Beta</span></p>
<div className="general-settings-panel__group-card extensions-settings-panel__browser-card">
<Section title="让 YCode 控制浏览器" action={<UnavailableButton label="安装" width={68} />} />
<div className="extensions-settings-panel__browser-list">
{BROWSER_ROWS.map((row) => (
<div key={row.key} className={`extensions-settings-panel__browser-row extensions-settings-panel__browser-row--${row.key}`}>
<img className="wb-icon extensions-settings-panel__browser-icon" src={row.icon} alt="" aria-hidden="true"
width={36} height={36} draggable={false} />
<div className="extensions-settings-panel__browser-copy">
<div className="extensions-settings-panel__browser-title">{row.title}</div>
<div className="extensions-settings-panel__browser-desc">{row.desc}</div>
</div>
<UnavailableButton label="前往安装" width={102} arrow />
</div>
))}
</div>
</div>
</div>
</div>
)
}
@@ -6,6 +6,7 @@ import AppearanceSettingsPanel from './AppearanceSettingsPanel'
import ShortcutsSettingsPanel from './ShortcutsSettingsPanel'
import PersonalizationSettingsPanel from './PersonalizationSettingsPanel'
import AboutHelpSettingsPanel from './AboutHelpSettingsPanel'
import AgentsSettingsPanel from './AgentsSettingsPanel'
import SourceSettingsNavigation from './SourceSettingsNavigation'
import { useSharedTheme } from '../useSharedTheme'
import { SOURCE_WINDOWS_SETTINGS_TABS } from './SourceSettingsNavigation'
@@ -56,7 +57,7 @@ export default function SettingsDialog({ theme, onThemeChange, onClose, nativeWi
return () => window.removeEventListener('keydown', escape)
}, [onClose])
const modal = <section className={`settings-modal${nativeWindow ? ' settings-modal--window' : ''}`} role="dialog" aria-label="设置" aria-modal={!nativeWindow}>
<div className="settings-modal__nav"><SourceSettingsNavigation activeTab={section} supportedTabs={['settings', 'models', 'appearance', 'keyboardShortcuts', 'personalization', 'helpFeedback', 'getHelp']} visibleTabs={VISIBLE_SETTINGS_TABS}
<div className="settings-modal__nav"><SourceSettingsNavigation activeTab={section} supportedTabs={['settings', 'models', 'appearance', 'keyboardShortcuts', 'personalization', 'extensions', 'helpFeedback', 'getHelp']} visibleTabs={VISIBLE_SETTINGS_TABS}
onTabChange={(id) => { setError(''); setSection(id === 'models' ? 'model' : id) }} onUnavailable={() => setError('该设置尚未接入')} /></div>
<div className="settings-modal__content"><div className="settings-modal__panel">
{error && <p role="alert">{error}</p>}
@@ -67,6 +68,7 @@ export default function SettingsDialog({ theme, onThemeChange, onClose, nativeWi
{section === 'keyboardShortcuts' && <ShortcutsSettingsPanel />}
{section === 'personalization' && <PersonalizationSettingsPanel />}
{(section === 'helpFeedback' || section === 'getHelp') && <AboutHelpSettingsPanel section={section} />}
{section === 'extensions' && <AgentsSettingsPanel />}
{section === 'about' && <div className="settings-window-panel"><h2>YCode</h2><p>{settings.appInfo?.version}</p></div>}
</div></div>
</section>
@@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'><defs><linearGradient id='a' x1='3.2173' y1='15' x2='44.7812' y2='15' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#d93025'/><stop offset='1' stop-color='#ea4335'/></linearGradient><linearGradient id='b' x1='20.7219' y1='47.6791' x2='41.5039' y2='11.6837' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#fcc934'/><stop offset='1' stop-color='#fbbc04'/></linearGradient><linearGradient id='c' x1='26.5981' y1='46.5015' x2='5.8161' y2='10.506' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#1e8e3e'/><stop offset='1' stop-color='#34a853'/></linearGradient></defs><circle cx='24' cy='23.9947' r='12' style='fill:#fff'/><path d='M3.2154,36A24,24,0,1,0,12,3.2154,24,24,0,0,0,3.2154,36ZM34.3923,18A12,12,0,1,1,18,13.6077,12,12,0,0,1,34.3923,18Z' style='fill:none'/><path d='M24,12H44.7812a23.9939,23.9939,0,0,0-41.5639.0029L13.6079,30l.0093-.0024A11.9852,11.9852,0,0,1,24,12Z' style='fill:url(#a)'/><circle cx='24' cy='24' r='9.5' style='fill:#1a73e8'/><path d='M34.3913,30.0029,24.0007,48A23.994,23.994,0,0,0,44.78,12.0031H23.9989l-.0025.0093A11.985,11.985,0,0,1,34.3913,30.0029Z' style='fill:url(#b)'/><path d='M13.6086,30.0031,3.218,12.006A23.994,23.994,0,0,0,24.0025,48L34.3931,30.0029l-.0067-.0068a11.9852,11.9852,0,0,1-20.7778.007Z' style='fill:url(#c)'/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'><defs><radialGradient id='b' cx='161.8' cy='68.9' r='95.4' gradientTransform='matrix(1 0 0 -.95 0 248.8)' gradientUnits='userSpaceOnUse'><stop offset='.7' stop-opacity='0'/><stop offset='.9' stop-opacity='.5'/><stop offset='1'/></radialGradient><radialGradient id='d' cx='-340.3' cy='63' r='143.2' gradientTransform='matrix(.15 -.99 -.8 -.12 176.6 -125.4)' gradientUnits='userSpaceOnUse'><stop offset='.8' stop-opacity='0'/><stop offset='.9' stop-opacity='.5'/><stop offset='1'/></radialGradient><radialGradient id='e' cx='113.4' cy='570.2' r='202.4' gradientTransform='matrix(-.04 1 2.13 .08 -1179.5 -106.7)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#35c1f1'/><stop offset='.1' stop-color='#34c1ed'/><stop offset='.2' stop-color='#2fc2df'/><stop offset='.3' stop-color='#2bc3d2'/><stop offset='.7' stop-color='#36c752'/></radialGradient><radialGradient id='f' cx='376.5' cy='568' r='97.3' gradientTransform='matrix(.28 .96 .78 -.23 -303.8 -148.5)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#66eb6e'/><stop offset='1' stop-color='#66eb6e' stop-opacity='0'/></radialGradient><linearGradient id='a' x1='63.3' y1='84' x2='241.7' y2='84' gradientTransform='matrix(1 0 0 -1 0 266)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#0c59a4'/><stop offset='1' stop-color='#114a8b'/></linearGradient><linearGradient id='c' x1='157.3' y1='161.4' x2='46' y2='40.1' gradientTransform='matrix(1 0 0 -1 0 266)' gradientUnits='userSpaceOnUse'><stop offset='0' stop-color='#1b9de2'/><stop offset='.2' stop-color='#1595df'/><stop offset='.7' stop-color='#0680d7'/><stop offset='1' stop-color='#0078d4'/></linearGradient></defs><path d='M235.7 195.5a93.7 93.7 0 0 1-10.6 4.7 101.9 101.9 0 0 1-35.9 6.4c-47.3 0-88.5-32.5-88.5-74.3a31.5 31.5 0 0 1 16.4-27.3c-42.8 1.8-53.8 46.4-53.8 72.5 0 74 68.1 81.4 82.8 81.4 7.9 0 19.8-2.3 27-4.6l1.3-.4a128.3 128.3 0 0 0 66.6-52.8 4 4 0 0 0-5.3-5.6Z' transform='translate(-4.6 -5)' style='fill:url(#a)'/><path d='M235.7 195.5a93.7 93.7 0 0 1-10.6 4.7 101.9 101.9 0 0 1-35.9 6.4c-47.3 0-88.5-32.5-88.5-74.3a31.5 31.5 0 0 1 16.4-27.3c-42.8 1.8-53.8 46.4-53.8 72.5 0 74 68.1 81.4 82.8 81.4 7.9 0 19.8-2.3 27-4.6l1.3-.4a128.3 128.3 0 0 0 66.6-52.8 4 4 0 0 0-5.3-5.6Z' transform='translate(-4.6 -5)' style='isolation:isolate;opacity:.35;fill:url(#b)'/><path d='M110.3 246.3A79.2 79.2 0 0 1 87.6 225a80.7 80.7 0 0 1 29.5-120c3.2-1.5 8.5-4.1 15.6-4a32.4 32.4 0 0 1 25.7 13 31.9 31.9 0 0 1 6.3 18.7c0-.2 24.5-79.6-80-79.6-43.9 0-80 41.6-80 78.2a130.2 130.2 0 0 0 12.1 56 128 128 0 0 0 156.4 67 75.5 75.5 0 0 1-62.8-8Z' transform='translate(-4.6 -5)' style='fill:url(#c)'/><path d='M110.3 246.3A79.2 79.2 0 0 1 87.6 225a80.7 80.7 0 0 1 29.5-120c3.2-1.5 8.5-4.1 15.6-4a32.4 32.4 0 0 1 25.7 13 31.9 31.9 0 0 1 6.3 18.7c0-.2 24.5-79.6-80-79.6-43.9 0-80 41.6-80 78.2a130.2 130.2 0 0 0 12.1 56 128 128 0 0 0 156.4 67 75.5 75.5 0 0 1-62.8-8Z' transform='translate(-4.6 -5)' style='opacity:.41;fill:url(#d);isolation:isolate'/><path d='M157 153.8c-.9 1-3.4 2.5-3.4 5.6 0 2.6 1.7 5.2 4.8 7.3 14.3 10 41.4 8.6 41.5 8.6a59.6 59.6 0 0 0 30.3-8.3 61.4 61.4 0 0 0 30.4-52.9c.3-22.4-8-37.3-11.3-43.9C228 28.8 182.3 5 132.6 5a128 128 0 0 0-128 126.2c.5-36.5 36.8-66 80-66 3.5 0 23.5.3 42 10a72.6 72.6 0 0 1 30.9 29.3c6.1 10.6 7.2 24.1 7.2 29.5s-2.7 13.3-7.8 19.9Z' transform='translate(-4.6 -5)' style='fill:url(#e)'/><path d='M157 153.8c-.9 1-3.4 2.5-3.4 5.6 0 2.6 1.7 5.2 4.8 7.3 14.3 10 41.4 8.6 41.5 8.6a59.6 59.6 0 0 0 30.3-8.3 61.4 61.4 0 0 0 30.4-52.9c.3-22.4-8-37.3-11.3-43.9C228 28.8 182.3 5 132.6 5a128 128 0 0 0-128 126.2c.5-36.5 36.8-66 80-66 3.5 0 23.5.3 42 10a72.6 72.6 0 0 1 30.9 29.3c6.1 10.6 7.2 24.1 7.2 29.5s-2.7 13.3-7.8 19.9Z' transform='translate(-4.6 -5)' style='fill:url(#f)'/></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,7 @@
/* REA light=ev_1cc896e04c0cc9ceab39b0a252f4982c019305609195ad44804ae9b5fb7379a8; dark=ev_3384aa03af8a1ccdffb8e025dbd4a354636d8d6b0c207abd21e12fc5eb2ae719; ExtensionsSettingsPanel-BnsXh3oa.css sha256=cbd4b37c6061381943d96ca454176b2f083e49eb8e04c34cb5d31eef3dbcb8f5; tokens sha256=13f2bf82c96e5167a7d7098e36950c6c22c475b3337dbb8bb29ad2fe79cde696. */
.extensions-settings-panel__browser-extension-status { box-sizing: border-box; min-width: 84px; height: 32px; padding: 0 var(--wb-spacing-4); color: var(--wb-color-text-secondary); font-size: var(--wb-font-body-size); font-weight: 400; line-height: var(--wb-font-body-line-height); white-space: nowrap; cursor: default; align-items: center; display: inline-grid; position: relative; }
.extensions-settings-panel__browser-extension-status::before { content: attr(data-install-label); visibility: hidden; padding-right: 20px; font-weight: 500; }
.extensions-settings-panel__browser-extension-status-content { justify-content: center; align-items: center; gap: var(--wb-spacing-3); display: flex; position: absolute; inset: 0px; }
.extensions-settings-panel__browser-extension-status-dot { background: var(--wb-status-success); border-radius: 50%; flex: 0 0 6px; width: 6px; height: 6px; }
.extensions-settings-panel{--wb-spacing-4:12px;--wb-color-text-secondary:var(--wb-palette-black-70);--wb-font-body-size:var(--wb-font-size-5);--wb-font-body-line-height:var(--wb-font-line-height-5-2);--wb-spacing-3:8px;--wb-status-success:var(--wb-palette-green-8);--wb-palette-black-70:#000000b3;--wb-font-size-5:14px;--wb-font-line-height-5-2:22px;--wb-palette-green-8:#0cbf5b;--wb-color-text-success-default:var(--wb-palette-green-8);--wb-palette-green-6:#47cf84}
[data-theme=dark] .extensions-settings-panel{--wb-spacing-4:12px;--wb-color-text-secondary:#ffffffa6;--wb-font-body-size:var(--wb-font-size-5);--wb-font-body-line-height:var(--wb-font-line-height-5-2);--wb-spacing-3:8px;--wb-status-success:var(--wb-color-text-success-default);--wb-palette-black-70:#000000b3;--wb-font-size-5:14px;--wb-font-line-height-5-2:22px;--wb-palette-green-8:#43f08f;--wb-color-text-success-default:var(--wb-palette-green-6);--wb-palette-green-6:#24c46c}
@@ -0,0 +1,181 @@
/*
* 设置 → 智能体(TODO 15)。
* - source-agents-panels-recovered.css:REA 回收的 ExtensionsSettingsPanel-BnsXh3oa.css 投影。
* - 卡片/section/按钮/开关所需的通用 UI kit 由 GeneralSettingsPanel 已加载的 source-general-settings.css 提供;
* 这里不再重复 @import,避免同一份通用样式表被打包多次改变层叠顺序。
*/
@import './source-agents-panels-recovered.css';
/*
* REA ev_3384aa…(深色):正文 color 为 rgb(242,243,245);原版设置窗口把 --wb-color-text-primary
* 覆盖为 #f2f3f5,比全局 tokens 的 #fff 更具体,这里按同一优先级还原。
*/
:root[data-theme=dark] .general-settings-panel.extensions-settings-panel {
--wb-color-text-primary: #f2f3f5;
}
/* ---- 智能体团队管理下拉 ---- */
.extensions-settings-panel__team-dropdown {
position: relative;
flex-shrink: 0;
}
.extensions-settings-panel__team-dropdown .wb-select {
width: 120px;
min-width: 120px;
height: 32px;
padding: 0 8px;
justify-content: space-between;
gap: 2px;
color: var(--wb-color-text-primary);
background: var(--wb-bg-primary);
border: 1px solid var(--wb-color-border-primary);
border-radius: 8px;
font-family: Arial, system-ui, sans-serif;
font-size: 14px;
line-height: 22px;
cursor: pointer;
display: inline-flex;
align-items: center;
}
:root[data-theme=dark] .extensions-settings-panel__team-dropdown .wb-select {
border-color: var(--wb-color-border-solid);
}
.extensions-settings-panel__team-popover {
position: absolute;
top: calc(100% + 8px);
right: 0;
z-index: 30;
box-sizing: border-box;
width: 120px;
padding: 4px;
background: var(--wb-bg-primary);
border: 1px solid var(--wb-border-default);
border-radius: 12px;
box-shadow: color(srgb 0 0 0 / 0.12) 0 8px 48px -4px, color(srgb 0 0 0 / 0.06) 0 6px 12px -4px;
}
.extensions-settings-panel__team-popover .wb-select__option {
box-sizing: border-box;
width: 100%;
height: 32px;
padding: 0 8px;
color: var(--wb-color-text-primary);
font: inherit;
font-size: 14px;
text-align: left;
background: none;
border: none;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.extensions-settings-panel__team-popover .wb-select__option:hover {
background: var(--wb-bg-hover);
}
.extensions-settings-panel__team-popover .wb-select__check {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
}
/* ---- 浏览器操控(Beta) ---- */
.extensions-settings-panel__browser-label {
display: flex;
align-items: center;
gap: 4px;
}
.extensions-settings-panel__beta {
box-sizing: border-box;
padding: 0 8px;
color: var(--wb-color-text-tertiary);
background: var(--wb-bg-primary);
border: 1px solid var(--wb-border-default);
border-radius: 4px;
font-size: 11px;
line-height: 18px;
font-weight: 400;
}
.extensions-settings-panel__browser-card {
display: flex;
flex-direction: column;
}
.extensions-settings-panel__browser-card > .general-settings-panel__section .extensions-settings-panel__action-button {
width: 68px;
min-width: 68px;
}
.extensions-settings-panel__action-button {
flex-shrink: 0;
}
.extensions-settings-panel__browser-list {
box-sizing: border-box;
width: 100%;
height: 193px;
padding-top: 1px;
overflow: hidden;
border-radius: 8px;
box-shadow: inset 0 0 0 1px var(--wb-border-subtle);
}
.extensions-settings-panel__browser-row {
box-sizing: border-box;
width: 100%;
height: 68px;
display: flex;
align-items: center;
gap: 12px;
}
.extensions-settings-panel__browser-row:not(:last-child) {
border-bottom: 1px solid var(--wb-border-subtle);
}
.extensions-settings-panel__browser-row--qq {
height: 56px;
align-items: flex-start;
padding-top: 12px;
}
.extensions-settings-panel__browser-row--qq .extensions-settings-panel__action-button {
margin-top: 6px;
}
.extensions-settings-panel__browser-icon {
width: 36px;
height: 36px;
flex-shrink: 0;
display: block;
object-fit: contain;
}
.extensions-settings-panel__browser-copy {
flex: 1;
min-width: 0;
}
.extensions-settings-panel__browser-title {
color: var(--wb-color-text-primary);
font-size: 14px;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.extensions-settings-panel__browser-desc {
color: var(--wb-color-text-secondary);
font-size: 14px;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 未接入行为保持原版形态但禁用(与通用页约定一致)。 */
.extensions-settings-panel [data-settings-unavailable=true] { cursor: default }
.extensions-settings-panel [data-settings-unavailable=true] .wb-switch { cursor: default }
.general-settings-panel.extensions-settings-panel .extensions-settings-panel__unavailable-control
.wb-switch:has(.wb-switch__input:checked:disabled) .wb-switch__track {
background-color: #00c29a;
border-color: #00c29a;
}
:root[data-theme=dark] .general-settings-panel.extensions-settings-panel .extensions-settings-panel__unavailable-control
.wb-switch:has(.wb-switch__input:checked:disabled) .wb-switch__track {
background-color: #22cba8;
border-color: #22cba8;
}
+8 -1
View File
@@ -15,7 +15,9 @@ import type {
GeneralSettingsPatch,
GeneralSettingsView,
PersonalizationPatch,
PersonalizationSettings
PersonalizationSettings,
AgentSettings,
AgentSettingsPatch
} from './types'
import type { DownloadPayload, DownloadResult, ResourceEntry, ResourceRange, WorkspaceResource } from './resources'
import type { ChatAttachment, InteractionResponse, MessageQuote, PermissionMode } from './conversation'
@@ -120,6 +122,11 @@ export interface YCodeApi {
get(): Ipc<PersonalizationSettings>
update(patch: PersonalizationPatch): Ipc<PersonalizationSettings>
}
/** 设置 → 智能体:智能体团队管理偏好。 */
agents: {
get(): Ipc<AgentSettings>
update(patch: AgentSettingsPatch): Ipc<AgentSettings>
}
workspace: {
get(): Ipc<WorkspaceInfo>
choose(): Ipc<WorkspaceInfo & { changed: boolean }>
+11
View File
@@ -172,6 +172,17 @@ export interface PersonalizationSettings {
export type PersonalizationPatch = Partial<PersonalizationSettings>
/**
* 设置 → 智能体(TODO 15)。
* 本轮只接入「智能体团队管理」这一个偏好;经济模式与浏览器操控仅按原版布局呈现并明确标注未接入。
*/
export interface AgentSettings {
/** 开启后复杂任务自动拆分多个智能体协作完成(原版默认开启)。 */
teamManagement: boolean
}
export type AgentSettingsPatch = Partial<AgentSettings>
/** 主进程 → 渲染进程的流式事件 */
export type ChatEvent = { sessionId?: string; turnDurationMs?: number } & (
| { runId: string; type: 'started'; messageId: string }