feat(host): 导出 schemastery Config -> DSH 设置页出现悬浮窗配置卡片;实测 profile config 经 apply(ctx,config) 下发到浮窗;README 补设置入口说明

This commit is contained in:
2026-09-27 14:52:08 +08:00
parent c3ca013d20
commit 8b74c2d2b4
3 changed files with 77 additions and 1 deletions
+49 -1
View File
@@ -14,6 +14,7 @@
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { createRequire } from 'node:module'
import { randomUUID } from 'node:crypto'
import { fileURLToPath } from 'node:url'
@@ -28,6 +29,46 @@ function dshHome() {
return process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
}
// ---------------------------------------------------------------- 配置 schema
// 0.1.7 的设置模型是"编辑插件自身的 Config(profile 条目配置)",所以这里导出一个
// schemastery schema,DSH 的「设置 → 插件」就能渲染出悬浮窗的配置卡片。
// 难点:插件用 link: 安装时自身目录没有 node_modules,裸 import 会失败 ——
// 用 createRequire 以"宿主入口 / profile 目录"为基准去解析 schemastery。
function loadSchemastery() {
const tries = [
() => process.argv[1] && createRequire(process.argv[1]),
() => createRequire(path.join(dshHome(), 'profiles', 'desktop', 'package.json')),
() => createRequire(path.join(dshHome(), 'profiles', 'web', 'package.json')),
]
for (const make of tries) {
try {
const req = make()
if (!req) continue
const m = req('@deepseek-ai/schemastery')
const z = m?.default || m
if (z?.object) return z
} catch { }
}
return null
}
const z = loadSchemastery()
const floatSchema = z
? z.object({
topmost: z.boolean().default(true).description('始终置顶'),
posLock: z.boolean().default(false).description('固定位置(图钉)'),
sizeLock: z.boolean().default(false).description('锁定尺寸(小锁)'),
exclude: z.boolean().default(true).description('抓屏时隐藏本窗'),
opacity: z.number().min(0.3).max(1).default(0.92).description('不透明度(毛玻璃时是霜化浓度)'),
scale: z.number().min(0.8).max(1.5).default(1).description('界面缩放'),
backdrop: z.union(['opaque', 'translucent', 'acrylic', 'mica', 'blur']).default('opaque').description('背景效果'),
})
: undefined
/** 浮窗设置项(DSH 设置页写进 profile 条目 config,这里读取并下发给浮窗) */
export const Config = floatSchema
/** 从 content 数组/字符串里取纯文本 */
function textOf(content) {
if (typeof content === 'string') return content
@@ -103,7 +144,13 @@ function sanitize(v, depth = 0) {
const SAMPLE_TYPES = new Set(['user/message', 'assistant/chunk', 'assistant/message', 'tool/call', 'tool/result', 'session/title', 'request/header', 'request/context'])
export function apply(ctx) {
export function apply(ctx, config) {
// 浮窗设置:DSH 设置页写进 profile 条目 config 的值 + 默认值
const floatCfg = Object.assign(
{ topmost: true, posLock: false, sizeLock: false, exclude: true, opacity: 0.92, scale: 1, backdrop: 'opaque' },
(config && typeof config === 'object') ? config : {},
)
// 运行状态与自检日志落盘:插件跑在 DSH 内部,控制台不一定看得到,
// 有了这个文件(和 /health)才能在用户机器上远程排障。
const notes = []
@@ -297,6 +344,7 @@ export function apply(ctx) {
title: (id && titles.get(id)) || session?.meta?.title || session?.title || null,
messages: (id && rows.get(id)) || [],
metrics: session ? metricsFor(session) : { turns: null, steps: null, tps: null, tokens: null, cacheHit: null, contextUsed: null },
settings: floatCfg,
sessions: sessions.slice(-10).map((s) => ({ id: s?.id, title: s?.meta?.title || s?.title || null })),
}
}