Skip to content
Merged
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
4 changes: 3 additions & 1 deletion packages/kilo-docs/source-links.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Source Code Links

<!-- Auto-generated by script/extract-source-links.ts — DO NOT EDIT -->
<!-- 77 unique URLs extracted from extension and CLI source -->
<!-- 78 unique URLs extracted from extension and CLI source -->

- <https://api.apertis.ai/v1>
<!-- packages/opencode/src/provider/model-cache.ts -->
Expand Down Expand Up @@ -55,6 +55,8 @@
<!-- packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx -->
- <https://github.com/Kilo-Org/kilocode/issues/6986>
<!-- packages/kilo-vscode/src/agent-manager/constants.ts -->
- <https://github.com/Kilo-Org/kilocode/issues/new?template=bug-report.yml>
<!-- packages/opencode/src/cli/cmd/tui/app.tsx -->
- <https://github.com/Kilo-Org/kilocode/issues/new/choose>
<!-- packages/kilo-vscode/webview-ui/src/components/chat/FeedbackDialog.tsx -->
- <https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip>
Expand Down
123 changes: 120 additions & 3 deletions packages/opencode/src/cli/cmd/tui/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render, TimeToFirstDraw, useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/solid"
import { Clipboard } from "@tui/util/clipboard"
import { Selection } from "@tui/util/selection"
import { createCliRenderer, MouseButton, type CliRendererConfig } from "@opentui/core"
import { createCliRenderer, MouseButton, TextAttributes, type CliRendererConfig } from "@opentui/core" // kilocode_change
import { RouteProvider, useRoute } from "@tui/context/route"
import {
Switch,
Expand All @@ -16,12 +16,12 @@ import {
on,
onCleanup,
} from "solid-js"
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
import { win32DisableProcessedInput, win32FlushInputBuffer, win32InstallCtrlCGuard } from "./win32" // kilocode_change
import { Flag } from "@/flag/flag"
import semver from "semver"
import { DialogProvider, useDialog } from "@tui/ui/dialog"
import { DialogProvider as DialogProviderList } from "@tui/component/dialog-provider"
import { ErrorComponent } from "@tui/component/error-component"
import { Installation } from "@/installation" // kilocode_change
import { PluginRouteMissing } from "@tui/component/plugin-route-missing"
import { SDKProvider, useSDK } from "@tui/context/sdk"
import { StartupLoading } from "@tui/component/startup-loading"
Expand Down Expand Up @@ -202,9 +202,11 @@ export function tui(input: {
await render(() => {
return (
<ErrorBoundary
// kilocode_change start
fallback={(error, reset) => (
<ErrorComponent error={error} reset={reset} onBeforeExit={onBeforeExit} onExit={onExit} mode={mode} />
)}
// kilocode_change end
>
<ArgsProvider {...input.args}>
<ExitProvider onBeforeExit={onBeforeExit} onExit={onExit}>
Expand Down Expand Up @@ -965,7 +967,122 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
</Show>
{plugin()}
<TuiPluginRuntime.Slot name="app" />
{/* kilocode_change start */}
<StartupLoading ready={ready} />
</box>
)
}
// kilocode_change end

// kilocode_change start — guard against missing renderer context in ErrorBoundary fallback
function tryUseRenderer() {
try {
return useRenderer()
} catch {
return undefined
}
}

function tryUseTerminalDimensions() {
try {
return useTerminalDimensions()
} catch {
return undefined
}
}
// kilocode_change end

// kilocode_change start — inlined ErrorComponent with safe renderer/keyboard guards
function ErrorComponent(props: {
error: Error
reset: () => void
onBeforeExit?: () => Promise<void>
onExit: () => Promise<void>
mode?: "dark" | "light"
}) {
const term = tryUseTerminalDimensions()
const renderer = tryUseRenderer()

const height = () => term?.().height ?? process.stdout.rows ?? 24

const handleExit = async () => {
await props.onBeforeExit?.()
renderer?.setTerminalTitle("")
renderer?.destroy()
win32FlushInputBuffer()
await props.onExit()
}

try {
useKeyboard((evt) => {
if (evt.ctrl && evt.name === "c") {
handleExit()
}
})
} catch {
// Keyboard handler unavailable — renderer context may be missing.
// Ctrl+C will still work via the default signal handler.
}

const [copied, setCopied] = createSignal(false)

const issueURL = new URL("https://github.com/Kilo-Org/kilocode/issues/new?template=bug-report.yml")

// Choose safe fallback colors per mode since theme context may not be available
const isLight = props.mode === "light"
const colors = {
bg: isLight ? "#ffffff" : "#0a0a0a",
text: isLight ? "#1a1a1a" : "#eeeeee",
muted: isLight ? "#8a8a8a" : "#808080",
primary: isLight ? "#3b7dd8" : "#fab283",
}

if (props.error.message) {
issueURL.searchParams.set("title", `opentui: fatal: ${props.error.message}`)
}

if (props.error.stack) {
issueURL.searchParams.set(
"description",
"```\n" + props.error.stack.substring(0, 6000 - issueURL.toString().length) + "...\n```",
)
}

issueURL.searchParams.set("opencode-version", Installation.VERSION)

const copyIssueURL = () => {
Clipboard.copy(issueURL.toString()).then(() => {
setCopied(true)
})
}

return (
<box flexDirection="column" gap={1} backgroundColor={colors.bg}>
<box flexDirection="row" gap={1} alignItems="center">
<text attributes={TextAttributes.BOLD} fg={colors.text}>
Please report an issue.
</text>
<box onMouseUp={copyIssueURL} backgroundColor={colors.primary} padding={1}>
<text attributes={TextAttributes.BOLD} fg={colors.bg}>
Copy issue URL (exception info pre-filled)
</text>
</box>
{copied() && <text fg={colors.muted}>Successfully copied</text>}
</box>
<box flexDirection="row" gap={2} alignItems="center">
<text fg={colors.text}>A fatal error occurred!</text>
<box onMouseUp={props.reset} backgroundColor={colors.primary} padding={1}>
<text fg={colors.bg}>Reset TUI</text>
</box>
<box onMouseUp={handleExit} backgroundColor={colors.primary} padding={1}>
<text fg={colors.bg}>Exit</text>
</box>
</box>
<scrollbox height={Math.floor(height() * 0.7)}>
<text fg={colors.muted}>{props.error.stack}</text>
</scrollbox>
<text fg={colors.text}>{props.error.message}</text>
</box>
)
}
// kilocode_change end
Loading