feat(fileshare): recipient-field clarity, draft persistence & compose/nav polish - #306
Conversation
Users in the July 2026 user test typed the recipient's email address into the optional message box because the recipient field did not clearly read as "who you're sending to" (#289). - Relabel the recipient input "Recipient email address" (EN) / "E-mailadres ontvanger" (NL) instead of the generic "Email address". - Style it distinctly as an address field: leading envelope icon plus email autocomplete / inputmode / autocapitalize / spellcheck hints. - Field order is already Recipient -> optional Message -> Send; confirmed and locked in with a regression test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict: REQUEST_CHANGES — posted as a COMMENT only because GitHub blocks self-request-changes on a bot-authored PR. Treat as blocking.
Rules Dobby 2 — gatekeeper review (1 blocking finding).
The UI change itself is sound: the recipient input now carries an explicit "Recipient email address" label (en/nl), reads as an address field (type=email + autocomplete/inputmode/autocapitalize/spellcheck), gains a decorative leading envelope icon, and sits before the optional message box — cleanly addressing #289. Design, a11y (the icon is correctly aria-hidden/pointer-events:none), and the regression test's intent all check out.
Blocking: the new regression test fails CI's Svelte Check job with a type error (elementHandle() may be null passed to a Node param). See the inline comment for the exact reproduction and fix. Build, ESLint, Prettier, Stylelint and the Playwright run (13/13) all pass — this is the only issue.
Non-blocking note: PRs #303–#308 from the July-2026 user-test batch each touch the shared en.json/nl.json bundle. They edit distinct keys (no overlapping lines) and are independent per-component fixes, so separate PRs are acceptable here — just flagging per consolidate-overlapping-prs in case a conflict surfaces at merge time.
…nt ordering test message.elementHandle() can resolve to null; narrow it with a guard before passing to recipient.evaluate so `Svelte Check` (CI) type-checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict: APPROVE (sign-off) — posted as
event=COMMENTbecause GitHub blocks self-APPROVE on a bot-authored PR (this PR is authored bydobby-coder[bot]). Treat as approved.
Rules Dobby 2 — gatekeeper review (cycle 2): no findings, clean.
The cycle-1 blocking finding (regression test failed Svelte Check — nullable elementHandle() passed to a Node param) is resolved at HEAD by commit 0423f89. Re-verified on a fresh checkout: npm run check → svelte-check reports 0 errors / 0 warnings across 819 files.
Rule-compliance and code-correctness checks both came back clean:
- Conventional-commit title ✓, closing keyword (
Closes #289) ✓, regression test present ✓ (tests-required-on-fixes). - WCAG: decorative envelope icon is
aria-hidden="true"/focusable="false"/pointer-events:none; input keepstype=email+autocomplete/inputmode/autocapitalize/spellcheckand its accessible name ("Recipient email address"); label/input association intact. - No layout/type regressions —
.pg-inputstays full-width inside the new flex wrapper.
Review Dobby 2 handed forward 0 findings (Playwright 13/13, build clean). Flipping the PR to ready-for-review.
…nt-field-clarity # Conflicts: # src/lib/components/filesharing/RecipientSelectionFields.svelte
Native <details>/<summary> shows and hides its content instantly with no transition. Drive the disclosure from Svelte state instead so the content can slide vertically in and out with a transition, honoring the disclosure a11y pattern (aria-expanded/aria-controls) and prefers-reduced-motion.
FF_BUSINESS was read from the runtime-injected APP_CONFIG at module load. On prerendered marketing pages the flag is false at build time (no APP_CONFIG), so the static HTML omitted the business nav/footer item; at runtime the flag is true, so hydration re-added it. That prerender-vs-client mismatch made the navbar visibly reflow on every refresh (#297-adjacent). The feature is fully rolled out, so make business a build-time constant that is always present. Prerendered HTML and client render now agree — no reflow.
Add an ExternalLinkIcon that reproduces the docs.postguard.eu (VitePress) external-link marker 1:1 — the Material north-east arrow glyph, 11px, muted tertiary colour, nudged 4px right / 1px up. Apply it to off-site links in the desktop header, hamburger menu, and both footers (docs, business, GitHub, Yivi, Caesar), and open those links in a new tab with rel=noopener. The arrow is decorative; a visually-hidden "opens in a new tab" hint (en/nl) keeps the link's accessible name intact.
Refreshing /fileshare previously cleared everything the user had entered. Persist the recipient email(s), requested attributes, message and attached files so a refresh restores the compose step. - Files + metadata are stored in IndexedDB (files can be large). FileInput re-injects restored files via Dropzone.addFile so their previews render and they flow into state like a normal add. - A sessionStorage marker scopes the draft to the tab session: it survives a refresh but a new session drops any stale draft, so file contents don't linger. The draft is also cleared on a successful send. - Saves are gated until the initial load runs, so an early empty save can't clobber the draft being restored.
Closes #289 (part of encryption4all/postguard-js#188).
This began as the recipient-field clarity fix for #289 and picked up several
related compose, navigation, and marketing polish items requested alongside it.
Latest
mainis merged in.Recipient field clarity (#289)
In the July 2026 user test a participant typed the recipient's email address
into the optional message box instead of the recipient field — the recipient
input read generically as "Email address" and didn't signal "who you're sending
to".
(EN) / "E-mailadres ontvanger" (NL) instead of the generic "Email
address".
type=emailwithautocomplete="email",inputmode="email",autocapitalize="none",spellcheck="false", plusinline invalidity (shown only after the field is left) and a typo suggestion
(e.g.
gmial.com→gmail.com) so what turns red matches the send-timecheck.
(
recipient@example.com) + field semantics carry the distinction without it.Compose step
message, and attached files are persisted so refreshing
/filesharenolonger wipes a half-written message. Files + metadata live in IndexedDB;
restored files are re-injected into the dropzone so their previews reappear.
Scope is bounded to the tab session via a
sessionStoragemarker (survives arefresh, dropped on tab close) and cleared on a successful send.
unencrypted in the browser's IndexedDB. That's the deliberate trade-off for
surviving a refresh; it's dropped on tab close (next-load cleanup) and on
send.
font-size: md.Help toggle
<details>). They now slide vertically open/closed (Svelte-controlled,reduced-motion aware, keeps the
aria-expanded/aria-controlsdisclosuresemantics).
Navigation & marketing
FF_BUSINESSwas read from theruntime-injected
APP_CONFIG, so prerendered marketing pages omitted the"PostGuard for Business" nav/footer item (flag false at build) while the
client re-added it at runtime. That mismatch reflowed the navbar on every
refresh. The feature is fully rolled out, so business is now a build-time
constant that's always present — prerendered HTML and client render agree.
Caesar) in the header, hamburger menu, and footers now carry the
docs.postguard.eu (VitePress) external-link marker — reproduced 1:1: the
Material north-east glyph, 11px, muted, opening in a new tab with
rel=noopenerand a screen-reader "opens in a new tab" hint.Manual verification — draft persistence
Worth checking on the preview since it's hard to cover end-to-end automatically:
/fileshare, attach a file and type a recipient email + message, thenreload — the file preview, email, and message should all return.
/fileshare— the draft should be gone (emptyform), with no leftover data in IndexedDB (
postguard-fileshare).Checks
svelte-check(0 errors/0 warnings),eslint, andprettier --checkclean onchanged files; Svelte MCP autofixer reports no issues. The external-link arrow
was screenshot-compared against docs.postguard.eu, and draft persistence was
verified in a real browser (add → refresh → restore; new session → cleared).