feat(ui): add runtime logger and replace console usage

This commit is contained in:
Shantur Rathore
2025-12-05 15:07:49 +00:00
parent 49143bd049
commit 971abe24d7
44 changed files with 406 additions and 138 deletions

View File

@@ -3,6 +3,9 @@ import { For, Show, createEffect, createMemo } from "solid-js"
import { agents, fetchAgents, sessions } from "../stores/sessions"
import { ChevronDown } from "lucide-solid"
import type { Agent } from "../types/session"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
interface AgentSelectorProps {
instanceId: string
@@ -49,10 +52,11 @@ export default function AgentSelector(props: AgentSelectorProps) {
createEffect(() => {
if (instanceAgents().length === 0) {
fetchAgents(props.instanceId).catch(console.error)
fetchAgents(props.instanceId).catch((error) => log.error("Failed to fetch agents", error))
}
})
const handleChange = async (value: Agent | null) => {
if (value && value.name !== props.currentAgent) {
await props.onAgentChange(value.name)

View File

@@ -8,6 +8,9 @@ import { normalizeDiffText } from "../lib/diff-utils"
import { setCacheEntry } from "../lib/global-cache"
import type { CacheEntryParams } from "../lib/global-cache"
import type { DiffViewMode } from "../stores/preferences"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
disableCache()
@@ -110,7 +113,7 @@ export function ToolCallDiffViewer(props: ToolCallDiffViewerProps) {
>
{(data) => (
<ErrorBoundary fallback={(error) => {
console.warn("Failed to render diff view", error)
log.warn("Failed to render diff view", error)
return <pre class="tool-call-diff-fallback">{props.diffText}</pre>
}}>
<DiffView

View File

@@ -2,6 +2,9 @@ import { Component, Show, For, createSignal, createMemo, createEffect, onCleanup
import { Folder as FolderIcon, File as FileIcon, Loader2, Search, X, ArrowUpLeft } from "lucide-solid"
import type { FileSystemEntry, FileSystemListingMetadata } from "../../../server/src/api-types"
import { serverApi } from "../lib/api-client"
import { getLogger } from "../lib/logger"
const log = getLogger("actions")
const MAX_RESULTS = 200
@@ -172,7 +175,7 @@ const FileSystemBrowserDialog: Component<FileSystemBrowserDialogProps> = (props)
function handleNavigateTo(path: string) {
void fetchDirectory(path, true).catch((err) => {
console.error("Failed to open directory", err)
log.error("Failed to open directory", err)
setError(err instanceof Error ? err.message : "Unable to open directory")
})
}

View File

@@ -1,6 +1,9 @@
import { Component, Show, For, createSignal, createEffect, onCleanup } from "solid-js"
import type { Instance, RawMcpStatus } from "../types/instance"
import { fetchLspStatus, updateInstance } from "../stores/instances"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
interface InstanceInfoProps {
instance: Instance
@@ -113,7 +116,7 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
} catch (error) {
if (!cancelled) {
console.error("Failed to load instance metadata:", error)
log.error("Failed to load instance metadata", error)
}
} finally {
pendingMetadataRequests.delete(instanceId)

View File

@@ -6,6 +6,9 @@ import KeyboardHint from "./keyboard-hint"
import Kbd from "./kbd"
import { keyboardRegistry, type KeyboardShortcut } from "../lib/keyboard-registry"
import { isMac } from "../lib/keyboard-utils"
import { getLogger } from "../lib/logger"
const log = getLogger("actions")
interface InstanceWelcomeViewProps {
@@ -189,7 +192,7 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
const session = await createSession(props.instance.id)
setActiveParentSession(props.instance.id, session.id)
} catch (error) {
console.error("Failed to create session:", error)
log.error("Failed to create session:", error)
} finally {
setIsCreating(false)
}

View File

@@ -17,6 +17,9 @@ import CommandPalette from "../command-palette"
import Kbd from "../kbd"
import ContextUsagePanel from "../session/context-usage-panel"
import SessionView from "../session/session-view"
import { getLogger } from "../../lib/logger"
const log = getLogger("session")
interface InstanceShellProps {
instance: Instance
@@ -119,13 +122,13 @@ const InstanceShell: Component<InstanceShellProps> = (props) => {
onClose={(id) => {
const result = props.onCloseSession(id)
if (result instanceof Promise) {
void result.catch((error) => console.error("Failed to close session:", error))
void result.catch((error) => log.error("Failed to close session:", error))
}
}}
onNew={() => {
const result = props.onNewSession()
if (result instanceof Promise) {
void result.catch((error) => console.error("Failed to create session:", error))
void result.catch((error) => log.error("Failed to create session:", error))
}
}}
showHeader

View File

@@ -1,6 +1,9 @@
import { createEffect, createSignal, onMount, onCleanup } from "solid-js"
import { renderMarkdown, onLanguagesLoaded, initMarkdown, decodeHtmlEntities } from "../lib/markdown"
import type { TextPart } from "../types/message"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
interface MarkdownProps {
part: TextPart
@@ -43,7 +46,7 @@ export function Markdown(props: MarkdownProps) {
notifyRendered()
}
} catch (error) {
console.error("Failed to render markdown:", error)
log.error("Failed to render markdown:", error)
if (latestRequestedText === text) {
setHtml(text)
notifyRendered()
@@ -68,7 +71,7 @@ export function Markdown(props: MarkdownProps) {
notifyRendered()
}
} catch (error) {
console.error("Failed to render markdown:", error)
log.error("Failed to render markdown:", error)
if (latestRequestedText === text) {
setHtml(text)
part.renderCache = { text, html: text, theme: themeKey }
@@ -124,7 +127,7 @@ export function Markdown(props: MarkdownProps) {
notifyRendered()
}
} catch (error) {
console.error("Failed to re-render markdown after language load:", error)
log.error("Failed to re-render markdown after language load:", error)
}
})

View File

@@ -3,6 +3,9 @@ import { createEffect, createMemo, createSignal } from "solid-js"
import { providers, fetchProviders } from "../stores/sessions"
import { ChevronDown } from "lucide-solid"
import type { Model } from "../types/session"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
interface ModelSelectorProps {
instanceId: string
@@ -25,7 +28,7 @@ export default function ModelSelector(props: ModelSelectorProps) {
createEffect(() => {
if (instanceProviders().length === 0) {
fetchProviders(props.instanceId).catch(console.error)
fetchProviders(props.instanceId).catch((error) => log.error("Failed to fetch providers", error))
}
})

View File

@@ -4,6 +4,9 @@ import { useConfig } from "../stores/preferences"
import { serverApi } from "../lib/api-client"
import FileSystemBrowserDialog from "./filesystem-browser-dialog"
import { openNativeFileDialog, supportsNativeDialogs } from "../lib/native/native-functions"
import { getLogger } from "../lib/logger"
const log = getLogger("actions")
interface BinaryOption {
path: string
@@ -83,7 +86,7 @@ const OpenCodeBinarySelector: Component<OpenCodeBinarySelectorProps> = (props) =
setTimeout(() => {
pathsToValidate.forEach((path) => {
validateBinary(path).catch(console.error)
validateBinary(path).catch((error) => log.error("Failed to validate binary", { path, error }))
})
}, 0)
})

View File

@@ -10,6 +10,9 @@ import Kbd from "./kbd"
import { getActiveInstance } from "../stores/instances"
import { agents, getSessionDraftPrompt, setSessionDraftPrompt, clearSessionDraftPrompt } from "../stores/sessions"
import { showAlertDialog } from "../stores/alerts"
import { getLogger } from "../lib/logger"
const log = getLogger("actions")
interface PromptInputProps {
instanceId: string
@@ -563,7 +566,7 @@ export default function PromptInput(props: PromptInputProps) {
})
setHistoryIndex(-1)
} catch (historyError) {
console.error("Failed to update prompt history:", historyError)
log.error("Failed to update prompt history:", historyError)
}
}
@@ -586,7 +589,7 @@ export default function PromptInput(props: PromptInputProps) {
}
void refreshHistory()
} catch (error) {
console.error("Failed to send message:", error)
log.error("Failed to send message:", error)
showAlertDialog("Failed to send message", {
title: "Send failed",
detail: error instanceof Error ? error.message : String(error),

View File

@@ -8,6 +8,9 @@ import { serverApi } from "../lib/api-client"
import { restartCli } from "../lib/native/cli"
import { preferences, setListeningMode } from "../stores/preferences"
import { showConfirmDialog } from "../stores/alerts"
import { getLogger } from "../lib/logger"
const log = getLogger("actions")
interface RemoteAccessOverlayProps {
open: boolean
@@ -62,7 +65,7 @@ export function RemoteAccessOverlay(props: RemoteAccessOverlayProps) {
const dataUrl = await toDataURL(url, { margin: 1, scale: 4 })
setQrCodes((prev) => ({ ...prev, [url]: dataUrl }))
} catch (err) {
console.error("Failed to generate QR code", err)
log.error("Failed to generate QR code", err)
}
}
}
@@ -101,7 +104,7 @@ export function RemoteAccessOverlay(props: RemoteAccessOverlayProps) {
try {
window.open(url, "_blank", "noopener,noreferrer")
} catch (err) {
console.error("Failed to open URL", err)
log.error("Failed to open URL", err)
}
}

View File

@@ -7,6 +7,9 @@ import Kbd from "./kbd"
import { keyboardRegistry } from "../lib/keyboard-registry"
import { formatShortcut } from "../lib/keyboard-utils"
import { showToastNotification } from "../lib/notifications"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
interface SessionListProps {
@@ -106,7 +109,7 @@ const SessionList: Component<SessionListProps> = (props) => {
await navigator.clipboard.writeText(sessionId)
showToastNotification({ message: "Session ID copied", variant: "success" })
} catch (error) {
console.error(`Failed to copy session ID ${sessionId}:`, error)
log.error(`Failed to copy session ID ${sessionId}:`, error)
showToastNotification({ message: "Unable to copy session ID", variant: "error" })
}
}

View File

@@ -4,6 +4,9 @@ import type { Session, Agent } from "../types/session"
import { getParentSessions, createSession, setActiveParentSession } from "../stores/sessions"
import { instances, stopInstance } from "../stores/instances"
import { agents } from "../stores/sessions"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
interface SessionPickerProps {
instanceId: string
@@ -55,7 +58,7 @@ const SessionPicker: Component<SessionPickerProps> = (props) => {
setActiveParentSession(props.instanceId, session.id)
props.onClose()
} catch (error) {
console.error("Failed to create session:", error)
log.error("Failed to create session:", error)
} finally {
setIsCreating(false)
}

View File

@@ -8,6 +8,9 @@ import PromptInput from "../prompt-input"
import { instances } from "../../stores/instances"
import { loadMessages, sendMessage, forkSession, isSessionMessagesLoading, setActiveParentSession, setActiveSession, runShellCommand } from "../../stores/sessions"
import { showAlertDialog } from "../../stores/alerts"
import { getLogger } from "../../lib/logger"
const log = getLogger("session")
function isTextPart(part: ClientPart): part is ClientPart & { type: "text"; text: string } {
return part?.type === "text" && typeof (part as any).text === "string"
@@ -33,7 +36,7 @@ export const SessionView: Component<SessionViewProps> = (props) => {
const currentSession = session()
if (currentSession) {
loadMessages(props.instanceId, currentSession.id).catch(console.error)
loadMessages(props.instanceId, currentSession.id).catch((error) => log.error("Failed to load messages", error))
}
})
@@ -84,7 +87,7 @@ export const SessionView: Component<SessionViewProps> = (props) => {
}
}
} catch (error) {
console.error("Failed to revert:", error)
log.error("Failed to revert message", error)
showAlertDialog("Failed to revert to message", {
title: "Revert failed",
variant: "error",
@@ -94,7 +97,7 @@ export const SessionView: Component<SessionViewProps> = (props) => {
async function handleFork(messageId?: string) {
if (!messageId) {
console.warn("Fork requires a user message id")
log.warn("Fork requires a user message id")
return
}
@@ -109,7 +112,7 @@ export const SessionView: Component<SessionViewProps> = (props) => {
setActiveSession(props.instanceId, forkedSession.id)
}
await loadMessages(props.instanceId, forkedSession.id).catch(console.error)
await loadMessages(props.instanceId, forkedSession.id).catch((error) => log.error("Failed to load forked session messages", error))
if (restoredText) {
const textarea = document.querySelector(".prompt-input") as HTMLTextAreaElement
@@ -120,7 +123,7 @@ export const SessionView: Component<SessionViewProps> = (props) => {
}
}
} catch (error) {
console.error("Failed to fork session:", error)
log.error("Failed to fork session", error)
showAlertDialog("Failed to fork session", {
title: "Fork failed",
variant: "error",

View File

@@ -11,6 +11,9 @@ import type { TextPart, RenderCache } from "../types/message"
import { resolveToolRenderer } from "./tool-call/renderers"
import type { DiffPayload, DiffRenderOptions, MarkdownRenderOptions, ToolCallPart, ToolRendererContext } from "./tool-call/types"
import { getRelativePath, getToolIcon, getToolName, isToolStateCompleted, isToolStateError, isToolStateRunning } from "./tool-call/utils"
import { getLogger } from "../lib/logger"
const log = getLogger("session")
type ToolState = import("@opencode-ai/sdk").ToolState
@@ -540,7 +543,7 @@ export default function ToolCall(props: ToolCallProps) {
const sessionId = permission.sessionID || props.sessionId
await sendPermissionResponse(props.instanceId, sessionId, permission.id, response)
} catch (error) {
console.error("Failed to send permission response:", error)
log.error("Failed to send permission response", error)
setPermissionError(error instanceof Error ? error.message : "Unable to update permission")
} finally {
setPermissionSubmitting(false)

View File

@@ -2,6 +2,9 @@ import { isRenderableDiffText } from "../../lib/diff-utils"
import { getLanguageFromPath } from "../../lib/markdown"
import type { ToolState } from "@opencode-ai/sdk"
import type { DiffPayload } from "./types"
import { getLogger } from "../../lib/logger"
const log = getLogger("session")
export type ToolStateRunning = import("@opencode-ai/sdk").ToolStateRunning
export type ToolStateCompleted = import("@opencode-ai/sdk").ToolStateCompleted
@@ -134,7 +137,7 @@ export function formatUnknown(value: unknown): { text: string; language?: string
try {
return { text: JSON.stringify(value, null, 2), language: "json" }
} catch (error) {
console.error("Failed to stringify tool call output", error)
log.error("Failed to stringify tool call output", error)
return { text: String(value) }
}
}

View File

@@ -2,6 +2,9 @@ import { Component, createSignal, createEffect, For, Show, onCleanup } from "sol
import type { Agent } from "../types/session"
import type { OpencodeClient } from "@opencode-ai/sdk/client"
import { serverApi } from "../lib/api-client"
import { getLogger } from "../lib/logger"
const log = getLogger("actions")
const SEARCH_RESULT_LIMIT = 100
const SEARCH_DEBOUNCE_MS = 200
@@ -124,7 +127,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
return snapshot
})
.catch((error) => {
console.error(`[UnifiedPicker] Failed to load workspace files:`, error)
log.error(`[UnifiedPicker] Failed to load workspace files:`, error)
setAllFiles([])
setCachedWorkspaceId(null)
throw error
@@ -178,7 +181,7 @@ const UnifiedPicker: Component<UnifiedPickerProps> = (props) => {
applyFileResults(mapEntriesToFileItems(results))
} catch (error) {
if (workspaceId === props.workspaceId) {
console.error(`[UnifiedPicker] Failed to fetch files:`, error)
log.error(`[UnifiedPicker] Failed to fetch files:`, error)
if (shouldApplyResults(requestId, workspaceId)) {
applyFileResults([])
}