Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const toolNames = [
"fetch_instructions",
"codebase_search",
"update_todo_list",
"web_preview",
] as const

export const toolNamesSchema = z.enum(toolNames)
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export const commandIds = [
"focusInput",
"acceptInput",
"focusPanel",
"openWebPreview",
"getSelectedElement",
] as const

export type CommandId = (typeof commandIds)[number]
Expand Down
27 changes: 27 additions & 0 deletions src/activate/registerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TelemetryService } from "@roo-code/telemetry"
import { Package } from "../shared/package"
import { getCommand } from "../utils/commands"
import { ClineProvider } from "../core/webview/ClineProvider"
import { WebPreviewProvider } from "../core/webview/WebPreviewProvider"
import { ContextProxy } from "../core/config/ContextProxy"
import { focusPanel } from "../utils/focusPanel"

Expand All @@ -32,6 +33,7 @@ export function getVisibleProviderOrLog(outputChannel: vscode.OutputChannel): Cl
// Store panel references in both modes
let sidebarPanel: vscode.WebviewView | undefined = undefined
let tabPanel: vscode.WebviewPanel | undefined = undefined
let webPreviewProvider: WebPreviewProvider | undefined = undefined

/**
* Get the currently active panel
Expand All @@ -57,6 +59,10 @@ export function setPanel(
}
}

export function setWebPreviewProvider(provider: WebPreviewProvider) {
webPreviewProvider = provider
}

export type RegisterCommandOptions = {
context: vscode.ExtensionContext
outputChannel: vscode.OutputChannel
Expand Down Expand Up @@ -218,6 +224,27 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt

visibleProvider.postMessageToWebview({ type: "acceptInput" })
},
openWebPreview: async (url?: string) => {
if (!webPreviewProvider) {
outputChannel.appendLine("Web Preview Provider not initialized")
return
}

if (url) {
await webPreviewProvider.openUrl(url)
} else {
// Show the web preview panel without navigating
await vscode.commands.executeCommand("roo-code.webPreview.focus")
}
},
getSelectedElement: async () => {
if (!webPreviewProvider) {
outputChannel.appendLine("Web Preview Provider not initialized")
return null
}

return webPreviewProvider.getSelectedElementContext()
},
})

export const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {
Expand Down
6 changes: 6 additions & 0 deletions src/core/assistant-message/presentAssistantMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { askFollowupQuestionTool } from "../tools/askFollowupQuestionTool"
import { switchModeTool } from "../tools/switchModeTool"
import { attemptCompletionTool } from "../tools/attemptCompletionTool"
import { newTaskTool } from "../tools/newTaskTool"
import { webPreviewTool } from "../tools/webPreviewTool"

import { checkpointSave } from "../checkpoints"
import { updateTodoListTool } from "../tools/updateTodoListTool"
Expand Down Expand Up @@ -214,6 +215,8 @@ export async function presentAssistantMessage(cline: Task) {
const modeName = getModeBySlug(mode, customModes)?.name ?? mode
return `[${block.name} in ${modeName} mode: '${message}']`
}
case "web_preview":
return `[${block.name} for '${block.params.action}'${block.params.url ? ` - ${block.params.url}` : ""}]`
}
}

Expand Down Expand Up @@ -522,6 +525,9 @@ export async function presentAssistantMessage(cline: Task) {
askFinishSubTaskApproval,
)
break
case "web_preview":
await webPreviewTool(cline, block, askApproval, handleError, pushToolResult)
break
}

break
Expand Down
91 changes: 91 additions & 0 deletions src/core/tools/webPreviewTool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import * as vscode from "vscode"
import { Task } from "../task/Task"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
import { ToolUse, AskApproval, HandleError, PushToolResult } from "../../shared/tools"

export async function webPreviewTool(
cline: Task,
block: ToolUse,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
) {
const action = block.params.action
const url = block.params.url

if (!action) {
cline.consecutiveMistakeCount++
const errorMsg = await cline.sayAndCreateMissingParamError("web_preview", "action")
pushToolResult(formatResponse.toolError(errorMsg))
return
}

if (action === "open" && !url) {
cline.consecutiveMistakeCount++
const errorMsg = await cline.sayAndCreateMissingParamError("web_preview", "url")
pushToolResult(formatResponse.toolError(errorMsg))
return
}

try {
// Handle partial message
if (block.partial) {
const partialMessage = JSON.stringify({
tool: "web_preview",
action,
url,
} satisfies ClineSayTool)
await cline.ask("tool", partialMessage, block.partial).catch(() => {})
return
}

// Ask for approval
const completeMessage = JSON.stringify({
tool: "web_preview",
action,
url,
} satisfies ClineSayTool)

const { response, text, images } = await cline.ask("tool", completeMessage, false)

if (response !== "yesButtonClicked") {
if (text) {
await cline.say("user_feedback", text, images)
}
cline.didRejectTool = true
pushToolResult(formatResponse.toolDenied())
return
}

if (text) {
await cline.say("user_feedback", text, images)
}

// Execute the web preview action
if (action === "open") {
// Open the web preview panel with the URL
await vscode.commands.executeCommand("roo-code.openWebPreview", url)
pushToolResult(formatResponse.toolResult(`Opened web preview for: ${url}`))
} else if (action === "select") {
// Get the current selected element context from the preview
const selectedContext = await vscode.commands.executeCommand("roo-code.getSelectedElement")
if (selectedContext) {
pushToolResult(
formatResponse.toolResult(`Selected element context:\n${JSON.stringify(selectedContext, null, 2)}`),
)
} else {
pushToolResult(
formatResponse.toolResult(
"No element is currently selected. Click on an element in the preview to select it.",
),
)
}
} else {
pushToolResult(formatResponse.toolError(`Unknown action: ${action}. Valid actions are: open, select`))
}
} catch (error) {
await handleError("web preview operation", error instanceof Error ? error : new Error(String(error)))
pushToolResult(formatResponse.toolError(error instanceof Error ? error.message : String(error)))
}
}
Loading
Loading