Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f3db966
Add support for LSP workspace/didChangeWatchedFiles (#6524)
marceldev89 Dec 31, 2025
de50e7f
chore: generate
actions-user Dec 31, 2025
dc3e731
ci: tweak changelog ensure all cmd/ things fall under tui section
rekram1-node Dec 31, 2025
e7422ee
chore: rm unused import
rekram1-node Dec 31, 2025
87f9ebd
fix(tui): don't show 'Agent not found' toast for subagents (#6528)
vtemian Dec 31, 2025
97a0fd1
Revert "fix(tui): don't show 'Agent not found' toast for subagents (#…
rekram1-node Dec 31, 2025
a2857bb
fix(desktop): prompt input cleanup
adamdotdevin Dec 31, 2025
3a1cfa6
chore(app): keybind tooltip component
adamdotdevin Dec 31, 2025
3807364
chore(app): tool args cleanup
adamdotdevin Dec 31, 2025
b5546dc
wip(app): better variant toggle
adamdotdevin Dec 31, 2025
65bc720
fix(desktop): more defensive access
adamdotdevin Dec 31, 2025
93845db
fix(desktop): don't show notifs if auto-accepting
adamdotdevin Dec 31, 2025
eab2373
chore: cleanup
adamdotdevin Dec 31, 2025
31e2c8b
wip: input changes
adamdotdevin Dec 31, 2025
7a4bfbe
fix(app): text selection
adamdotdevin Dec 31, 2025
d4a2652
feat(desktop): better affordance for auto-accept
adamdotdevin Dec 31, 2025
7f3a0b8
fix(desktop): tooltip colors
adamdotdevin Dec 31, 2025
16957fd
fix(app): auto-accept colors
adamdotdevin Dec 31, 2025
538ac20
Revert "tweak: adjust keys for uniqueness calculations to use provide…
rekram1-node Dec 31, 2025
b419b0e
Reapply "tweak: adjust keys for uniqueness calculations to use provid…
rekram1-node Dec 31, 2025
34aecda
tweak: default to ai-sdk/opeai-compatible if no npm package provided
rekram1-node Dec 31, 2025
b2aa387
feat(app): better model selector
adamdotdevin Dec 31, 2025
e842205
chore: cleanup
adamdotdevin Dec 31, 2025
c93e762
release: v1.0.221
Dec 31, 2025
8a1d5ab
sync: merge upstream v1.0.221 into integration
shuv1337 Dec 31, 2025
3905520
fix: correct indentation in prompt-input.tsx
shuv1337 Dec 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/last-synced-tag
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.0.220
v1.0.221
389 changes: 27 additions & 362 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.0.220",
"version": "1.0.221",
"description": "",
"type": "module",
"exports": {
Expand Down
57 changes: 30 additions & 27 deletions packages/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Diff } from "@opencode-ai/ui/diff"
import { Code } from "@opencode-ai/ui/code"
import { ThemeProvider } from "@opencode-ai/ui/theme"
import { GlobalSyncProvider } from "@/context/global-sync"
import { PermissionProvider } from "@/context/permission"
import { LayoutProvider } from "@/context/layout"
import { GlobalSDKProvider } from "@/context/global-sdk"
import { ServerProvider, useServer } from "@/context/server"
Expand Down Expand Up @@ -75,34 +76,36 @@ export function App() {
<ServerKey>
<GlobalSDKProvider>
<GlobalSyncProvider>
<LayoutProvider>
<NotificationProvider>
<Router
root={(props) => (
<CommandProvider>
<Layout>{props.children}</Layout>
</CommandProvider>
<Router
root={(props) => (
<PermissionProvider>
<LayoutProvider>
<NotificationProvider>
<CommandProvider>
<Layout>{props.children}</Layout>
</CommandProvider>
</NotificationProvider>
</LayoutProvider>
</PermissionProvider>
)}
>
<Route path="/" component={Home} />
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={() => <Navigate href="session" />} />
<Route
path="/session/:id?"
component={(p) => (
<Show when={p.params.id ?? "new"} keyed>
<TerminalProvider>
<PromptProvider>
<Session />
</PromptProvider>
</TerminalProvider>
</Show>
)}
>
<Route path="/" component={Home} />
<Route path="/:dir" component={DirectoryLayout}>
<Route path="/" component={() => <Navigate href="session" />} />
<Route
path="/session/:id?"
component={(p) => (
<Show when={p.params.id ?? "new"} keyed>
<TerminalProvider>
<PromptProvider>
<Session />
</PromptProvider>
</TerminalProvider>
</Show>
)}
/>
</Route>
</Router>
</NotificationProvider>
</LayoutProvider>
/>
</Route>
</Router>
</GlobalSyncProvider>
</GlobalSDKProvider>
</ServerKey>
Expand Down
112 changes: 72 additions & 40 deletions packages/app/src/components/dialog-select-model.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, createMemo, Show } from "solid-js"
import { Popover as Kobalte } from "@kobalte/core/popover"
import { Component, createMemo, createSignal, JSX, Show } from "solid-js"
import { useLocal } from "@/context/local"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { popularProviders } from "@/hooks/use-providers"
Expand All @@ -9,9 +10,12 @@ import { List } from "@opencode-ai/ui/list"
import { DialogSelectProvider } from "./dialog-select-provider"
import { DialogManageModels } from "./dialog-manage-models"

export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
const ModelList: Component<{
provider?: string
class?: string
onSelect: () => void
}> = (props) => {
const local = useLocal()
const dialog = useDialog()

const models = createMemo(() =>
local.model
Expand All @@ -20,6 +24,70 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
.filter((m) => (props.provider ? m.provider.id === props.provider : true)),
)

return (
<List
class={`flex-1 min-h-0 [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:min-h-0 ${props.class ?? ""}`}
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x.provider.id}:${x.id}`}
items={models}
current={local.model.current()}
filterKeys={["provider.name", "name", "id"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
groupBy={(x) => x.provider.name}
sortGroupsBy={(a, b) => {
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 bProvider = b.items[0].provider.id
if (popularProviders.includes(aProvider) && !popularProviders.includes(bProvider)) return -1
if (!popularProviders.includes(aProvider) && popularProviders.includes(bProvider)) return 1
return popularProviders.indexOf(aProvider) - popularProviders.indexOf(bProvider)
}}
onSelect={(x) => {
local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, {
recent: true,
})
props.onSelect()
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-2 text-13-regular">
<span class="truncate">{i.name}</span>
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
<Tag>Free</Tag>
</Show>
<Show when={i.latest}>
<Tag>Latest</Tag>
</Show>
</div>
)}
</List>
)
}

export const ModelSelectorPopover: Component<{
provider?: string
children: JSX.Element
}> = (props) => {
const [open, setOpen] = createSignal(false)

return (
<Kobalte open={open()} onOpenChange={setOpen} placement="top-start" gutter={8}>
<Kobalte.Trigger as="div">{props.children}</Kobalte.Trigger>
<Kobalte.Portal>
<Kobalte.Content class="w-72 h-80 flex flex-col rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none">
<Kobalte.Title class="sr-only">Select model</Kobalte.Title>
<ModelList provider={props.provider} onSelect={() => setOpen(false)} class="p-1" />
</Kobalte.Content>
</Kobalte.Portal>
</Kobalte>
)
}

export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
const dialog = useDialog()

return (
<Dialog
title="Select model"
Expand All @@ -34,43 +102,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
</Button>
}
>
<List
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x.provider.id}:${x.id}`}
items={models}
current={local.model.current()}
filterKeys={["provider.name", "name", "id"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
groupBy={(x) => x.provider.name}
sortGroupsBy={(a, b) => {
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 bProvider = b.items[0].provider.id
if (popularProviders.includes(aProvider) && !popularProviders.includes(bProvider)) return -1
if (!popularProviders.includes(aProvider) && popularProviders.includes(bProvider)) return 1
return popularProviders.indexOf(aProvider) - popularProviders.indexOf(bProvider)
}}
onSelect={(x) => {
local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, {
recent: true,
})
dialog.close()
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-3">
<span>{i.name}</span>
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
<Tag>Free</Tag>
</Show>
<Show when={i.latest}>
<Tag>Latest</Tag>
</Show>
</div>
)}
</List>
<ModelList provider={props.provider} onSelect={() => dialog.close()} />
<Button
variant="ghost"
class="ml-3 mt-5 mb-6 text-text-base self-start"
Expand Down
Loading