Skip to content

Commit 2075784

Browse files
committed
feat(ClineProvider): adding simple diagnostics for grey screen debug
1 parent 494650b commit 2075784

4 files changed

Lines changed: 74 additions & 10 deletions

File tree

src/core/webview/ClineProvider.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,8 @@ export class ClineProvider
848848
const viewStateDisposable = webviewView.onDidChangeViewState(() => {
849849
if (this.view?.visible) {
850850
this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
851+
} else {
852+
this.logWebviewHiddenDiagnostics()
851853
}
852854
})
853855

@@ -857,6 +859,8 @@ export class ClineProvider
857859
const visibilityDisposable = webviewView.onDidChangeVisibility(() => {
858860
if (this.view?.visible) {
859861
this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
862+
} else {
863+
this.logWebviewHiddenDiagnostics()
860864
}
861865
})
862866

@@ -2913,6 +2917,21 @@ export class ClineProvider
29132917
return this.clineStack[this.clineStack.length - 1]
29142918
}
29152919

2920+
private logWebviewHiddenDiagnostics(): void {
2921+
const task = this.getCurrentTask()
2922+
if (!task || task.abort || task.abandoned) {
2923+
return
2924+
}
2925+
this.log(
2926+
`[Zoo Code] Webview hidden during active task.\n` +
2927+
` taskId: ${task.taskId}\n` +
2928+
` messageCount: ${task.clineMessages.length}\n` +
2929+
` stackDepth: ${this.clineStack.length}\n` +
2930+
` timestamp: ${new Date().toISOString()}\n` +
2931+
`If the panel appears gray after this, share this log with support@zoocode.dev`,
2932+
)
2933+
}
2934+
29162935
public getRecentTasks(): string[] {
29172936
if (this.recentTasksCache) {
29182937
return this.recentTasksCache

src/core/webview/__tests__/ClineProvider.spec.ts

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ describe("ClineProvider", () => {
366366
let provider: ClineProvider
367367
let mockContext: vscode.ExtensionContext
368368
let mockOutputChannel: vscode.OutputChannel
369-
let mockWebviewView: vscode.WebviewView
369+
let mockWebviewView: any
370370
let mockPostMessage: any
371371
let updateGlobalStateSpy: any
372372

@@ -445,7 +445,7 @@ describe("ClineProvider", () => {
445445
return { dispose: vi.fn() }
446446
}),
447447
onDidChangeVisibility: vi.fn().mockImplementation(() => ({ dispose: vi.fn() })),
448-
} as unknown as vscode.WebviewView
448+
}
449449

450450
provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext))
451451

@@ -505,6 +505,48 @@ describe("ClineProvider", () => {
505505
expect(mockWebviewView.webview.html).toContain("<!DOCTYPE html>")
506506
})
507507

508+
describe("logWebviewHiddenDiagnostics", () => {
509+
let visibilityCallback: () => void
510+
511+
beforeEach(async () => {
512+
// Capture the visibility callback registered during resolveWebviewView
513+
mockWebviewView.onDidChangeVisibility = vi.fn().mockImplementation((cb: () => void) => {
514+
visibilityCallback = cb
515+
return { dispose: vi.fn() }
516+
})
517+
// @ts-ignore - accessing private property for testing
518+
provider.view = mockWebviewView
519+
await provider.resolveWebviewView(mockWebviewView)
520+
;(mockOutputChannel.appendLine as ReturnType<typeof vi.fn>).mockClear()
521+
})
522+
523+
test("does not log when no task is active", () => {
524+
// view becomes hidden with no task on the stack
525+
Object.defineProperty(mockWebviewView, "visible", { value: false, configurable: true })
526+
visibilityCallback()
527+
expect(mockOutputChannel.appendLine).not.toHaveBeenCalled()
528+
})
529+
530+
test("does not log when the active task is aborted", async () => {
531+
const task = new Task(defaultTaskOptions)
532+
Object.defineProperty(task, "taskId", { value: "aborted-task", writable: true })
533+
task.abort = true
534+
await provider.addClineToStack(task)
535+
Object.defineProperty(mockWebviewView, "visible", { value: false, configurable: true })
536+
visibilityCallback()
537+
expect(mockOutputChannel.appendLine).not.toHaveBeenCalled()
538+
})
539+
540+
test("logs task state to output channel when an active task is running", async () => {
541+
const task = new Task(defaultTaskOptions)
542+
Object.defineProperty(task, "taskId", { value: "running-task", writable: true })
543+
await provider.addClineToStack(task)
544+
Object.defineProperty(mockWebviewView, "visible", { value: false, configurable: true })
545+
visibilityCallback()
546+
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(expect.stringContaining("running-task"))
547+
})
548+
})
549+
508550
test("resolveWebviewView sets up webview correctly in development mode even if local server is not running", async () => {
509551
provider = new ClineProvider(
510552
{ ...mockContext, extensionMode: vscode.ExtensionMode.Development },
@@ -2059,7 +2101,7 @@ describe("Project MCP Settings", () => {
20592101
let provider: ClineProvider
20602102
let mockContext: vscode.ExtensionContext
20612103
let mockOutputChannel: vscode.OutputChannel
2062-
let mockWebviewView: vscode.WebviewView
2104+
let mockWebviewView: any
20632105
let mockPostMessage: any
20642106

20652107
beforeEach(async () => {
@@ -2113,7 +2155,7 @@ describe("Project MCP Settings", () => {
21132155
visible: true,
21142156
onDidDispose: vi.fn(),
21152157
onDidChangeVisibility: vi.fn(),
2116-
} as unknown as vscode.WebviewView
2158+
}
21172159
;(vscode.window as any).activeTextEditor = undefined
21182160
;(vscode.workspace.getWorkspaceFolder as any).mockReset()
21192161
provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext))
@@ -2376,7 +2418,7 @@ describe("ClineProvider - Router Models", () => {
23762418
let provider: ClineProvider
23772419
let mockContext: vscode.ExtensionContext
23782420
let mockOutputChannel: vscode.OutputChannel
2379-
let mockWebviewView: vscode.WebviewView
2421+
let mockWebviewView: any
23802422
let mockPostMessage: any
23812423

23822424
beforeEach(() => {
@@ -2435,7 +2477,7 @@ describe("ClineProvider - Router Models", () => {
24352477
return { dispose: vi.fn() }
24362478
}),
24372479
onDidChangeVisibility: vi.fn().mockImplementation(() => ({ dispose: vi.fn() })),
2438-
} as unknown as vscode.WebviewView
2480+
}
24392481

24402482
if (!TelemetryService.hasInstance()) {
24412483
TelemetryService.createInstance([])
@@ -2687,7 +2729,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
26872729
let provider: ClineProvider
26882730
let mockContext: vscode.ExtensionContext
26892731
let mockOutputChannel: vscode.OutputChannel
2690-
let mockWebviewView: vscode.WebviewView
2732+
let mockWebviewView: any
26912733
let mockPostMessage: any
26922734
let defaultTaskOptions: TaskOptions
26932735

@@ -2756,7 +2798,7 @@ describe("ClineProvider - Comprehensive Edit/Delete Edge Cases", () => {
27562798
return { dispose: vi.fn() }
27572799
}),
27582800
onDidChangeVisibility: vi.fn().mockImplementation(() => ({ dispose: vi.fn() })),
2759-
} as unknown as vscode.WebviewView
2801+
}
27602802

27612803
provider = new ClineProvider(mockContext, mockOutputChannel, "sidebar", new ContextProxy(mockContext))
27622804

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,10 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
14851485
],
14861486
)
14871487

1488-
const computeMessageKey = useCallback((_index: number, messageOrGroup: ClineMessage) => messageOrGroup.ts, [])
1488+
const computeMessageKey = useCallback(
1489+
(index: number, messageOrGroup: ClineMessage) => `${messageOrGroup.ts}-${index}`,
1490+
[],
1491+
)
14891492

14901493
// Function to handle mode switching
14911494
const switchToNextMode = useCallback(() => {

webview-ui/src/components/chat/__tests__/ChatView.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ describe("ChatView - Virtualization Configuration", () => {
536536

537537
expect(mockVirtuosoState.lastConfig?.defaultItemHeight).toBe(180)
538538
expect(mockVirtuosoState.lastConfig?.increaseViewportBy).toEqual({ top: 600, bottom: 800 })
539-
expect(mockVirtuosoState.lastConfig?.computeItemKey?.(1, { type: "say", ts: rowTs })).toBe(rowTs)
539+
expect(mockVirtuosoState.lastConfig?.computeItemKey?.(1, { type: "say", ts: rowTs })).toBe(`${rowTs}-1`)
540540
})
541541
})
542542

0 commit comments

Comments
 (0)