feat(settings): replicate the WorkBuddy agents panel and persist team-management preference (TODO 15)
This commit is contained in:
1 parent
b3be45715e
commit
143516b3a8
33 files changed
+1071
-21
No files matched your search
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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 }>
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in new issue
Block a user