feat(settings): replicate the WorkBuddy personalization panel and wire identity, tone and prompts (TODO 13)
Recover the WorkBuddy 5.8.3 个性化 page from REA evidence (PersonalizationPanel-D16rRGjy.css + tokens): 45 module rules plus scoped light/dark tokens, the five-card 1160px layout (identity card 638x100 with inline editors, reply preference card 638x204, three 638x264 long-text cards with 598x160 previews) and the 640x520 editor modal. Persist identity, tone, welcome tips, custom instructions, persona and long-term memory in config.json, expose personalization:get/update over IPC, and append the resulting personalization context to each turn's prompt for both the OMP and built-in kernels. Wire 欢迎语 to the streaming loading tips and keep 显示文件修改过程 visibly disabled because YCode has no incremental file-change renderer yet. Verify with scripts/verify-personalization-settings.cjs: 29 assertions covering geometry, tone options, switches, inline identity editing with blur-discard, editor modal save/cancel, config persistence, reopen, dark surfaces and no renderer errors. Add a pure prompt-builder regression and keep every existing settings regression green.
This commit is contained in:
1 parent
19460f0b13
commit
0dc088f1f7
54 files changed
+2038
-248
No files matched your search
+14
-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 } from '@shared/types'
|
||||
import type { GatewayConfig, GatewayConfigView, PersonalizationSettings } from '@shared/types'
|
||||
|
||||
interface StoredConfig {
|
||||
speechBaseUrl?: string
|
||||
@@ -26,6 +26,8 @@ interface StoredConfig {
|
||||
preventSleep?: boolean
|
||||
/** 开机自动启动(通用设置;默认关闭)。 */
|
||||
autoLaunch?: boolean
|
||||
/** 设置 → 个性化(身份、回复风格、指令、人设、长期记忆)。 */
|
||||
personalization?: Partial<PersonalizationSettings>
|
||||
}
|
||||
|
||||
const DEFAULTS: StoredConfig = {
|
||||
@@ -118,6 +120,17 @@ export async function setAutoLaunch(enabled: boolean): Promise<void> {
|
||||
await persist({ ...stored, autoLaunch: enabled })
|
||||
}
|
||||
|
||||
/** 设置 → 个性化的原始持久化值(缺省字段由 personalization.ts 补齐)。 */
|
||||
export async function getStoredPersonalization(): Promise<Partial<PersonalizationSettings>> {
|
||||
const stored = await load()
|
||||
return stored.personalization ?? {}
|
||||
}
|
||||
|
||||
export async function setStoredPersonalization(value: Partial<PersonalizationSettings>): Promise<void> {
|
||||
const stored = await load()
|
||||
await persist({ ...stored, personalization: { ...(stored.personalization ?? {}), ...value } })
|
||||
}
|
||||
|
||||
/** 供渲染进程使用:脱敏视图 */
|
||||
export async function getConfigView(): Promise<GatewayConfigView> {
|
||||
const stored = await load()
|
||||
|
||||
+7
-2
@@ -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, WindowState } from '@shared/types'
|
||||
import type { 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'
|
||||
@@ -34,6 +34,7 @@ import { assistantWireHistory } from './agent/history'
|
||||
import * as models from './models'
|
||||
import * as workspaces from './workspaces'
|
||||
import * as generalSettings from './general-settings'
|
||||
import * as personalization from './personalization'
|
||||
import { prepareOmpModel } from './omp-model'
|
||||
import type { CustomModelInput, ResolvedModel } from '@shared/models'
|
||||
import type { SessionContext } from '@shared/workspaces'
|
||||
@@ -408,6 +409,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('general:update', async (_event, patch: { autoLaunch?: boolean; preventSleep?: boolean }) => {
|
||||
if (patch.preventSleep !== undefined) {
|
||||
await config.setPreventSleep(patch.preventSleep)
|
||||
@@ -740,7 +743,9 @@ export function registerIpc(getWindow: () => BrowserWindow | null): void {
|
||||
const interactionBroker = createInteractionBroker(runId, controller.signal, emit)
|
||||
// 对话中引用的内容块(原版 message-quote)随消息一起交给模型;表格 markdown 原样附带。
|
||||
const quoteText = (payload.quotes ?? []).map((quote) => quote.markdown.trim()).filter(Boolean).join('\n\n')
|
||||
const promptText = [text, quoteText, promptAttachments.text, references.length ? `引用工作区资源:\n${references.join('\n')}` : '', capabilitySnapshot.context].filter(Boolean).join('\n\n')
|
||||
// 设置 → 个性化的身份/风格/指令/人设/长期记忆随每轮请求发送,OMP 与自建循环共用同一段文本。
|
||||
const personalizationPrompt = personalization.buildPersonalizationPrompt(await personalization.getPersonalization())
|
||||
const promptText = [text, quoteText, promptAttachments.text, references.length ? `引用工作区资源:\n${references.join('\n')}` : '', capabilitySnapshot.context, personalizationPrompt].filter(Boolean).join('\n\n')
|
||||
|
||||
// 回合在后台推进,立刻把 runId 还给渲染进程
|
||||
const initialMessages = payload.action ? structuredClone([...history, userMessage, assistant]) : undefined
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 设置 → 个性化(TODO 13)的持久化与运行链路。
|
||||
*
|
||||
* REA 依据(WorkBuddy 5.8.3,设置窗口 900×680):
|
||||
* - ev_af920edf33af5e811d58f05dad15210b29cd891f28e0fa74f6b18d63212241b1(浅色顶部 DOM/layout/computed style + 模块 CSS)
|
||||
* - ev_88af0774005c02e9c75284ade2be45de4943ee5aaf73fe5b13ac8a38b85be786(深色同页)
|
||||
* - ev_4f4f76054a930a186199ee28a5f0169f7de48a05a295b8e9eb630c796f07be4a(最终浅色顶部)
|
||||
*
|
||||
* 选项表、默认人设与个性化上下文拼装放在 @shared/personalization(纯函数,可离线回归);
|
||||
* 本模块只负责读写 config.json 的 personalization 字段。
|
||||
*/
|
||||
import type { PersonalizationPatch, PersonalizationSettings } from '@shared/types'
|
||||
import { normalizePersonalization } from '@shared/personalization'
|
||||
import { getStoredPersonalization, setStoredPersonalization } from './config'
|
||||
|
||||
export { buildPersonalizationPrompt, DEFAULT_PERSONA, DEFAULT_PERSONALIZATION, TONE_OPTIONS } from '@shared/personalization'
|
||||
export { normalizePersonalization } from '@shared/personalization'
|
||||
|
||||
export async function getPersonalization(): Promise<PersonalizationSettings> {
|
||||
return normalizePersonalization(await getStoredPersonalization())
|
||||
}
|
||||
|
||||
export async function updatePersonalization(patch: PersonalizationPatch): Promise<PersonalizationSettings> {
|
||||
const current = await getPersonalization()
|
||||
const next = normalizePersonalization({ ...current, ...patch })
|
||||
await setStoredPersonalization(next)
|
||||
return next
|
||||
}
|
||||
Reference in new issue
Block a user