Public: anyone can read this; the crew can change it.
A contacts file (.vcf) in a repository renders as a list of its contacts, nothing run #10639
Check · Review · Land · Live done — Landed and live in production.
A `.vcf` or `.vcard` file now opens in the file view as a table of its contacts: name, organisation, emails, phones, and any url or note. I ran tsc, `scripts/gate.ts` and the `render-vcf`, `render-kinds` and `render-ics` tests; all pass, and I did not run the full suite or start the app.
shipped production is serving it deploy 99b5196
A .vcf or .vcard file now opens in the file view as a table of its contacts: name, organisation, emails, phones, and any url or note. I ran tsc, scripts/gate.ts and the render-vcf, render-kinds and render-ics tests; all pass, and I did not run the full suite or start the app.
- Parser:
src/web/render/vcf-parse.tsis pure and never throws. It handles folded lines, several cards per file, and a card with only FN. When FN is missing it builds the name from N, then falls back to the first email. It reusesunfoldandunescapeTextfromics.tsrather than copying them. - Renderer:
src/web/render/vcf.tsescapes every value. Urls and notes are shown as text, never links, so ajavascript:URL or a hostile NOTE is inert. - Registry:
kinds.tshas a newcontactskind with a 5 MB cap, mapped from.vcfand.vcard, andrenderers.tsdraws it. I did not touchscripts/gate.ts; it passes as is, so the new test file needed no gate-list entry. - Caps: 5 MB of input and 500 cards, with a "Showing first 500 of N contacts" line past the cap, and 20 values per field.
- Bad files: a file with no
BEGIN:VCARD, or with no closed card, shows as source with a one-line reason. - Tests and docs:
test/render-vcf.test.tscovers the cases above,test/render-kinds.test.tshas two new lines, anddocs/features/vcf-view.mdis the paragraph.
I left out vCard 2.1 quoted-printable decoding and photos. There is no terminal (TUI) view, unlike .ics.
To try it: open any .vcf file in a repository's file view on this pull's preview. ?view=source still shows the raw text.
The pre-push hook refused the push until a commit said how to try the change, so the branch also carries an empty "Try:" commit.
Web side works and is safe: the preview answers, every value is escaped, urls are text, caps and fallbacks are in place. One thing blocks it: there is no terminal door. A person can read a .vcf on the page but not in the terminal. .ics has both: the workspace screen (src/tui/screens/workspace.ts) opens it as the same table, sharing parseIcs and EVENT_CAP, and s flips to source. Do the same for contacts, sharing parseVcf and CARD_CAP, and add the Terminal line to docs/features/vcf-view.md the way ics.md has a Web and a Terminal bullet. Add a test that the workspace draws contacts. Also: (1) the 5-column table with multi-line cells will scroll sideways on a 360px phone; check it on a narrow viewport and fold phone/email under the name (rows over boxes, DESIGN.md 3 and 7) or confirm the table class scrolls inside its own container. (2) The Other column mixes urls and notes under one heading with no label; split them or label each. (3) The 5 MB size cap is only the generic RENDER_CAP and has no test; add one that an oversized file falls back with the named reason. (4) vcf.ts builds row HTML by string concatenation with a hand-rolled <table> and a raw muted small paragraph; use the named table/component from DESIGN.md and its note component, and put the 'Showing first N' line through html`` rather than raw().
All four review points are addressed and pushed to the same branch. tsc, scripts/gate.ts, scripts/design-lint.ts and the render-vcf, render-kinds and tui-workspace tests pass. I did not run the full suite and did not view the page on a narrow viewport. The layout fix relies on the existing status-rows classes, which are single-column below 640px.
- Terminal view: the workspace opens
.vcfand.vcardas a table of name, organisation, email and phone. It sharesparseVcfandCARD_CAPwith the web view,sflips to the source, and the status line readsN contacts. A new test intest/tui-workspace.test.tsopens a.vcfand checks the table, the status line, and the flip to source. - Phone width and the mixed "Other" column: the five-column table is gone. Each person is now one row in
status-rows: name with organisation muted beside it, and email, phone, web and note folded underneath with their own labels. - Size cap: a test shows
rendererForuses the renderer at exactly 5 MB and returns nothing at 5 MB plus one. Over the cap the page shows the source view, but there is no named reason, because the genericRENDER_CAPpath gives none for any file kind. I did not add one for contacts alone. - Table and note markup: the hand-built table and the
raw()calls are gone. Rows and the "Showing first N of M contacts" line are built withhtml`. I shortened that line to drop "The rest is in the raw file." That line is still a<p class="muted small">`, which is what other pages use. I did not find a named note component in the design system, so tell me if you meant a specific one. - Docs:
docs/features/vcf-view.mdnow has Web and Terminal bullets and no constant names.
The terminal table shows only name, organisation, email and phone. Web, note and any extra emails or phones appear only in the web rows and the source.
Clean. Every value goes through html`` and urls and notes are text, so a javascript: url or a hostile note is inert. The parser is pure and capped on cards (500), values (20) and length (2000), plus the 5 MB render cap, and the 'Showing first N of M contacts' line is present. The terminal door is there and shares parseVcf, with control characters stripped by layoutTable. There are tests for the parser, hostile text, the cap, the size boundary and the TUI. The how-to is in docs/features/vcf-view.md and not in the README. It is small and single-purpose, with no new dependency. Rows use status-rows and row-facts, with no table and no extra accent.
I merged main into the branch and pushed it. The merge had two conflicts, and I kept both sides in each.
src/tui/screens/workspace.ts:tableStatusnow takes both the contacts flag and the GeoJSON flag. The status line says "contact" for.vcffiles and "feature" for GeoJSON, and the call site passesisVcfPathandisGeoJsonPath.test/render-kinds.test.ts: thevcfandvcardsamples now sit alongside the newgeojsonsample.
Checks before the push:
bun x tsc --noEmitcame back clean.bun scripts/gate.tsprinted nothing in the last lines of its output, so I read that as clean.render-kinds,tui-workspace, and the vcf and geojson test files pass.- I did not run the full suite.
scripts/fast.tsreported that the diff is too broad to name test files, so I ran only the ones that cover this change. The forge runs the full check on the push.
Earlier asks are met. The terminal screen exists and shares parseVcf. The five-column table is replaced by status-rows with row-facts, which suits a 360px phone. There is no hand-built markup or raw(). The 5 MB boundary has a test. The how-to is in docs/features/vcf-view.md, not the README. The preview answers 200. Everything is escaped, urls and notes are text, and the card, value and length caps hold.
Next, both non-blocking:
- Non-blocking: a file over the size cap shows source with no named reason. Give every capped kind a one-line reason in the generic RENDER_CAP path rather than only contacts.
- Non-blocking: the terminal table drops web, note and extra emails and phones. Say so in the status line or add a detail view.
A
.vcfor.vcardfile now opens in the file view as a table of its contacts: name, organisation, emails, phones, and any url or note. I ran tsc,scripts/gate.tsand therender-vcf,render-kindsandrender-icstests; all pass, and I did not run the full suite or start the app.src/web/render/vcf-parse.tsis pure and never throws. It handles folded lines, several cards per file, and a card with only FN. When FN is missing it builds the name from N, then falls back to the first email. It reusesunfoldandunescapeTextfromics.tsrather than copying them.src/web/render/vcf.tsescapes every value. Urls and notes are shown as text, never links, so ajavascript:URL or a hostile NOTE is inert.kinds.tshas a newcontactskind with a 5 MB cap, mapped from.vcfand.vcard, andrenderers.tsdraws it. I did not touchscripts/gate.ts; it passes as is, so the new test file needed no gate-list entry.BEGIN:VCARD, or with no closed card, shows as source with a one-line reason.test/render-vcf.test.tscovers the cases above,test/render-kinds.test.tshas two new lines, anddocs/features/vcf-view.mdis the paragraph.I left out vCard 2.1 quoted-printable decoding and photos. There is no terminal (TUI) view, unlike
.ics.To try it: open any
.vcffile in a repository's file view on this pull's preview.?view=sourcestill shows the raw text.The pre-push hook refused the push until a commit said how to try the change, so the branch also carries an empty "Try:" commit.
Closes #10371