fix webview

This commit is contained in:
Trevor Hudson
2025-05-07 09:59:22 -07:00
parent 552b562269
commit 588e735493
8 changed files with 95 additions and 22 deletions
+2 -1
View File
@@ -1672,6 +1672,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
planActSeparateModelsSetting,
globalClineRulesToggles,
shellIntegrationTimeout,
showWelcome,
} = await getAllExtensionState(this.context)
const localClineRulesToggles =
@@ -1704,7 +1705,7 @@ Here is the project's README to help you get started:\n\n${mcpDetails.readmeCont
mcpMarketplaceEnabled,
telemetrySetting,
planActSeparateModelsSetting,
showWelcome: false,
showWelcome,
vscMachineId: vscode.env.machineId,
globalClineRulesToggles: globalClineRulesToggles || {},
localClineRulesToggles: localClineRulesToggles || {},
+1
View File
@@ -81,5 +81,6 @@ export type GlobalStateKey =
| "favoritedModelIds"
| "requestTimeoutMs"
| "shellIntegrationTimeout"
| "showWelcome"
export type LocalStateKey = "localClineRulesToggles"
+3
View File
@@ -131,6 +131,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
globalClineRulesToggles,
requestTimeoutMs,
shellIntegrationTimeout,
showWelcome,
] = await Promise.all([
getGlobalState(context, "apiProvider") as Promise<ApiProvider | undefined>,
getGlobalState(context, "apiModelId") as Promise<string | undefined>,
@@ -210,6 +211,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
getGlobalState(context, "globalClineRulesToggles") as Promise<ClineRulesToggles | undefined>,
getGlobalState(context, "requestTimeoutMs") as Promise<number | undefined>,
getGlobalState(context, "shellIntegrationTimeout") as Promise<number | undefined>,
getGlobalState(context, "showWelcome") as Promise<boolean | undefined>,
])
let apiProvider: ApiProvider
@@ -334,6 +336,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
telemetrySetting: telemetrySetting || "unset",
planActSeparateModelsSetting,
shellIntegrationTimeout: shellIntegrationTimeout || 4000,
showWelcome,
}
}
+41 -2
View File
@@ -8,6 +8,7 @@ import { findLast } from "@shared/array"
import { readFile } from "fs/promises"
import path from "node:path"
import { WebviewType } from "@shared/WebviewMessage"
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts
@@ -87,12 +88,31 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
// WebviewView and WebviewPanel have all the same properties except for this visibility listener
// panel
webviewView.onDidChangeViewState(
() => {
async () => {
if (this.view?.visible) {
// Check if showWelcome flag is set in global state
const showWelcome = await this.controller.context.globalState.get<boolean>("showWelcome")
// Send didBecomeVisible action
this.controller.postMessageToWebview({
type: "action",
action: "didBecomeVisible",
})
// If showWelcome flag is set, send showWelcome message and clear the flag
// We're now sending it to all webviews to ensure it's displayed
if (showWelcome) {
console.log(`Sending showWelcome message to ${this.webviewType} webview`)
this.controller.postMessageToWebview({
type: "showWelcome",
})
// Add a delay before clearing the flag to ensure the webview has time to process it
await setTimeoutPromise(1000)
// Clear the flag after sending the message
await this.controller.context.globalState.update("showWelcome", undefined)
}
}
},
null,
@@ -101,12 +121,30 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
} else if ("onDidChangeVisibility" in webviewView) {
// sidebar
webviewView.onDidChangeVisibility(
() => {
async () => {
if (this.view?.visible) {
// Check if showWelcome flag is set in global state
const showWelcome = await this.controller.context.globalState.get<boolean>("showWelcome")
// Send didBecomeVisible action
this.controller.postMessageToWebview({
type: "action",
action: "didBecomeVisible",
})
// If showWelcome flag is set, send showWelcome message and clear the flag
if (showWelcome) {
console.log(`Sending showWelcome message to ${this.webviewType} webview`)
this.controller.postMessageToWebview({
type: "showWelcome",
})
// Add a delay before clearing the flag to ensure the webview has time to process it
await setTimeoutPromise(1000)
// Clear the flag after sending the message
await this.controller.context.globalState.update("showWelcome", undefined)
}
}
},
null,
@@ -222,6 +260,7 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="webview-type" content="${this.webviewType}">
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<link href="${codiconsUri}" rel="stylesheet" />
<link href="${katexCssUri}" rel="stylesheet" />
+30 -12
View File
@@ -82,37 +82,46 @@ export function activate(context: vscode.ExtensionContext) {
}),
)
const openClineInNewTab = async (): Promise<WebviewProvider> => {
const openClineInNewTab = async (isWelcome = false): Promise<WebviewProvider> => {
Logger.log("Opening Cline in new tab")
// (this example uses webviewProvider activation event which is necessary to deserialize cached webview, but since we use retainContextWhenHidden, we don't need to use that event)
// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
const tabWebview = new WebviewProvider(context, outputChannel, "tab")
//const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined
const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
let targetCol: vscode.ViewColumn
// Check if there are any visible text editors, otherwise open a new group to the right
const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
if (!hasVisibleEditors) {
await vscode.commands.executeCommand("workbench.action.newGroupRight")
if (!isWelcome) {
//const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined
const lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))
// Check if there are any visible text editors, otherwise open a new group to the right
const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0
if (!hasVisibleEditors) {
await vscode.commands.executeCommand("workbench.action.newGroupRight")
}
targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
} else {
targetCol = vscode.ViewColumn.One
}
const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two
const panel = vscode.window.createWebviewPanel(WebviewProvider.tabPanelId, "Cline", targetCol, {
enableScripts: true,
retainContextWhenHidden: true,
localResourceRoots: [context.extensionUri],
})
// TODO: use better svg icon with light and dark variants (see https://stackoverflow.com/questions/58365687/vscode-extension-iconpath)
panel.iconPath = {
light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "robot_panel_light.png"),
dark: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "robot_panel_dark.png"),
}
tabWebview.resolveWebviewView(panel)
await tabWebview.resolveWebviewView(panel)
// Lock the editor group so clicking on files doesn't open them over the panel
await setTimeoutPromise(100)
await vscode.commands.executeCommand("workbench.action.lockEditorGroup")
if (isWelcome) {
panel.reveal(targetCol)
}
return tabWebview
}
@@ -432,8 +441,17 @@ export function activate(context: vscode.ExtensionContext) {
const previous = context.globalState.get<string>(VERSION_KEY)
if (!previous) {
const tab = await openClineInNewTab()
console.log("First time installation detected, setting showWelcome flag")
// Set the showWelcome flag to true in global state
await context.globalState.update("showWelcome", true)
// Open a single tab with the welcome screen
const tab = await openClineInNewTab(true)
console.log("Tab opened for welcome screen")
// Send the showWelcome message directly to the webview
await tab.controller.postMessageToWebview({ type: "showWelcome" })
console.log("Sent showWelcome message directly to webview")
// persist for the next activation
await context.globalState.update(VERSION_KEY, current)
+1
View File
@@ -144,6 +144,7 @@ export interface ExtensionState {
localClineRulesToggles: ClineRulesToggles
localCursorRulesToggles: ClineRulesToggles
localWindsurfRulesToggles: ClineRulesToggles
showWelcome?: boolean
}
export interface ClineMessage {
+17 -2
View File
@@ -14,12 +14,21 @@ import { McpViewTab } from "@shared/mcp"
import WelcomeWrapper from "./components/welcome/WelcomeWrapper"
const AppContent = () => {
const { didHydrateState, shouldShowAnnouncement, showMcp, mcpTab, showWelcome } = useExtensionState()
const { didHydrateState, shouldShowAnnouncement, showMcp, mcpTab } = useExtensionState()
const [showSettings, setShowSettings] = useState(false)
const hideSettings = useCallback(() => setShowSettings(false), [])
const [showHistory, setShowHistory] = useState(false)
const [showAccount, setShowAccount] = useState(false)
const [showAnnouncement, setShowAnnouncement] = useState(false)
// Use local state for welcome view, initialized from extension state
const [showWelcomeLocal, setShowWelcomeLocal] = useState(true)
const { setShowWelcome } = useExtensionState()
// Sync local state with extension state
useEffect(() => {
console.log("Setting showWelcome in extension state to true")
setShowWelcome(true)
}, [setShowWelcome])
const { setShowMcp, setMcpTab } = useExtensionState()
@@ -31,7 +40,12 @@ const AppContent = () => {
const handleMessage = useCallback(
(e: MessageEvent) => {
const message: ExtensionMessage = e.data
console.log("Received message in App.tsx:", message)
switch (message.type) {
case "showWelcome":
console.log("Received showWelcome message in App.tsx")
setShowWelcomeLocal(true)
break
case "action":
switch (message.action!) {
case "settingsButtonClicked":
@@ -66,6 +80,7 @@ const AppContent = () => {
setShowHistory(false)
closeMcpView()
setShowAccount(false)
setShowWelcomeLocal(false) // Hide welcome view when chat button is clicked
break
}
break
@@ -98,7 +113,7 @@ const AppContent = () => {
return (
<>
{showWelcome ? (
{showWelcomeLocal ? (
<WelcomeWrapper />
) : (
<>
@@ -118,11 +118,6 @@ const WelcomeTabView = memo(({ onToggleView, showApiOptions, setShowApiOptions }
<button className="primary-button" onClick={handleLogin}>
Let's Create your Account!
</button>
{showApiOptions !== undefined && setShowApiOptions !== undefined && !showApiOptions && (
<button className="secondary-button" onClick={() => setShowApiOptions(true)}>
Use your own API key
</button>
)}
</div>
{showApiOptions !== undefined && <ApiOptionsSection showApiOptions={showApiOptions} />}