feat: 目的地中文显示名(labels 映射)+ 改名自动同步默认目的地 + 保存校验提示可用目的地

This commit is contained in:
QQ597901027
2026-10-19 14:14:26 +08:00
parent d9cc954f37
commit bde2d48557
3 changed files with 94 additions and 24 deletions
+44 -9
View File
@@ -358,15 +358,23 @@ export function apply(ctx, config) {
}
}
/** Read the workspace's UI-only settings (whitelists); defaults when absent. */
/** Read the workspace's UI-only settings (whitelists + display labels). */
async function readUiSettings(workspace) {
const fallback = { ...WHITELIST_DEFAULTS }
const fallback = { ...WHITELIST_DEFAULTS, labels: {} }
try {
const raw = await readFile(join(workspace, CONFIG_DIR, UI_SETTINGS_FILE), 'utf8')
const parsed = JSON.parse(raw)
const pick = (key) =>
typeof parsed?.[key] === 'string' && parsed[key].trim() ? parsed[key].trim() : fallback[key]
return { allowedPackages: pick('allowedPackages'), allowedTransports: pick('allowedTransports') }
const labels = {}
if (parsed?.labels && typeof parsed.labels === 'object') {
for (const [name, label] of Object.entries(parsed.labels)) {
if (typeof name === 'string' && name && typeof label === 'string' && label.trim()) {
labels[name] = label.trim().slice(0, 80)
}
}
}
return { allowedPackages: pick('allowedPackages'), allowedTransports: pick('allowedTransports'), labels }
} catch {
return fallback
}
@@ -376,6 +384,7 @@ export function apply(ctx, config) {
const payload = {
allowedPackages: values.allowedPackages || WHITELIST_DEFAULTS.allowedPackages,
allowedTransports: values.allowedTransports || WHITELIST_DEFAULTS.allowedTransports,
labels: values.labels && typeof values.labels === 'object' ? values.labels : {},
}
await mkdir(join(workspace, CONFIG_DIR), { recursive: true })
await writeFile(join(workspace, CONFIG_DIR, UI_SETTINGS_FILE), `${JSON.stringify(payload, null, 2)}\n`, 'utf8')
@@ -510,12 +519,21 @@ export function apply(ctx, config) {
const presetInfo = presetInfoOf(sessionId)
const mode = modeFromPolicy(parsed.policy)
const whitelist = await readUiSettings(cwd)
// Display labels are UI-only: attach the Chinese label beside the stable
// destination name so the page/chip can show it without touching the plugin
// schema (which accepts only the known destination keys).
const labels = whitelist.labels || {}
const destinations = normalizeDestinations(parsed.destinations).map((dest) => ({
...dest,
label: labels[dest.name] || '',
}))
return {
workspace: cwd,
workspaces: listWorkspaces(),
file: { path: file, exists, bytes: raw.length },
chip: chipGate(cwd, exists),
destinations: normalizeDestinations(parsed.destinations),
destinations,
labels,
defaultDestination: parsed.defaultDestination || '',
policy: parsed.policy || {},
parser: parsed.parser,
@@ -723,7 +741,10 @@ export function apply(ctx, config) {
defaultDestination !== 'demo' &&
!destinations.some((dest) => dest.name === defaultDestination)
) {
sendJson(res, 400, { ok: false, error: `defaultDestination "${defaultDestination}" is not a destination name` })
sendJson(res, 400, {
ok: false,
error: `defaultDestination "${defaultDestination}" is not a destination name (available: demo, ${destinations.map((dest) => dest.name).join(', ') || '(none)'})`,
})
return
}
const file = existingWorkspaceFile(workspace)
@@ -737,10 +758,23 @@ export function apply(ctx, config) {
}
}
await mkdir(join(workspace, CONFIG_DIR), { recursive: true })
const whitelist = await readUiSettings(workspace)
const text = setModeInText(emitDestinations({ defaultDestination, destinations }), existingMode, whitelist)
const uiSettings = await readUiSettings(workspace)
// Display labels: keep only labels of destinations that still exist.
const names = new Set(destinations.map((dest) => dest.name))
let labels = uiSettings.labels || {}
if (body.labels && typeof body.labels === 'object') {
labels = {}
for (const [name, label] of Object.entries(body.labels)) {
if (typeof name === 'string' && name && typeof label === 'string' && label.trim()) {
labels[name] = label.trim().slice(0, 80)
}
}
}
labels = Object.fromEntries(Object.entries(labels).filter(([name]) => names.has(name)))
await writeUiSettings(workspace, { ...uiSettings, labels })
const text = setModeInText(emitDestinations({ defaultDestination, destinations }), existingMode, uiSettings)
await writeFile(file, text, 'utf8')
sendJson(res, 200, { ok: true, path: file, destinations: destinations.length, mode: existingMode })
sendJson(res, 200, { ok: true, path: file, destinations: destinations.length, mode: existingMode, labels })
return
}
@@ -781,7 +815,8 @@ export function apply(ctx, config) {
}
}
if (body.allowedPackages !== undefined || body.allowedTransports !== undefined) {
await writeUiSettings(workspace, nextWhitelist)
// Preserve display labels when persisting whitelists.
await writeUiSettings(workspace, { ...current, ...nextWhitelist })
}
await writeFile(file, setModeInText(raw, mode, nextWhitelist), 'utf8')
sendJson(res, 200, {