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:
@@ -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)
|
||||
})
|
||||
Reference in New Issue
Block a user