Skip to content

Conversation

naaa760
Copy link

@naaa760 naaa760 commented Oct 2, 2025

Description

  • fixed screenshot functionality in the chat interface by improving the DOM-to-canvas rendering fallback mechanism. The screenshot button was failing when the primary screen capture method encountered issues, so I enhanced the error handling and made the DOM rendering more robust to ensure screenshots work reliably across different scenarios.

Key improvements:

  • Fixed incorrect async/await usage that was causing rendering failures
  • Added better error handling for problematic DOM elements
  • Improved fallback mechanisms when screen capture isn't available
  • Enhanced element filtering and rendering logic

Related Issues

fixes: #2907

Fixes screenshot button not working in chat interface

Type of Change

  • Bug fix

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability of screen capture to prevent crashes and stalled captures.
    • Better handling of video playback and errors during screenshot creation.
    • Skips problematic or tiny/out-of-bounds elements to avoid rendering issues.
  • Refactor

    • Streamlined rendering flow for DOM elements to reduce failures and improve stability.
    • Hardened sorting and style parsing for consistent visual output.
  • Chores

    • Enhanced logging for easier troubleshooting and clearer diagnostics.

Important

Improves screenshot functionality in screenshot.ts with enhanced error handling and fallback mechanisms for reliable operation.

  • Behavior:
    • Improved captureScreenshot() in screenshot.ts with better error handling and fallback to DOM rendering if getDisplayMedia fails.
    • Enhanced async/await usage in captureScreenshot() to prevent rendering failures.
    • Added error handling for problematic DOM elements in renderDomToCanvas().
    • Improved element filtering and rendering logic in renderDomToCanvas().
  • Error Handling:
    • Added try-catch blocks around element property access in renderDomToCanvas() to skip problematic elements.
    • Added error logging for skipped elements and rendering failures.
  • Misc:
    • Minor changes to logging format for screenshot size.

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

Copy link

vercel bot commented Oct 2, 2025

@naaa760 is attempting to deploy a commit to the Onlook Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

coderabbitai bot commented Oct 2, 2025

Walkthrough

Refactors screenshot capture and DOM rendering in apps/web/preload/script/api/screenshot.ts: adjusts getDisplayMedia constraints, strengthens promise/error handling for video playback, converts async functions to sync where appropriate, adds defensive guards and per-element try/catch, improves sorting and parsing fallbacks, and tightens rendering conditions and bounds checks.

Changes

Cohort / File(s) Change Summary
Screenshot capture flow and DOM rendering
apps/web/preload/script/api/screenshot.ts
Updated getDisplayMedia constraints with mediaSource: 'screen'; reinforced promise handling and video.play() error propagation; added outer and per-element try/catch; converted renderDomToCanvas and renderElement to synchronous usage; safer z-index/opacity parsing; bounds/size checks to skip invalid elements; adjusted background rendering conditions; improved logging and non-crashing fallbacks; ensured file ends with newline.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant A as App (screenshot.ts)
  participant M as mediaDevices.getDisplayMedia
  participant V as HTMLVideoElement
  participant C as Canvas

  U->>A: captureScreenshot()
  A->>M: request display media {video: {mediaSource: 'screen', ...}}
  alt success
    M-->>A: MediaStream
    A->>V: attach stream, play()
    alt video plays
      V-->>A: playing
      A->>C: draw frame, renderDomToCanvas()
      note over A,C: Per-element try/catch, bounds/z-index/opacity fallbacks
      A-->>U: return screenshot image
    else play error
      V-->>A: onerror/catch
      A-->>U: reject with error
    end
    A->>M: stop tracks (cleanup)
  else failure
    M-->>A: error
    A->>A: log and fallback path
    A-->>U: reject or proceed with fallback
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I clicked the glass and caught the screen,
A hop, a frame—now crisp and clean.
With z-index dreams and bounds in sight,
I nibble bugs to set things right.
Canvas sings, the stream grows tame—
Screenshot magic, hare’s new game. 🐇📸

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly highlights that the pull request improves screenshot functionality and adds better error handling, which aligns with the main changes in the code.
Linked Issues Check ✅ Passed The changes restore and enhance the screenshot button by fixing async/await usage, adding robust error handling, and strengthening fallback DOM-to-canvas rendering as required to ensure reliable capture and preview in the chat interface, directly addressing linked issue #2907.
Out of Scope Changes Check ✅ Passed All modifications are confined to the screenshot capture and fallback rendering logic in screenshot.ts and directly relate to improving screenshot functionality without touching unrelated components.
Description Check ✅ Passed The pull request description provides a clear summary of the changes made, links the related issue, and specifies the type of change in accordance with the template’s primary sections; however, it does not include a Testing section outlining how the changes were verified, and it omits the optional Screenshots and Additional Notes headings.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 53ec325 and 4c28f87.

📒 Files selected for processing (1)
  • apps/web/preload/script/api/screenshot.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/preload/script/api/screenshot.ts
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/preload/script/api/screenshot.ts

Comment on lines +37 to 51
await new Promise<void>((resolve, reject) => {
video.onloadedmetadata = () => {
video.play();
video.play().catch(reject);
video.oncanplay = () => {
context.drawImage(video, 0, 0, viewportWidth, viewportHeight);
stream.getTracks().forEach(track => track.stop());
resolve();
try {
context.drawImage(video, 0, 0, viewportWidth, viewportHeight);
stream.getTracks().forEach((track) => track.stop());
resolve();
} catch (drawError) {
reject(drawError);
}
};
};
video.onerror = reject;
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Always stop the screen-capture stream on failures.

If drawImage, video.play(), or video.onerror paths reject, the promise exits without stopping stream tracks. The browser keeps capturing the user’s screen (and keeps the share indicator active) until GC, which is a privacy and resource leak. Move the stream.getTracks().forEach(track => track.stop()) call into a shared cleanup that runs on both success and error paths before resolving/rejecting.

-                await new Promise<void>((resolve, reject) => {
+                await new Promise<void>((resolve, reject) => {
+                    const cleanup = () => {
+                        stream.getTracks().forEach((track) => track.stop());
+                    };
+                    const handleError = (error: unknown) => {
+                        cleanup();
+                        reject(error);
+                    };
                     video.onloadedmetadata = () => {
-                        video.play().catch(reject);
+                        video.play().catch(handleError);
                         video.oncanplay = () => {
                             try {
                                 context.drawImage(video, 0, 0, viewportWidth, viewportHeight);
-                                stream.getTracks().forEach((track) => track.stop());
-                                resolve();
-                            } catch (drawError) {
-                                reject(drawError);
+                                cleanup();
+                                resolve();
+                            } catch (drawError) {
+                                handleError(drawError);
                             }
                         };
                     };
-                    video.onerror = reject;
+                    video.onerror = handleError;
                 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await new Promise<void>((resolve, reject) => {
video.onloadedmetadata = () => {
video.play();
video.play().catch(reject);
video.oncanplay = () => {
context.drawImage(video, 0, 0, viewportWidth, viewportHeight);
stream.getTracks().forEach(track => track.stop());
resolve();
try {
context.drawImage(video, 0, 0, viewportWidth, viewportHeight);
stream.getTracks().forEach((track) => track.stop());
resolve();
} catch (drawError) {
reject(drawError);
}
};
};
video.onerror = reject;
});
await new Promise<void>((resolve, reject) => {
const cleanup = () => {
stream.getTracks().forEach((track) => track.stop());
};
const handleError = (error: unknown) => {
cleanup();
reject(error);
};
video.onloadedmetadata = () => {
video.play().catch(handleError);
video.oncanplay = () => {
try {
context.drawImage(video, 0, 0, viewportWidth, viewportHeight);
- stream.getTracks().forEach((track) => track.stop());
cleanup();
resolve();
} catch (drawError) {
handleError(drawError);
}
};
};
video.onerror = handleError;
});
🤖 Prompt for AI Agents
In apps/web/preload/script/api/screenshot.ts around lines 37 to 51, the promise
currently only stops stream tracks on the successful draw path, leaving the
screen-capture stream running on errors; extract a shared cleanup that calls
stream.getTracks().forEach(t => t.stop()) and invoke it before both resolve()
and reject(), e.g. attach centralized cleanup logic run in video.oncanplay
success and in all rejection paths (video.play().catch, video.onerror, and the
catch around drawImage), and also remove or null out event handlers after
cleanup to avoid double-calls.

}
};
};
video.onerror = reject;
Copy link
Contributor

Choose a reason for hiding this comment

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

If video.onerror fires, stream tracks might not be stopped, leading to potential resource leaks. Consider adding cleanup (e.g. stopping tracks) in the error path or a finally block.

Suggested change
video.onerror = reject;
video.onerror = (e) => { stream.getTracks().forEach(track => track.stop()); reject(e); };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[bug] Screenshots added from the screenshot button don't seem to work
1 participant