Skip to content

feat(web): redesign landing, pricing, and FAQ, and add a download page - #602

Merged
titanism merged 1 commit into
masterfrom
feat/redesign-poc
Sep 23, 2026
Merged

titanism merged 1 commit into
masterfrom
feat/redesign-poc

Conversation

@shaunwarman

Copy link
Copy Markdown
Contributor

Landing and pricing

  • New hero with a console view, solid CTAs, and a trust row that fits the first viewport; per-protocol chips and an API reference link in the developer panel.
  • Testimonials replaced with a proof section: three quotes plus receipts a reader can check.
  • Footer IA consolidated, with an accordion on mobile.
  • Crawler user agents get their own markup; RTL locales fixed.

FAQ

  • Every answer collapsed in a details element with a live filter, a topic rail with counts, and a scroll spy marking the topic being read. Deep links open their answer.

Download page

  • Release matrix rendered from the cached GitHub release, with a checked-in snapshot so the page never degrades to the /releases listing.
  • OS and architecture autodetect with a manual platform override.
  • Stores and package managers driven by config/mail-app-channels.js, with live, coming soon and not-applicable states, so flipping a channel live is a config edit.
  • Verification surfaced: per-file SHA-256, the minisign updater key, SLSA provenance with a runnable gh attestation command, and the F-Droid repository URL and fingerprint once that channel is live.
  • Honest platform notes derived from the release and config: SmartScreen while Windows installers are unsigned, no arm64 AppImage on Linux, no arm64 MSI on Windows.
  • Copy buttons fall back to execCommand where the async clipboard API is unavailable.

Build

  • CSS build sped up; the fe stylesheets are linted for token-only colour, size and radius values and one class naming form.

@titanism
titanism merged commit f8e6245 into master Sep 23, 2026
2 of 15 checks passed
@titanism
titanism deleted the feat/redesign-poc branch September 23, 2026 08:14
titanism added a commit that referenced this pull request Sep 23, 2026
1) Cache stampede (DoS) across all three read-through caches
   helpers/single-flight-cache.js (new), get-faq-index.js, get-faq-schema.js,
   get-mail-app-releases.js. Each did a plain "read redis, on a miss compute +
   write redis", so a cold key let every concurrent request across every worker
   run the expensive compute at once (N markdown parses; N GitHub API calls).
   The new helper wraps the compute in a redis SET NX PX lock released by a
   compare-and-delete Lua script (the get-database.js idiom): one caller
   computes, the rest wait or fall back. Fails open, never memoises a null/empty
   result, validates cached values on read (an entry failing the caller's
   shouldCache is re-computed, not served — matching the old getFaqSchema), and
   accepts either a raw string or an already-parsed object from client.get (a
   get reply transformer, which the tests use, hands back the latter). Bounds
   the GitHub fetch with AbortSignal.timeout(10s).

2) isSANB is not defined — the FAQ was always empty
   routes/web/index.js used isSANB(ctx.query.q) but never required it; the throw
   was swallowed and the page fell back to an empty index ("0 answers"). Added
   the require. FAQ now renders all 147 answers.

3) Guide pages rendered blank
   controllers/web/guides.js renders faq/index.pug to scrape an answer out of it
   (#send-mail-as-content, #smtp-instructions, #legacy-free-guide) but didn't
   pass `faq`, so the mixin threw and the scrape came back empty. Fixes:
   - guides.js populates `faq` (via #helpers/get-faq-index) before rendering.
   - get-faq-index.js sanitizer keeps `id` on every element (it named only the
     heading tags, stripping the <div id>/<li id>/<table id> anchors the scrape
     and in-page links need).
   - get-faq-index.js cache key bumped to `faq_index:v2:` — the parsed HTML
     shape changed and the cache has a 12h TTL, so without a new key a deploy
     would keep serving the old id-stripped HTML and the guides would stay blank
     for up to 12h. The new key sidesteps the stale cache with no manual flush.
   - _fe-faq.pug defaults `faq` to an empty index so the mixin can't 500.

4) Footer dropdowns jumped the page (mobile/tablet)
   _footer.pug: collapse toggles used href="#footer-col-*", which the global
   a[href^='#'] handler scrolls to. Switched to href="#" + data-target, the
   framework's non-jumping idiom. Collapse still toggles; the page does not move.

5) Dark mode — consistent across the whole site
   _fe-tokens.scss, app-dark.scss.
   - .fe-surface-light was locked light in both schemes, so the FAQ body and
     other sections stayed near-white in dark mode. Re-pointed it to the dark
     ramp, and to the same Ink as every other surface, so a lighter band no
     longer sets the redesigned pages apart from the legacy ones — every page is
     one cohesive Ink in dark mode.
   - The marketing/blog comparison tables (.bg-themed) kept light
     .table-success/-primary row tints; re-pointed to dark tints with light
     text, scoped to .bg-themed so the app's status tables keep theirs.
   - The article pages (about, guides, blog, docs, terms, privacy) render
     through .markdown-body, which github-markdown-dark paints in GitHub's own
     palette (brighter #f0f6fc text, a different #4493f8 blue link). Re-pointed
     the markdown text, headings and links to the shared app tokens so those
     pages match the redesign's colors too.

6) Test fixes
   - config/mail-app-release-fallback.json: added the release's SHA256SUMS.txt
     asset, which the checked-in snapshot omitted — get-app-downloads asserts
     the checksum link points at that file, so the test failed on a clean PR.
   - get-faq-schema cache tests pass again (see the read-side handling in 1).
   - ava.config.js: exclude test/visual/**. test/visual/sweep.js is a standalone
     puppeteer CLI (reads process.argv, calls process.exit), not an AVA test;
     AVA was running it and failing on the process.exit. Excluded the same way
     test/utils.js already is.
   - test/web page snapshots made build-independent. The local-only
     (`if (!isCI)`) index.js/otp.js snapshots embedded rev-hashed asset
     filenames and SRI hashes, which differ between build environments, so a
     fresh checkout failed them with no source change (and the footer markup
     change also needed them updated). Added utils.normalizeBuildHashes to strip
     those hashes before snapshotting — the same intent as otp.js already
     stripping the <head> — so the snapshot captures page structure, not build
     noise, and passes on any build. Snapshots regenerated in that normalized
     form (0 build-specific hashes remain; the footer fix is captured).
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