v0.6.0: beautifului 整合 + 会话/工作区/队列/权限/命令/附件全链路

- beautifului.dev 组件直连:Sidebar Nav、Tool Chips、Approval Card(抽屉化)、Streaming/Thinking、Code Block、Selection Actions、Prompt Bar、Search
This commit is contained in:
Rick Ye
2026-09-06 09:57:14 +08:00
parent 3c2e9e96c5
commit a5951849c8
96 changed files with 11828 additions and 4649 deletions
+87
View File
@@ -0,0 +1,87 @@
// 生成应用图标(build/icon.ico + build/icon.png):
// npx electron scripts/make-icons.cjs
// 用 Electron 离屏渲染 build/icon.svg(居中于方形画布、留 9% 边距),
// 输出多尺寸 PNG 并打包为 PNG 内嵌式 ICO(Vista+ 支持)。
const { app, BrowserWindow } = require('electron')
const { readFileSync, writeFileSync } = require('node:fs')
const { join } = require('node:path')
app.commandLine.appendSwitch('disable-gpu')
const SIZES = [16, 24, 32, 48, 64, 128, 256]
const ROOT = join(__dirname, '..')
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
/** PNG 内嵌式 ICO 打包 */
function buildIco(pngs) {
const count = pngs.length
const header = Buffer.alloc(6)
header.writeUInt16LE(0, 0) // reserved
header.writeUInt16LE(1, 2) // type: icon
header.writeUInt16LE(count, 4)
const entries = Buffer.alloc(16 * count)
const blobs = []
let offset = 6 + 16 * count
pngs.forEach((png, i) => {
const size = png.size
entries.writeUInt8(size >= 256 ? 0 : size, i * 16 + 0) // width
entries.writeUInt8(size >= 256 ? 0 : size, i * 16 + 1) // height
entries.writeUInt8(0, i * 16 + 2) // palette
entries.writeUInt8(0, i * 16 + 3) // reserved
entries.writeUInt16LE(1, i * 16 + 4) // planes
entries.writeUInt16LE(32, i * 16 + 6) // bpp
entries.writeUInt32LE(png.data.length, i * 16 + 8)
entries.writeUInt32LE(offset, i * 16 + 12)
offset += png.data.length
blobs.push(png.data)
})
return Buffer.concat([header, entries, ...blobs])
}
async function renderSize(win, svgDataUrl, size) {
await win.loadURL(
'data:text/html,' +
encodeURIComponent(
`<!doctype html><html><body style="margin:0;background:transparent">` +
`<div style="display:flex;align-items:center;justify-content:center;width:${size}px;height:${size}px">` +
`<img src="${svgDataUrl}" style="width:${Math.round(size * 0.86)}px;height:auto;display:block"/>` +
`</div></body></html>`
)
)
await sleep(350)
const image = await win.webContents.capturePage({ x: 0, y: 0, width: size, height: size })
return image.toPNG()
}
async function run() {
const svg = readFileSync(join(ROOT, 'build', 'icon.svg'), 'utf8')
const svgDataUrl = 'data:image/svg+xml;base64,' + Buffer.from(svg, 'utf8').toString('base64')
const win = new BrowserWindow({
width: 256,
height: 256,
show: false,
frame: false,
transparent: true,
webPreferences: { paintWhenInitiallyHidden: true, offscreen: true }
})
const pngs = []
for (const size of SIZES) {
await win.setSize ? win.setSize(size, size) : null
const png = await renderSize(win, svgDataUrl, size)
pngs.push({ size, data: png })
console.log('rendered', size, `${png.length}B`)
}
writeFileSync(join(ROOT, 'build', 'icon.png'), pngs.find((p) => p.size === 256).data)
writeFileSync(join(ROOT, 'build', 'icon.ico'), buildIco(pngs))
console.log('written build/icon.ico & build/icon.png')
app.exit(0)
}
app.whenReady().then(run).catch((err) => {
console.error(err)
app.exit(1)
})