Skip to content

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Jul 20, 2025

Fixes #5971

Summary

This PR implements an integrated web preview feature with element selection capabilities for AI context, as requested in issue #5971. The implementation provides a seamless web development experience by eliminating the need to switch between the IDE and external browsers.

Features Implemented

1. Web Preview Panel

  • Added a new sidebar panel in VSCode for previewing web applications
  • Implemented URL navigation with address bar and refresh functionality
  • Created responsive viewport controls with preset device sizes (Desktop, Tablet, Mobile) and custom dimensions

2. Element Selection & Context Extraction

  • Click-to-select functionality with visual highlighting
  • Comprehensive element context extraction including:
    • HTML snippet
    • CSS selector path
    • XPath
    • Position and dimensions
    • Computed styles
    • Element attributes

3. AI Integration

  • Created web_preview tool for AI assistant integration
  • Allows AI to open URLs in the preview panel
  • Enables AI to access selected element context for better assistance
  • Integrated with the existing tool approval flow

4. Technical Implementation

  • WebPreviewProvider: Core provider class managing the webview panel lifecycle
  • React UI Component: Modern UI with VSCode webview UI toolkit integration
  • Message Passing: Bidirectional communication between extension and webview
  • Type Safety: Full TypeScript support with proper type definitions

Changes Made

  • Added src/core/webview/WebPreviewProvider.ts - Main provider class
  • Added webview-ui/src/components/web-preview/WebPreview.tsx - React UI component
  • Added webview-ui/src/components/web-preview/WebPreview.css - Styling
  • Added src/core/tools/webPreviewTool.ts - AI tool integration
  • Updated src/extension.ts - Registered the preview provider
  • Updated src/activate/registerCommands.ts - Added preview commands
  • Updated src/package.json - Added view container and commands
  • Updated type definitions across the codebase for tool integration
  • Updated Vite configuration for new webview entry point

Testing

  • Built successfully with no TypeScript errors
  • ESLint checks pass
  • Manual testing shows:
    • Preview panel opens correctly in sidebar
    • URL navigation works as expected
    • Element selection highlights elements on hover
    • Element context is properly extracted and sent to extension
    • AI tool integration functions correctly

Screenshots

The implementation matches the requested functionality from the issue, providing an integrated preview experience similar to Windsurf IDE.

Next Steps

This implementation provides a solid foundation that can be extended with additional features like:

  • Browser DevTools integration
  • Network request monitoring
  • Console output capture
  • Multiple preview tabs
  • Preview history navigation

Important

Adds integrated web preview with element selection and AI integration in VSCode, including a new sidebar panel and commands.

  • Web Preview Panel:
    • Adds a sidebar panel in VSCode for web app preview with URL navigation and responsive viewport controls.
    • Implements WebPreviewProvider in WebPreviewProvider.ts to manage the webview lifecycle.
  • Element Selection & Context Extraction:
    • Enables click-to-select with visual highlighting and context extraction (HTML, CSS, XPath, etc.).
    • Uses WebPreview.tsx for React UI component and WebPreview.css for styling.
  • AI Integration:
    • Introduces web_preview tool in webPreviewTool.ts for AI assistant integration.
    • Allows AI to open URLs and access selected element context.
  • Commands and Configuration:
    • Adds openWebPreview and getSelectedElement commands in registerCommands.ts.
    • Updates package.json to register new commands and view.
  • Miscellaneous:
    • Updates vite.config.ts for new webview entry point.
    • Adds webPreview.html for webview setup.

This description was created by Ellipsis for ab7ccf1. You can customize this summary. It will automatically update as commits are pushed.

- Add WebPreviewProvider to manage preview panel in sidebar
- Create React-based UI with URL navigation and viewport controls
- Implement element selection with context extraction (HTML, CSS, XPath)
- Add web_preview tool for AI integration
- Register commands and update package.json
- Update type definitions and tool configurations

This implementation provides:
- Web preview panel in VSCode sidebar
- Click-to-select elements with visual highlighting
- Element context extraction for AI assistance
- Responsive viewport controls
- Integration with Roo Code AI through web_preview tool
@roomote roomote bot requested review from cte, jr and mrubens as code owners July 20, 2025 04:08
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Jul 20, 2025
| "finishTask"
| "searchAndReplace"
| "insertContent"
| "web_preview"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typographical issue: The new union value "web_preview" is using snake_case whereas the other values use camelCase (e.g. "finishTask", "searchAndReplace", "insertContent"). Consider using "webPreview" for consistency.

{state.url ? (
<iframe
ref={iframeRef}
src={state.url}

Check warning

Code scanning / CodeQL

Client-side URL redirect Medium

Untrusted URL redirection depends on a
user-provided value
.

Copilot Autofix

AI 5 months ago

To address the issue, we need to ensure that only trusted URLs are used as the src attribute for the <iframe>. The best way to fix this is to implement a whitelist of allowed URLs or domains and validate state.url against this list before using it. If the URL is not in the whitelist, we should not render it in the iframe.

  1. Define a list of allowed domains or URLs in the component.
  2. Create a utility function to validate a URL against the whitelist.
  3. Before setting state.url, validate the URL. If it is not valid, discard or handle it appropriately (e.g., show an error message).
  4. Update the handleMessage function to include this validation and ensure only safe URLs are stored in the component's state.

Suggested changeset 1
webview-ui/src/components/web-preview/WebPreview.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/webview-ui/src/components/web-preview/WebPreview.tsx b/webview-ui/src/components/web-preview/WebPreview.tsx
--- a/webview-ui/src/components/web-preview/WebPreview.tsx
+++ b/webview-ui/src/components/web-preview/WebPreview.tsx
@@ -37,16 +37,31 @@
 	const [isSelectionMode, setIsSelectionMode] = useState(false)
 	const [highlightedElement, setHighlightedElement] = useState<HTMLElement | null>(null)
 
+	// List of allowed domains for iframe URLs
+	const allowedDomains = ["example.com", "trusted-site.org"]
+
+	// Utility function to validate URLs
+	const isValidUrl = (url: string): boolean => {
+		try {
+			const parsedUrl = new URL(url)
+			return allowedDomains.includes(parsedUrl.hostname)
+		} catch {
+			return false
+		}
+	}
+
 	// Handle messages from extension
 	useEffect(() => {
 		const handleMessage = (event: MessageEvent) => {
 			const message = event.data
 			switch (message.type) {
 				case "updateState":
-					setState(message.state)
-					if (message.state.url) {
+					// Validate the URL before updating state
+					if (message.state.url && isValidUrl(message.state.url)) {
+						setState(message.state)
 						setUrlInput(message.state.url)
-					}
+					} else {
+						console.warn("Invalid URL received
 					break
 			}
 		}
EOF
@@ -37,16 +37,31 @@
const [isSelectionMode, setIsSelectionMode] = useState(false)
const [highlightedElement, setHighlightedElement] = useState<HTMLElement | null>(null)

// List of allowed domains for iframe URLs
const allowedDomains = ["example.com", "trusted-site.org"]

// Utility function to validate URLs
const isValidUrl = (url: string): boolean => {
try {
const parsedUrl = new URL(url)
return allowedDomains.includes(parsedUrl.hostname)
} catch {
return false
}
}

// Handle messages from extension
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
const message = event.data
switch (message.type) {
case "updateState":
setState(message.state)
if (message.state.url) {
// Validate the URL before updating state
if (message.state.url && isValidUrl(message.state.url)) {
setState(message.state)
setUrlInput(message.state.url)
}
} else {
console.warn("Invalid URL received
break
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
{state.url ? (
<iframe
ref={iframeRef}
src={state.url}

Check failure

Code scanning / CodeQL

Client-side cross-site scripting High

Cross-site scripting vulnerability due to
user-provided value
.

Copilot Autofix

AI 5 months ago

To fix this client-side XSS vulnerability, validate and sanitize the URL received from event.data.state.url before using it as the src of the <iframe>. The best way is to enforce that only trusted, well-formed URLs are accepted. This can be achieved by checking that the URL is a valid HTTP/HTTPS URL and/or matches a whitelist of allowed domains.

The fix should be implemented in the handleMessage function around line 42. Before updating the state with a new URL, validate it using the built-in URL constructor and ensure it uses an allowed protocol (http: or https:) and (optionally) matches a trusted domain. If the URL is invalid, ignore the update or show an error. You may also want to add a helper function for this validation.

The changes are limited to the region in the provided file where the message is handled and state is updated, so no changes are needed outside webview-ui/src/components/web-preview/WebPreview.tsx. You may need to add a helper function, imports, and update how setState and setUrlInput are called.


Suggested changeset 1
webview-ui/src/components/web-preview/WebPreview.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/webview-ui/src/components/web-preview/WebPreview.tsx b/webview-ui/src/components/web-preview/WebPreview.tsx
--- a/webview-ui/src/components/web-preview/WebPreview.tsx
+++ b/webview-ui/src/components/web-preview/WebPreview.tsx
@@ -39,22 +39,38 @@
 
 	// Handle messages from extension
 	useEffect(() => {
+		// Helper to validate URLs before use in iframe
+		function isSafeUrl(url: string | undefined): boolean {
+			if (!url) return false;
+			try {
+				const parsed = new URL(url, window.location.origin);
+				// Only allow http(s), optionally restrict to trusted domains
+				return parsed.protocol === "http:" || parsed.protocol === "https:";
+			} catch {
+				return false;
+			}
+		}
+
 		const handleMessage = (event: MessageEvent) => {
 			const message = event.data
 			switch (message.type) {
 				case "updateState":
-					setState(message.state)
-					if (message.state.url) {
-						setUrlInput(message.state.url)
+					// Defensive copy and sanitize url
+					const safeState = { ...message.state }
+					if (safeState.url && !isSafeUrl(safeState.url)) {
+						// If unsafe, ignore url (or set to empty)
+						safeState.url = undefined
 					}
+					setState(safeState)
+					if (safeState.url) {
+						setUrlInput(safeState.url)
+					}
 					break
 			}
 		}
 
-		window.addEventListener("message", handleMessage)
-		return () => window.removeEventListener("message", handleMessage)
-	}, [])
 
+
 	// Navigate to URL
 	const handleNavigate = useCallback(() => {
 		if (urlInput) {
EOF
@@ -39,22 +39,38 @@

// Handle messages from extension
useEffect(() => {
// Helper to validate URLs before use in iframe
function isSafeUrl(url: string | undefined): boolean {
if (!url) return false;
try {
const parsed = new URL(url, window.location.origin);
// Only allow http(s), optionally restrict to trusted domains
return parsed.protocol === "http:" || parsed.protocol === "https:";
} catch {
return false;
}
}

const handleMessage = (event: MessageEvent) => {
const message = event.data
switch (message.type) {
case "updateState":
setState(message.state)
if (message.state.url) {
setUrlInput(message.state.url)
// Defensive copy and sanitize url
const safeState = { ...message.state }
if (safeState.url && !isSafeUrl(safeState.url)) {
// If unsafe, ignore url (or set to empty)
safeState.url = undefined
}
setState(safeState)
if (safeState.url) {
setUrlInput(safeState.url)
}
break
}
}

window.addEventListener("message", handleMessage)
return () => window.removeEventListener("message", handleMessage)
}, [])


// Navigate to URL
const handleNavigate = useCallback(() => {
if (urlInput) {
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Jul 20, 2025
@daniel-lxs
Copy link
Member

Closing, the implementation will be done by @SannidhyaSah

@daniel-lxs daniel-lxs closed this Jul 22, 2025
@github-project-automation github-project-automation bot moved this from Triage to Done in Roo Code Roadmap Jul 22, 2025
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Jul 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Add integrated web application preview with element selection for AI context

4 participants