Skip to content

feat: add multi-class artifact viewer#49

Merged
steipete merged 31 commits into
openclaw:mainfrom
jjjhenriksen:codex/artifact-viewer
Jul 12, 2026
Merged

feat: add multi-class artifact viewer#49
steipete merged 31 commits into
openclaw:mainfrom
jjjhenriksen:codex/artifact-viewer

Conversation

@jjjhenriksen

@jjjhenriksen jjjhenriksen commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add an in-app, read-only artifact viewer for code, text, Markdown, PDF, DOCX, and uploaded HTML files
  • replace document download-only cards with an accessible open action while preserving downloads and existing image/audio/video behavior
  • add desktop side-pane and mobile full-screen presentation with focus restoration, Escape handling, loading states, and pane-local fallbacks
  • classify using stored upload MIME metadata plus the original filename because unsafe upload types are served as application/octet-stream
  • add the viewer dependencies, embedded production assets, documentation, and browser coverage

Viewer behavior

  • code and text render as inert escaped source; known languages up to 256 KiB are highlighted in a terminable worker, while larger files use escaped plain-text fallback
  • Markdown supports sanitized preview and source modes through the existing marked and DOMPurify pipeline
  • PDF supports one-page-at-a-time navigation, page counts, and zoom using the existing pdfjs-dist integration
  • DOCX converts semantically in-browser with Mammoth and sanitizes the resulting HTML
  • uploaded HTML is sanitized, placed in an empty-sandbox iframe.srcdoc, and constrained by a CSP that permits only inline styles and local data:/blob: assets
  • unsupported files show metadata and a download fallback without execution

Safety contract

  • text, code, Markdown, and HTML previews are limited to 2 MiB
  • syntax highlighting is isolated from the UI thread in a terminable worker with a 2-second timeout and 2 MiB output cap; files above 256 KiB remain escaped source without syntax parsing
  • PDF thumbnail and viewer canvases are checked before allocation: each DPR-scaled backing dimension is capped at 8,192 pixels and the backing store at 16 megapixels, with download fallback for pages outside the policy
  • DOCX conversion is limited to 16 MiB; PDF preview uses the existing 64 MiB upload ceiling
  • previews use same-origin authenticated fetches, abort on close/switch, and release worker/PDF resources
  • HTML scripts, forms, frames, external links/resources, and network-bearing CSS references are removed before rendering; server download headers and CSP are unchanged
  • v1 is read-only and does not add artifact storage, editing, collaboration, or CRDT state
  • no database, OpenAPI, SDK, or public transport contract changes

Verification

  • pnpm check
  • pnpm coverage — 86.5% total Go coverage
  • pnpm exec playwright test tests/e2e/artifact-viewer.spec.ts — 6 passed, covering classification, worker highlighting, near-limit escaped fallback and cancellation, Markdown source/preview, normal PDF page/zoom, PDF canvas dimension and pixel budgets, oversized-page download fallback, bounded DOCX conversion, hostile-HTML isolation, oversized/malformed fallbacks, mobile sizing, and message.updated
  • CAPTURE_ARTIFACT_PROOF=1 pnpm exec playwright test tests/e2e/artifact-viewer.spec.ts -g "opens safe code" — 1 passed and produced live HTML-isolation diagnostics
  • CAPTURE_PDF_LIMIT_PROOF=1 pnpm exec playwright test tests/e2e/artifact-viewer.spec.ts -g "shows local fallbacks" — 1 passed and produced live oversized-PDF allocation diagnostics
  • production web/SDK build and embedded asset refresh completed through the E2E web server
  • browser visual inspection completed for the desktop side pane, mobile full-screen layout, HTML isolation, and PDF canvas-limit fallback

Real behavior proof

These captures come from the local OpenClaw → ClickClack delivery path and the running viewer.

Oversized PDF canvas allocation

The crafted fixture is a small valid PDF with a 20,000 × 20,000-point page. Browser-observed diagnostics show the configured 8,192-pixel dimension ceiling and 16-megapixel budget, zero viewer canvases allocated, and the authenticated download fallback visibly available.

Oversized PDF rejected before canvas allocation

Uploaded HTML isolation

The hostile fixture attempts script execution, form submission, nested framing, external navigation, image loading, stylesheet import, and CSS URL loading. Browser-observed diagnostics show an empty sandbox token list, an absent script marker, zero requests to the sentinel host, no scripts/forms/frames in the preview DOM, and stripped external URL references.

Uploaded HTML isolation with live browser diagnostics

Reproduction details: docs/artifact-viewer-security-proof.md

Behavior while testing PR

Markdown preview side pane

Markdown source side pane

Current behavior before merging PR

OpenClaw-delivered ClickClack attachment

Download control behavior

Out of scope

  • OpenClaw sendMedia delivery remains a separate companion PR
  • collaborative Markdown, mutable uploads, revisioned artifacts, and Jot/CRDT storage remain follow-up work

Follow-up review fixes

  • Capture pdfScale before the asynchronous PDF page load so zoom changes restart rendering immediately.
  • Remember when an artifact was opened from a thread and navigate back to the parent conversation when the artifact closes, keeping the URL consistent with the visible view.

Validation after these fixes: pnpm typecheck, pnpm lint, and pnpm fmt:ts:check all pass.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 11, 2026
@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: stale review; fresh review needed.

Summary
The latest durable ClawSweeper review was for head 5c1daffe844a03a6fa04f00ac0773fb1f79176cb, but the PR head is now 77bbaa4c50243dfc6a0dc0900ae6ed0b703a7be1. Its old verdict and PR readiness labels are no longer current.

Next step
Run or wait for a fresh ClawSweeper review on the current PR head.

Review history (10 earlier review cycles; latest 8 shown)
  • reviewed 2026-07-11T18:10:50.770Z sha 6365657 :: needs real behavior proof before merge. :: [P1] Move syntax highlighting off the browser main thread
  • reviewed 2026-07-11T18:34:56.841Z sha a2460a1 :: needs changes before merge. :: [P1] Bound PDF canvas allocation before rendering
  • reviewed 2026-07-11T18:40:42.058Z sha a2460a1 :: needs changes before merge. :: [P1] Bound PDF canvas allocation before rendering
  • reviewed 2026-07-11T18:49:47.583Z sha 2bfceea :: needs maintainer review before merge. :: none
  • reviewed 2026-07-11T18:55:05.422Z sha 2bfceea :: needs maintainer review before merge. :: none
  • reviewed 2026-07-11T19:02:18.572Z sha 2bfceea :: needs maintainer review before merge. :: none
  • reviewed 2026-07-11T21:34:03.207Z sha 631960e :: needs maintainer review before merge. :: none
  • reviewed 2026-07-11T21:40:58.872Z sha 631960e :: needs maintainer review before merge. :: none

@jjjhenriksen

Copy link
Copy Markdown
Contributor Author

@clawsweeper review

@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed proof: sufficient Contributor real behavior proof is sufficient. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 11, 2026
@jjjhenriksen

Copy link
Copy Markdown
Contributor Author

@clawsweeper review

@clawsweeper

clawsweeper Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 11, 2026
@steipete
steipete force-pushed the codex/artifact-viewer branch from 631960e to 5c1daff Compare July 12, 2026 07:49
@steipete

Copy link
Copy Markdown
Contributor

Rebased and substantially hardened this proposal on current main while preserving Jacqueline's authored commits and contributor credit.

Key review fixes include inert-template HTML sanitization before live-DOM insertion, complete attribute stripping, authoritative DOCX/download-only routing, streamed byte and structured-output limits, interruptible highlighting, bounded PDF load/render/canvas/text/page-cache behavior, mobile modal focus/inertness, accessible PDF text, and preservation of thread reply/scroll/DOM state.

Exact-head proof:

  • pnpm check — pass (Go, fakeco AWS, desktop, TypeScript, lint, formatting)
  • pnpm build — pass; embedded web assets regenerated
  • pnpm test:e2e — 58 passed
  • focused artifact viewer suite — 10 passed
  • source-blind existing-profile Chrome proof — hostile HTML, Markdown preview/source, JSON, malformed PDF, DOCX download-only, navigation/network isolation, accessibility controls, and close cleanup passed
  • production dependency audit — no known vulnerabilities
  • autoreview — clean; zero accepted/actionable findings
  • Public Model Identifier Gate — not applicable; no model-bearing surface changed

Risk: medium. This is a security-sensitive ingest/render/download boundary, but hostile inputs now fail closed to escaped source or authenticated download, resource budgets are explicit, and the full browser/repository gates are green locally.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​highlight.js@​11.11.19910010084100

View full report

@clawsweeper clawsweeper Bot removed merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. feature: ✨ showcase ClawSweeper spotlight: unusually compelling feature idea for maintainer attention. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. labels Jul 12, 2026
@steipete
steipete merged commit eb6e05d into openclaw:main Jul 12, 2026
7 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77bbaa4c50

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

class="artifact-viewer__body"
class:is-pdf={kind === "pdf"}
role="region"
tabindex="0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Block type-to-focus while artifact content is focused

With this focusable preview region, desktop artifacts are not treated as modals by ChatApp's redirectTypingToComposer guard (isModalOpen() still excludes selectedArtifact). When a PDF/source preview has focus, pressing Space to scroll—or any character after selecting text in the artifact—is captured by the global type-to-focus handler and moves focus into the chat/reply composer (Space is even inserted) instead of staying in the artifact. Add selectedArtifact to that guard or stop printable key propagation from the viewer while it is open.

Useful? React with 👍 / 👎.

@jjjhenriksen
jjjhenriksen deleted the codex/artifact-viewer branch July 13, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants