feat: browser-side annotation screenshots for web preview

Capture the preview/browser iframe DOM with snapDOM (html-to-image
fallback) so web annotation screenshots match the visible viewport,
without a headless Chromium dependency.

- Preserve document scroll via viewport crop and re-bake nested scroll
  (e.g. the Starlight sidebar) deterministically on the clone
- Pin position:fixed elements to their measured viewport rect so headers
  and sidebars land correctly in the crop
- Extract preview capture/proxy helpers into
  lib/preview/screenshot-capture.ts to slim down ContextPanel
- Guard the external preview proxy against SSRF to private, loopback and
  reserved/link-local addresses (incl. cloud metadata)
- Fully validate preview bridge messages before formatting/use
- Warn on the empty browser tab that pages run with full access, so
  users browse untrusted sites knowingly
This commit is contained in:
Bohdan Triapitsyn
2026-05-30 02:04:32 +03:00
parent 49ed0b52c9
commit 7f90ffb878
15 changed files with 1591 additions and 210 deletions
+2
View File
@@ -843,6 +843,8 @@ export const dict: Record<I18nKey, string> = {
"contextPanel.browser.addressAria": "Dirección del navegador",
"contextPanel.browser.empty": "Navegador web",
"contextPanel.browser.emptyHint": "Ingrese una dirección arriba para comenzar a navegar",
"contextPanel.browser.inspectUnavailable": "Esta página no se puede inspeccionar desde el panel del navegador.",
"contextPanel.browser.trustNotice": "Las páginas que abras aquí se ejecutan con acceso completo a OpenChamber: necesario para la inspección y las capturas. Abre solo sitios de confianza: una página maliciosa podría leer tus datos o actuar en tu nombre.",
"contextPanel.tab.closeTabAria": "Cerrar pestaña {label}",
"contextPanel.actions.collapsePanel": "Colapsar panel",
"contextPanel.actions.expandPanel": "Expandir panel",