Buckets:
| import { createStore } from "solid-js/store" | |
| import { createSimpleContext } from "./helper" | |
| import { batch, createEffect, createMemo } from "solid-js" | |
| import { useSync } from "./sync" | |
| import { useEvent } from "./event" | |
| import path from "path" | |
| import { useTuiPaths } from "./runtime" | |
| import { useArgs } from "./args" | |
| import { useSDK } from "./sdk" | |
| import { RGBA } from "@opentui/core" | |
| import { readJson, writeJsonAtomic } from "../util/persistence" | |
| import { useTheme } from "./theme" | |
| import { useToast } from "../ui/toast" | |
| import { useRoute } from "./route" | |
| export type LocalTheme = { | |
| secondary: RGBA | |
| accent: RGBA | |
| success: RGBA | |
| warning: RGBA | |
| primary: RGBA | |
| error: RGBA | |
| info: RGBA | |
| } | |
| export function parseModel(model: string) { | |
| const [providerID, ...rest] = model.split("/") | |
| return { | |
| providerID: providerID, | |
| modelID: rest.join("/"), | |
| } | |
| } | |
| export function recentModels( | |
| model: { providerID: string; modelID: string }, | |
| recent: { providerID: string; modelID: string }[], | |
| ) { | |
| const seen = new Set<string>() | |
| return [model, ...recent] | |
| .filter((item) => { | |
| const key = `${item.providerID}/${item.modelID}` | |
| if (seen.has(key)) return false | |
| seen.add(key) | |
| return true | |
| }) | |
| .slice(0, 10) | |
| .map((item) => ({ providerID: item.providerID, modelID: item.modelID })) | |
| } | |
| export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ | |
| name: "Local", | |
| init: () => { | |
| const sync = useSync() | |
| const sdk = useSDK() | |
| const toast = useToast() | |
| const theme = useTheme().theme | |
| const route = useRoute() | |
| const paths = useTuiPaths() | |
| function isModelValid(model: { providerID: string; modelID: string }) { | |
| const provider = sync.data.provider.find((item) => item.id === model.providerID) | |
| return !!provider?.models[model.modelID] | |
| } | |
| function getFirstValidModel(...modelFns: (() => { providerID: string; modelID: string } | undefined)[]) { | |
| for (const modelFn of modelFns) { | |
| const model = modelFn() | |
| if (!model) continue | |
| if (isModelValid(model)) return model | |
| } | |
| } | |
| function createAgent() { | |
| 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, | |
| }) | |
| const colors = createMemo(() => [ | |
| theme.secondary, | |
| theme.accent, | |
| theme.success, | |
| theme.warning, | |
| theme.primary, | |
| theme.error, | |
| theme.info, | |
| ]) | |
| return { | |
| list() { | |
| return agents() | |
| }, | |
| current() { | |
| return agents().find((x) => x.name === agentStore.current) ?? agents().at(0) | |
| }, | |
| set(name: string) { | |
| if (!agents().some((x) => x.name === name)) | |
| return toast.show({ | |
| variant: "warning", | |
| message: `Agent not found: ${name}`, | |
| duration: 3000, | |
| }) | |
| setAgentStore("current", name) | |
| }, | |
| move(direction: 1 | -1) { | |
| batch(() => { | |
| const current = this.current() | |
| if (!current) return | |
| let next = agents().findIndex((x) => x.name === current.name) + direction | |
| if (next < 0) next = agents().length - 1 | |
| if (next >= agents().length) next = 0 | |
| const value = agents()[next] | |
| setAgentStore("current", value.name) | |
| }) | |
| }, | |
| color(name: string) { | |
| const index = visibleAgents().findIndex((x) => x.name === name) | |
| if (index === -1) return colors()[0] | |
| const agent = visibleAgents()[index] | |
| if (agent?.color) { | |
| const color = agent.color | |
| if (color.startsWith("#")) return RGBA.fromHex(color) | |
| // already validated by config, just satisfying TS here | |
| return theme[color as keyof typeof theme] as RGBA | |
| } | |
| return colors()[index % colors().length] | |
| }, | |
| } | |
| } | |
| 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> | |
| }>({ | |
| ready: false, | |
| model: {}, | |
| recent: [], | |
| favorite: [], | |
| variant: {}, | |
| }) | |
| const filePath = path.join(paths.state, "model.json") | |
| const state = { | |
| pending: false, | |
| } | |
| function save() { | |
| if (!modelStore.ready) { | |
| state.pending = true | |
| return | |
| } | |
| state.pending = false | |
| void writeJsonAtomic(filePath, { | |
| recent: modelStore.recent, | |
| favorite: modelStore.favorite, | |
| variant: modelStore.variant, | |
| }) | |
| } | |
| 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>) | |
| }) | |
| .catch(() => {}) | |
| .finally(() => { | |
| setModelStore("ready", true) | |
| if (state.pending) save() | |
| }) | |
| const args = useArgs() | |
| const fallbackModel = createMemo(() => { | |
| if (args.model) { | |
| const { providerID, modelID } = parseModel(args.model) | |
| if (isModelValid({ providerID, modelID })) { | |
| return { | |
| providerID, | |
| modelID, | |
| } | |
| } | |
| } | |
| if (sync.data.config.model) { | |
| const { providerID, modelID } = parseModel(sync.data.config.model) | |
| if (isModelValid({ providerID, modelID })) { | |
| return { | |
| providerID, | |
| modelID, | |
| } | |
| } | |
| } | |
| for (const item of modelStore.recent) { | |
| if (isModelValid(item)) { | |
| return item | |
| } | |
| } | |
| 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: provider.id, | |
| modelID: model, | |
| } | |
| }) | |
| const currentModel = createMemo(() => { | |
| const a = agent.current() | |
| return ( | |
| getFirstValidModel( | |
| () => a && modelStore.model[a.name], | |
| () => a && a.model, | |
| fallbackModel, | |
| ) ?? undefined | |
| ) | |
| }) | |
| return { | |
| current: currentModel, | |
| get ready() { | |
| return modelStore.ready | |
| }, | |
| recent() { | |
| return modelStore.recent | |
| }, | |
| favorite() { | |
| return modelStore.favorite | |
| }, | |
| parsed: createMemo(() => { | |
| const value = currentModel() | |
| if (!value) { | |
| return { | |
| provider: "Connect a provider", | |
| model: "No provider selected", | |
| reasoning: false, | |
| } | |
| } | |
| 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: info?.capabilities?.reasoning ?? false, | |
| } | |
| }), | |
| cycle(direction: 1 | -1) { | |
| const current = currentModel() | |
| if (!current) return | |
| const recent = modelStore.recent | |
| const index = recent.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID) | |
| if (index === -1) return | |
| let next = index + direction | |
| if (next < 0) next = recent.length - 1 | |
| if (next >= recent.length) next = 0 | |
| const val = recent[next] | |
| if (!val) return | |
| const a = agent.current() | |
| if (!a) return | |
| setModelStore("model", a.name, { ...val }) | |
| }, | |
| cycleFavorite(direction: 1 | -1) { | |
| const favorites = modelStore.favorite.filter((item) => isModelValid(item)) | |
| if (!favorites.length) { | |
| toast.show({ | |
| variant: "info", | |
| message: "Add a favorite model to use this shortcut", | |
| duration: 3000, | |
| }) | |
| return | |
| } | |
| const current = currentModel() | |
| let index = -1 | |
| if (current) { | |
| index = favorites.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID) | |
| } | |
| if (index === -1) { | |
| index = direction === 1 ? 0 : favorites.length - 1 | |
| } else { | |
| index += direction | |
| if (index < 0) index = favorites.length - 1 | |
| if (index >= favorites.length) index = 0 | |
| } | |
| const next = favorites[index] | |
| if (!next) return | |
| const a = agent.current() | |
| if (!a) return | |
| setModelStore("model", a.name, { ...next }) | |
| setModelStore("recent", recentModels(next, modelStore.recent)) | |
| save() | |
| }, | |
| set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) { | |
| batch(() => { | |
| if (!isModelValid(model)) { | |
| toast.show({ | |
| message: `Model ${model.providerID}/${model.modelID} is not valid`, | |
| variant: "warning", | |
| duration: 3000, | |
| }) | |
| return | |
| } | |
| const a = agent.current() | |
| if (!a) return | |
| setModelStore("model", a.name, model) | |
| if (options?.recent) { | |
| setModelStore("recent", recentModels(model, modelStore.recent)) | |
| save() | |
| } | |
| }) | |
| }, | |
| toggleFavorite(model: { providerID: string; modelID: string }) { | |
| batch(() => { | |
| if (!isModelValid(model)) { | |
| toast.show({ | |
| message: `Model ${model.providerID}/${model.modelID} is not valid`, | |
| variant: "warning", | |
| duration: 3000, | |
| }) | |
| return | |
| } | |
| const exists = modelStore.favorite.some( | |
| (x) => x.providerID === model.providerID && x.modelID === model.modelID, | |
| ) | |
| const next = exists | |
| ? modelStore.favorite.filter((x) => x.providerID !== model.providerID || x.modelID !== model.modelID) | |
| : [model, ...modelStore.favorite] | |
| setModelStore( | |
| "favorite", | |
| next.map((x) => ({ providerID: x.providerID, modelID: x.modelID })), | |
| ) | |
| save() | |
| }) | |
| }, | |
| variant: { | |
| selected() { | |
| const m = currentModel() | |
| if (!m) return undefined | |
| const key = `${m.providerID}/${m.modelID}` | |
| return modelStore.variant[key] | |
| }, | |
| current() { | |
| const v = this.selected() | |
| if (!v) return undefined | |
| if (!this.list().includes(v)) return undefined | |
| return v | |
| }, | |
| list() { | |
| const m = currentModel() | |
| if (!m) return [] | |
| 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() | |
| if (!m) return | |
| const key = `${m.providerID}/${m.modelID}` | |
| setModelStore("variant", key, value ?? "default") | |
| 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(undefined) | |
| return | |
| } | |
| this.set(variants[index + 1]) | |
| }, | |
| }, | |
| } | |
| } | |
| const model = createModel() | |
| function createSession() { | |
| const [sessionStore, setSessionStore] = createStore<{ | |
| ready: boolean | |
| pinned: string[] | |
| }>({ | |
| ready: false, | |
| pinned: [], | |
| }) | |
| const filePath = path.join(paths.state, "session.json") | |
| const state = { | |
| pending: false, | |
| } | |
| function save() { | |
| if (!sessionStore.ready) { | |
| state.pending = true | |
| return | |
| } | |
| state.pending = false | |
| void writeJsonAtomic(filePath, { | |
| pinned: sessionStore.pinned, | |
| }) | |
| } | |
| readJson<unknown>(filePath) | |
| .then((x) => { | |
| if (!x || typeof x !== "object") return | |
| const pinned = (x as Record<string, unknown>).pinned | |
| if (Array.isArray(pinned)) | |
| setSessionStore( | |
| "pinned", | |
| pinned.filter((item): item is string => typeof item === "string"), | |
| ) | |
| }) | |
| .catch(() => {}) | |
| .finally(() => { | |
| setSessionStore("ready", true) | |
| if (state.pending) save() | |
| }) | |
| const event = useEvent() | |
| const slots = createMemo(() => { | |
| const existing = new Set(sync.data.session.filter((x) => x.parentID === undefined).map((x) => x.id)) | |
| return sessionStore.pinned.filter((id) => existing.has(id)).slice(0, 9) | |
| }) | |
| function prune(sessionID: string) { | |
| batch(() => { | |
| if (sessionStore.pinned.includes(sessionID)) { | |
| setSessionStore( | |
| "pinned", | |
| sessionStore.pinned.filter((x) => x !== sessionID), | |
| ) | |
| } | |
| save() | |
| }) | |
| } | |
| event.on("session.deleted", (evt) => { | |
| prune(evt.properties.info.id) | |
| }) | |
| return { | |
| get ready() { | |
| return sessionStore.ready | |
| }, | |
| pinned() { | |
| return sessionStore.pinned | |
| }, | |
| slots, | |
| isPinned(sessionID: string) { | |
| return sessionStore.pinned.includes(sessionID) | |
| }, | |
| togglePin(sessionID: string) { | |
| batch(() => { | |
| const exists = sessionStore.pinned.includes(sessionID) | |
| const next = exists | |
| ? sessionStore.pinned.filter((x) => x !== sessionID) | |
| : [...sessionStore.pinned, sessionID] | |
| setSessionStore("pinned", next) | |
| save() | |
| }) | |
| }, | |
| quickSwitch(slot: number) { | |
| const target = slots()[slot - 1] | |
| if (!target) return | |
| if (route.data.type === "session" && route.data.sessionID === target) return | |
| route.navigate({ type: "session", sessionID: target }) | |
| }, | |
| } | |
| } | |
| const session = createSession() | |
| const mcp = { | |
| isEnabled(name: string) { | |
| const status = sync.data.mcp[name] | |
| return status?.status === "connected" | |
| }, | |
| async toggle(name: string) { | |
| const status = sync.data.mcp[name] | |
| if (status?.status === "connected") { | |
| // Disable: disconnect the MCP | |
| await sdk.client.mcp.disconnect({ name }) | |
| } else { | |
| // Enable/Retry: connect the MCP (handles disabled, failed, and other states) | |
| await sdk.client.mcp.connect({ name }) | |
| } | |
| }, | |
| } | |
| createEffect(() => { | |
| const value = agent.current() | |
| if (!value?.model) return | |
| if (isModelValid(value.model)) return | |
| toast.show({ | |
| variant: "warning", | |
| message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`, | |
| duration: 3000, | |
| }) | |
| }) | |
| const result = { | |
| model, | |
| agent, | |
| mcp, | |
| session, | |
| } | |
| return result | |
| }, | |
| }) | |
Xet Storage Details
- Size:
- 16.7 kB
- Xet hash:
- 77f89bcd53478e2a797e6b6e8d1bb82b58ef8087547a1e9fcb714e23bd82a214
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.