feat: complete WorkBuddy parity and Windows release workflow
This commit is contained in:
1 parent
143516b3a8
commit
d5512df549
434 files changed
+54977
-1854
No files matched your search
@@ -1,10 +1,6 @@
|
||||
// TODO 15 验收:设置 → 智能体的 1:1 布局 + 团队管理偏好真实持久化 + 未接入项标注 + 深浅主题。
|
||||
// 真实隔离 YCode(真实 preload/IPC/渲染进程/文件系统)+ 原生设置窗口,视口 900×680(与原版一致)。
|
||||
//
|
||||
// 原版依据(REA ev_bd0f4b…/ev_3384aa…/ev_01940b…/ev_1cc896…):
|
||||
// 页面根 [232,40,638,503];团队管理卡 638×90(section 606×66、下拉 120×32 @734,69);
|
||||
// 经济模式卡 638×90(开关 34×20 @820,181);浏览器操控标签 638×22 + Beta 39.391×20 @314,253;
|
||||
// 浏览器卡片 638×261(主 section 606×44、安装 68×32 @786,294、列表 606×193、行 68/68/56、按钮 102×32 @752)。
|
||||
// TODO 15 验收:智能体团队管理布局、真实持久化和深浅主题。
|
||||
// 2026-10-18 用户要求移除经济模式与浏览器操控,页面仅保留 638x90 团队管理卡。
|
||||
// 真实隔离 YCode(preload/IPC/渲染进程/文件系统)+ 原生设置窗口,视口 900x680。
|
||||
const fs = require('node:fs/promises')
|
||||
const path = require('node:path')
|
||||
const assert = require('node:assert/strict')
|
||||
@@ -79,50 +75,22 @@ if (!process.versions.electron) {
|
||||
const rect = node => { if (!node) return null; const r = node.getBoundingClientRect()
|
||||
return { x: round(r.x), y: round(r.y), width: round(r.width), height: round(r.height) } }
|
||||
const panel = document.querySelector('.extensions-settings-panel')
|
||||
const cards = [...document.querySelectorAll('.extensions-settings-panel > .general-settings-panel__group-card')]
|
||||
const sections = [...document.querySelectorAll('.extensions-settings-panel .general-settings-panel__group-card > .general-settings-panel__section')]
|
||||
const buttons = [...document.querySelectorAll('.extensions-settings-panel__action-button')]
|
||||
const rows = [...document.querySelectorAll('.extensions-settings-panel__browser-row')]
|
||||
const select = document.querySelector('.extensions-settings-panel__team-dropdown .wb-select')
|
||||
const switchInput = document.querySelector('#agents-economy-switch, .extensions-settings-panel .wb-switch__input')
|
||||
const cards = [...panel.querySelectorAll(':scope > .general-settings-panel__group-card')]
|
||||
const sections = [...panel.querySelectorAll('.general-settings-panel__group-card > .general-settings-panel__section')]
|
||||
const select = panel.querySelector('.extensions-settings-panel__team-dropdown .wb-select')
|
||||
return {
|
||||
panel: rect(panel),
|
||||
cards: cards.map(card => rect(card)),
|
||||
sections: sections.map(section => ({
|
||||
rect: rect(section),
|
||||
panel: rect(panel), cards: cards.map(card => rect(card)),
|
||||
sections: sections.map(section => ({ rect: rect(section),
|
||||
title: section.querySelector('.general-settings-panel__section-title')?.textContent?.trim(),
|
||||
desc: section.querySelector('.general-settings-panel__section-desc')?.textContent?.trim()
|
||||
})),
|
||||
desc: section.querySelector('.general-settings-panel__section-desc')?.textContent?.trim() })),
|
||||
select: select ? { rect: rect(select), value: select.querySelector('.wb-select__value')?.textContent?.trim(), title: select.getAttribute('title') } : null,
|
||||
switch: switchInput ? (() => { const track = switchInput.closest('.wb-switch')?.querySelector('.wb-switch__track')
|
||||
return { rect: rect(track), trackBackground: track ? getComputedStyle(track).backgroundColor : null,
|
||||
trackOpacity: track ? getComputedStyle(track).opacity : null, thumb: rect(track?.querySelector('.wb-switch__thumb')),
|
||||
checked: switchInput.checked, disabled: switchInput.disabled,
|
||||
unavailable: switchInput.closest('.extensions-settings-panel__unavailable-control')?.getAttribute('title') } })() : null,
|
||||
browserLabel: (() => { const node = document.querySelector('.extensions-settings-panel__browser-label'); return node ? { ...rect(node), text: node.firstChild?.textContent?.trim() } : null })(),
|
||||
beta: (() => { const node = document.querySelector('.extensions-settings-panel__beta'); return node ? { ...rect(node), text: node.textContent.trim() } : null })(),
|
||||
browserCard: rect(document.querySelector('.extensions-settings-panel__browser-card')),
|
||||
browserList: rect(document.querySelector('.extensions-settings-panel__browser-list')),
|
||||
rows: rows.map(row => ({
|
||||
rect: rect(row),
|
||||
title: row.querySelector('.extensions-settings-panel__browser-title')?.textContent?.trim(),
|
||||
desc: row.querySelector('.extensions-settings-panel__browser-desc')?.textContent?.trim(),
|
||||
icon: (() => { const node = row.querySelector('.extensions-settings-panel__browser-icon')
|
||||
return node ? { ...rect(node), tagName: node.tagName, src: node.getAttribute('src'), placeholderTitle: node.getAttribute('title'),
|
||||
complete: node.complete === true, naturalWidth: node.naturalWidth, naturalHeight: node.naturalHeight } : null })(),
|
||||
button: (() => { const node = row.querySelector('.extensions-settings-panel__action-button')
|
||||
return node ? { rect: rect(node), text: node.textContent.trim(), ariaDisabled: node.getAttribute('aria-disabled'), title: node.getAttribute('title'), arrow: !!node.querySelector('.wb-button__icon svg') } : null })()
|
||||
})),
|
||||
buttons: buttons.map(button => ({ text: button.textContent.trim(), rect: rect(button), ariaDisabled: button.getAttribute('aria-disabled'), title: button.getAttribute('title') })),
|
||||
anchors: [...document.querySelectorAll('a')].map(anchor => anchor.getAttribute('href')).filter(Boolean),
|
||||
obsoleteControls: panel.querySelectorAll('.wb-switch, .extensions-settings-panel__browser-group, .extensions-settings-panel__action-button').length,
|
||||
obsoleteText: ['经济模式', '浏览器操控', '扩展程序', '前往安装'].filter(text => panel.textContent.includes(text)),
|
||||
cardBackground: cards[0] ? getComputedStyle(cards[0]).backgroundColor : null,
|
||||
titleColor: (() => { const node = document.querySelector('.extensions-settings-panel .general-settings-panel__section-title'); return node ? getComputedStyle(node).color : null })(),
|
||||
descColor: (() => { const node = document.querySelector('.extensions-settings-panel .general-settings-panel__section-desc'); return node ? getComputedStyle(node).color : null })(),
|
||||
titleColor: (() => { const node = panel.querySelector('.general-settings-panel__section-title'); return node ? getComputedStyle(node).color : null })(),
|
||||
descColor: (() => { const node = panel.querySelector('.general-settings-panel__section-desc'); return node ? getComputedStyle(node).color : null })(),
|
||||
selectBackground: select ? getComputedStyle(select).backgroundColor : null,
|
||||
selectBorder: select ? getComputedStyle(select).borderTopColor : null,
|
||||
buttonBackground: buttons[0] ? getComputedStyle(buttons[0]).backgroundColor : null,
|
||||
buttonBorder: buttons[0] ? getComputedStyle(buttons[0]).borderTopColor : null,
|
||||
betaBackground: (() => { const node = document.querySelector('.extensions-settings-panel__beta'); return node ? getComputedStyle(node).backgroundColor : null })(),
|
||||
theme: document.documentElement.dataset.theme ?? 'light'
|
||||
}})()`
|
||||
|
||||
@@ -146,45 +114,21 @@ if (!process.versions.electron) {
|
||||
const light = await read(layoutSource, settings)
|
||||
snapshots.light = light
|
||||
await fs.writeFile(path.join(directory, 'ycode-agents-light.png'), (await settings.webContents.capturePage()).toPNG())
|
||||
check('agents panel keeps the recovered 638x503 box at 232,40',
|
||||
[near(light.panel.x, 232), near(light.panel.y, 40), near(light.panel.width, 638), near(light.panel.height, 503)], [true, true, true, true])
|
||||
check('team-management and economy cards keep 638x90 with 606x66 sections',
|
||||
[light.cards.slice(0, 2).map(card => [near(card.width, 638), near(card.height, 90)].every(Boolean)),
|
||||
light.sections.slice(0, 2).map(section => [near(section.rect.width, 606), near(section.rect.height, 66)].every(Boolean)),
|
||||
near(light.cards[0].y, 40), near(light.cards[1].y, 146)], [[true, true], [true, true], true, true])
|
||||
check('team-management keeps the recovered title, description and 120x32 select',
|
||||
check('agents panel only occupies the 638x90 team-management card at 232,40',
|
||||
[near(light.panel.x, 232), near(light.panel.y, 40), near(light.panel.width, 638), near(light.panel.height, 90)], [true, true, true, true])
|
||||
check('team-management keeps one 638x90 card with its 606x66 section',
|
||||
[light.cards.length, near(light.cards[0].width, 638), near(light.cards[0].height, 90),
|
||||
near(light.sections[0].rect.width, 606), near(light.sections[0].rect.height, 66), near(light.cards[0].y, 40)],
|
||||
[1, true, true, true, true, true])
|
||||
check('team-management reflects the active scheduler rules with a 120x32 select',
|
||||
[light.sections[0].title, light.sections[0].desc,
|
||||
near(light.select.rect.x, 734), near(light.select.rect.y, 69), near(light.select.rect.width, 120), near(light.select.rect.height, 32),
|
||||
light.select.value, light.select.title],
|
||||
['智能体团队管理', '开启后,复杂任务会自动拆分多个智能体协作完成。专家团队任务不受此设置影响。',
|
||||
true, true, true, true, '开启', '仅保存偏好;多智能体协作执行尚未接入'])
|
||||
check('economy mode keeps the recovered copy, 34x20 switch and disabled annotation',
|
||||
[light.sections[1].title, light.sections[1].desc,
|
||||
near(light.switch.rect.x, 820), near(light.switch.rect.y, 181), near(light.switch.rect.width, 34), near(light.switch.rect.height, 20),
|
||||
light.switch.trackBackground, light.switch.checked, light.switch.disabled, light.switch.unavailable],
|
||||
['经济模式', '自动在合适时机触发上下文压缩,减少长对话中重复传输的上下文,降低积分消耗',
|
||||
true, true, true, true, 'rgb(0, 194, 154)', true, true, '暂未接入'])
|
||||
check('browser group keeps the recovered label 638x22 and Beta badge',
|
||||
[near(light.browserLabel.x, 232), near(light.browserLabel.y, 252), near(light.browserLabel.width, 638), near(light.browserLabel.height, 22),
|
||||
light.browserLabel.text, near(light.beta.x, 314), near(light.beta.y, 253), near(light.beta.width, 39.391), near(light.beta.height, 20), light.beta.text],
|
||||
[true, true, true, true, '浏览器操控', true, true, true, true, 'Beta'])
|
||||
check('browser card keeps 638x261 with 606x44 main section and 68x32 install button',
|
||||
[near(light.browserCard.y, 282), near(light.browserCard.width, 638), near(light.browserCard.height, 261),
|
||||
near(light.sections[2].rect.y, 294), near(light.sections[2].rect.width, 606), near(light.sections[2].rect.height, 44),
|
||||
light.sections[2].title, near(light.buttons[0].rect.x, 786), near(light.buttons[0].rect.width, 68), near(light.buttons[0].rect.height, 32),
|
||||
light.buttons[0].ariaDisabled, light.buttons[0].title],
|
||||
[true, true, true, true, true, true, '让 YCode 控制浏览器', true, true, true, 'true', '尚未配置'])
|
||||
check('browser list keeps the recovered 606x193 with 68/68/56 rows',
|
||||
[near(light.browserList.y, 338), near(light.browserList.width, 606), near(light.browserList.height, 193),
|
||||
light.rows.map(row => near(row.rect.height, row.title.includes('QQ') ? 56 : 68)),
|
||||
light.rows.every(row => row.icon && row.icon.tagName === 'IMG' && near(row.icon.width, 36) && near(row.icon.height, 36)
|
||||
&& row.icon.complete && row.icon.naturalWidth > 0 && row.icon.naturalHeight > 0 && row.icon.src && row.icon.placeholderTitle === null)],
|
||||
[true, true, true, [true, true, true], true])
|
||||
check('browser rows keep the recovered titles, descriptions and disabled 102x32 actions',
|
||||
[light.rows.map(row => row.title),
|
||||
light.rows.every(row => row.button && near(row.button.rect.width, 102) && near(row.button.rect.height, 32) && row.button.text === '前往安装' && row.button.ariaDisabled === 'true' && row.button.title === '尚未配置' && row.button.arrow)],
|
||||
[['YCode Chrome 扩展程序', 'YCode Edge 扩展程序', 'YCode QQ 浏览器扩展程序'], true])
|
||||
check('agents page contains no anchor links', light.anchors, [])
|
||||
['智能体团队管理', '开启后,复杂任务可由多个智能体协作完成。关闭后取消子任务,主对话继续。',
|
||||
true, true, true, true, '开启', null])
|
||||
check('economy mode and browser controls are fully removed',
|
||||
[light.sections.map(section => section.title), light.obsoleteControls, light.obsoleteText],
|
||||
[['智能体团队管理'], 0, []])
|
||||
|
||||
await step('agents-team-toggle')
|
||||
await read(`document.querySelector('.extensions-settings-panel__team-dropdown .wb-select').click()`, settings)
|
||||
@@ -215,13 +159,13 @@ if (!process.versions.electron) {
|
||||
const dark = await read(layoutSource, settings)
|
||||
snapshots.dark = dark
|
||||
await fs.writeFile(path.join(directory, 'ycode-agents-dark.png'), (await settings.webContents.capturePage()).toPNG())
|
||||
check('dark theme keeps the recovered card, text, select and button surfaces',
|
||||
[dark.cardBackground, dark.titleColor, dark.descColor, dark.selectBackground, dark.selectBorder, dark.buttonBackground, dark.buttonBorder, dark.betaBackground],
|
||||
['rgb(31, 31, 31)', 'rgb(242, 243, 245)', 'rgba(255, 255, 255, 0.5)', 'rgb(31, 31, 31)', 'rgb(61, 61, 61)', 'rgb(38, 38, 38)', 'rgb(61, 61, 61)', 'rgb(31, 31, 31)'])
|
||||
check('dark theme keeps every recovered geometry',
|
||||
[near(dark.panel.width, 638), near(dark.panel.height, 503), near(dark.cards[0].height, 90),
|
||||
near(dark.browserCard.height, 261), near(dark.browserList.height, 193)], [true, true, true, true, true])
|
||||
check('dark economy switch keeps the recovered enabled brand track', dark.switch.trackBackground, 'rgb(34, 203, 168)')
|
||||
check('dark theme keeps the recovered card, text and select surfaces',
|
||||
[dark.cardBackground, dark.titleColor, dark.descColor, dark.selectBackground, dark.selectBorder],
|
||||
['rgb(31, 31, 31)', 'rgb(242, 243, 245)', 'rgba(255, 255, 255, 0.5)', 'rgb(31, 31, 31)', 'rgb(61, 61, 61)'])
|
||||
check('dark theme only occupies the team-management card with removed controls absent',
|
||||
[near(dark.panel.width, 638), near(dark.panel.height, 90), near(dark.cards[0].height, 90),
|
||||
dark.sections.map(section => section.title), dark.obsoleteControls, dark.obsoleteText],
|
||||
[true, true, true, ['智能体团队管理'], 0, []])
|
||||
await openSettings('appearance')
|
||||
await wait('!!document.querySelector(".appearance-card")', 'appearance panel again', settings)
|
||||
await read(`document.querySelector('[aria-label="浅色"].appearance-card').click()`, settings)
|
||||
|
||||
Reference in new issue
Block a user