feat: 插件声明式设置体系——registerSettings schema + 标准控件表单 + 一级导航入口开关
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { app, BrowserWindow, dialog, ipcMain, shell } from 'electron'
|
||||
import { mkdirSync } from 'node:fs'
|
||||
import { mkdirSync, readFileSync } from 'node:fs'
|
||||
import { userInfo } from 'node:os'
|
||||
import { AgentService } from './pi/agent-service'
|
||||
import { getPluginHost } from './plugins/plugin-host'
|
||||
@@ -404,6 +404,48 @@ export function registerIpc(): AgentService {
|
||||
if (typeof id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(id)) throw new Error('invalid plugin id')
|
||||
return getPluginHost().readRendererSource(id)
|
||||
})
|
||||
// ---- 插件设置表单(声明式 schema;值持久化与动作回推在 plugin-host) ----
|
||||
ipcMain.handle('plugins:settingsSchemas', () => getPluginHost().settingsSchemasList())
|
||||
ipcMain.handle('plugins:settingsValues', () => getPluginHost().settingsValuesList())
|
||||
ipcMain.handle('plugins:setSettingValue', (_e, id: unknown, key: unknown, value: unknown) => {
|
||||
if (typeof id !== 'string' || typeof key !== 'string' || !key) throw new Error('invalid setting value')
|
||||
getPluginHost().setSettingValue(id, key, value)
|
||||
})
|
||||
ipcMain.handle('plugins:setSettingsNav', (_e, id: unknown, on: unknown) => {
|
||||
if (typeof id !== 'string' || typeof on !== 'boolean') throw new Error('invalid settings nav toggle')
|
||||
return getPluginHost().setSettingsNav(id, on)
|
||||
})
|
||||
ipcMain.handle('plugins:settingsAction', (_e, id: unknown, key: unknown) => {
|
||||
if (typeof id !== 'string' || typeof key !== 'string') throw new Error('invalid settings action')
|
||||
getPluginHost().settingsAction(id, key)
|
||||
})
|
||||
// path/image 控件的系统选择框
|
||||
ipcMain.handle('plugins:pickPath', async (event, mode: unknown) => {
|
||||
if (mode !== 'file' && mode !== 'directory' && mode !== 'image') throw new Error('invalid path mode')
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
const filters =
|
||||
mode === 'image'
|
||||
? [{ name: '图片', extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg', 'ico', 'avif'] }]
|
||||
: undefined
|
||||
const options = { filters }
|
||||
const result =
|
||||
mode === 'directory'
|
||||
? await (win ? dialog.showOpenDialog(win, { ...options, properties: ['openDirectory'] }) : dialog.showOpenDialog({ ...options, properties: ['openDirectory'] }))
|
||||
: await (win ? dialog.showOpenDialog(win, { ...options, properties: ['openFile'] }) : dialog.showOpenDialog({ ...options, properties: ['openFile'] }))
|
||||
return result.canceled || result.filePaths.length === 0 ? null : result.filePaths[0]
|
||||
})
|
||||
// image 控件缩略图:渲染层(dev 是 http 源)不能直接 <img src="file://">,回传 data URL
|
||||
ipcMain.handle('plugins:readImage', (_e, path: unknown) => {
|
||||
if (typeof path !== 'string' || !path || !/\.(png|jpe?g|gif|webp|bmp|svg|ico|avif)$/i.test(path)) return null
|
||||
try {
|
||||
const buf = readFileSync(path)
|
||||
if (buf.length > 5 * 1024 * 1024) return null
|
||||
const ext = path.split('.').pop()!.toLowerCase()
|
||||
return `data:image/${ext === 'jpg' ? 'jpeg' : ext};base64,${buf.toString('base64')}`
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
})
|
||||
ipcMain.handle('plugins:setEnabled', (_e, id: unknown, enabled: unknown) => {
|
||||
if (typeof id !== 'string' || typeof enabled !== 'boolean') throw new Error('invalid plugin toggle')
|
||||
return getPluginHost().setEnabled(id, enabled)
|
||||
|
||||
Reference in New Issue
Block a user