Skip to content

[Enhancement] Open HTML files from the Files tree in a new browser tab (preview mode) #795

@wuc623123

Description

@wuc623123

Use case

Working in a project that generates standalone HTML reports — backtest summaries, playwright reports, jupyter exports, diagram outputs — the only way to view them is to download via the right-click menu and open from local fs, or rely on a cd && python -m http.server running on the side. Both flow-break the editor.

Would be much nicer to single-click an .html in the Files tree and have it open in a new browser tab, rendered, with its relative CSS/JS/img assets resolving correctly.

Proposed design (already prototyped locally)

Backend: new route GET /api/projects/:projectId/preview/*

  • Auth: existing authenticateToken (already supports ?token= query for SSE), plus an expandCookieToQuery middleware that reads a scoped HttpOnly cookie so subsequent relative-asset fetches don't need the token in every URL
  • HTML files: inject <base href="./"> after <head> so relative refs resolve back to /preview/; serve as text/html
  • Non-HTML files (CSS / JS / images): stream raw bytes with mime.lookup()-detected Content-Type
  • Path safety: same startsWith(projectRoot) guard as /files/content

Verified working request matrix in my prototype:

# Scenario Result
1 ?token= query → HTML 200, <base href="./"> injected, Set-Cookie issued
2 Cookie-only HTML (page navigation back) 200
3 Cookie-only CSS / JS (relative asset fetch) 200 with correct mime
4 No auth 401
5 ../../../etc/passwd traversal 403

Frontend: capture-phase click listener

Walks React Fiber tree (not DOM-exposed __reactProps$, those don't carry the file object — it's closure-captured in onClick). At each fiber, checks memoizedProps for {file: {path, projectId}}. If found and path matches \.html?$, preventDefault + stopPropagation + window.open('/api/projects/{id}/preview/{path}?token=...').

This shape was discovered empirically — the row component renders <div onClick={() => openFile(file)}>...</div> and the file prop never lands on the rendered DOM node, only on the fiber.

// extractor — works against current 1.32.0 bundle
const cands = [p.file, p.item, p.node, p.entry, p.data, p.row, p.record];

Why this needs to be upstream

I'm running this as a patched-on-startup hotfix locally (sed-injection into dist/index.html + new payload file in dist/), but it'd be 10× cleaner as a first-class feature, especially since:

  • The right-click context menu already has "Download" — "Open in browser" is the obvious sibling
  • The /files/content route already does 80% of the work (mime + auth + path guard); preview just adds <base> injection + cookie path
  • Doesn't affect non-HTML default editor behavior at all

Happy to send a PR if there's interest. Full local implementation is ~200 lines (route handler + middleware + frontend hook).

Environment

  • Package: @cloudcli-ai/cloudcli@1.32.0 (downstream rebrand)
  • Browser: Firefox 151.0.1, Chromium 130 — both work
  • OS: Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions