From 567d6ed1a3cf6f06a81ac30e746c8c5ba3fce735 Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 11 Jun 2026 11:01:20 -0400 Subject: [PATCH] fix(tui): restore legacy sync consumers (#31908) --- packages/tui/src/component/dialog-model.tsx | 95 ++++++++++--------- .../src/component/dialog-workspace-create.tsx | 2 +- .../src/component/dialog-workspace-list.tsx | 2 +- .../tui/src/component/prompt/autocomplete.tsx | 23 +++-- packages/tui/src/component/use-connected.tsx | 10 +- packages/tui/src/context/local.tsx | 45 ++++----- .../tui/src/feature-plugins/home/footer.tsx | 9 +- .../src/routes/home/session-destination.tsx | 8 +- 8 files changed, 97 insertions(+), 97 deletions(-) diff --git a/packages/tui/src/component/dialog-model.tsx b/packages/tui/src/component/dialog-model.tsx index 1cfd8586c6..ff8715ef85 100644 --- a/packages/tui/src/component/dialog-model.tsx +++ b/packages/tui/src/component/dialog-model.tsx @@ -1,17 +1,17 @@ import { createMemo, createSignal } from "solid-js" import { useLocal } from "../context/local" -import { map, pipe, filter, sortBy, take } from "remeda" +import { map, pipe, flatMap, entries, filter, sortBy, take } from "remeda" import { DialogSelect } from "../ui/dialog-select" import { useDialog } from "../ui/dialog" import { createDialogProviderOptions, DialogProvider } from "./dialog-provider" import { DialogVariant } from "./dialog-variant" import * as fuzzysort from "fuzzysort" import { useConnected } from "./use-connected" -import { useData } from "../context/data" +import { useSync } from "../context/sync" export function DialogModel(props: { providerID?: string }) { const local = useLocal() - const data = useData() + const sync = useSync() const dialog = useDialog() const [query, setQuery] = createSignal("") @@ -29,21 +29,19 @@ export function DialogModel(props: { providerID?: string }) { function toOptions(items: typeof favorites, category: string) { if (!showSections) return [] return items.flatMap((item) => { - const provider = data.location.provider.list()?.find((provider) => provider.id === item.providerID) + const provider = sync.data.provider.find((provider) => provider.id === item.providerID) if (!provider) return [] - const model = data.location.model - .list() - ?.find((model) => model.providerID === item.providerID && model.id === item.modelID) + const model = provider.models[item.modelID] if (!model) return [] return [ { key: item, value: { providerID: provider.id, modelID: model.id }, - title: model.name, + title: model.name ?? item.modelID, description: provider.name, category, disabled: provider.id === "opencode" && model.id.includes("-nano"), - footer: model.cost[0]?.input === 0 && provider.id === "opencode" ? "Free" : undefined, + footer: model.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, onSelect: () => { onSelect(provider.id, model.id) }, @@ -61,43 +59,50 @@ export function DialogModel(props: { providerID?: string }) { ) const providerOptions = pipe( - data.location.model.list() ?? [], - filter((model) => model.status !== "deprecated"), - filter((model) => (props.providerID ? model.providerID === props.providerID : true)), + sync.data.provider, sortBy( - (model) => model.providerID !== "opencode", - (model) => data.location.provider.list()?.find((provider) => provider.id === model.providerID)?.name ?? "", - [(model) => model.time.released, "desc"], + (provider) => provider.id !== "opencode", + (provider) => provider.name, + ), + flatMap((provider) => + pipe( + provider.models, + entries(), + filter(([_, info]) => info.status !== "deprecated"), + filter(([_, info]) => (props.providerID ? info.providerID === props.providerID : true)), + map(([model, info]) => ({ + value: { providerID: provider.id, modelID: model }, + title: info.name ?? model, + releaseDate: info.release_date, + description: favorites.some((item) => item.providerID === provider.id && item.modelID === model) + ? "(Favorite)" + : undefined, + category: connected() ? provider.name : undefined, + disabled: provider.id === "opencode" && model.includes("-nano"), + footer: info.cost?.input === 0 && provider.id === "opencode" ? "Free" : undefined, + onSelect() { + onSelect(provider.id, model) + }, + })), + filter((option) => { + if (!showSections) return true + if ( + favorites.some( + (item) => item.providerID === option.value.providerID && item.modelID === option.value.modelID, + ) + ) + return false + if ( + recents.some( + (item) => item.providerID === option.value.providerID && item.modelID === option.value.modelID, + ) + ) + return false + return true + }), + (options) => sortModelOptions(options, props.providerID !== undefined), + ), ), - map((model) => ({ - value: { providerID: model.providerID, modelID: model.id }, - title: model.name, - releaseDate: model.time.released, - description: favorites.some((item) => item.providerID === model.providerID && item.modelID === model.id) - ? "(Favorite)" - : undefined, - category: connected() - ? data.location.provider.list()?.find((provider) => provider.id === model.providerID)?.name - : undefined, - disabled: !model.enabled || (model.providerID === "opencode" && model.id.includes("-nano")), - footer: model.cost[0]?.input === 0 && model.providerID === "opencode" ? "Free" : undefined, - onSelect() { - onSelect(model.providerID, model.id) - }, - })), - filter((option) => { - if (!showSections) return true - if ( - favorites.some((item) => item.providerID === option.value.providerID && item.modelID === option.value.modelID) - ) - return false - if ( - recents.some((item) => item.providerID === option.value.providerID && item.modelID === option.value.modelID) - ) - return false - return true - }), - (options) => sortModelOptions(options, props.providerID !== undefined), ) const popularProviders = !connected() @@ -122,7 +127,7 @@ export function DialogModel(props: { providerID?: string }) { }) const provider = createMemo(() => - props.providerID ? data.location.provider.list()?.find((item) => item.id === props.providerID) : null, + props.providerID ? sync.data.provider.find((item) => item.id === props.providerID) : null, ) const title = createMemo(() => { diff --git a/packages/tui/src/component/dialog-workspace-create.tsx b/packages/tui/src/component/dialog-workspace-create.tsx index b3e807b2fd..98c71bb000 100644 --- a/packages/tui/src/component/dialog-workspace-create.tsx +++ b/packages/tui/src/component/dialog-workspace-create.tsx @@ -132,7 +132,7 @@ export async function warpWorkspaceSession(input: { input.project.workspace.set(input.workspaceID) - await input.sync.bootstrap() + await input.sync.bootstrap({ fatal: false }).catch(() => undefined) const dir = input.project.instance.directory() || input.sync.path.directory if (dir) { diff --git a/packages/tui/src/component/dialog-workspace-list.tsx b/packages/tui/src/component/dialog-workspace-list.tsx index b37efc6225..eab2acf7c8 100644 --- a/packages/tui/src/component/dialog-workspace-list.tsx +++ b/packages/tui/src/component/dialog-workspace-list.tsx @@ -82,7 +82,7 @@ export function DialogWorkspaceList() { route.navigate({ type: "home" }) } await project.workspace.sync() - await sync.bootstrap() + await sync.bootstrap({ fatal: false }).catch(() => undefined) setRemoving(undefined) } diff --git a/packages/tui/src/component/prompt/autocomplete.tsx b/packages/tui/src/component/prompt/autocomplete.tsx index a8a6b12883..19875c2d41 100644 --- a/packages/tui/src/component/prompt/autocomplete.tsx +++ b/packages/tui/src/component/prompt/autocomplete.tsx @@ -317,9 +317,10 @@ export function Autocomplete(props: { const { lineRange, baseQuery } = extractLineRange(query ?? "") // Get files from SDK - const result = await sdk.client.find.files({ + const result = await sdk.client.v2.fs.find({ query: baseQuery, - workspace: project.workspace.current(), + limit: "20", + location: { workspace: project.workspace.current() }, }) const options: AutocompleteOption[] = [] @@ -329,15 +330,13 @@ export function Autocomplete(props: { if (!result.error && result.data) { const width = props.anchor().width - 4 options.push( - ...result.data.map((item): AutocompleteOption => { - const { filename, url, part } = createFilePart(item, lineRange) - - const isDir = item.endsWith("/") + ...result.data.data.map((item): AutocompleteOption => { + const { filename, url, part } = createFilePart(item.path, lineRange) return { display: Locale.truncateMiddle(filename, width), value: filename, - isDirectory: isDir, - path: item, + isDirectory: item.type === "directory", + path: item.path, onSelect: () => { insertPart(filename, part) }, @@ -390,15 +389,15 @@ export function Autocomplete(props: { }) const agents = createMemo(() => { - return (data.location.agent.list() ?? []) + return sync.data.agent .filter((agent) => !agent.hidden && agent.mode !== "primary") .map( (agent): AutocompleteOption => ({ - display: "@" + agent.id, + display: "@" + agent.name, onSelect: () => { - insertPart(agent.id, { + insertPart(agent.name, { type: "agent", - name: agent.id, + name: agent.name, source: { start: 0, end: 0, diff --git a/packages/tui/src/component/use-connected.tsx b/packages/tui/src/component/use-connected.tsx index 47886d67ad..dbf6f73b12 100644 --- a/packages/tui/src/component/use-connected.tsx +++ b/packages/tui/src/component/use-connected.tsx @@ -1,7 +1,11 @@ import { createMemo } from "solid-js" -import { useData } from "../context/data" +import { useSync } from "../context/sync" export function useConnected() { - const data = useData() - return createMemo(() => (data.location.provider.list() ?? []).some((provider) => provider.enabled !== false)) + const sync = useSync() + return createMemo(() => + sync.data.provider.some((provider) => + provider.id !== "opencode" || Object.values(provider.models).some((model) => model.cost?.input !== 0), + ), + ) } diff --git a/packages/tui/src/context/local.tsx b/packages/tui/src/context/local.tsx index cfeb6b9fd4..39e7bc111d 100644 --- a/packages/tui/src/context/local.tsx +++ b/packages/tui/src/context/local.tsx @@ -12,7 +12,6 @@ import { readJson, writeJsonAtomic } from "../util/persistence" import { useTheme } from "./theme" import { useToast } from "../ui/toast" import { useRoute } from "./route" -import { useData } from "./data" export type LocalTheme = { secondary: RGBA @@ -52,17 +51,15 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ name: "Local", init: () => { const sync = useSync() - const data = useData() const sdk = useSDK() const toast = useToast() const theme = useTheme().theme const route = useRoute() const paths = useTuiPaths() - const providers = createMemo(() => data.location.provider.list() ?? []) - const models = createMemo(() => data.location.model.list() ?? []) function isModelValid(model: { providerID: string; modelID: string }) { - return models().some((item) => item.providerID === model.providerID && item.id === model.modelID && item.enabled) + const provider = sync.data.provider.find((item) => item.id === model.providerID) + return !!provider?.models[model.modelID] } function getFirstValidModel(...modelFns: (() => { providerID: string; modelID: string } | undefined)[]) { @@ -74,18 +71,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } function createAgent() { - const all = createMemo(() => - (data.location.agent.list() ?? []).map((agent) => ({ - ...agent, - name: agent.id, - native: false, - model: agent.model - ? { providerID: agent.model.providerID, modelID: agent.model.id, variant: agent.model.variant } - : undefined, - })), - ) - const agents = createMemo(() => all().filter((agent) => agent.mode !== "subagent" && !agent.hidden)) - const visibleAgents = createMemo(() => all().filter((agent) => !agent.hidden)) + const agents = createMemo(() => sync.data.agent.filter((agent) => agent.mode !== "subagent" && !agent.hidden)) + const visibleAgents = createMemo(() => sync.data.agent.filter((agent) => !agent.hidden)) const [agentStore, setAgentStore] = createStore({ current: undefined as string | undefined, }) @@ -231,13 +218,15 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } } - const model = models().find( - (item) => item.enabled && providers().some((provider) => provider.id === item.providerID), - ) + const provider = sync.data.provider[0] + if (!provider) return undefined + const defaultModel = sync.data.provider_default[provider.id] + const firstModel = Object.values(provider.models)[0] + const model = defaultModel ?? firstModel?.id if (!model) return undefined return { - providerID: model.providerID, - modelID: model.id, + providerID: provider.id, + modelID: model, } }) @@ -272,12 +261,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ reasoning: false, } } - const provider = providers().find((item) => item.id === value.providerID) - const info = models().find((item) => item.providerID === value.providerID && item.id === value.modelID) + const provider = sync.data.provider.find((item) => item.id === value.providerID) + const info = provider?.models[value.modelID] return { provider: provider?.name ?? value.providerID, model: info?.name ?? value.modelID, - reasoning: false, + reasoning: info?.capabilities?.reasoning ?? false, } }), cycle(direction: 1 | -1) { @@ -383,8 +372,10 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ list() { const m = currentModel() if (!m) return [] - const info = models().find((item) => item.providerID === m.providerID && item.id === m.modelID) - return info?.variants.map((variant) => variant.id) ?? [] + const provider = sync.data.provider.find((item) => item.id === m.providerID) + const info = provider?.models[m.modelID] + if (!info?.variants) return [] + return Object.keys(info.variants) }, set(value: string | undefined) { const m = currentModel() diff --git a/packages/tui/src/feature-plugins/home/footer.tsx b/packages/tui/src/feature-plugins/home/footer.tsx index 88b49b003e..41bee5da5a 100644 --- a/packages/tui/src/feature-plugins/home/footer.tsx +++ b/packages/tui/src/feature-plugins/home/footer.tsx @@ -4,20 +4,19 @@ import { createMemo, Match, Show, Switch } from "solid-js" import { abbreviateHome } from "../../runtime" import { useTuiPaths } from "../../context/runtime" import { useHomeSessionDestination } from "../../routes/home/session-destination" -import { useData } from "../../context/data" const id = "internal:home-footer" function Directory(props: { api: TuiPluginApi }) { const theme = () => props.api.theme.current const destination = useHomeSessionDestination() - const data = useData() const paths = useTuiPaths() const dir = createMemo(() => { const selected = destination?.destination() - const directory = !selected || selected.type === "new" ? data.location.default().directory : selected.directory - const out = abbreviateHome(directory, paths.home) - const branch = directory === (props.api.state.path.directory || paths.cwd) ? props.api.state.vcs?.branch : undefined + if (!selected || selected.type === "new") return + const out = abbreviateHome(selected.directory, paths.home) + const branch = + selected.directory === (props.api.state.path.directory || paths.cwd) ? props.api.state.vcs?.branch : undefined if (branch) return out + ":" + branch return out }) diff --git a/packages/tui/src/routes/home/session-destination.tsx b/packages/tui/src/routes/home/session-destination.tsx index 1e4c650327..352010840b 100644 --- a/packages/tui/src/routes/home/session-destination.tsx +++ b/packages/tui/src/routes/home/session-destination.tsx @@ -7,7 +7,8 @@ import { type ParentProps, type Setter, } from "solid-js" -import { useData } from "../../context/data" +import { useSync } from "../../context/sync" +import { useTuiPaths } from "../../context/runtime" export type HomeSessionDestination = { type: "directory"; directory: string; subdirectory: boolean } | { type: "new" } @@ -20,10 +21,11 @@ type Context = { const HomeSessionDestinationContext = createContext() export function HomeSessionDestinationProvider(props: ParentProps) { - const data = useData() + const sync = useSync() + const paths = useTuiPaths() const [selected, setDestination] = createSignal() const destination = createMemo( - () => selected() ?? { type: "directory", directory: data.location.default().directory, subdirectory: false }, + () => selected() ?? { type: "directory", directory: sync.path.directory || paths.cwd, subdirectory: false }, ) return (