mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 09:44:21 +01:00
wip: desktop work
This commit is contained in:
@@ -71,7 +71,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { flat, active, onInput, onKeyDown } = useFilteredList<string>({
|
const { flat, active, onInput, onKeyDown, refetch } = useFilteredList<string>({
|
||||||
items: local.file.search,
|
items: local.file.search,
|
||||||
key: (x) => x,
|
key: (x) => x,
|
||||||
onSelect: (path) => {
|
onSelect: (path) => {
|
||||||
@@ -81,6 +81,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
local.model.recent()
|
||||||
|
refetch()
|
||||||
|
})
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
() => store.contentParts,
|
() => store.contentParts,
|
||||||
@@ -369,16 +374,20 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
items={local.model.list()}
|
items={local.model.list()}
|
||||||
current={local.model.current()}
|
current={local.model.current()}
|
||||||
filterKeys={["provider.name", "name", "id"]}
|
filterKeys={["provider.name", "name", "id"]}
|
||||||
groupBy={(x) => x.provider.name}
|
groupBy={(x) => (local.model.recent().includes(x) ? "Recent" : x.provider.name)}
|
||||||
sortGroupsBy={(a, b) => {
|
sortGroupsBy={(a, b) => {
|
||||||
const order = ["opencode", "anthropic", "github-copilot", "openai", "google", "openrouter", "vercel"]
|
const order = ["opencode", "anthropic", "github-copilot", "openai", "google", "openrouter", "vercel"]
|
||||||
|
if (a.category === "Recent" && b.category !== "Recent") return -1
|
||||||
|
if (b.category === "Recent" && a.category !== "Recent") return 1
|
||||||
const aProvider = a.items[0].provider.id
|
const aProvider = a.items[0].provider.id
|
||||||
const bProvider = b.items[0].provider.id
|
const bProvider = b.items[0].provider.id
|
||||||
if (order.includes(aProvider) && !order.includes(bProvider)) return -1
|
if (order.includes(aProvider) && !order.includes(bProvider)) return -1
|
||||||
if (!order.includes(aProvider) && order.includes(bProvider)) return 1
|
if (!order.includes(aProvider) && order.includes(bProvider)) return 1
|
||||||
return order.indexOf(aProvider) - order.indexOf(bProvider)
|
return order.indexOf(aProvider) - order.indexOf(bProvider)
|
||||||
}}
|
}}
|
||||||
onSelect={(x) => local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined)}
|
onSelect={(x) =>
|
||||||
|
local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, { recent: true })
|
||||||
|
}
|
||||||
trigger={
|
trigger={
|
||||||
<Button as="div" variant="ghost">
|
<Button as="div" variant="ghost">
|
||||||
{local.model.current()?.name ?? "Select model"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
|
|||||||
@@ -45,6 +45,37 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
|
|
||||||
|
function isModelValid(model: ModelKey) {
|
||||||
|
const provider = sync.data.provider.find((x) => x.id === model.providerID)
|
||||||
|
return !!provider?.models[model.modelID]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFirstValidModel(...modelFns: (() => ModelKey | undefined)[]) {
|
||||||
|
for (const modelFn of modelFns) {
|
||||||
|
const model = modelFn()
|
||||||
|
if (!model) continue
|
||||||
|
if (isModelValid(model)) return model
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically update model when agent changes
|
||||||
|
createEffect(() => {
|
||||||
|
const value = agent.current()
|
||||||
|
if (value.model) {
|
||||||
|
if (isModelValid(value.model))
|
||||||
|
model.set({
|
||||||
|
providerID: value.model.providerID,
|
||||||
|
modelID: value.model.modelID,
|
||||||
|
})
|
||||||
|
// else
|
||||||
|
// toast.show({
|
||||||
|
// type: "warning",
|
||||||
|
// message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`,
|
||||||
|
// duration: 3000,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const agent = (() => {
|
const agent = (() => {
|
||||||
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent"))
|
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent"))
|
||||||
const [store, setStore] = createStore<{
|
const [store, setStore] = createStore<{
|
||||||
@@ -76,11 +107,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
})()
|
})()
|
||||||
|
|
||||||
const model = (() => {
|
const model = (() => {
|
||||||
const list = createMemo(() =>
|
|
||||||
sync.data.provider.flatMap((p) => Object.values(p.models).map((m) => ({ ...m, provider: p }) as LocalModel)),
|
|
||||||
)
|
|
||||||
const find = (key: ModelKey) => list().find((m) => m.id === key?.modelID && m.provider.id === key.providerID)
|
|
||||||
|
|
||||||
const [store, setStore] = createStore<{
|
const [store, setStore] = createStore<{
|
||||||
model: Record<string, ModelKey>
|
model: Record<string, ModelKey>
|
||||||
recent: ModelKey[]
|
recent: ModelKey[]
|
||||||
@@ -95,27 +121,54 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
localStorage.setItem("model", JSON.stringify(store.recent))
|
localStorage.setItem("model", JSON.stringify(store.recent))
|
||||||
})
|
})
|
||||||
|
|
||||||
const fallback = createMemo(() => {
|
const list = createMemo(() =>
|
||||||
if (store.recent.length) return store.recent[0]
|
sync.data.provider.flatMap((p) => Object.values(p.models).map((m) => ({ ...m, provider: p }) as LocalModel)),
|
||||||
|
)
|
||||||
|
const find = (key: ModelKey) => list().find((m) => m.id === key?.modelID && m.provider.id === key.providerID)
|
||||||
|
|
||||||
|
const fallbackModel = createMemo(() => {
|
||||||
|
if (sync.data.config.model) {
|
||||||
|
const [providerID, modelID] = sync.data.config.model.split("/")
|
||||||
|
if (isModelValid({ providerID, modelID })) {
|
||||||
|
return {
|
||||||
|
providerID,
|
||||||
|
modelID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of store.recent) {
|
||||||
|
if (isModelValid(item)) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
const provider = sync.data.provider[0]
|
const provider = sync.data.provider[0]
|
||||||
const model = Object.values(provider.models)[0]
|
const model = Object.values(provider.models)[0]
|
||||||
return { modelID: model.id, providerID: provider.id }
|
return {
|
||||||
|
providerID: provider.id,
|
||||||
|
modelID: model.id,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const current = createMemo(() => {
|
const currentModel = createMemo(() => {
|
||||||
const a = agent.current()
|
const a = agent.current()
|
||||||
return find(store.model[agent.current().name]) ?? find(a.model ?? fallback())
|
const key = getFirstValidModel(
|
||||||
|
() => store.model[a.name],
|
||||||
|
() => a.model,
|
||||||
|
fallbackModel,
|
||||||
|
)!
|
||||||
|
return find(key)
|
||||||
})
|
})
|
||||||
|
|
||||||
const recent = createMemo(() => store.recent.map(find).filter(Boolean))
|
const recent = createMemo(() => store.recent.map(find).filter(Boolean))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
list,
|
current: currentModel,
|
||||||
current,
|
|
||||||
recent,
|
recent,
|
||||||
|
list,
|
||||||
set(model: ModelKey | undefined, options?: { recent?: boolean }) {
|
set(model: ModelKey | undefined, options?: { recent?: boolean }) {
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setStore("model", agent.current().name, model ?? fallback())
|
setStore("model", agent.current().name, model ?? fallbackModel())
|
||||||
if (options?.recent && model) {
|
if (options?.recent && model) {
|
||||||
const uniq = uniqueBy([model, ...store.recent], (x) => x.providerID + x.modelID)
|
const uniq = uniqueBy([model, ...store.recent], (x) => x.providerID + x.modelID)
|
||||||
if (uniq.length > 5) uniq.pop()
|
if (uniq.length > 5) uniq.pop()
|
||||||
|
|||||||
@@ -57,9 +57,6 @@
|
|||||||
border-bottom: 1px solid var(--border-weak-base);
|
border-bottom: 1px solid var(--border-weak-base);
|
||||||
border-right: 1px solid var(--border-weak-base);
|
border-right: 1px solid var(--border-weak-base);
|
||||||
background-color: var(--background-base);
|
background-color: var(--background-base);
|
||||||
transition:
|
|
||||||
background-color 0.15s ease,
|
|
||||||
color 0.15s ease;
|
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|||||||
@@ -11,18 +11,22 @@ export interface FilteredListProps<T> {
|
|||||||
current?: T
|
current?: T
|
||||||
groupBy?: (x: T) => string
|
groupBy?: (x: T) => string
|
||||||
sortBy?: (a: T, b: T) => number
|
sortBy?: (a: T, b: T) => number
|
||||||
sortGroupsBy?: (a: { category: string; items: T[] }, b: { category: string; items: T[] }) => number
|
sortGroupsBy?: (
|
||||||
|
a: { category: string; items: T[] },
|
||||||
|
b: { category: string; items: T[] },
|
||||||
|
) => number
|
||||||
onSelect?: (value: T | undefined) => void
|
onSelect?: (value: T | undefined) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFilteredList<T>(props: FilteredListProps<T>) {
|
export function useFilteredList<T>(props: FilteredListProps<T>) {
|
||||||
const [store, setStore] = createStore<{ filter: string }>({ filter: "" })
|
const [store, setStore] = createStore<{ filter: string }>({ filter: "" })
|
||||||
|
|
||||||
const [grouped] = createResource(
|
const [grouped, { refetch }] = createResource(
|
||||||
() => store.filter,
|
() => store.filter,
|
||||||
async (filter) => {
|
async (filter) => {
|
||||||
const needle = filter?.toLowerCase()
|
const needle = filter?.toLowerCase()
|
||||||
const all = (typeof props.items === "function" ? await props.items(needle) : props.items) || []
|
const all =
|
||||||
|
(typeof props.items === "function" ? await props.items(needle) : props.items) || []
|
||||||
const result = pipe(
|
const result = pipe(
|
||||||
all,
|
all,
|
||||||
(x) => {
|
(x) => {
|
||||||
@@ -76,10 +80,11 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filter: () => store.filter,
|
|
||||||
grouped,
|
grouped,
|
||||||
|
filter: () => store.filter,
|
||||||
flat,
|
flat,
|
||||||
reset,
|
reset,
|
||||||
|
refetch,
|
||||||
clear: () => setStore("filter", ""),
|
clear: () => setStore("filter", ""),
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
onInput,
|
onInput,
|
||||||
|
|||||||
Reference in New Issue
Block a user