Files
stablyai--orca/config/scripts/linux-wayland-renderer-diagnostics.mjs
2026-07-13 13:05:33 +08:00

165 lines
6.2 KiB
JavaScript

const pollTimeoutMs = 2_500
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async function pollRendererDiagnostics(read) {
const readPromise = Promise.resolve().then(read)
readPromise.catch(() => undefined)
// Why: the Wayland GPU stall can freeze renderer protocol calls, so
// diagnostics need a short deadline too.
const result = await Promise.race([
readPromise.then((value) => ({ timedOut: false, value })),
delay(pollTimeoutMs).then(() => ({ timedOut: true, value: null }))
])
if (result.timedOut) {
throw new Error(`Timed out polling renderer diagnostics after ${pollTimeoutMs}ms.`)
}
return result.value
}
export async function collectRendererDiagnostics(page) {
if (!page) {
return null
}
try {
return await pollRendererDiagnostics(() =>
page.evaluate(async () => {
const timed = (label, promise) =>
Promise.race([
Promise.resolve(promise).then(
(value) => ({ value }),
(error) => ({
error: error instanceof Error ? error.message : String(error)
})
),
new Promise((resolve) =>
setTimeout(() => resolve({ error: `Timed out collecting ${label}` }), 1_000)
)
])
const rectFor = (element) => {
if (!(element instanceof Element)) {
return null
}
const rect = element.getBoundingClientRect()
return {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height
}
}
const styleFor = (element) => {
if (!(element instanceof Element)) {
return null
}
const style = getComputedStyle(element)
return {
display: style.display,
visibility: style.visibility,
opacity: style.opacity
}
}
const store = window.__store
const state = store?.getState?.()
const worktreeId = state?.activeWorktreeId ?? null
const tabId = state?.activeTabId ?? null
const tabs = worktreeId ? (state?.tabsByWorktree?.[worktreeId] ?? []) : []
const activeTab = tabId ? (tabs.find((tab) => tab.id === tabId) ?? null) : null
const layout = tabId ? (state?.terminalLayoutsByTabId?.[tabId] ?? null) : null
const tabCount = worktreeId ? (state?.tabsByWorktree?.[worktreeId]?.length ?? 0) : null
const manager = tabId ? window.__paneManagers?.get(tabId) : null
const activePane = manager?.getActivePane?.() ?? null
const paneDiagnostics = (manager?.getPanes?.() ?? []).map((pane) => {
const xtermElement = pane.container?.querySelector?.('.xterm') ?? pane.terminal?.element
const viewport = pane.container?.querySelector?.('.xterm-viewport') ?? null
const buffer = pane.terminal?.buffer?.active ?? null
return {
paneId: pane.id ?? null,
leafId: pane.leafId ?? null,
isActive: activePane?.id === pane.id,
datasetPtyId: pane.container?.dataset?.ptyId ?? null,
terminalCols: pane.terminal?.cols ?? null,
terminalRows: pane.terminal?.rows ?? null,
bufferState: buffer
? {
baseY: buffer.baseY,
viewportY: buffer.viewportY,
cursorY: buffer.cursorY,
length: buffer.length
}
: null,
containerConnected: pane.container?.isConnected ?? null,
containerRect: rectFor(pane.container),
containerStyle: styleFor(pane.container),
xtermRect: rectFor(xtermElement),
viewportRect: rectFor(viewport),
viewportScroll: viewport
? {
scrollTop: viewport.scrollTop,
scrollHeight: viewport.scrollHeight,
clientHeight: viewport.clientHeight
}
: null
}
})
return {
hasStore: Boolean(store),
workspaceSessionReady: state?.workspaceSessionReady ?? null,
hydrationSucceeded: state?.hydrationSucceeded ?? null,
activeRepoId: state?.activeRepoId ?? null,
activeWorktreeId: worktreeId,
activeWorkspaceKey: state?.activeWorkspaceKey ?? null,
activeTabType: state?.activeTabType ?? null,
activeTabId: tabId,
repoIds: (state?.repos ?? []).map((repo) => repo.id),
worktreeIdsByRepo: Object.fromEntries(
Object.entries(state?.worktreesByRepo ?? {}).map(([id, worktrees]) => [
id,
worktrees.map((worktree) => worktree.id)
])
),
tabIdsByWorktree: Object.fromEntries(
Object.entries(state?.tabsByWorktree ?? {}).map(([id, worktreeTabs]) => [
id,
worktreeTabs.map((tab) => tab.id)
])
),
tabCount,
activeTab: activeTab
? {
id: activeTab.id,
ptyId: activeTab.ptyId ?? null,
title: activeTab.title ?? null,
pendingActivationSpawn: activeTab.pendingActivationSpawn ?? null
}
: null,
livePtyIdsForTab: tabId ? (state?.ptyIdsByTabId?.[tabId] ?? null) : null,
terminalLayout: layout
? {
activeLeafId: layout.activeLeafId ?? null,
expandedLeafId: layout.expandedLeafId ?? null,
ptyIdsByLeafId: layout.ptyIdsByLeafId ?? null,
root: layout.root ?? null
}
: null,
hasPaneManager: Boolean(manager),
paneDiagnostics,
renderingDiagnostics: manager?.getRenderingDiagnostics?.() ?? null,
ptyConnectDiagnostics: globalThis.__ptyConnectDiag ?? null,
ptySessions: await timed('PTY sessions', window.api?.pty?.listSessions?.()),
rendererDeliveryDebug: await timed(
'renderer delivery debug',
window.api?.pty?.getRendererDeliveryDebugSnapshot?.()
)
}
})
)
} catch (error) {
return {
error: error instanceof Error ? error.message : String(error)
}
}
}