Apply PR #30303: feat(tui): add session render state dump

This commit is contained in:
opencode-agent[bot] 2026-06-19 16:07:34 +00:00
commit 9da68a69b7

View file

@ -81,6 +81,7 @@ import { DialogRetryAction } from "../../component/dialog-retry-action"
import { getRevertDiffFiles } from "../../util/revert-diff"
import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut, useOpencodeKeymap } from "../../keymap"
import { PathFormatterProvider, usePathFormatter } from "../../context/path-format"
import { InstallationChannel, InstallationVersion } from "@opencode-ai/core/installation/version"
addDefaultParsers(parsers.parsers)
@ -90,6 +91,7 @@ const GO_UPSELL_ACCOUNT_RATE_LIMIT_LAST_SEEN_AT = "go_upsell_account_rate_limit_
const GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW = "go_upsell_account_rate_limit_dont_show"
const GO_UPSELL_WINDOW = 86_400_000 // 24 hrs
const GO_UPSELL_PROVIDERS = new Set(["opencode", "opencode-go"])
const SESSION_RENDER_STATE_CHANNELS = new Set(["local", "dev", "beta"])
type RetryAction = Extract<SessionStatus, { type: "retry" }>["action"]
@ -422,6 +424,49 @@ export function Session() {
}, 50)
}
async function dumpRenderState() {
const timestamp = Date.now()
const maxTop = Math.max(0, scroll.scrollHeight - scroll.viewport.height)
const filename = `session-${route.sessionID.slice(0, 8)}-render-state-${timestamp}.json`
renderer.dumpBuffers(timestamp)
await Filesystem.writeJson(path.join(process.cwd(), filename), {
capturedAt: timestamp,
channel: InstallationChannel,
version: InstallationVersion,
sessionID: route.sessionID,
scroll: {
top: scroll.scrollTop,
height: scroll.scrollHeight,
viewportHeight: scroll.viewport.height,
maxTop,
atBottom: scroll.scrollTop >= maxTop,
},
messages: messages().map((message) => ({
id: message.id,
role: message.role,
parts: (sync.data.part[message.id] ?? []).map((part) => {
const renderable = scroll.findDescendantById(`text-${part.id}`)
return {
id: part.id,
type: part.type,
...((part.type === "text" || part.type === "reasoning") && { textLength: part.text.length }),
...(renderable && {
renderable: {
id: renderable.id,
x: renderable.x,
y: renderable.y,
width: renderable.width,
height: renderable.height,
visible: renderable.visible,
},
}),
}
}),
})),
})
toast.show({ message: `Session render state written to ${filename}`, variant: "success" })
}
const local = useLocal()
function enterChild(sessionID: string) {
@ -1017,6 +1062,23 @@ export function Session() {
dialog.clear()
},
},
...(SESSION_RENDER_STATE_CHANNELS.has(InstallationChannel)
? [
{
title: "Dump session render state",
value: "session.debug.dump_render_state",
category: "Debug",
run: async () => {
try {
await dumpRenderState()
} catch {
toast.show({ message: "Failed to dump session render state", variant: "error" })
}
dialog.clear()
},
},
]
: []),
{
title: "Background subagents",
value: "session.background",