refactor(vscode): share the cached-state retry schedule across providers
The staggered re-send lived as three near-identical private methods in the chat view, agent manager, and session editor providers. One helper now owns the delays, the connected gate, and the replaced-target guard; the providers keep thin wrappers over their own panel/view identity. Also restores the package.json trailing newline the branch dropped.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import { scheduleCachedStateRetries } from './webviewCachedStateRetry';
|
||||||
import { handleBridgeMessage, type BridgeRequest, type BridgeResponse } from './bridge';
|
import { handleBridgeMessage, type BridgeRequest, type BridgeResponse } from './bridge';
|
||||||
import { getThemeKindName } from './theme';
|
import { getThemeKindName } from './theme';
|
||||||
import type { OpenCodeManager, ConnectionStatus } from './opencode';
|
import type { OpenCodeManager, ConnectionStatus } from './opencode';
|
||||||
@@ -24,30 +25,16 @@ export class AgentManagerPanelProvider {
|
|||||||
private readonly _webviewDevServerUrl: string | null;
|
private readonly _webviewDevServerUrl: string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The webview only leaves its initial loading screen once it receives a
|
* See webviewCachedStateRetry.ts — a single postMessage can be dropped
|
||||||
* `connectionStatus: connected` message. VS Code drops postMessage calls
|
* before the webview bridge is ready, leaving the loading screen stuck.
|
||||||
* made before the webview's acquireVsCodeApi bridge is ready (common in
|
|
||||||
* code-server / slow or flaky networks), so a single send can be lost
|
|
||||||
* forever. Re-send at staggered delays until the target panel is replaced.
|
|
||||||
*/
|
*/
|
||||||
private _scheduleCachedStateRetries(targetPanel: vscode.WebviewPanel | undefined): void {
|
private _scheduleCachedStateRetries(targetPanel: vscode.WebviewPanel | undefined): void {
|
||||||
if (this._cachedStatus !== 'connected') {
|
scheduleCachedStateRetries({
|
||||||
return;
|
target: targetPanel ?? this._panel,
|
||||||
}
|
getCurrent: () => this._panel,
|
||||||
const panel = targetPanel ?? this._panel;
|
isConnected: () => this._cachedStatus === 'connected',
|
||||||
if (!panel) {
|
send: () => this._sendCachedState(),
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
const delaysMs = [500, 1500, 3500, 7000, 12000, 20000];
|
|
||||||
for (const delayMs of delaysMs) {
|
|
||||||
setTimeout(() => {
|
|
||||||
// Only re-send if this exact panel is still the active one.
|
|
||||||
if (this._panel !== panel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._sendCachedState();
|
|
||||||
}, delayMs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import { scheduleCachedStateRetries } from './webviewCachedStateRetry';
|
||||||
import { handleBridgeMessage, type BridgeRequest, type BridgeResponse } from './bridge';
|
import { handleBridgeMessage, type BridgeRequest, type BridgeResponse } from './bridge';
|
||||||
import { getThemeKindName } from './theme';
|
import { getThemeKindName } from './theme';
|
||||||
import type { OpenCodeManager, ConnectionStatus } from './opencode';
|
import type { OpenCodeManager, ConnectionStatus } from './opencode';
|
||||||
@@ -59,30 +60,16 @@ export class ChatViewProvider implements vscode.WebviewViewProvider {
|
|||||||
private readonly _MAX_RETRIES = 3;
|
private readonly _MAX_RETRIES = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The webview only leaves its initial loading screen once it receives a
|
* See webviewCachedStateRetry.ts — a single postMessage can be dropped
|
||||||
* `connectionStatus: connected` message. VS Code drops postMessage calls
|
* before the webview bridge is ready, leaving the loading screen stuck.
|
||||||
* made before the webview's acquireVsCodeApi bridge is ready (common in
|
|
||||||
* code-server / slow or flaky networks), so a single send can be lost
|
|
||||||
* forever. Re-send at staggered delays until the target view is replaced.
|
|
||||||
*/
|
*/
|
||||||
private _scheduleCachedStateRetries(targetView: vscode.WebviewView | undefined): void {
|
private _scheduleCachedStateRetries(targetView: vscode.WebviewView | undefined): void {
|
||||||
if (this._cachedStatus !== 'connected') {
|
scheduleCachedStateRetries({
|
||||||
return;
|
target: targetView ?? this._view,
|
||||||
}
|
getCurrent: () => this._view,
|
||||||
const view = targetView ?? this._view;
|
isConnected: () => this._cachedStatus === 'connected',
|
||||||
if (!view) {
|
send: () => this._sendCachedState(),
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
const delaysMs = [500, 1500, 3500, 7000, 12000, 20000];
|
|
||||||
for (const delayMs of delaysMs) {
|
|
||||||
setTimeout(() => {
|
|
||||||
// Only re-send if this exact view is still the active one.
|
|
||||||
if (this._view !== view) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._sendCachedState();
|
|
||||||
}, delayMs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createMessageId(): string {
|
private _createMessageId(): string {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import { scheduleCachedStateRetries } from './webviewCachedStateRetry';
|
||||||
import { handleBridgeMessage, type BridgeRequest, type BridgeResponse } from './bridge';
|
import { handleBridgeMessage, type BridgeRequest, type BridgeResponse } from './bridge';
|
||||||
import { getThemeKindName } from './theme';
|
import { getThemeKindName } from './theme';
|
||||||
import type { OpenCodeManager, ConnectionStatus } from './opencode';
|
import type { OpenCodeManager, ConnectionStatus } from './opencode';
|
||||||
@@ -50,26 +51,16 @@ export class SessionEditorPanelProvider {
|
|||||||
private readonly _webviewDevServerUrl: string | null;
|
private readonly _webviewDevServerUrl: string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The webview only leaves its initial loading screen once it receives a
|
* See webviewCachedStateRetry.ts — a single postMessage can be dropped
|
||||||
* `connectionStatus: connected` message. VS Code drops postMessage calls
|
* before the webview bridge is ready, leaving the loading screen stuck.
|
||||||
* made before the webview's acquireVsCodeApi bridge is ready (common in
|
|
||||||
* code-server / slow or flaky networks), so a single send can be lost
|
|
||||||
* forever. Re-send at staggered delays until the target panel is replaced.
|
|
||||||
*/
|
*/
|
||||||
private _scheduleCachedStateRetries(panelId: string, entry: SessionPanelState): void {
|
private _scheduleCachedStateRetries(panelId: string, entry: SessionPanelState): void {
|
||||||
if (this._cachedStatus !== 'connected') {
|
scheduleCachedStateRetries({
|
||||||
return;
|
target: entry.panel,
|
||||||
}
|
getCurrent: () => this._panels.get(panelId)?.panel,
|
||||||
const delaysMs = [500, 1500, 3500, 7000, 12000, 20000];
|
isConnected: () => this._cachedStatus === 'connected',
|
||||||
for (const delayMs of delaysMs) {
|
send: () => this._sendCachedStateToPanel(entry),
|
||||||
setTimeout(() => {
|
});
|
||||||
// Only re-send if this exact panel is still registered.
|
|
||||||
if (this._panels.get(panelId)?.panel !== entry.panel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._sendCachedStateToPanel(entry);
|
|
||||||
}, delayMs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* The webview only leaves its initial loading screen once it receives a
|
||||||
|
* `connectionStatus: connected` message. VS Code drops postMessage calls made
|
||||||
|
* before the webview's acquireVsCodeApi bridge is ready (common in
|
||||||
|
* code-server / slow or flaky networks), so a single send can be lost
|
||||||
|
* forever. Re-sending the cached state at staggered delays bounds the wait
|
||||||
|
* without needing a webview-side ack protocol; the payload is idempotent
|
||||||
|
* (connection status + window focus), so duplicate deliveries are harmless.
|
||||||
|
*/
|
||||||
|
const CACHED_STATE_RETRY_DELAYS_MS = [500, 1500, 3500, 7000, 12000, 20000];
|
||||||
|
|
||||||
|
export function scheduleCachedStateRetries<Target>(input: {
|
||||||
|
/** The panel/view the retries belong to. */
|
||||||
|
target: Target | undefined;
|
||||||
|
/** Reads the provider's CURRENT panel/view, so a replaced target stops its stale retries. */
|
||||||
|
getCurrent: () => Target | undefined;
|
||||||
|
/** Retries only make sense for the connected transition. */
|
||||||
|
isConnected: () => boolean;
|
||||||
|
/** Re-sends the provider's cached state. */
|
||||||
|
send: () => void;
|
||||||
|
}): void {
|
||||||
|
if (!input.isConnected()) return;
|
||||||
|
const target = input.target;
|
||||||
|
if (!target) return;
|
||||||
|
for (const delayMs of CACHED_STATE_RETRY_DELAYS_MS) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (input.getCurrent() !== target) return;
|
||||||
|
input.send();
|
||||||
|
}, delayMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user