feat(plugin): expose app metadata (#38179)
This commit is contained in:
parent
2ed8fe5960
commit
8de40be6ea
71 changed files with 477 additions and 286 deletions
|
|
@ -32,10 +32,12 @@ import {
|
|||
} from "solid-js"
|
||||
import {
|
||||
TuiLifecycleProvider,
|
||||
TuiAppProvider,
|
||||
TuiPathsProvider,
|
||||
TuiStartupProvider,
|
||||
TuiTerminalEnvironmentProvider,
|
||||
useTuiStartup,
|
||||
type TuiApp,
|
||||
} from "./context/runtime"
|
||||
import { DialogProvider, useDialog } from "./ui/dialog"
|
||||
import { DialogIntegration } from "./component/dialog-integration"
|
||||
|
|
@ -139,6 +141,7 @@ const appBindingCommands = [
|
|||
] as const
|
||||
|
||||
export type TuiInput = {
|
||||
app: TuiApp
|
||||
server: {
|
||||
endpoint: Endpoint
|
||||
service?: {
|
||||
|
|
@ -224,7 +227,7 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
}
|
||||
if (process.env.OPENCODE_DRIVE) {
|
||||
const { Drive } = yield* Effect.promise(() => import("@opencode-ai/simulation/frontend"))
|
||||
return yield* Drive.create(options)
|
||||
return yield* Drive.create(options, input.app.version)
|
||||
}
|
||||
return yield* Effect.acquireRelease(
|
||||
Effect.tryPromise({
|
||||
|
|
@ -273,9 +276,10 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
}}
|
||||
>
|
||||
<EpilogueProvider set={(value) => (exit.epilogue = value)}>
|
||||
<ErrorBoundary
|
||||
fallback={(error, reset) => <ErrorComponent error={error} reset={reset} mode={mode} />}
|
||||
>
|
||||
<TuiAppProvider value={input.app}>
|
||||
<ErrorBoundary
|
||||
fallback={(error, reset) => <ErrorComponent error={error} reset={reset} mode={mode} />}
|
||||
>
|
||||
<TuiPathsProvider
|
||||
value={{
|
||||
cwd: process.cwd(),
|
||||
|
|
@ -381,7 +385,8 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
</TuiTerminalEnvironmentProvider>
|
||||
</TuiLifecycleProvider>
|
||||
</TuiPathsProvider>
|
||||
</ErrorBoundary>
|
||||
</ErrorBoundary>
|
||||
</TuiAppProvider>
|
||||
</EpilogueProvider>
|
||||
</ExitProvider>
|
||||
</LogProvider>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { createMemo, createSignal, For } from "solid-js"
|
||||
import { InstallationChannel, InstallationVersion } from "@opencode-ai/util/installation/version"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
|
|
@ -9,6 +8,7 @@ import { useLocal } from "../context/local"
|
|||
import { useClipboard } from "../context/clipboard"
|
||||
import { useToast } from "../ui/toast"
|
||||
import { describeOS, describeTerminal } from "../util/system"
|
||||
import { useTuiApp } from "../context/runtime"
|
||||
|
||||
export function DialogDebug() {
|
||||
const { themeV2 } = useTheme()
|
||||
|
|
@ -17,6 +17,7 @@ export function DialogDebug() {
|
|||
const local = useLocal()
|
||||
const clipboard = useClipboard()
|
||||
const toast = useToast()
|
||||
const app = useTuiApp()
|
||||
const [copied, setCopied] = createSignal(false)
|
||||
|
||||
dialog.setSize("large")
|
||||
|
|
@ -24,7 +25,7 @@ export function DialogDebug() {
|
|||
const entries = createMemo(() => {
|
||||
const model = local.model.current()
|
||||
return [
|
||||
{ label: "Version", value: `${InstallationVersion} (${InstallationChannel})` },
|
||||
{ label: "Version", value: `${app.version} (${app.channel})` },
|
||||
{ label: "Date", value: new Date().toISOString() },
|
||||
{ label: "OS", value: describeOS() },
|
||||
{ label: "Terminal", value: describeTerminal() },
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@ import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
|
|||
import { createSignal, For, Show } from "solid-js"
|
||||
import { getScrollAcceleration } from "../util/scroll"
|
||||
import { useClipboard } from "../context/clipboard"
|
||||
import { InstallationVersion } from "@opencode-ai/util/installation/version"
|
||||
import { useExit } from "../context/exit"
|
||||
import { useTuiApp } from "../context/runtime"
|
||||
import { describeOS, describeTerminal } from "../util/system"
|
||||
|
||||
export function ErrorComponent(props: { error: Error; reset: () => void; mode?: "dark" | "light" }) {
|
||||
const term = useTerminalDimensions()
|
||||
const exit = useExit()
|
||||
const clipboard = useClipboard()
|
||||
const app = useTuiApp()
|
||||
const [copied, setCopied] = createSignal(false)
|
||||
|
||||
// Safe fallback palette per mode (mirrors theme/assets/opencode.json) since the
|
||||
|
|
@ -42,7 +43,7 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?:
|
|||
|
||||
const message = props.error.message || "An unknown error occurred."
|
||||
const stack = props.error.stack || "No stack trace available."
|
||||
const issueURL = buildIssueURL(message, stack)
|
||||
const issueURL = buildIssueURL(message, stack, app.version)
|
||||
|
||||
const copyReport = () => {
|
||||
void clipboard.write?.(issueURL.toString()).then(() => setCopied(true))
|
||||
|
|
@ -192,7 +193,7 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?:
|
|||
? "Report copied — paste it into a new GitHub issue."
|
||||
: "Copy the report and open a GitHub issue to help us fix this."}
|
||||
</text>
|
||||
<text fg={colors.muted}>OpenCode {InstallationVersion}</text>
|
||||
<text fg={colors.muted}>OpenCode {app.version}</text>
|
||||
</box>
|
||||
</Show>
|
||||
</box>
|
||||
|
|
@ -200,13 +201,13 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?:
|
|||
)
|
||||
}
|
||||
|
||||
function buildIssueURL(message: string, stack: string) {
|
||||
function buildIssueURL(message: string, stack: string, version: string) {
|
||||
// Field keys match the ids in .github/ISSUE_TEMPLATE/bug-report.yml so the issue
|
||||
// form opens pre-filled. Populating os/terminal/reproduce keeps the report past
|
||||
// the contributing-guidelines compliance check, which pushes for system info.
|
||||
const url = new URL("https://github.com/anomalyco/opencode/issues/new?template=bug-report.yml")
|
||||
url.searchParams.set("title", `TUI crash: ${message}`)
|
||||
url.searchParams.set("opencode-version", InstallationVersion)
|
||||
url.searchParams.set("opencode-version", version)
|
||||
url.searchParams.set("os", describeOS())
|
||||
url.searchParams.set("terminal", describeTerminal())
|
||||
url.searchParams.set(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { createComponent, createContext, type JSX, useContext } from "solid-js"
|
||||
|
||||
export type TuiApp = Readonly<{
|
||||
name: string
|
||||
version: string
|
||||
channel: string
|
||||
}>
|
||||
|
||||
export type TuiPaths = Readonly<{
|
||||
cwd: string
|
||||
home: string
|
||||
|
|
@ -23,6 +29,7 @@ export type TuiLifecycle = Readonly<{
|
|||
}>
|
||||
|
||||
const PathsContext = createContext<TuiPaths>()
|
||||
const AppContext = createContext<TuiApp>()
|
||||
const TerminalEnvironmentContext = createContext<TuiTerminalEnvironment>()
|
||||
const StartupContext = createContext<TuiStartup>()
|
||||
const LifecycleContext = createContext<TuiLifecycle>()
|
||||
|
|
@ -40,6 +47,10 @@ export function TuiPathsProvider(props: { value: TuiPaths; children: JSX.Element
|
|||
return provider(PathsContext, props.value, () => props.children)
|
||||
}
|
||||
|
||||
export function TuiAppProvider(props: { value: TuiApp; children: JSX.Element }) {
|
||||
return provider(AppContext, props.value, () => props.children)
|
||||
}
|
||||
|
||||
export function TuiTerminalEnvironmentProvider(props: { value: TuiTerminalEnvironment; children: JSX.Element }) {
|
||||
return provider(TerminalEnvironmentContext, props.value, () => props.children)
|
||||
}
|
||||
|
|
@ -62,6 +73,10 @@ export function useTuiPaths() {
|
|||
return required(PathsContext, "TuiPathsProvider")
|
||||
}
|
||||
|
||||
export function useTuiApp() {
|
||||
return required(AppContext, "TuiAppProvider")
|
||||
}
|
||||
|
||||
export function useTuiTerminalEnvironment() {
|
||||
return required(TerminalEnvironmentContext, "TuiTerminalEnvironmentProvider")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { Plugin } from "@opencode-ai/plugin/v2/tui"
|
||||
import { InstallationVersion } from "@opencode-ai/util/installation/version"
|
||||
import { createMemo, Match, Show, Switch } from "solid-js"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { useTuiPaths } from "../../context/runtime"
|
||||
import { useTuiApp, useTuiPaths } from "../../context/runtime"
|
||||
import { useTheme } from "../../context/theme"
|
||||
import { abbreviateHome } from "../../runtime"
|
||||
import { FilePath } from "../../ui/file-path"
|
||||
|
|
@ -50,6 +49,7 @@ function Mcp(props: { context: Plugin.Context }) {
|
|||
|
||||
function View(props: { context: Plugin.Context }) {
|
||||
const { themeV2 } = useTheme()
|
||||
const app = useTuiApp()
|
||||
const dimensions = useTerminalDimensions()
|
||||
const mcpWidth = createMemo(() => {
|
||||
const list = props.context.data.location.mcp.server.list(props.context.location) ?? []
|
||||
|
|
@ -71,12 +71,12 @@ function View(props: { context: Plugin.Context }) {
|
|||
>
|
||||
<Directory
|
||||
context={props.context}
|
||||
maxWidth={Math.max(2, dimensions().width - 8 - stringWidth(InstallationVersion) - mcpWidth())}
|
||||
maxWidth={Math.max(2, dimensions().width - 8 - stringWidth(app.version) - mcpWidth())}
|
||||
/>
|
||||
<Mcp context={props.context} />
|
||||
<box flexGrow={1} />
|
||||
<box flexShrink={0}>
|
||||
<text fg={themeV2.text.subdued}>{InstallationVersion}</text>
|
||||
<text fg={themeV2.text.subdued}>{app.version}</text>
|
||||
</box>
|
||||
</box>
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue