Skip to content

feat(code-editor): render HTML files with a preview/source toggle#2782

Open
k11kirky wants to merge 2 commits into
mainfrom
posthog-code/html-file-preview
Open

feat(code-editor): render HTML files with a preview/source toggle#2782
k11kirky wants to merge 2 commits into
mainfrom
posthog-code/html-file-preview

Conversation

@k11kirky

@k11kirky k11kirky commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
Screenshot 2026-06-20 at 7 32 45 AM

What

Clicking a .html/.htm file in the file tree now renders it as a preview — the same way .md files already render markdown — with a toggle to switch between the rendered preview and the raw source. The toggle also applies to markdown.

Why

HTML files (generated reports, coverage output, exported pages, etc.) were only viewable as raw source. Rendering them inline makes the file viewer useful for these, and the toggle keeps the source one click away.

How

  • @posthog/corefileKind.ts: added getRenderableKind(filename): "markdown" | "html" | null, a single discriminator over previewable file kinds. The existing isMarkdownFile/isHtmlFile predicates now reuse the shared, dotfile-robust getFileExtension from @posthog/shared instead of inlining extension parsing.
  • filePreviewStore.ts (new): a persisted Zustand store holding renderPreview: Record<RenderableKind, boolean> (defaults to rendered) plus a toggleKind action. Keyed by kind so adding a future previewable type (SVG, CSV, …) is a one-line default + one render branch, not a new pair of fields/toggles.
  • CodeEditorPanel.tsx: derives a single renderableKind and routes to the markdown renderer, the new HtmlFilePreview, or the CodeMirror source view. A header toggle (eye / code icons) flips preview ↔ source.
  • HtmlFilePreview: renders the file in a null-origin sandboxed iframe (sandbox="allow-scripts", no allow-same-origin) — the same isolation pattern already used by the freeform canvas, so a viewed HTML file can't reach the host renderer's DOM/storage.

Notes / trade-offs

  • The preference is global per file kind (mirroring the diff-viewer mode toggle), not per-file.
  • The HTML preview renders self-contained content (inline CSS/JS, absolute URLs). Relative references to sibling files won't resolve inside the sandboxed srcDoc — the expected limitation for a safe inline preview.

Testing

  • pnpm --filter @posthog/core typecheck and pnpm --filter @posthog/ui typecheck — clean.
  • Biome lint on changed files — clean.
  • New fileKind.test.ts covering isMarkdownFile/isHtmlFile/getRenderableKind — 24 cases pass.

🤖 Generated with Claude Code

Clicking an HTML file in the file tree now renders it, like markdown, with a
toggle to switch between the rendered preview and the raw source.

- Add `getRenderableKind()` to core, a single discriminator over the previewable
  file kinds (markdown, HTML), reusing the shared `getFileExtension` helper.
- HTML renders in a null-origin sandboxed iframe (`allow-scripts` without
  `allow-same-origin`), the same isolation pattern used by the freeform canvas.
- Persist the rendered-vs-source preference per kind in a keyed
  `renderPreview` record so adding a future previewable type is a one-line change.
- A header toggle (eye/code) flips between preview and CodeMirror source for any
  renderable file.

Generated-By: PostHog Code
Task-Id: f9f523bf-9c44-41e8-a1ed-dd6bc9d52140
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 434a44c.

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/ui/src/features/code-editor/filePreviewStore.ts:27-29
The `persist` middleware performs a shallow merge at the top level, so `renderPreview` from localStorage completely replaces the initial object. Any future `RenderableKind` added to the defaults (e.g. `svg: true`) will be absent from the hydrated state for existing users, making `s.renderPreview[newKind]` return `undefined` (falsy) — they'd see source view instead of the rendered preview default. Adding a `version` and a `merge` strategy protects against this.

```suggestion
    {
      name: "file-preview-storage",
      version: 1,
      merge: (persisted, current) => ({
        ...current,
        ...(persisted as Partial<FilePreviewStore>),
        renderPreview: {
          ...(current as FilePreviewStore).renderPreview,
          ...((persisted as Partial<FilePreviewStore>)?.renderPreview ?? {}),
        },
      }),
    },
```

### Issue 2 of 2
packages/core/src/code-editor/fileKind.test.ts:1-47
Missing dotfile coverage despite "dotfile-robust" claim

The PR description says `getFileExtension` is "dotfile-robust", but no test cases exercise dotfiles like `.html` or `.md`. Looking at the implementation, `getFileExtension(".html")` returns `"html"` (because `lastDot = 0 >= 0`), so `isHtmlFile(".html")` returns `true`. Whether that is the intended behavior should be documented with an explicit test case — both to pin the behavior and to clarify the contract for future readers.

Reviews (1): Last reviewed commit: "feat(code-editor): render HTML files wit..." | Re-trigger Greptile

Comment thread packages/ui/src/features/code-editor/filePreviewStore.ts
Comment thread packages/core/src/code-editor/fileKind.test.ts
…behavior

Address Greptile review on the file preview feature:

- filePreviewStore: add a `merge` strategy so a `RenderableKind` added to the
  defaults later survives hydration for users with older stored state (a shallow
  merge would drop it and fall back to source view).
- fileKind.test: pin dotfile behavior (".md"/".html" read as their extension;
  ".gitignore"/".htaccess" are not renderable).

Generated-By: PostHog Code
Task-Id: f9f523bf-9c44-41e8-a1ed-dd6bc9d52140
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.

1 participant