carry over legacy custom tui config

split-config-fixes
Sebastian Herrlinger 2026-02-26 22:43:02 +01:00
parent 5817d1094f
commit a295e66b54
3 changed files with 17 additions and 28 deletions

View File

@ -3,7 +3,7 @@ import { type ParseError as JsoncParseError, applyEdits, modify, parse as parseJ
import { mergeDeep, unique } from "remeda"
import z from "zod"
import { ConfigPaths } from "./paths"
import { TuiInfo, TuiOptions } from "./tui-schema"
import { TuiInfo } from "./tui-schema"
import { Instance } from "@/project/instance"
import { Flag } from "@/flag/flag"
import { Log } from "@/util/log"
@ -17,14 +17,6 @@ const TUI_SCHEMA_URL = "https://opencode.ai/tui.json"
const LegacyTheme = TuiInfo.shape.theme.optional()
const LegacyRecord = z.record(z.string(), z.unknown()).optional()
const TuiLegacy = z
.object({
scroll_speed: TuiOptions.shape.scroll_speed.catch(undefined),
scroll_acceleration: TuiOptions.shape.scroll_acceleration.catch(undefined),
diff_style: TuiOptions.shape.diff_style.catch(undefined),
})
.strip()
interface MigrateInput {
directories: string[]
custom?: string
@ -103,12 +95,11 @@ async function parseLegacyFile(file: string) {
const theme = LegacyTheme.safeParse("theme" in data ? data.theme : undefined)
const keybinds = LegacyRecord.safeParse("keybinds" in data ? data.keybinds : undefined)
const legacyTui = LegacyRecord.safeParse("tui" in data ? data.tui : undefined)
const tui = legacyTui.success && legacyTui.data ? normalizeTui(legacyTui.data) : undefined
const legacy: Record<string, unknown> = {}
if (theme.success && theme.data !== undefined) legacy.theme = theme.data
if (keybinds.success && keybinds.data !== undefined) legacy.keybinds = keybinds.data
if (tui) Object.assign(legacy, tui)
if (legacyTui.success && legacyTui.data) Object.assign(legacy, legacyTui.data)
if (!Object.keys(legacy).length) return
return {
@ -118,19 +109,6 @@ async function parseLegacyFile(file: string) {
}
}
function normalizeTui(data: Record<string, unknown>) {
const parsed = TuiLegacy.safeParse(data)
if (!parsed.success) return
if (
parsed.data.scroll_speed === undefined &&
parsed.data.diff_style === undefined &&
parsed.data.scroll_acceleration === undefined
) {
return
}
return parsed.data
}
async function backupAndStripLegacy(file: string, source: string) {
const backup = file + ".tui-migration.bak"
const hasBackup = await Filesystem.exists(backup)

View File

@ -107,7 +107,7 @@ export namespace TuiConfig {
}
})()
const parsed = Info.safeParse(normalized)
const parsed = Info.strip().safeParse(normalized)
if (!parsed.success) {
log.warn("invalid tui config", { path: configFilepath, issues: parsed.error.issues })
return {}

View File

@ -87,7 +87,11 @@ test("merges legacy tui keys from opencode.jsonc and opencode.json before migrat
path.join(dir, "opencode.jsonc"),
`{
"theme": "from-jsonc",
"tui": { "scroll_speed": 2 },
"tui": {
"scroll_speed": 2,
"legacy_alpha": 1,
"legacy_nested": { "from_jsonc": true }
},
"keybinds": { "app_exit": "ctrl+q" }
}`,
)
@ -97,6 +101,10 @@ test("merges legacy tui keys from opencode.jsonc and opencode.json before migrat
{
theme: "from-json",
keybinds: { theme_list: "ctrl+k" },
tui: {
legacy_beta: 2,
legacy_nested: { from_json: true },
},
},
null,
2,
@ -118,6 +126,9 @@ test("merges legacy tui keys from opencode.jsonc and opencode.json before migrat
expect(JSON.parse(text)).toMatchObject({
theme: "from-json",
scroll_speed: 2,
legacy_alpha: 1,
legacy_beta: 2,
legacy_nested: { from_jsonc: true, from_json: true },
keybinds: { app_exit: "ctrl+q", theme_list: "ctrl+k" },
})
@ -169,7 +180,7 @@ test("migrates project legacy tui keys even when global tui.json already exists"
})
})
test("drops unknown legacy tui keys during migration", async () => {
test("preserves unknown legacy tui keys during migration", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
@ -196,7 +207,7 @@ test("drops unknown legacy tui keys during migration", async () => {
const text = await Filesystem.readText(path.join(tmp.path, "tui.json"))
const migrated = JSON.parse(text)
expect(migrated.scroll_speed).toBe(2)
expect(migrated.foo).toBeUndefined()
expect(migrated.foo).toBe(1)
},
})
})