feat(files): render markdown in the File Viewer, with Lines/Wrap toggles - #503
JDProfresh wants to merge 2 commits into
Conversation
Clicking a .md in the Files panel showed wrapped source with an Edit pencil and no way to see it rendered, although marked + DOMPurify were already on the page for the Response Viewer. The viewer now renders .md/.markdown through that same pipeline (one parser, one click delegate) with an MD pill back to source, and the plain-text view gains Lines (CSS-counter gutter) and Wrap toggles. All three persist per device in their own localStorage keys. - Relative images are rebased onto the workspace-confined file-raw route under the document's directory, built inside a <template> so no fetch fires before the rewrite; a failed load degrades to alt text. Relative links become a.rv-path so the existing delegate opens them in the viewer; fragment and http(s) links are untouched. - The rendered container carries data-i18n-skip so the translator does not rewrite the document's prose. - Markdown fetches the route's 10000-line ceiling; other text keeps 500. - avif renders inline (file-content image set, file-raw MIME map), and avif/ico printed paths open the viewer instead of tailing bytes. .md deliberately stays with the tail viewer for printed paths.
|
Thanks for this, @JDProfresh, and welcome. This PR makes the File Viewer render Two things need fixing before merge, both small: 1. Encoded relative paths get encoded twice ( marked percent-encodes link and image destinations. So Links break the same way: Please strip let rel = ref.split('#')[0].split('?')[0];
try {
rel = decodeURIComponent(rel);
} catch {
/* malformed escape: keep the ref as written */
}
for (const seg of (dir + rel).split('/')) {Then add a 2. A markdown file can break every inline The sanitizer allowlists This hole is older than your PR (the Response Viewer has it too). Rendering repo READMEs is what makes it reachable from any cloned repository. Please drop Smaller things that fit in the same pass:
No need to split out the avif/ico change. It is small and tested, so it stays here. Once 1 and 2 are in, this is ready to merge. |
… drop name= from the sanitizer Review follow-up on Ark0N#503. marked percent-encodes link and image destinations, and the rebase pass encoded them a second time, so a space or a CJK character in a file name made file-raw look for a file literally named my%20image.png; refs are now decoded once (a malformed escape is kept as written) and stripped of ?query along with #fragment. Root-relative refs resolve from the workspace root as on GitHub instead of falling through as Codeman URLs. Rebased links carry the preview's own session id and the response-viewer delegate prefers it, so a document opened from another session's attachment card opens its links in that workspace rather than the active tab's. The sanitizer no longer allows name=: marked never emits it, and <img name="app"> made document.app that image, which every inline onclick="app.…()" handler resolves before the global, so one rendered README broke every viewer button until a reload. Adds the zh-CN strings for the three toolbar titles.
|
Thanks for the review. All five are in 612c69d:
|
Summary
Clicking a
.mdin the Files panel opened the File Viewer as wrapped source with an Edit pencil and no way to see it rendered, although marked + DOMPurify were already on the page for the Response Viewer. This PR renders.md/.markdownthrough that same pipeline, with anMDpill back to source, and gives the plain-text viewLinesandWraptoggles. It also lets.avifrender inline and routes printed.avif/.icopaths to the viewer instead of tailing bytes.What it does for the user
MD,LinesandWrapremember their state per device. Lines is a real gutter that never lands in a copy.edit=1as before.Design notes
_renderMarkdown()and binds the Response Viewer's existing click delegate on the preview body (container-bound, idempotent), so there is no second parser and no second handler.<template>so nothing is fetched before the rewrite; relative images are rebased onto the workspace-confinedfile-rawroute under the document's directory, and a failed load (CSP-blocked remote image, a 404, an SVG served as a download) degrades to alt text. Relative links becomea.rv-pathfor the delegate and lose thetargetmarked gave them; fragment and http(s) links are untouched. No route was widened.data-i18n-skip, otherwise the translator rewrites the document's prose.SettingsUpdateSchema..mdpaths still open the tail viewer, per the live-follow invariant indocs/architecture-invariants.md; the rendered view is reached from the Files panel. Out-of-workspace avif/ico stay unregistrable like svg/bmp.Testing
npm testgate green (433 files), plus typecheck, lint, format:check, check:frontend-syntax and check:public-assets.test/file-preview-markdown.test.ts(jsdom-in-vm, same technique asresponse-viewer-file-links.test.ts) pins the render, the toggle re-render without refetch, the fetch caps, the image and link rebasing, the pref persistence, toggles hidden for images and during edit, and the extension set.test/routes/file-routes.test.tsgains the avif cases...link back, and an Edit round-trip.Docs
CLAUDE.mdparagraph, a section indocs/architecture-invariants.md, and the Markdown row indocs/wiki/Working-With-Files.md. No version bump or changeset, per CONTRIBUTING.Happy to split the avif/ico bit into its own PR if you would rather keep this one to the viewer alone.