refactor(tui): extract shared frontend helpers (#37868)

This commit is contained in:
Simon Klee 2026-07-20 10:43:02 +02:00 committed by GitHub
commit 0eb71d0fc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 1655 additions and 1614 deletions

View file

@ -9,6 +9,14 @@ import { useArgs } from "./args"
import { useClient } from "./client"
import { RGBA } from "@opentui/core"
import { readJson, writeJsonAtomic } from "../util/persistence"
import {
createModelPreferenceRepository,
cycleModelVariant,
modelPreferenceKey,
normalizeModelVariant,
type ModelPreference,
type ModelPreferenceModel,
} from "../model-preference"
import { useTheme } from "./theme"
import { useToast } from "../ui/toast"
import { useRoute } from "./route"
@ -34,13 +42,13 @@ export function parseModel(model: string) {
}
export function recentModels(
model: { providerID: string; modelID: string },
recent: { providerID: string; modelID: string }[],
model: ModelPreferenceModel,
recent: ModelPreferenceModel[],
) {
const seen = new Set<string>()
return [model, ...recent]
.filter((item) => {
const key = `${item.providerID}/${item.modelID}`
const key = modelPreferenceKey(item)
if (seen.has(key)) return false
seen.add(key)
return true
@ -62,13 +70,13 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const event = useEvent()
const permission = usePermission()
function isModelValid(model: { providerID: string; modelID: string }) {
function isModelValid(model: ModelPreferenceModel) {
return !!data.location.model
.list()
?.some((item) => item.providerID === model.providerID && item.id === model.modelID)
}
function getFirstValidModel(...modelFns: (() => { providerID: string; modelID: string } | undefined)[]) {
function getFirstValidModel(...modelFns: (() => ModelPreferenceModel | undefined)[]) {
for (const modelFn of modelFns) {
const model = modelFn()
if (!model) continue
@ -144,25 +152,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const agent = createAgent()
function createModel() {
const [modelStore, setModelStore] = createStore<{
ready: boolean
model: Record<
string,
{
providerID: string
modelID: string
}
>
recent: {
providerID: string
modelID: string
}[]
favorite: {
providerID: string
modelID: string
}[]
variant: Record<string, string | undefined>
}>({
const [modelStore, setModelStore] = createStore<
ModelPreference & {
ready: boolean
model: Record<string, ModelPreferenceModel>
}
>({
ready: false,
model: {},
recent: [],
@ -170,7 +165,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
variant: {},
})
const filePath = path.join(paths.state, "model.json")
const repository = createModelPreferenceRepository(path.join(paths.state, "model.json"))
const state = {
pending: false,
}
@ -181,21 +176,21 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
return
}
state.pending = false
void writeJsonAtomic(filePath, {
recent: modelStore.recent,
favorite: modelStore.favorite,
variant: modelStore.variant,
})
void repository
.patch({
recent: modelStore.recent,
favorite: modelStore.favorite,
variant: modelStore.variant,
})
.catch(() => undefined)
}
readJson<unknown>(filePath)
.then((x) => {
if (!x || typeof x !== "object") return
const value = x as Record<string, unknown>
if (Array.isArray(value.recent)) setModelStore("recent", value.recent)
if (Array.isArray(value.favorite)) setModelStore("favorite", value.favorite)
if (typeof value.variant === "object" && value.variant !== null)
setModelStore("variant", value.variant as Record<string, string | undefined>)
repository
.load()
.then((value) => {
setModelStore("recent", value.recent)
setModelStore("favorite", value.favorite)
setModelStore("variant", value.variant)
})
.catch(() => {})
.finally(() => {
@ -360,14 +355,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
selected() {
const m = currentModel()
if (!m) return undefined
const key = `${m.providerID}/${m.modelID}`
return modelStore.variant[key] ?? "default"
return normalizeModelVariant(modelStore.variant[modelPreferenceKey(m)])
},
current() {
const v = this.selected()
if (!v) return undefined
if (v !== "default" && this.list().includes(v)) return v
return "default"
if (v && this.list().includes(v)) return v
return undefined
},
list() {
const m = currentModel()
@ -380,24 +373,13 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
set(value: string | undefined) {
const m = currentModel()
if (!m) return
const key = `${m.providerID}/${m.modelID}`
setModelStore("variant", key, value ?? "default")
setModelStore("variant", modelPreferenceKey(m), normalizeModelVariant(value))
save()
},
cycle() {
const variants = this.list()
if (variants.length === 0) return
const current = this.current()
if (!current) {
this.set(variants[0])
return
}
const index = variants.indexOf(current)
if (index === -1 || index === variants.length - 1) {
this.set(variants[0])
return
}
this.set(variants[index + 1])
this.set(cycleModelVariant(this.current(), variants))
},
},
}

View file

@ -1,5 +1,4 @@
import path from "path"
import { abbreviateHome } from "../runtime"
import { formatPath } from "../util/path-format"
import { useLocation } from "./location"
import { useTuiPaths } from "./runtime"
@ -8,17 +7,6 @@ export function usePathFormatter() {
const location = useLocation()
return {
path: () => location.current?.directory || paths.cwd,
format: (input?: string) => formatPath(input, location.current?.directory || paths.cwd, paths.home),
format: (input?: string) => formatPath(input, { base: location.current?.directory || paths.cwd, home: paths.home }),
}
}
function formatPath(input: string | undefined, base: string, home: string) {
if (typeof input !== "string" || !input) return ""
const absolute = path.isAbsolute(input) ? input : path.resolve(base, input)
const relative = path.relative(base, absolute)
if (!relative) return "."
if (relative !== ".." && !relative.startsWith(".." + path.sep)) return relative
return abbreviateHome(absolute, home)
}