Skip to content

feat(workspaces): add secure preview widget#101354

Merged
steipete merged 5 commits into
openclaw:mainfrom
100yenadmin:feat/dashboard-preview-widget
Jul 17, 2026
Merged

feat(workspaces): add secure preview widget#101354
steipete merged 5 commits into
openclaw:mainfrom
100yenadmin:feat/dashboard-preview-widget

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Workspaces has no first-class preview panel for inspecting an allowed development server or deployed page without leaving the operator workspace.

Why This Change Was Made

This adds builtin:preview to the current Workspaces registry and schema. The preview keeps a distinct, owner-approved saved widget contract while sharing the iframe plumbing and URL policy with builtin:iframe-embed. It fails closed for disallowed origins, preserves the existing iframe sandbox ceiling (allow-scripts, never allow-same-origin), sends no referrer, and reloads without reaching into contentWindow.

Resolved bindings.value URLs take precedence when declared, unrelated bindings do not suppress props.url, and viewport selection lives in the owning workspace view state so rerenders preserve it.

User Impact

Workspace authors can preview an allowed URL, reload it, and switch between desktop, tablet, and mobile viewports. Disallowed origins render an explicit blocked state. Loading a remote preview makes the browser contact that origin directly, so it can observe ordinary network metadata such as IP address and user agent.

Visual Proof

Current main — preview kind unavailable:

Current main shows builtin preview as unavailable

Candidate — allowed same-origin preview plus blocked external origin:

Allowed preview and blocked external origin

Candidate — mobile viewport:

Mobile preview viewport

Artifact manifest

Evidence

  • Final rebased head: a7da1103db68126c3f9fa4a36a81e89930d25fcf.
  • Focused Vitest: 58 assertions passed across widget, workspace view, schema, cell, and browser E2E surfaces.
  • E2E covers allowed bound URL, blocked external URL, viewport persistence, and reload.
  • All 20 generated locale bundles synchronized; repository locale checks and hosted changed gates passed.
  • Hosted changed gate: Blacksmith Testbox run.
  • Fresh branch autoreview: clean, no accepted/actionable findings.
  • git diff --check: clean.

Thanks @100yenadmin for the original implementation and Workspaces design.

@100yenadmin
100yenadmin requested a review from a team as a code owner July 7, 2026 04:21
@100yenadmin

Copy link
Copy Markdown
Contributor Author

Refile of #101334, which was auto-closed to make PR room — same branch and commit, nothing changed.

On the dependency-guard check: this PR is stacked on the not-yet-merged Wave 1 (#101098), so its cumulative diff against main includes Wave 1's single new-plugin pnpm-lock.yaml importer entry. This PR adds no new dependency (it touches only ui/ + regenerated i18n bundles); it clears once Wave 1 lands or with the head-SHA authorization noted on the Wave 1 PRs. Green locally: UI dashboard suite (100 tests), ui:i18n:check, pnpm ui:build, oxlint.

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Dependency graph guard cleared

This PR no longer has blocked dependency graph changes. A future dependency graph change requires a fresh /allow-dependencies-change comment after the guard blocks that new head SHA.

  • Current SHA: a7da1103db68126c3f9fa4a36a81e89930d25fcf

@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 13, 2026, 8:40 AM ET / 12:40 UTC.

Summary
Adds a builtin:preview Workspaces widget with gated iframe loading, reload and viewport controls, schema and renderer registration, styling, English copy, and focused tests.

PR surface: Source +207, Tests +92, Other -1. Total +298 across 9 files.

Reproducibility: not applicable. as a bug reproduction: current main clearly lacks dedicated preview chrome, while already supporting the underlying iframe-embed capability.

Review metrics: 1 noteworthy metric.

  • Persisted widget contract: 1 added, 0 changed, 0 removed. A new builtin kind becomes part of saved workspace documents and should have an intentional long-term compatibility story before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #101138
Summary: This PR is the phase-one implementation candidate for the dedicated preview-widget issue; the roadmap is broader and the earlier PR was replaced by this refile.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦐 gold shrimp
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Get maintainer confirmation on one canonical embed/preview widget contract.
  • [P1] Fix resolved binding consumption and persist viewport selection in owned UI state.
  • [P1] Attach a redacted final-head Workspaces recording covering success and blocked paths.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: An earlier recording was associated with a pre-final revision, but the video label was removed before the final force-push and the current body contains only test/lint evidence; add a redacted recording from head 101e1323354e494503fdedfaa8758ead70dc8a3f showing allowed load, reload, viewport switching, and blocked URLs, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.

Risk before merge

  • [P1] Merging a second persisted URL-embed kind commits OpenClaw to two overlapping workspace contracts; later consolidation could require document migration and compatibility handling.
  • [P1] The final head has no inspectable after-fix recording demonstrating allowed loading, blocked URLs, reload, and viewport switching in a real Workspaces setup.

Maintainer options:

  1. Consolidate the widget contract (recommended)
    Refactor the preview chrome into the existing iframe embed path, fix binding and state ownership, and provide final-head browser proof before merge.
  2. Accept a permanent preview kind
    Explicitly approve the parallel persisted kind and require upgrade-contract tests plus final-head behavior proof before landing.
  3. Pause until the distinction is justified
    Close or defer this branch if reload and viewport chrome alone do not justify a second persisted widget contract.

Next step before merge

  • [P1] A maintainer must first choose the permanent widget contract; after that, the contributor has concrete binding, state-ownership, and final-head proof work.

Maintainer decision needed

  • Question: Should Workspaces permanently expose builtin:preview as a distinct saved widget kind, or should preview controls extend the existing builtin:iframe-embed contract?
  • Rationale: Both designs are technically possible, but choosing a persisted widget identity is a long-lived product and compatibility decision that automation cannot safely make.
  • Likely owner: steipete — The current Workspaces integration and widget contract entered main through their merged implementation.
  • Options:
    • Consolidate before merge (recommended): Extend the existing iframe widget with typed optional preview controls so saved workspaces retain one URL-embed contract.
    • Sponsor a distinct kind: Approve builtin:preview as a permanent separate contract and require focused compatibility tests, renderer fixes, and current-head proof.
    • Defer the preview tier: Keep the existing iframe widget unchanged and pause this PR until later browser or dev-server capabilities justify a distinct builtin.

Security
Cleared: The patch reuses the existing external-URL gate, retains allow-scripts without allow-same-origin, uses no-referrer, and introduces no dependency or executable supply-chain change.

Review findings

  • [P1] Consolidate the existing URL-embed capability — extensions/workspaces/src/schema.ts:68
  • [P2] Render the resolved binding value — ui/src/lib/workspace/widgets/preview.ts:28-31
  • [P2] Keep viewport selection in owned state — ui/src/lib/workspace/widgets/preview.ts:51
Review details

Best possible solution:

Keep one canonical URL-embed contract—preferably extending iframe-embed with explicitly modeled preview controls—then consume resolved binding data, move viewport selection into owned state, and attach redacted final-head browser proof.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug reproduction: current main clearly lacks dedicated preview chrome, while already supporting the underlying iframe-embed capability.

Is this the best way to solve the issue?

No. A separate persisted kind is not yet proven better than extending the existing embed widget, and the proposed renderer currently ignores resolved bindings and keeps viewport selection outside owned state.

Full review comments:

  • [P1] Consolidate the existing URL-embed capability — extensions/workspaces/src/schema.ts:68
    Current main already persists and renders builtin:iframe-embed through the same URL gate and sandbox. Adding another saved kind solely for reload and viewport chrome creates two sources of truth and a future migration burden; extend the existing widget with typed preview controls, or obtain explicit maintainer approval for a permanently distinct contract.
    Confidence: 0.91
  • [P2] Render the resolved binding value — ui/src/lib/workspace/widgets/preview.ts:28-31
    The builtin renderer receives the resolved workspace value as its second argument, but this implementation discards it and reads only widget.props.url. A preview bound through the normal data-binding path therefore stays missing or stale instead of showing the resolved URL.
    Confidence: 0.98
  • [P2] Keep viewport selection in owned state — ui/src/lib/workspace/widgets/preview.ts:51
    Viewport selection is initialized inside this pure render function and then maintained through DOM-local mutation. Any workspace rerender reconstructs the template from defaultViewport, snapping the user's tablet or mobile selection back; store this state in the owning controller/component keyed by widget.
    Confidence: 0.9

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 265f79e60721.

Label changes

Label changes:

  • add proof: 🎥 video: Contributor real behavior proof includes video or recording evidence. An earlier recording was associated with a pre-final revision, but the video label was removed before the final force-push and the current body contains only test/lint evidence; add a redacted recording from head 101e1323354e494503fdedfaa8758ead70dc8a3f showing allowed load, reload, viewport switching, and blocked URLs, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🧂 unranked krab, so this older rating label is no longer current.

Label justifications:

  • P2: This is a bounded Workspaces feature with normal user-facing value, but it has product-contract and correctness blockers rather than urgent runtime impact.
  • merge-risk: 🚨 compatibility: The new builtin:preview identifier creates a second persisted URL-embed contract that may require migration if maintainers later consolidate the overlap.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: An earlier recording was associated with a pre-final revision, but the video label was removed before the final force-push and the current body contains only test/lint evidence; add a redacted recording from head 101e1323354e494503fdedfaa8758ead70dc8a3f showing allowed load, reload, viewport switching, and blocked URLs, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.
  • proof: 🎥 video: Contributor real behavior proof includes video or recording evidence. An earlier recording was associated with a pre-final revision, but the video label was removed before the final force-push and the current body contains only test/lint evidence; add a redacted recording from head 101e1323354e494503fdedfaa8758ead70dc8a3f showing allowed load, reload, viewport switching, and blocked URLs, then update the PR body to trigger review or ask a maintainer for @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +207, Tests +92, Other -1. Total +298 across 9 files.

View PR surface stats
Area Files Added Removed Net
Source 6 208 1 +207
Tests 2 93 1 +92
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 0 1 -1
Total 9 301 3 +298

What I checked:

Likely related people:

  • steipete: Authored and merged the current agent-composable Workspaces foundation, including the owner boundary and existing iframe widget behavior this PR extends. (role: Workspaces integration author and merger; confidence: high; commits: 0af846713749; files: extensions/workspaces/src/schema.ts, ui/src/lib/workspace/widgets/iframe-embed.ts, ui/src/lib/workspace/widgets/index.ts)
  • 100yenadmin: Designed the modular Workspaces roadmap and authored the preview implementation and its earlier stacked predecessor, providing strong domain context beyond this single PR. (role: original feature designer and adjacent area contributor; confidence: medium; commits: 50d76070fb83, 101e1323354e; files: ui/src/lib/workspace/widgets/preview.ts, ui/src/lib/workspace/widgets/widgets.test.ts, extensions/workspaces/src/schema.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (13 earlier review cycles; latest 8 shown)
  • reviewed 2026-07-07T23:44:09.325Z sha b23a3b2 :: needs real behavior proof before merge. :: [P2] Use the resolved binding value for the preview URL | [P2] Persist the selected preview viewport across renders | [P3] Remove the root carve scratch document
  • reviewed 2026-07-07T23:53:43.029Z sha b23a3b2 :: needs real behavior proof before merge. :: [P2] Use the resolved binding value for the preview URL | [P2] Persist the selected preview viewport across renders | [P3] Remove the root carve scratch document
  • reviewed 2026-07-08T04:28:01.609Z sha b23a3b2 :: needs real behavior proof before merge. :: [P2] Use the resolved binding value for the preview URL | [P2] Keep the selected viewport in dashboard state | [P3] Remove the root carve scratch document
  • reviewed 2026-07-09T23:26:38.158Z sha 8584dbf :: needs real behavior proof before merge. :: [P2] Use the resolved binding value for the preview URL | [P2] Keep the selected viewport in dashboard state | [P3] Remove the root carve scratch document
  • reviewed 2026-07-10T08:35:30.145Z sha 46c8741 :: needs real behavior proof before merge. :: [P2] Use the resolved binding value for the preview URL | [P2] Keep the selected viewport in dashboard state | [P1] Move dashboard persistence into SQLite | [P3] Remove the root carve scratch document
  • reviewed 2026-07-13T02:58:31.378Z sha 727af42 :: needs real behavior proof before merge. :: [P1] Consolidate the existing URL-embed capability | [P2] Render the resolved binding value | [P2] Keep viewport selection in owned state | [P2] Synchronize the generated locale bundles
  • reviewed 2026-07-13T07:35:00.082Z sha 4abeebd :: needs real behavior proof before merge. :: [P1] Consolidate the existing URL-embed capability | [P2] Render the resolved binding value | [P2] Keep viewport selection in owned state | [P2] Synchronize the generated locale bundles
  • reviewed 2026-07-13T09:46:00.482Z sha 8069aa0 :: needs real behavior proof before merge. :: [P1] Consolidate the existing URL-embed capability | [P2] Render the resolved binding value | [P2] Keep viewport selection in owned state | [P2] Synchronize the generated locale bundles

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jul 7, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. label Jul 10, 2026
@100yenadmin
100yenadmin force-pushed the feat/dashboard-preview-widget branch from 46c8741 to 3c16a91 Compare July 13, 2026 02:27
@100yenadmin 100yenadmin changed the title Dashboard: preview widget — embed a live URL with reload + viewport controls Workspaces: add secure preview widget Jul 13, 2026
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts plugin: workspaces Agent-composable Workspaces plugin size: M and removed size: XL labels Jul 13, 2026
@github-actions github-actions Bot removed the dependencies-changed PR changes dependency-related files label Jul 13, 2026
@clawsweeper clawsweeper Bot removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 13, 2026
@clawsweeper clawsweeper Bot added proof: 🎥 video Contributor real behavior proof includes video or recording evidence. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 13, 2026
@100yenadmin
100yenadmin force-pushed the feat/dashboard-preview-widget branch from 727af42 to 4abeebd Compare July 13, 2026 06:42
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jul 13, 2026
@100yenadmin
100yenadmin force-pushed the feat/dashboard-preview-widget branch from 4abeebd to 8069aa0 Compare July 13, 2026 09:03
@clawsweeper clawsweeper Bot removed the proof: 🎥 video Contributor real behavior proof includes video or recording evidence. label Jul 13, 2026
@100yenadmin
100yenadmin force-pushed the feat/dashboard-preview-widget branch from 8069aa0 to 101e132 Compare July 13, 2026 11:27
@clawsweeper clawsweeper Bot added proof: 🎥 video Contributor real behavior proof includes video or recording evidence. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 13, 2026
@steipete
steipete force-pushed the feat/dashboard-preview-widget branch from 101e132 to 12a6927 Compare July 17, 2026 00:50
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: XL and removed scripts Repository scripts size: M labels Jul 17, 2026
@steipete
steipete force-pushed the feat/dashboard-preview-widget branch from 12a6927 to 054674d Compare July 17, 2026 01:00
@steipete steipete changed the title Workspaces: add secure preview widget feat(workspaces): add secure preview widget Jul 17, 2026
@steipete
steipete force-pushed the feat/dashboard-preview-widget branch from 054674d to 64cb929 Compare July 17, 2026 01:06
@steipete
steipete force-pushed the feat/dashboard-preview-widget branch from 64cb929 to a7da110 Compare July 17, 2026 01:11
@steipete
steipete merged commit 368941a into openclaw:main Jul 17, 2026
114 of 117 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 17, 2026
* feat(workspaces): add secure preview widget

* fix(workspaces): type preview reload assertion

* fix(workspaces): harden preview widget

* fix(workspaces): honor preview URL binding ownership

* chore(workspaces): remove unused widget exports

---------

Co-authored-by: Eva <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui docs Improvements or additions to documentation merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. plugin: workspaces Agent-composable Workspaces plugin proof: 🎥 video Contributor real behavior proof includes video or recording evidence. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants