From dffecb6478d4cc20caa9ca0a1b245cce880b3b0c Mon Sep 17 00:00:00 2001 From: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:20:15 +0800 Subject: [PATCH 01/12] fix(desktop): gate first launch onboarding (#34930) --- packages/app/src/app.tsx | 87 ++++++++++++-------- packages/app/src/context/server.tsx | 2 +- packages/app/src/index.ts | 6 ++ packages/app/src/pages/new-session.tsx | 14 ++-- packages/desktop/src/main/index.ts | 3 + packages/desktop/src/main/ipc.ts | 6 ++ packages/desktop/src/main/onboarding.ts | 28 +++++++ packages/desktop/src/main/store-keys.ts | 1 + packages/desktop/src/preload/index.ts | 3 + packages/desktop/src/preload/types.ts | 2 + packages/desktop/src/renderer/index.tsx | 16 +++- packages/desktop/src/renderer/onboarding.tsx | 79 ++++++++++++++++++ 12 files changed, 204 insertions(+), 43 deletions(-) create mode 100644 packages/desktop/src/main/onboarding.ts create mode 100644 packages/desktop/src/renderer/onboarding.tsx diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx index c7cdc73f9b3d..9c2e7d6a1f0e 100644 --- a/packages/app/src/app.tsx +++ b/packages/app/src/app.tsx @@ -126,10 +126,10 @@ function SelectedServerProviders(props: ParentProps) { ) } -function LegacyServerLayout(props: ParentProps) { +function LegacyServerLayout(props: ParentProps<{ serverScoped?: JSX.Element }>) { return ( - {props.children} + {props.children} ) } @@ -263,12 +263,14 @@ function DesktopCommands() { type ServerScopedShellProps = ParentProps<{ directory?: () => string | undefined sessionID?: () => string | undefined + serverScoped?: JSX.Element }> function ServerScopedProviders(props: ServerScopedShellProps) { return ( + {props.serverScoped} {props.children} @@ -277,16 +279,16 @@ function ServerScopedProviders(props: ServerScopedShellProps) { function LegacyServerScopedShell(props: ServerScopedShellProps) { return ( - + {props.children} ) } -function NewAppLayout(props: ParentProps) { +function NewAppLayout(props: ParentProps<{ serverScoped?: JSX.Element }>) { return ( - + {props.children} @@ -347,7 +349,7 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) { ) } -function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean }>) { +function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean; startup?: Promise }>) { const server = useServer() const checkServerHealth = useCheckServerHealth() @@ -376,34 +378,45 @@ function ConnectionGate(props: ParentProps<{ disableHealthCheck?: boolean }>) { const checking = createMemo( () => checkMode() === "blocking" && ["unresolved", "pending"].includes(startupHealthCheck.state), ) + const [startup] = createResource(async () => { + if (!props.startup) return true + await props.startup.catch((error) => { + console.error("[startup] startup gate failed", error) + }) + return true + }) + const startupChecking = createMemo( + () => startupHealthCheck.latest === true && ["unresolved", "pending"].includes(startup.state), + ) + const loading = createMemo(() => checking() || startupChecking()) return ( - + <> + + { + if (checkMode() === "background") void healthCheckActions.refetch() + }} + onServerSelected={(key) => { + setCheckMode("blocking") + server.setActive(key) + void healthCheckActions.refetch() + }} + /> + } + > + {props.children} + + + +
- } - > - { - if (checkMode() === "background") void healthCheckActions.refetch() - }} - onServerSelected={(key) => { - setCheckMode("blocking") - server.setActive(key) - void healthCheckActions.refetch() - }} - /> - } - > - {props.children} -
+ ) } @@ -470,6 +483,8 @@ export function AppInterface(props: { servers?: Array router?: Component disableHealthCheck?: boolean + startup?: Promise + serverScoped?: JSX.Element }) { // The visual new layout lives in the router root so it remains mounted across // route changes. Draft and session routes override only their server-bound data @@ -491,7 +506,7 @@ export function AppInterface(props: { > - + - {routerProps.children} + {routerProps.children} )} > - + @@ -517,12 +532,16 @@ export function AppInterface(props: { ) } -function Routes() { +function Routes(props: { serverScoped?: JSX.Element }) { const settings = useSettings() return ( <> - + ( + {routeProps.children} + )} + > {} } /> diff --git a/packages/app/src/context/server.tsx b/packages/app/src/context/server.tsx index 95c6940d70c5..960617f443aa 100644 --- a/packages/app/src/context/server.tsx +++ b/packages/app/src/context/server.tsx @@ -312,7 +312,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext( }) } - const isReady = createMemo(() => ready() && !!state.active) + const isReady = Object.assign(createMemo(() => ready() && !!state.active), { promise: ready.promise }) const scope = (key = state.active) => ServerScope.fromServerKey(key, props.canonicalLocalServer) const projects = createServerProjects({ scope, store, setStore }) diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index 2bc9262fde63..ced001ceccf9 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -1,4 +1,10 @@ export { AppBaseProviders, AppInterface } from "./app" +export { useLayout } from "./context/layout" +export { useServerSDK } from "./context/server-sdk" +export { useServerSync } from "./context/server-sync" +export { useServer } from "./context/server" +export { useTabs } from "./context/tabs" +export { useProviders } from "./hooks/use-providers" export { ACCEPTED_FILE_EXTENSIONS, ACCEPTED_FILE_TYPES, filePickerFilters } from "./constants/file-picker" export { useCommand } from "./context/command" export { loadLocaleDict, normalizeLocale, type Locale } from "./context/language" diff --git a/packages/app/src/pages/new-session.tsx b/packages/app/src/pages/new-session.tsx index fdb9929eca58..461be60450b4 100644 --- a/packages/app/src/pages/new-session.tsx +++ b/packages/app/src/pages/new-session.tsx @@ -26,7 +26,7 @@ import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout" import { PromptWorkspaceSelector } from "@/components/prompt-workspace-selector" import { useTitlebarRightMount } from "@/components/titlebar" -const showWorkspaceBar = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" +const workspaceBarEnabled = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" /** * The `/new-session` draft page. Unlike `session.tsx`, this only renders the prompt @@ -63,7 +63,9 @@ export default function NewSessionPage() { const [store, setStore] = createStore<{ worktree?: string }>({}) const rightMount = useTitlebarRightMount() + const showWorkspaceBar = createMemo(() => workspaceBarEnabled && sync().project?.vcs === "git") const newSessionWorktree = createMemo(() => { + if (!showWorkspaceBar()) return "main" if (store.worktree) return store.worktree const project = sync().project if (project && sdk().directory !== project.worktree) return sdk().directory @@ -123,7 +125,7 @@ export default function NewSessionPage() { } > -
+
- + pendingDeepLinks.splice(0), getDefaultServerUrl: () => getDefaultServerUrl(), setDefaultServerUrl: (url) => setDefaultServerUrl(url), + isFirstLaunchOnboardingPending, + finishFirstLaunchOnboarding, getDisplayBackend: async () => null, setDisplayBackend: async () => undefined, parseMarkdown: async (markdown) => parseMarkdown(markdown), diff --git a/packages/desktop/src/main/ipc.ts b/packages/desktop/src/main/ipc.ts index 22560ff7da3a..073d288e5cf0 100644 --- a/packages/desktop/src/main/ipc.ts +++ b/packages/desktop/src/main/ipc.ts @@ -27,6 +27,8 @@ type Deps = { consumeInitialDeepLinks: () => Promise | string[] getDefaultServerUrl: () => Promise | string | null setDefaultServerUrl: (url: string | null) => Promise | void + isFirstLaunchOnboardingPending: () => Promise | boolean + finishFirstLaunchOnboarding: (createDefaultProject: boolean) => Promise | string | null getDisplayBackend: () => Promise setDisplayBackend: (backend: string | null) => Promise | void parseMarkdown: (markdown: string) => Promise | string @@ -50,6 +52,10 @@ export function registerIpcHandlers(deps: Deps) { ipcMain.handle("set-default-server-url", (_event: IpcMainInvokeEvent, url: string | null) => deps.setDefaultServerUrl(url), ) + ipcMain.handle("is-first-launch-onboarding-pending", () => deps.isFirstLaunchOnboardingPending()) + ipcMain.handle("finish-first-launch-onboarding", (_event: IpcMainInvokeEvent, createDefaultProject: boolean) => + deps.finishFirstLaunchOnboarding(createDefaultProject), + ) ipcMain.handle("get-display-backend", () => deps.getDisplayBackend()) ipcMain.handle("set-display-backend", (_event: IpcMainInvokeEvent, backend: string | null) => deps.setDisplayBackend(backend), diff --git a/packages/desktop/src/main/onboarding.ts b/packages/desktop/src/main/onboarding.ts new file mode 100644 index 000000000000..926506e7b9ba --- /dev/null +++ b/packages/desktop/src/main/onboarding.ts @@ -0,0 +1,28 @@ +import { mkdir } from "node:fs/promises" +import { join } from "node:path" +import { app } from "electron" +import { getStore } from "./store" +import { FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY } from "./store-keys" +import { write as writeLog } from "./logging" + +const DEFAULT_PROJECT_DIR = "New OpenCode Project" + +export function isFirstLaunchOnboardingPending() { + const pending = getStore().get(FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY) !== true + writeLog("onboarding", "first launch onboarding pending checked", { pending }) + return pending +} + +export async function finishFirstLaunchOnboarding(createDefaultProject: boolean) { + if (!isFirstLaunchOnboardingPending()) { + writeLog("onboarding", "first launch onboarding already completed") + return null + } + + const defaultProject = createDefaultProject ? join(app.getPath("documents"), DEFAULT_PROJECT_DIR) : null + if (defaultProject) await mkdir(defaultProject, { recursive: true }) + + getStore().set(FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY, true) + writeLog("onboarding", "first launch onboarding completed", { createDefaultProject, defaultProject }) + return defaultProject +} diff --git a/packages/desktop/src/main/store-keys.ts b/packages/desktop/src/main/store-keys.ts index 270ffe7504e0..506924b74a66 100644 --- a/packages/desktop/src/main/store-keys.ts +++ b/packages/desktop/src/main/store-keys.ts @@ -1,5 +1,6 @@ export const SETTINGS_STORE = "opencode.settings" export const DEFAULT_SERVER_URL_KEY = "defaultServerUrl" +export const FIRST_LAUNCH_ONBOARDING_COMPLETE_KEY = "firstLaunchOnboardingComplete" export const WSL_SERVERS_KEY = "wslServers" export const PINCH_ZOOM_ENABLED_KEY = "pinchZoomEnabled" export const WINDOW_IDS_KEY = "windowIds" diff --git a/packages/desktop/src/preload/index.ts b/packages/desktop/src/preload/index.ts index 2cb8a9ee073c..47a757a557d3 100644 --- a/packages/desktop/src/preload/index.ts +++ b/packages/desktop/src/preload/index.ts @@ -59,6 +59,9 @@ const api: ElectronAPI = { consumeInitialDeepLinks: () => ipcRenderer.invoke("consume-initial-deep-links"), getDefaultServerUrl: () => ipcRenderer.invoke("get-default-server-url"), setDefaultServerUrl: (url) => ipcRenderer.invoke("set-default-server-url", url), + isFirstLaunchOnboardingPending: () => ipcRenderer.invoke("is-first-launch-onboarding-pending"), + finishFirstLaunchOnboarding: (createDefaultProject) => + ipcRenderer.invoke("finish-first-launch-onboarding", createDefaultProject), getDisplayBackend: () => ipcRenderer.invoke("get-display-backend"), setDisplayBackend: (backend) => ipcRenderer.invoke("set-display-backend", backend), parseMarkdownCommand: (markdown) => ipcRenderer.invoke("parse-markdown", markdown), diff --git a/packages/desktop/src/preload/types.ts b/packages/desktop/src/preload/types.ts index 5401c2070d7a..d7509580ad05 100644 --- a/packages/desktop/src/preload/types.ts +++ b/packages/desktop/src/preload/types.ts @@ -49,6 +49,8 @@ export type ElectronAPI = { consumeInitialDeepLinks: () => Promise getDefaultServerUrl: () => Promise setDefaultServerUrl: (url: string | null) => Promise + isFirstLaunchOnboardingPending: () => Promise + finishFirstLaunchOnboarding: (createDefaultProject: boolean) => Promise getDisplayBackend: () => Promise setDisplayBackend: (backend: LinuxDisplayBackend | null) => Promise parseMarkdownCommand: (markdown: string) => Promise diff --git a/packages/desktop/src/renderer/index.tsx b/packages/desktop/src/renderer/index.tsx index 5369ab21e310..966fb0c0bf3a 100644 --- a/packages/desktop/src/renderer/index.tsx +++ b/packages/desktop/src/renderer/index.tsx @@ -23,6 +23,7 @@ import { render } from "solid-js/web" import pkg from "../../package.json" import { initI18n, t } from "./i18n" import { initializationData, initializationReady } from "./initialization" +import { DesktopFirstLaunchOnboarding } from "./onboarding" import { resetZoom, setPinchZoomEnabled, webviewZoom, zoomIn, zoomOut } from "./webview-zoom" import { availableStartupServer, readyWslConnections } from "./wsl/connections" import "./styles.css" @@ -347,6 +348,7 @@ function DesktopRoot(props: { windowState: DesktopWindowState }) { const router = (props: BaseRouterProps) => ( ) + const onboarding = Promise.withResolvers() function handleClick(e: MouseEvent) { const link = (e.target as HTMLElement).closest("a.external-link") as HTMLAnchorElement | null @@ -400,12 +402,22 @@ function DesktopRoot(props: { windowState: DesktopWindowState }) { const effectiveDefaultServer = createMemo(() => ServerConnection.Key.make(availableStartupServer(defaultServer.latest, wslServers.data)), ) - return ( }> {(key) => ( - + + } + > )} diff --git a/packages/desktop/src/renderer/onboarding.tsx b/packages/desktop/src/renderer/onboarding.tsx new file mode 100644 index 000000000000..47758bc78869 --- /dev/null +++ b/packages/desktop/src/renderer/onboarding.tsx @@ -0,0 +1,79 @@ +import { + ServerConnection, + useLayout, + useProviders, + useServer, + useServerSDK, + useServerSync, + useTabs, +} from "@opencode-ai/app" +import { onMount, startTransition } from "solid-js" + +export function DesktopFirstLaunchOnboarding(props: { initialUrl: string; onLoaded: () => void }) { + const server = useServer() + const serverSDK = useServerSDK() + const serverSync = useServerSync() + const layout = useLayout() + const providers = useProviders() + const tabs = useTabs() + + onMount(() => { + void runFirstLaunchOnboarding().finally(props.onLoaded) + }) + + async function runFirstLaunchOnboarding() { + try { + await Promise.all( + [server.ready.promise, layout.ready.promise, tabs.ready.promise, tabs.recentReady.promise].map( + (p) => p ?? Promise.resolve(), + ), + ) + if (!server.isLocal()) return + + const pending = await window.api.isFirstLaunchOnboardingPending() + if (!pending) return + + const sessions = await serverSDK() + .client.session.list() + .then((x) => x.data ?? []) + .catch(() => undefined) + const connectedProviders = providers.connected() + const paidProviders = providers.paid() + const persistedProjects = layout.projects.list() + const shouldTrigger = + props.initialUrl === "/" && + sessions?.length === 0 && + paidProviders.length === 0 && + persistedProjects.length === 0 && + tabs.store.length === 0 && + server.list.every(ServerConnection.builtin) + + console.info("[desktop-onboarding] first launch onboarding evaluated", { + pending, + shouldTrigger, + initialUrl: props.initialUrl, + sessions: sessions?.length, + connectedProviders: connectedProviders.length, + paidProviders: paidProviders.length, + serverProjects: serverSync().data.project.length, + persistedProjects: persistedProjects.length, + tabs: tabs.store.length, + servers: server.list.map(ServerConnection.key), + }) + + const directory = await window.api.finishFirstLaunchOnboarding(shouldTrigger) + if (!shouldTrigger || !directory) return + + console.info("[desktop-onboarding] starting first launch draft", { directory }) + server.projects.open(directory) + server.projects.touch(directory) + await startTransition(() => { + tabs.newDraft({ server: server.key, directory }) + }) + } catch (error) { + console.error("[desktop-onboarding] first launch onboarding failed", error) + } + } + + return null +} From 977a40af686ced65d14e86d52ca41c9137b7e515 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Mon, 6 Jul 2026 08:21:25 +0000 Subject: [PATCH 02/12] chore: generate --- packages/app/src/context/server.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/app/src/context/server.tsx b/packages/app/src/context/server.tsx index 960617f443aa..450129f45884 100644 --- a/packages/app/src/context/server.tsx +++ b/packages/app/src/context/server.tsx @@ -312,7 +312,10 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext( }) } - const isReady = Object.assign(createMemo(() => ready() && !!state.active), { promise: ready.promise }) + const isReady = Object.assign( + createMemo(() => ready() && !!state.active), + { promise: ready.promise }, + ) const scope = (key = state.active) => ServerScope.fromServerKey(key, props.canonicalLocalServer) const projects = createServerProjects({ scope, store, setStore }) From b0e41ff2c4069bafdaac9c11327659b1bf911f87 Mon Sep 17 00:00:00 2001 From: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:37:50 +0800 Subject: [PATCH 03/12] fix(app): use selected home project for new sessions (#35530) --- packages/app/src/components/titlebar.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 8706fd93c904..2a818d565da1 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -334,6 +334,21 @@ export function Titlebar(props: { update?: TitlebarUpdate }) { return } + if (route.type === "home") { + const selection = layout.home.selection() + const conn = global.servers.list().find((item) => ServerConnection.key(item) === selection.server) + const project = conn + ? global + .ensureServerCtx(conn) + .projects.list() + .find((item) => item.worktree === selection.directory) + : undefined + if (conn && project) { + tabs.newDraft({ server: ServerConnection.key(conn), directory: project.worktree }, "") + return + } + } + const current = layout.projects.list()[0] if (current) { tabs.newDraft({ server: server.key, directory: current.worktree }, "") From 561070fbc24eaf4c691ef69e8a1fa07c5dab0f87 Mon Sep 17 00:00:00 2001 From: Simon Klee Date: Mon, 6 Jul 2026 13:01:08 +0200 Subject: [PATCH 04/12] deps: upgrade OpenTUI to v0.4.3 (#35226) --- bun.lock | 50 ++++++++++++------- package.json | 6 +-- packages/plugin/package.json | 6 +-- .../tui/src/component/dialog-provider.tsx | 6 +-- packages/tui/src/routes/session/index.tsx | 2 +- packages/tui/src/ui/dialog-prompt.tsx | 2 +- 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/bun.lock b/bun.lock index 742578ad2b25..49f4ff3d7a96 100644 --- a/bun.lock +++ b/bun.lock @@ -704,9 +704,9 @@ "typescript": "catalog:", }, "peerDependencies": { - "@opentui/core": ">=0.3.4", - "@opentui/keymap": ">=0.3.4", - "@opentui/solid": ">=0.3.4", + "@opentui/core": ">=0.4.3", + "@opentui/keymap": ">=0.4.3", + "@opentui/solid": ">=0.4.3", }, "optionalPeers": [ "@opentui/core", @@ -1096,9 +1096,9 @@ "@npmcli/arborist": "9.4.0", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@opentui/core": "0.3.4", - "@opentui/keymap": "0.3.4", - "@opentui/solid": "0.3.4", + "@opentui/core": "0.4.3", + "@opentui/keymap": "0.4.3", + "@opentui/solid": "0.4.3", "@pierre/diffs": "1.2.10", "@playwright/test": "1.59.1", "@sentry/solid": "10.36.0", @@ -2031,27 +2031,27 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="], - "@opentui/core": ["@opentui/core@0.3.4", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.3.4", "@opentui/core-darwin-x64": "0.3.4", "@opentui/core-linux-arm64": "0.3.4", "@opentui/core-linux-arm64-musl": "0.3.4", "@opentui/core-linux-x64": "0.3.4", "@opentui/core-linux-x64-musl": "0.3.4", "@opentui/core-win32-arm64": "0.3.4", "@opentui/core-win32-x64": "0.3.4" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-y0DlrChP9lcJ4jC5z/1wMS34+ygfSTW7gD5OJHwJaAScfmlFvuJOZbwmCGrJURZ+5wFBxuOi9LatZsmeAUIKAA=="], + "@opentui/core": ["@opentui/core@0.4.3", "", { "dependencies": { "bun-ffi-structs": "0.2.4", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.4.3", "@opentui/core-darwin-x64": "0.4.3", "@opentui/core-linux-arm64": "0.4.3", "@opentui/core-linux-arm64-musl": "0.4.3", "@opentui/core-linux-x64": "0.4.3", "@opentui/core-linux-x64-musl": "0.4.3", "@opentui/core-win32-arm64": "0.4.3", "@opentui/core-win32-x64": "0.4.3" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-rrJfAk13tALDqldYjhc78eWQ+aKq1iknJgffIOg3OwyZoqQo+p6gtuqyhmWvXIfQzlNUbpgpCPcxbXlhMnlaHQ=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4A7JYXUsZqhu9PPCe07E30ourSJYkitkwMujUyNKjM5e/dHNDVnz+5r5cO3M5snofLafc1DN7+9jEPn4UQzchQ=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.4.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p5+7AAxpxGuDGagyQfewKtmTFnN7THvTVY4FyKqUtJomNaHdQXPHztapNNzMx0DGWbwOUbVKzpL+yc3CZY3chQ=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-Jvm9E8n2sPhKEyKSXn9GlmJcj8WoJXJTooXb3djwjVaiimjihIj0XxHzCWhdqbDtQp+VxDFyCKoQagOOz20qhA=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.4.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-+fh0vEUE0lwVC7RW5ijYLRlTLp5NfvCRj8SzxDVd7IL2j2ssB6YXcfIbXq2EW7UGnrejwPRXf1tgUrIXW9KmOw=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-0uPuHCeZxm/O7+L+iNQl8zRAfehiwYstKkT9J0uTZO64/byBCLvy5lvn1DiE/72s/nTJ5nwpLN+pQs2/WYVKLQ=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.4.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-gl6qA5QJy6u8Cbt7gOtHbhhfMZ4qQDb0kEwFXHcMGmbnKzz4OHoq74D6tNjyvSQB9saoC7C6C0tvn2DcJOuNog=="], - "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-sJYUzYcSOb5PCXRlhwsse/fdsMiVomNvIwq/2TDhAANef+YPO3Br+OH9kQRbuj0bjVDmUS36SGYWSTFu2lUO+A=="], + "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.4.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-8p8g8/AEq/xFGpQ7XcIFKcAqjc0QwsZcv+Ll9RbCDpUA56FGH6jfLDir0KYTNTgYXJTIrBIENI9K46VuxMUMQA=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-btYIQeNdPbN4JCrCjVB/RwMGrnRY7qWB2piNEfALSByuULKNjPKQ33PYIj38Yd01zCvCV7FotIeXEGSHx3tgCA=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.4.3", "", { "os": "linux", "cpu": "x64" }, "sha512-dXpJitiZdYE3hq2Pvx6e9I0uPQSOcnaLLp1pDgWAHv+3kvKSHEX//9Yr/pV/Ua6qqT7p+2D/K4vXNap/NKVo2w=="], - "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-fhmUey4oJJ2+N62xlIgAPxAl36Fa7wYffqDOT4QLpm0jfyD5xzo+wL/hr2zUqaEI439R8Iq6jHNxf/Nsx1WuuQ=="], + "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.4.3", "", { "os": "linux", "cpu": "x64" }, "sha512-/QiFpCrpU2O7vy8QYmLIQYbvAtKDgmqcVjR7dGtqSzkiQk3ktNJoo5RozG7ueXnjung1Wp0nKldKxo2Csg/OrA=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.3.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-sh432vPU+eLp8eA4I0KWKKn7D0VHbk01YTg6mA9/ihCNYHntc6LZ8/sLvsPv8CvKscMotfIkh3M5YhdS36BuXw=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.4.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-Mx2zuOjrhm/z2SDS6RExIyjP/SnN/8QhhagxURUw0jQi/NssGSeAllu1cBAFFnhobJL5QLTE4FU4CRhUK9svgg=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-dw8FcjUZaLAjw25P3/7BarobCh/QOHn3srYaWYQdysoqyvSlPkQumpI8kV/KgpJtdITU1GW02MQC4EeLIFFalA=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.4.3", "", { "os": "win32", "cpu": "x64" }, "sha512-NuoqvWKGXaYnmlqvu7Gg2lLI6yVMnS9OfWBvxp+7Q+McSgHFSTQmYBXaPpvQ8HikpQXE1nCeMPtuSG4PdZHe2w=="], - "@opentui/keymap": ["@opentui/keymap@0.3.4", "", { "dependencies": { "@opentui/core": "0.3.4" }, "peerDependencies": { "@opentui/react": "0.3.4", "@opentui/solid": "0.3.4", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-8fo6BZWQgCjANfbKkzPo0ghAzS1E7TlHjDDS+SUhrX01qEUO1clFTRssKluHbXd2UJY1Ehle01TV5bFmY78f8w=="], + "@opentui/keymap": ["@opentui/keymap@0.4.3", "", { "dependencies": { "@opentui/core": "0.4.3" }, "peerDependencies": { "@opentui/react": "0.4.3", "@opentui/solid": "0.4.3", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-sinX0pyQBRrEvo89PSSUbSUDIYpL3xWo81VEfec58VFoVRB5FG48/deAtvRTQfJ8w1kgbzN8hzdOXdSm61zBmw=="], - "@opentui/solid": ["@opentui/solid@0.3.4", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.3.4", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-gin1VnsVBahX0nrU3mpgh5U1qvyJBIZu4NE5mc0YnObWOEf9HVNxKY4/BpUvQPh91kT6zeOzTBvAvYK4R7g9MQ=="], + "@opentui/solid": ["@opentui/solid@0.4.3", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.4.3", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-RcV0+S8HMdXOASyr7HmJUBuTUIaFPzAxMDa44VftS5C2JUgrmAuWo0Njv1q3TWRB1owjHnyKhEfWGKq7A82wxw=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -3243,7 +3243,7 @@ "builder-util-runtime": ["builder-util-runtime@9.7.0", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw=="], - "bun-ffi-structs": ["bun-ffi-structs@0.2.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-N/ZWtyN0piZlrXQT7TO0V+q952orYqkfhXRXM1Hcbb+R3QSiBH4vLnib187Mrs1H7pWIYECAmPeapGYDOMCl+w=="], + "bun-ffi-structs": ["bun-ffi-structs@0.2.4", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-AJzsqoVFs1KBbJbWHIYrVZLDC3NhTqqh25awRXqzoLzmBAKr5oqk6+CwuYHAekKx+VBCYVohBoKuRq40dV+TYg=="], "bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="], @@ -6383,6 +6383,8 @@ "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + + "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], @@ -7091,6 +7093,17 @@ "opencode/@ai-sdk/cerebras/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.33", "", { "dependencies": { "@ai-sdk/provider": "3.0.12", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-nJ0bAfegMAIJtrzMJtbzer1cS3nb7c7DsyU1S4nrPm7ZU0Mn6SBBZv5IGZZGTbpWTJwqKTSPeZJTXalbAxt1BA=="], + + + + + + + + + + + "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], @@ -7425,6 +7438,7 @@ "opencode/@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], diff --git a/package.json b/package.json index fd86ad3decd2..e5526b22ffb1 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,9 @@ "@octokit/rest": "22.0.0", "@hono/standard-validator": "0.2.0", "@hono/zod-validator": "0.4.2", - "@opentui/core": "0.3.4", - "@opentui/keymap": "0.3.4", - "@opentui/solid": "0.3.4", + "@opentui/core": "0.4.3", + "@opentui/keymap": "0.4.3", + "@opentui/solid": "0.4.3", "@tanstack/solid-virtual": "3.13.28", "@shikijs/stream": "4.2.0", "ulid": "3.0.1", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 50aeffb8e10a..1b16375c032e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -27,9 +27,9 @@ "zod": "catalog:" }, "peerDependencies": { - "@opentui/core": ">=0.3.4", - "@opentui/keymap": ">=0.3.4", - "@opentui/solid": ">=0.3.4" + "@opentui/core": ">=0.4.3", + "@opentui/keymap": ">=0.4.3", + "@opentui/solid": ">=0.4.3" }, "peerDependenciesMeta": { "@opentui/core": { diff --git a/packages/tui/src/component/dialog-provider.tsx b/packages/tui/src/component/dialog-provider.tsx index 2767508eb2d2..0fd51e3c1c71 100644 --- a/packages/tui/src/component/dialog-provider.tsx +++ b/packages/tui/src/component/dialog-provider.tsx @@ -366,8 +366,8 @@ function ApiMethod(props: ApiMethodProps) { + ({ opencode: ( @@ -390,7 +390,7 @@ function ApiMethod(props: ApiMethodProps) { ), - }[props.providerID] ?? undefined + })[props.providerID] ?? undefined } onConfirm={async (value) => { if (!value) return diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 6df6b00d2d11..6d77b0ea58fd 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -1528,7 +1528,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las customBorderChars={SplitBorder.customBorderChars} borderColor={theme.error} > - {props.message.error?.data.message} + {errorMessage(props.message.error)} diff --git a/packages/tui/src/ui/dialog-prompt.tsx b/packages/tui/src/ui/dialog-prompt.tsx index d627ea2970d9..f518fb2950b7 100644 --- a/packages/tui/src/ui/dialog-prompt.tsx +++ b/packages/tui/src/ui/dialog-prompt.tsx @@ -83,7 +83,7 @@ export function DialogPrompt(props: DialogPromptProps) { - {props.description} + {props.description?.()}