diff --git a/packages/app/src/components/dialog-select-mcp.tsx b/packages/app/src/components/dialog-select-mcp.tsx
index 05253381f0..4f1a3cd239 100644
--- a/packages/app/src/components/dialog-select-mcp.tsx
+++ b/packages/app/src/components/dialog-select-mcp.tsx
@@ -43,7 +43,7 @@ export const DialogSelectMcp: Component = () => {
filterKeys={["name", "status"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
onSelect={(x) => {
- if (!x || toggle.isPending) return
+ if (!x || x.status === "pending" || toggle.isPending) return
toggle.mutate(x.name)
}}
>
@@ -76,7 +76,7 @@ export const DialogSelectMcp: Component = () => {
e.stopPropagation()}>
{
if (toggle.isPending) return
toggle.mutate(i.name)
diff --git a/packages/app/src/components/dialog-select-model-unpaid-v2.stories.tsx b/packages/app/src/components/dialog-select-model-unpaid-v2.stories.tsx
new file mode 100644
index 0000000000..bd4beb2ec9
--- /dev/null
+++ b/packages/app/src/components/dialog-select-model-unpaid-v2.stories.tsx
@@ -0,0 +1,55 @@
+// @ts-nocheck
+import { Button } from "@opencode-ai/ui/button"
+import { useDialog } from "@opencode-ai/ui/context/dialog"
+import { createSignal, onMount } from "solid-js"
+import { DialogSelectModelUnpaidV2 } from "./dialog-select-model-unpaid-v2"
+
+const names = [
+ "MiMo V2.5 Free",
+ "Nemotron 3 Ultra Free",
+ "Deepseek V4 Flash Free",
+ "North Mini Code Free",
+ "Hy3 Free",
+ "Big Pickle",
+]
+
+function SelectModelWithoutProviders() {
+ const dialog = useDialog()
+ const models = names.map((name, index) => ({
+ id: name.toLowerCase().replaceAll(" ", "-"),
+ name,
+ provider: { id: "opencode", name: "OpenCode" },
+ cost: { input: 0, output: 0 },
+ limit: { context: 128_000 },
+ capabilities: {
+ reasoning: index !== 5,
+ input: { text: true, image: false, audio: false, video: false, pdf: false },
+ },
+ }))
+ const [current, setCurrent] = createSignal(models[2])
+ const model = {
+ list: () => models,
+ current,
+ set(value) {
+ setCurrent(models.find((item) => item.id === value?.modelID))
+ },
+ }
+ const open = () => dialog.show(() => )
+
+ onMount(open)
+
+ return (
+
+ )
+}
+
+export default {
+ title: "App/Dialogs/Select Model",
+ id: "app-dialog-select-model",
+}
+
+export const WithoutProviders = {
+ render: () => ,
+}
diff --git a/packages/app/src/components/dialog-select-model-unpaid-v2.tsx b/packages/app/src/components/dialog-select-model-unpaid-v2.tsx
index 0ce9f18c16..2d0c3a7305 100644
--- a/packages/app/src/components/dialog-select-model-unpaid-v2.tsx
+++ b/packages/app/src/components/dialog-select-model-unpaid-v2.tsx
@@ -1,23 +1,26 @@
import { DialogBody, DialogHeader, DialogTitle, DialogV2 } from "@opencode-ai/ui/v2/dialog-v2"
import { Icon } from "@opencode-ai/ui/v2/icon"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
-import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { Tag } from "@opencode-ai/ui/v2/badge-v2"
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
import { useDialog } from "@opencode-ai/ui/context/dialog"
+import { useTheme } from "@opencode-ai/ui/theme"
import { createMemo, onCleanup, onMount, type Component, For, Show } from "solid-js"
import { useLocal } from "@/context/local"
-import { popularProviders, useProviders } from "@/hooks/use-providers"
+import { useProviders } from "@/hooks/use-providers"
import { decode64 } from "@/utils/base64"
import { useLanguage } from "@/context/language"
import { ModelTooltip } from "./model-tooltip"
type ModelState = ReturnType["model"]
+const featuredProviders = ["opencode", "opencode-go", "openai", "anthropic", "google", "github-copilot"]
+const displayModelName = (name: string) => name.replace(/\s+(?:\(free\)|free)$/i, "")
export const DialogSelectModelUnpaidV2: Component<{ model?: ModelState }> = (props) => {
const local = useLocal()
const model = props.model ?? local.model
const dialog = useDialog()
+ const theme = useTheme()
const directory = () => decode64(local.slug())
const providers = useProviders(directory)
const language = useLanguage()
@@ -28,6 +31,7 @@ export const DialogSelectModelUnpaidV2: Component<{ model?: ModelState }> = (pro
})
const isFree = (item: ReturnType[number]) =>
item.provider.id === "opencode" && (!item.cost || item.cost.input === 0)
+ const freeModels = createMemo(() => model.list().filter(isFree))
const openProviders = (provider?: string) => {
void import("./dialog-connect-provider").then((x) => {
@@ -62,111 +66,110 @@ export const DialogSelectModelUnpaidV2: Component<{ model?: ModelState }> = (pro
})
return (
-
+