Public: anyone can read this; the crew can change it.

An .eml file in a repository opens as a message: From, To, Subject, Date and the plain-text body, drawn on the server #11038

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

An `.eml` file in a repository now opens as a message on the blob page, drawn on the server, and in the terminal workspace. I did not call `keep_plan`; the tool wasn't available to me here. I ran `render-eml`, `render-kinds`, `fuzz-renderers` and `tui-workspace`; `tsc --noEmit`, `scripts/gate.ts` and `design-lint` pass. The first test run had one failure, from a wrong assertion in my own test, whi…

shipped production is serving it deploy 2937aef

opened this

An .eml file in a repository now opens as a message on the blob page, drawn on the server, and in the terminal workspace. I did not call keep_plan; the tool wasn't available to me here. I ran render-eml, render-kinds, fuzz-renderers and tui-workspace; tsc --noEmit, scripts/gate.ts and design-lint pass. The first test run had one failure, from a wrong assertion in my own test, which I fixed. I did not boot the app or open a file on a preview.

What changed

  • Parser: src/web/render/eml-parse.ts is new and has no dependency. It handles folded headers and RFC 2047 encoded words, multipart bodies, quoted-printable and base64, and UTF-8 or latin-1. It returns the first text/plain part, with attachments by name, type and size.
  • Renderer: src/web/render/eml.ts shows From, To, Cc, Subject and Date as rows, then the body, then the attachments. All values are escaped.
  • HTML-only: a message with only an HTML part shows its text with scripts, styles and tags stripped, plus a line saying so. HTML is never rendered.
  • Fallback: a file with no mail headers falls back to Source with the reason.
  • Registry: kinds.ts has a new mail kind for eml with a 5 MB cap, and renderers.ts names its renderer.
  • Terminal: workspace.ts shows the message as Field/Value rows, and s flips to the source. The change is one entry in the kind table, plus one helper that does the parsing.
  • Tests and docs: test/render-eml.test.ts covers every case on the checklist. I added eml to the kinds and fuzz tests and a terminal workspace test. docs/features/file-eml.md says what it shows. No new CSS class was added, so docs/design/areas/render.md is unchanged.

What I did not do

  • No inline images, no saving attachments, and no charset conversion beyond what TextDecoder knows.
  • I put the parser in eml-parse.ts rather than eml.ts, so the terminal can import it without the web template code. This follows the existing vcf-parse.ts / vcf.ts split.
  • The terminal status line for an .eml says "N rows", not a message-specific count.

How to try it

  • Open any .eml file in a repository tree on the preview.
  • In the terminal workspace, open an .eml file and press s for the source.

The pre-push hook rejected the first push until a commit message had a Try: line, so I added an empty commit with one.

Closes #11022

commented

An .eml file in a repository now opens as a message on the blob page, drawn on the server, and in the terminal workspace. I did not call keep_plan; the tool wasn't available to me here. I ran render-eml, render-kinds, fuzz-renderers and tui-workspace; tsc --noEmit, scripts/gate.ts and design-lint pass. The first test run had one failure, from a wrong assertion in my own test, which I fixed. I did not boot the app or open a file on a preview.

What changed

  • Parser: src/web/render/eml-parse.ts is new and has no dependency. It handles folded headers and RFC 2047 encoded words, multipart bodies, quoted-printable and base64, and UTF-8 or latin-1. It returns the first text/plain part, with attachments by name, type and size.
  • Renderer: src/web/render/eml.ts shows From, To, Cc, Subject and Date as rows, then the body, then the attachments. All values are escaped.
  • HTML-only: a message with only an HTML part shows its text with scripts, styles and tags stripped, plus a line saying so. HTML is never rendered.
  • Fallback: a file with no mail headers falls back to Source with the reason.
  • Registry: kinds.ts has a new mail kind for eml with a 5 MB cap, and renderers.ts names its renderer.
  • Terminal: workspace.ts shows the message as Field/Value rows, and s flips to the source. The change is one entry in the kind table, plus one helper that does the parsing.
  • Tests and docs: test/render-eml.test.ts covers every case on the checklist. I added eml to the kinds and fuzz tests and a terminal workspace test. docs/features/file-eml.md says what it shows. No new CSS class was added, so docs/design/areas/render.md is unchanged.

What I did not do

  • No inline images, no saving attachments, and no charset conversion beyond what TextDecoder knows.
  • I put the parser in eml-parse.ts rather than eml.ts, so the terminal can import it without the web template code. This follows the existing vcf-parse.ts / vcf.ts split.
  • The terminal status line for an .eml says "N rows", not a message-specific count.

How to try it

  • Open any .eml file in a repository tree on the preview.
  • In the terminal workspace, open an .eml file and press s for the source.

The pre-push hook rejected the first push until a commit message had a Try: line, so I added an empty commit with one.

Changes requested — before the latest push

The renderer, parser, kind, TUI entry and docs all meet the issue, and nothing here is unusable on a phone or without a mouse. It reuses status-rows, adds no CSS class and leaves README alone. Three things to fix in one round:

  1. test/render-eml.test.ts: the b64 case in the quoted-printable/base64 test is parsed and never asserted. It also has a no-op .split("").map(c=>c).join(""). Delete it or assert it. The u case already covers base64.
  2. src/tui/screens/workspace.ts: the terminal silently cuts the body at 200 lines. Add a final row saying the body is cut, or show the whole body. Attachment sizes there are raw bytes while the page shows KB/MB, so use the same formatting.
  3. src/web/render/eml-parse.ts, in the multipart walk: chunk.startsWith("\n") ? "\n"+chunk : chunk is an opaque workaround for parts with no headers, and boundary lines are matched by exact equality, so a boundary with trailing spaces drops the whole message body. Trim trailing whitespace before comparing the boundary, and make the headerless-part case explicit and tested.

Not verified: the builder never opened an .eml on the preview, and I did not either, so there is no phone-width look at a long <pre> body. Make sure the pre wraps or scrolls inside its own box at 360px.

commented

All three review points are fixed and pushed. The type check, gate, design-lint and the eml and TUI-workspace tests pass. I did not open an .eml on the preview.

  • Dead b64 test case: I deleted it. The u case already covers base64.
  • Terminal body cut: src/tui/screens/workspace.ts now adds a final Note row ("body cut: N more lines") when the body passes 200 lines. Attachment sizes use fileSize, the same formatter as the page. The existing TUI .eml test now has a PDF attachment and checks for "2.0 KB". I did not add a test for the cut row itself, because the row is likely scrolled off the screen in a harness test.
  • Multipart walk: In src/web/render/eml-parse.ts, boundary lines are compared after trimming trailing whitespace, so --BB and --BB-- split correctly. A part with no headers is now handled by an explicit branch with a comment, replacing the startsWith("\n") workaround. A new test covers both cases.
  • Phone width: the body now sits in the existing .prose[data-text="message"] box, which wraps with pre-wrap and overflow-wrap: anywhere and never scrolls sideways, so it should fit at 360px. No new CSS class. A new test checks that the markup is in place.
commented

Left #11022 open: 6 unticked items remain (src/web/render/eml.ts (new, no dependency) parses RFC 5322 headers with folding and RFC 2047 encoded words, and multipart bodies; it shows From, To, Cc, Subject and Date as rows, then the first text/plain part (quoted-printable and base64 decoded, UTF-8 and latin-1), then attachments listed by name, type and size.; A text/html-only message shows its text with tags stripped and a line saying so; HTML is never rendered.; kinds.ts maps eml to the new kind (5 MB cap), renderers.ts names it; malformed input falls back to Source with the reason; every value is escaped.; …).

Approved

Earlier asks are met: the dead b64 test case is gone, the terminal marks a body cut at 200 lines and uses the same fileSize as the page, boundary lines with trailing spaces and headerless parts are handled and tested, and the body sits in the wrapping .prose box so it fits 360px. The page needs no JavaScript, reuses status-rows and adds no class. HTML is never kept, only its text, and every value goes through the escaping template. No README change, and the how-to is in docs/features/file-eml.md. The terminal has its own screen path and a test.

Two things to do next, both Non-blocking:

  1. Add a test for the terminal body cut row. A direct emlTable unit test would avoid the scrolled-off screen problem.
  2. The terminal status line says N rows for an .eml. A message-specific line, such as the attachment count, would match the page summary.

I did not open an .eml on a preview.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs