Skip to content

Add feature for preview of markdown files#10

Open
rickmoonex wants to merge 1 commit intotermermc:masterfrom
rickmoonex:master
Open

Add feature for preview of markdown files#10
rickmoonex wants to merge 1 commit intotermermc:masterfrom
rickmoonex:master

Conversation

@rickmoonex
Copy link
Copy Markdown

@rickmoonex rickmoonex commented Apr 20, 2026

Summary

Adds a full-page markdown preview for .md files in a share, plus an opt-in "auto-open README.md" setting. Peer-supplied markdown is rendered with the same same-origin / external-link discipline as profile pages, so the trust model matches what users already expect.

Why

Clicking a .md file in the share browser previously dumped raw markdown into the small sidebar preview as plain text. Shares that are natural fits for markdown — notes, recipe books, documentation — were unreadable without downloading. A full-page preview makes these shares directly useful; auto-opening a README.md matches the convention used on GitHub, GitLab, filesystem browsers, etc.

What changed

1. Full-page markdown preview

  • New route: /server/:uuid/md/:username/*path (ServerMdPreviewPage.tsx + .module.css).
  • Clicking a .md / .markdown file in ServerBrowsePage now navigates to this route (href, not onClick), so middle-click opens in a new tab and the URL is shareable.
  • Pipeline: fetch the file from the peer's file server → marked.parse(..., { gfm: true })DOMPurify.sanitize(..., strict allowlist) → DOM-level URL rewriting pass → inject.
  • Breadcrumb identical to the browse page's, so clicking a path segment drops back into the directory listing.
  • HEAD-first size check; files > 1 MiB show a download button instead of rendering.
  • Footer "View raw" toggle for inspecting the source.
  • Intra-share .md.md links use useNavigate so they feel like single-page transitions; non-.md in-share targets get a file-server URL and open in a new tab.

2. Auto-open README.md (new client setting)

  • New "Browsing" section in client settings with a toggle: "Auto-open README.md when entering a directory?"
  • Preference is stored in localStorage (per-browser, synchronous, no RPC/proto changes — it's a pure UI preference).
  • In ServerBrowsePage, the check runs inside the getDirFiles streaming loop, so navigation fires as soon as a chunk containing README.md arrives — no waiting for the full listing to enumerate.
  • Case-insensitive match on readme.md.
  • Auto-open uses navigate(..., { replace: true }) so browser-back skips the directory entry rather than immediately re-triggering the preview.
  • A "✖ Close preview" button in the preview header returns to the parent directory with ?noauto=1, which suppresses auto-open for that one visit (you can always get the listing). Subsequent navigation into other directories auto-opens again.

3. Security — aligned with profile-page rules

Peer markdown is untrusted content, so the renderer mirrors the existing profile-page constraints (see docs):

Rule Profile pages Markdown preview
No scripts / inline handlers iframe sandbox="" + <script> / onerror / <iframe> rejected DOMPurify strips scripts, event handlers, iframes, objects, embeds, forms, <style>, <meta>
No javascript: URLs Rejected DOMPurify default
No absolute paths (/foo) Paths must be relative /-prefixed href/srcdata-blocked
No backslashes in paths Rejected Blocked
No external resources (images, etc.) All src/href must resolve within the profile root Relative src must resolve within the current share; http(s) and data: image URLs stripped; escaping relative paths blocked
External <a> links need target="_blank", not normally clickable Enforced by iframe sandbox Rewriter adds target="_blank" rel="noopener noreferrer", adds a "Middle-click or right-click to open" tooltip, and swallows plain left-click while letting middle-click / Ctrl-Cmd-Shift-Alt-click pass through
Only in-origin content Share root Share root — relative links resolve against the current .md's directory, and anything whose resolved path escapes the share's first segment is blocked with a tooltip explaining why

Intra-share .md.md links get the /md/... preview URL; non-.md in-share references become file-server URLs opened in a new tab. Fragment-only links (#anchor) scroll within the rendered content.

Test plan

  • npm run check and npm run build clean.
  • Fixture share containing README.md, subdirectory .md files with relative links/images, and a spicy_test.md with deliberate XSS / escape / absolute-path / external-image / external-link payloads.
  • Click a .md → full-page preview renders; breadcrumb back returns to the directory.
  • Middle-click a .md → opens in a new tab.
  • Toggle auto-open on in settings → entering the share root navigates straight to the README preview; browser back does not loop.
  • "Close preview" button returns to the listing with ?noauto=1, no re-trigger.
  • Spicy test: no scripts execute, no alert pops, <iframe> / <script> / onerror / javascript: / absolute-path / external-image / escape-link cases all inert.
  • External https://example.com link: plain left-click does nothing; middle-click opens a new tab; right-click copies URL.
  • Peer view: scenarios above all hold when viewing the share from a second client.
  • File > 1 MiB: shows the "too large" fallback with a download button, no render.

Non-goals

  • Syntax highlighting in fenced code blocks (Prism/highlight.js can be layered on later; .content pre code is the target selector).
  • Backend / proto changes: the auto-open preference is intentionally browser-local.
  • Changes to the sidebar Previewer or the existing guessFileCategory.md is now routed independently.

@termermc
Copy link
Copy Markdown
Owner

Thanks for your PR, I like the proposed feature and I think it would make the client a lot nicer to use for some people.

However, based on my review, there are quite a few things that need to be reworked.

Also, one question: did you use AI for this PR?

@rickmoonex
Copy link
Copy Markdown
Author

@termermc , used Claude to draft a PR description since there was no template. I've written the code myself.

Would like to know the in depth feedback so I can do the reworking where needed.

@termermc
Copy link
Copy Markdown
Owner

@termermc , used Claude to draft a PR description since there was no template. I've written the code myself.

Would like to know the in depth feedback so I can do the reworking where needed.

If you scroll up, you should see my comments on pieces of code, but it can be summed up to the following points:

  • Using the same method used for profiles with a <template> and <iframe sandbox> is preferable to adding a new dependency with DOMPurify
  • Using a separate page for markdown previews is not ideal when we already have a preview pane that could be extended
  • The readme.md setting is stored clientside only, which is inconsistent with how all other settings are stored (serverside)
  • The behavior of automatically redirecting to the readme.md file preview when it's encountered in a directory seems like it would be annoying for most users, and is confusing generally

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.

2 participants