Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 5 additions & 3 deletions packages/opencode/src/cli/cmd/tui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import { DialogSelect } from "./ui/dialog-select"
import { Provider } from "@/provider"
import { ArgsProvider, useArgs, type Args } from "./context/args"
import open from "open"
import { Process } from "@/util"
import { Process, Log } from "@/util"
import { PromptRefProvider, usePromptRef } from "./context/prompt"
import { TuiConfigProvider, useTuiConfig } from "./context/tui-config"
import { TuiConfig } from "@/cli/cmd/tui/config/tui"
Expand All @@ -70,6 +70,8 @@ import { FormatError, FormatUnknownError } from "@/cli/error"
import { isPlainTerminal } from "./util/terminal"

import type { EventSource } from "./context/sdk"

const log = Log.create({ service: "tui" })
import { DialogVariant } from "./component/dialog-variant"

function rendererConfig(_config: TuiConfig.Info, plainTerminal: boolean): CliRendererConfig {
Expand Down Expand Up @@ -99,7 +101,7 @@ function rendererConfig(_config: TuiConfig.Info, plainTerminal: boolean): CliRen
keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }],
onCopySelection: (text) => {
Clipboard.copy(text).catch((error) => {
console.error(`Failed to copy console selection to clipboard: ${error}`)
log.error("Failed to copy console selection to clipboard", { error })
})
},
},
Expand Down Expand Up @@ -272,7 +274,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
config: tuiConfig,
})
.catch((error) => {
console.error("Failed to load TUI plugins", error)
log.error("Failed to load TUI plugins", { error })
})
.finally(() => {
setReady(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ const IMAGE_EXT = new Set([".png", ".jpg", ".jpeg"])
const NONE_VALUE = "__mimocode_image_none__"
const IMPORT_VALUE = "__mimocode_image_import__"

const log = Log.create({ service: "dialog-image-list" })

async function listBackgrounds() {
await fs.mkdir(BG_DIR, { recursive: true }).catch(() => {})
await fs.mkdir(BG_DIR, { recursive: true }).catch((error) => {
log.error("Failed to create backgrounds directory", { dir: BG_DIR, error })
})
const items = await fs.readdir(BG_DIR).catch(() => [] as string[])
return items
.filter((f) => IMAGE_EXT.has(path.extname(f).toLowerCase()))
Expand Down
7 changes: 5 additions & 2 deletions packages/opencode/src/cli/cmd/tui/component/dialog-mcp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { useTheme } from "../context/theme"
import { Keybind } from "@/util"
import { TextAttributes } from "@opentui/core"
import { useSDK } from "@tui/context/sdk"
import { Log } from "@/util"

const log = Log.create({ service: "dialog-mcp" })

function Status(props: { enabled: boolean; loading: boolean }) {
const { theme } = useTheme()
Expand Down Expand Up @@ -61,10 +64,10 @@ export function DialogMcp() {
if (status.data) {
sync.set("mcp", status.data)
} else {
console.error("Failed to refresh MCP status: no data returned")
log.error("Failed to refresh MCP status: no data returned")
}
} catch (error) {
console.error("Failed to toggle MCP:", error)
log.error("Failed to toggle MCP", { error })
} finally {
setLoading(null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { useSDK } from "../context/sdk"
import { useSync } from "@tui/context/sync"
import { useRoute } from "@tui/context/route"
import { useToast } from "../ui/toast"
import { Log } from "@/util"

const log = Log.create({ service: "dialog-worktree" })
import path from "path"

const CREATE_SENTINEL = "__create_worktree__"
Expand Down Expand Up @@ -53,7 +56,9 @@ export function DialogWorktree() {

async function switchTo(directory: string) {
setBusy("Switching to worktree...")
await sdk.client.instance.dispose().catch(() => {})
await sdk.client.instance.dispose().catch((error) => {
log.error("Failed to dispose instance during worktree switch", { error })
})
sdk.switchDirectory(directory)
await sync.bootstrap()
route.navigate({ type: "home" })
Expand Down
6 changes: 4 additions & 2 deletions packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createEffect, createMemo, onMount, createSignal, onCleanup, on, Show, S
import "opentui-spinner/solid"
import path from "path"
import { fileURLToPath } from "url"
import { Filesystem } from "@/util"
import { Filesystem, Log } from "@/util"
import { useLocal } from "@tui/context/local"
import { tint, useTheme } from "@tui/context/theme"
import { EmptyBorder, SplitBorder } from "@tui/component/border"
Expand Down Expand Up @@ -46,6 +46,8 @@ import { DialogWorkspaceUnavailable } from "../dialog-workspace-unavailable"
import { DialogAgreement, FREE_AGREEMENT_KEY, FREE_MODEL_IDS } from "../dialog-agreement"
import { useArgs } from "@tui/context/args"

const log = Log.create({ service: "prompt" })

export type PromptProps = {
sessionID?: string
workspaceID?: string
Expand Down Expand Up @@ -1063,7 +1065,7 @@ export function Prompt(props: PromptProps) {
const res = await sdk.client.session.create({ workspace: props.workspaceID })

if (res.error) {
console.log("Creating a session failed:", res.error)
log.error("Creating a session failed", { error: res.error })

toast.show({
message: "Creating a session failed. Open console for more details.",
Expand Down
9 changes: 5 additions & 4 deletions packages/opencode/src/cli/cmd/tui/context/kv.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Global } from "@/global"
import { Filesystem } from "@/util"
import { Filesystem, Log } from "@/util"
import { Flock } from "@mimo-ai/shared/util/flock"
import { rename, rm } from "fs/promises"
import { createSignal, type Setter } from "solid-js"
Expand All @@ -10,6 +10,7 @@ import path from "path"
export const { use: useKV, provider: KVProvider } = createSimpleContext({
name: "KV",
init: () => {
const log = Log.create({ service: "kv" })
const [ready, setReady] = createSignal(false)
const [store, setStore] = createStore<Record<string, any>>()
const filePath = path.join(Global.Path.state, "kv.json")
Expand All @@ -34,7 +35,7 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
setStore(x)
})
.catch((error) => {
console.error("Failed to read KV state", { filePath, error })
log.error("Failed to read KV state", { filePath, error })
})
.finally(() => {
setReady(true)
Expand Down Expand Up @@ -67,7 +68,7 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
write = write
.then(() => Flock.withLock(lock, () => writeSnapshot(snapshot)))
.catch((error) => {
console.error("Failed to write KV state", { filePath, error })
log.error("Failed to write KV state", { filePath, error })
})
},
delete(key: string) {
Expand All @@ -77,7 +78,7 @@ export const { use: useKV, provider: KVProvider } = createSimpleContext({
write = write
.then(() => Flock.withLock(lock, () => writeSnapshot(snapshot)))
.catch((error) => {
console.error("Failed to write KV state", { filePath, error })
log.error("Failed to write KV state", { filePath, error })
})
},
}
Expand Down
5 changes: 4 additions & 1 deletion packages/opencode/src/cli/cmd/tui/plugin/slots.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { TuiPluginApi, TuiSlotContext, TuiSlotMap, TuiSlotProps } from "@mimo-ai/plugin/tui"
import { createSlot, createSolidSlotRegistry, type JSX, type SolidPlugin } from "@opentui/solid"
import { isRecord } from "@/util/record"
import { Log } from "@/util"

const log = Log.create({ service: "tui.slot" })

type RuntimeSlotMap = TuiSlotMap<Record<string, object>>

Expand Down Expand Up @@ -38,7 +41,7 @@ export function setupSlots(api: HostPluginApi): HostSlots {
},
{
onPluginError(event) {
console.error("[tui.slot] plugin error", {
log.error("plugin error", {
plugin: event.pluginId,
slot: event.slot,
phase: event.phase,
Expand Down
6 changes: 5 additions & 1 deletion packages/opencode/src/cli/cmd/tui/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ declare global {

type RpcClient = ReturnType<typeof Rpc.client<typeof rpc>>

const log = Log.create({ service: "tui-thread" })

function createWorkerFetch(client: RpcClient): typeof fetch {
const fn = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
const request = new Request(input, init)
Expand Down Expand Up @@ -286,7 +288,9 @@ export const TuiThreadCommand = cmd({
}

setTimeout(() => {
client.call("checkUpgrade", { directory: cwd }).catch(() => {})
client.call("checkUpgrade", { directory: cwd }).catch((error) => {
log.error("Upgrade check failed", { error })
})
}, 1000).unref?.()

try {
Expand Down
15 changes: 9 additions & 6 deletions packages/opencode/src/cli/cmd/tui/util/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import path from "path"
import fs from "fs/promises"
import * as Filesystem from "../../../../util/filesystem"
import * as Process from "../../../../util/process"
import { Log } from "../../../../util/log"

const log = Log.create({ service: "clipboard" })

// Lazy load which and clipboardy to avoid expensive execa/which/isexe chain at startup
const getWhich = lazy(async () => {
Expand Down Expand Up @@ -115,7 +118,7 @@ const getCopyMethod = lazy(async () => {
const which = await getWhich()

if (os === "darwin" && which("osascript")) {
console.log("clipboard: using osascript")
log.debug("using osascript")
return async (text: string) => {
const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
await Process.run(["osascript", "-e", `set the clipboard to "${escaped}"`], { nothrow: true })
Expand All @@ -124,7 +127,7 @@ const getCopyMethod = lazy(async () => {

if (os === "linux") {
if (process.env["WAYLAND_DISPLAY"] && which("wl-copy")) {
console.log("clipboard: using wl-copy")
log.debug("using wl-copy")
return async (text: string) => {
const proc = Process.spawn(["wl-copy"], { stdin: "pipe", stdout: "ignore", stderr: "ignore" })
if (!proc.stdin) return
Expand All @@ -134,7 +137,7 @@ const getCopyMethod = lazy(async () => {
}
}
if (which("xclip")) {
console.log("clipboard: using xclip")
log.debug("using xclip")
return async (text: string) => {
const proc = Process.spawn(["xclip", "-selection", "clipboard"], {
stdin: "pipe",
Expand All @@ -148,7 +151,7 @@ const getCopyMethod = lazy(async () => {
}
}
if (which("xsel")) {
console.log("clipboard: using xsel")
log.debug("using xsel")
return async (text: string) => {
const proc = Process.spawn(["xsel", "--clipboard", "--input"], {
stdin: "pipe",
Expand All @@ -164,7 +167,7 @@ const getCopyMethod = lazy(async () => {
}

if (os === "win32") {
console.log("clipboard: using powershell")
log.debug("using powershell")
return async (text: string) => {
// Pipe via stdin to avoid PowerShell string interpolation ($env:FOO, $(), etc.)
const proc = Process.spawn(
Expand All @@ -189,7 +192,7 @@ const getCopyMethod = lazy(async () => {
}
}

console.log("clipboard: no native support")
log.debug("no native support")
return async (text: string) => {
const clipboardy = await getClipboardy()
await clipboardy.write(text).catch(() => {})
Expand Down