Changelog
All notable changes to Patens. Format loosely follows Keep a Changelog; versions are tagged as vX.Y.Z on the GitHub repo.
[Unreleased]
The OSS-readiness arc + TypeCon Portland launch prep. Accumulates since v1.5.2; will tag as v1.6.0 once the demo GIF lands + real-device responsive sweep is complete.
Security
- Share delete-tokens stored as
sha256:digests (PR #8). Previously the Vercel Blob holding the delete-token was public-readable and contained the raw token. Now we store only the SHA-256 digest with a stable prefix; the originator keeps the raw token client-side.tokenMatchesStoredfalls back to a constant-time raw compare for pre-existing shares so existing tokens stay valid until their next write (migration is automatic on re-share). - Baseline HTTP security headers on every response via the SvelteKit
handlehook:X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), plusStrict-Transport-Securitygated on HTTPS so local dev still works. Verified in production viacurl -I. - Production error sanitization. Stack traces and internal paths no longer leak to the client in production; the
+error.svelteboundary gets a generic "Something went wrong." Dev keeps full details. The previous dev-overlay XSS surface (it usedinnerHTMLonerror.message) is closed — overlay now uses DOM API +textContent. /api/ai/messagesAnthropic proxy hardening. Request body capped at 256 KB; max 8 messages per request; max 24 K characters per text block; max 12 K characters insystem; max 4 096max_tokens. Model allowlist enforcesclaude-opus-4-7/claude-sonnet-4-6/claude-haiku-4-5-20251001. Upstream error bodies no longer forwarded verbatim — replaced with a sanitized{ error, status }payload to prevent leaking rate-limit / quota detail./api/share/[id]/versionsno longer returns direct Vercel Blob URLs. Consumers route through/api/share/[id]?v=Nso future access controls don't get bypassed. ShareVersion type updated;/share/[id]page consumes the slimmer shape.
Added
- CLI:
npx patens audit— the 94-code audit module + preflight + compatibility checks shipped as a Node CLI. Single bundled ESM via esbuild, 3 output formats (text/json/githubfor PR annotations), exit code 2 on errors. Drop-in CI step; closes the "audit is locked inside the editor" critique. 13 unit tests cover the formatters. - 10 Architecture Decision Records under
docs/decisions/(Michael Nygard format). Documents the load-bearing choices: browser-native, MIT-forever, SSR-at-root, audit worker, share-as-capability, storage namespace staysfont-studio-*, BYOK AI, strict quality gates, demo open-by-default. - TypeDoc API reference —
pnpm run docs:apibuilds HTML docs underdocs/api/for the audit + export + family-kerning + kerning-classes + path + font-machinery modules. Output gitignored, regenerable from source. - 2 new perf benchmarks —
buildFont(50/162/500-glyph projects: 2.4ms / 19ms / 126ms per call) +expandKerningClasses(small/medium/large with up to 360k expanded pairs in 19ms). Mirror the existingaudit.benchpattern. - 30-second demo GIF storyboard at
docs/launch/demo-gif-storyboard.md— frame-by-frame pre-record blueprint: setup checklist, ffmpeg compression commands, per-surface delivery table. - Responsive audit + sign-off checklist at
docs/launch/responsive-audit.md— code-level findings plus the iPhone + iPad device-test list that requires real hardware. - AGENTS.md — Linux Foundation cross-tool AI-assistant onboarding standard.
- ARCHITECTURE.md — six-layer map + the core flows (cold-load, audit, share-write, export) + the SSR posture rationale.
- DESIGN_PHILOSOPHY.md — the why behind Patens (teaching-first, audit-as-citizen, no AI-led marketing, never paywall the editor). Referenced from PR template.
- MAINTAINERS.md — solo-maintainer response SLA, triage cadence, scope vs. out-of-scope.
docs/release-process.md— solo-maintainer release playbook: cadence, semver rules (audit-code IDs stable forever), pre-release checklist, tagging, post-release verification, hotfix flow, rollback via Vercel promote.- CodeQL SAST + OpenSSF Scorecard + Dependency-review + Release-drafter GitHub Actions workflows.
- CycloneDX SBOM via cdxgen in CI — EU CRA future-proofing.
- Devcontainer config for Codespaces + VS Code dev-container.
- OSS-readiness signal artifacts:
FUNDING.yml, RFC 9116security.txt,humans.txt,CONTRIBUTORS.md. Plus 6 paste-ready prep docs underdocs/launch/— NLnet NGI Zero Commons, OpenSSF Best Practices, USPTO trademark walkthrough, GitHub Sponsors, Polar, 3 awesome-list PRs. SUPPORT.md+.github/ISSUE_TEMPLATE/config.yml— channel routing: bugs → issues, ideas + Q&A → Discussions (now enabled on the repo), security → SECURITY.md. The chooser config exposes Discussions + /help + the GitHub Security Advisory form as contact_links above the Bug / Feature templates; the question.md template was removed since Q&A now has a dedicated home in Discussions.- GitHub social preview image —
static/github-social.png(1280×640, 30 KB) generated bypnpm build:github-social. Renders the new round O + smooth-shoulder n + HONE/TONE samples from the demo OTF + 94-rule audit-module strap-line. Drop into Settings → General → Social preview (no GitHub API exposed for this). SoftwareSourceCodeJSON-LD entity on the home page @graph — cross-references the existingWebApplicationviatargetProduct@id. Useful for AI-agent retrieval ("what's the source for this product") + SERPs that want to surface the GitHub repo alongside the live web app.- OpenSSF Best Practices Silver-tier self-cert answers appended to
docs/launch/openssf-best-practices.md. Silver adds 55 criteria over Passing; ~85% are already satisfied by the existing setup. Two SHOULDs use the rationale-acceptance escape hatch (solo-maintenance + SLSA-provenance deferred). Gold deliberately not pursued — requires bus-factor ≥ 2. - Codecov upload in CI (
codecov/codecov-action@v5, tokenless OIDC for public repos) after the existingvitest --coveragestep.fail_ci_if_error: falsekeeps merges green when Codecov has an outage. Closes OpenSSF Silver "automated test coverage tracking" gap. - Cryptographic tag signing for releases — documented setup + release flow + consumer-verification path in
docs/release-process.md. One-time setup:git config --global tag.gpgsign true(reuses the SSH key already signing commits). Closes OpenSSF Silver "cryptographic release signing" gap. fast-checkproperty tests for the audit module (9 invariants × 100 random inputs each = 900 randomized checks per run). Asserts:auditGlyphnever throws, returns an array, every issue has the full{codepoint, severity, code, message}shape, codes are kebab-case, severities are within the enum,sortBySeveritypreserves count + ordering, audit is deterministic, every issue codepoint matches the current glyph. Closes OpenSSF Silver "fuzz testing on parsers" gap.- Unit tests for the 7 pure-helper modules extracted during the editor decomposition — 145 new tests across
onboarding-targets,glyph-deriveds,path-edit-transforms,glyph-paste,glyph-export,fix-issue, andaudit-catalogue. Per-file@vitest-environment happy-domopt-in for the DOM-touching glyph-export tests; the other suites stay on the fast node env. Suite count: 528 → 672 (+144). /privacy+/securitypages — plain-English, GDPR-shaped data inventory + responsible-disclosure flow. Both prerendered with full JSON-LD./presskit — factsheet (structured-data canonical), three-length descriptions, brand assets + screenshots placeholder, milestones.- Re-share version picker UI on
/share/[id]+ multi-master axis-pair picker on/designspace. Closes the two "server-ready, UI pending" items from the v1.5 backlog. - 94 dedicated audit-rule pages at
/audit/[code]— single source-of-truth catalogue atsrc/lib/font/audit-catalogue.tsbinding code → title → category, with description sourced fromaudit.ts'sdescribeAuditCode()so editor inline help + public pages stay in sync. Each page is prerendered (94 static HTML files) and shipsDefinedTerm+TechArticle+BreadcrumbListJSON-LD cross-referenced by@id. Targets the long-tail typography queries that previously had no dedicated landing page on the open web ("what is overshoot in type design", "what is contour-winding-collision")./learn/audit-codesupdated to link each code to its dedicated page; sitemap folds in all 94 URLs at priority 0.6. /pronunciationpage withFAQPageJSON-LD — answers "how do you pronounce Patens" with IPA + the Latin etymology (patēns, "to lie open"). LLMs extract Q&A directly; niche brand names benefit measurably from a focused page per the launch AIO research. Linked fromSiteFooterunder "Company".docs/launch/typecon-runway.md— canonical 10-week launch plan that supersedes the partial calendars inmaster-plan.md § 6andapp-improvements-plan.md's P0/P1 sections. Covers ownership splits (yours / mine), week-by-week calendar, eight named risks with mitigations, six decision points each with a recommendation, and a done-definition for launch.- NEW: dedicated
/auditmarketing page — differentiator landing for the 94-code audit module. Hero + 9-family breakdown + 6 verbatim sample codes + CLI integration + 7-Q&A FAQPage + see-it-in-action CTAs. Prerendered to ~33KB. WebPage + BreadcrumbList + FAQPage JSON-LD for AI-engine citation. - Trust band on home page — 4-column Swiss-disciplined "by the numbers" row below the hero: 94 audit codes · 162 demo glyphs · 528 tests · MIT open source. Mono-uppercase labels + Hoefler-serif numerals.
- "How the audit teaches" 3-card sample on home — verbatim
describeAuditCode()prose forself-intersecting(fixable),xheight-misaligned(designer judgment),sidebearing-class-drift-lsb(designer judgment). Demonstrates the differentiator IN SITU. - SiteHeader + SiteFooter components —
src/lib/ui/SiteHeader.svelte(sticky top nav, audit-led 4-item navigation with prefix-match active state) +src/lib/ui/SiteFooter.svelte(4-column grid: Product · Learn · Company · Legal & Source). Wired into 16 marketing pages + the 404 page. - 6 dedicated Satori-rendered OG variants —
/og/audit,/og/learn,/og/compare,/og/press,/og/about, plus the existing/og/brandand/og/home. Each marketing page'sog:image+twitter:imagenow point at its route-specific card with route-specific copy + URL stamp. - Article schema per release on
/changelog— top 10 releases now ship as discrete schema-typed entities (Article + datePublished + author + publisher + description) so AI engines can cite "what changed in Patens v1.5.2" as a specific citable answer.
Changed
- Audit-led home welcome strip copy. The first-visit non-blocking strip now leads with the audit module as the differentiator: "A type design tool that teaches as you draw." Compresses cleanly to a tweet or a Show HN headline. Welcome dialog later enriched with a mono-uppercase metadata row (94 codes · 30 one-click fixes · MIT · no installs) + a new "Why audit-as-teaching →" CTA pointing at
/audit. - Swiss-influenced heading scale-up across 8 marketing pages. Hero
sm:64→sm:80, h136→48, h2 canonical20→28(incl./changelog24→28,/learn22/40→28/48). h2 vertical rhythmmt-12 mb-3→mt-16 mb-4. Body sizes unchanged — Patens's density (14-15) is deliberate. - Section rules on document-flow pages (
/about,/privacy,/security) —border-t border-border/30 pt-12above each h2 for structural skeleton. - CONTRIBUTING + MAINTAINERS cross-links to ADRs, DESIGN_PHILOSOPHY, AGENTS, release-process. Lint-gate exception rationale points at ADR-009.
- README opening blurb reworked to lead with audit-as-teaching positioning, not feature-list. Capability table reorders Audit to the top as the headline row. Closing paragraph back-links to
/auditso GitHub visitors can drill into the differentiator marketing page. - Comprehensive SEO + AIO completeness sweep. Every public route now ships: JSON-LD (WebSite + Organization + Person + WebApplication on home; CollectionPage + ItemList on /learn; FAQPage on /audit + /help; Article × 10 on /changelog; TechArticle on reference pages; HowTo on tutorials), canonical URLs, absolute
og:imageURLs,og:image:alt+twitter:image:alt,twitter:site+twitter:creator(@patenstype),meta name="author",og:localeen_US,og:image:typeimage/png. Sitemap shipped with per-route honestlastmoddates (replacing the "everything updated today" anti-pattern that lowers crawler trust). - PWA manifest enrichment — added
id,display_override(window-controls-overlay),screenshots, and 2 appshortcuts(Demo + Audit) for richer install prompts on Chrome Desktop + Android. /help+/changelogtitles carry "2026" — closing the year-in-title pass per the Leapd GEO finding (visible year in<title>≈ +30% citation rate from AI engines). The other landing routes (/,/about,/compare,/learn/*) already had it.- Demo OTF expanded 9 glyphs → 26. Was: space + H/O/T/I/E/N + o/n only. Now: A C D E G H I M N O P R S T U + a e h n o s t + 0 1 2 + . , - !. Lets the home hero, OG image, and GitHub social preview render "PATENS · STUDIO GEOMETRIC · HONE THE TONE" in the project's own font instead of "Hn" + "HONE" + "TONE". Three winding-rule artifacts fixed in the process: bowl-ring outer-left now aligns with stem-left (P/D/R/U); C/G/e mouth-cuts clipped to outer curve to prevent the +1 outer − 1 cut = filled-crescent artifact; digit 2 rebuilt as a single CCW contour. Demo OTF grew 2.3 KB → 4.4 KB.
- Demo OTF proportions refined. Bowl rx 0.32 → 0.40 of CAP_W on P/R; D bowl rx 0.4 → 0.48 (was reading as "I with a balloon"); A rebuilt as two parallelograms meeting at a STEM-wide flat apex with a properly-clamped crossbar; G's spur extended from cx → cx+rx so it visibly attaches to the right wall; lowercase a rx formula corrected (115 → 160; was rendering as a narrow oval). Kerning regression snapshot updated — values shifted with the new silhouettes but all changes typographically sensible (PA tightens with bigger P bowl; Ta loosens with wider A).
- In-editor demo project (the
/project/demo/editshipped font) now uses curved Béziers for bowl letters.demo-project.tswas using straight-line polygons only — 16-sided polygon for O, three-rect stacks for P/R/D/B/C/G/U "bowls". Now: kappa-Bézier rings via a newcurvedRinghelper +stemBowlfor the bowl-stem composites. TheBezierContourformat already supported cubic curves viaPathCommand.type === 'C'; the old code just chose to emit L-only. Editor still renders, edits, and runs boolean ops on the curves natively. /share/[id]version selector —window.location.hrefreload replaced with SvelteKitgoto(url, { invalidateAll, noScroll })for SPA-smooth version switching (no white-flash full reload). Dropdown options now show file size alongside date. New "copy permalink" button appears when a historical version is pinned, so a designer can hand around the exact-version URL without memorising the path.
Fixed
- Home page Lighthouse A11y 98 → 100. axe-core flagged
landmark-one-main: the home page lacked a<main>landmark. The outer drag-drop surface (<div role="application">) became<main>; drag-drop event handlers unchanged. - Editor mobile-gate. A phone hitting
/project/[id]/editused to get the desktop editor crammed into a 375px viewport with no warning. Non-dismissible<1024pxbanner inside the project layout now names the constraint. iPad in landscape (≈1180px) clears it. - Pin button on home cards was invisible on touch devices (
opacity-0 group-hover:). Nowopacity-60on mobile, hover-revealed onsm:+. - Stale
security.txtpointed atfont-studio.vercel.app; updated to canonicalpatens.designURLs. /families500 in production —+page.tscalledlistFamilies()(idb-keyval) in the SSR load function, whereindexedDBis undefined. Addedexport const ssr = false;matching the posture of/share/[id]and/project/[id]/*.- Dev-server cache thrash — Vite's HMR file-watcher was firing on every
.vercel/output/static/regeneration (~17 HTML files per build × 50 commits = hundreds of bogus reload cycles, corrupting the dep-optimizer cache). Watcher now ignores.vercel/,.svelte-kit/output/*,build/,dist/, all.md, and doc trees. Also addedpnpm run cleanscript for one-command cache wipes. - Bench test bounds + timeouts for CI —
buildFont500-glyph bench bound 2000ms → 8000ms (CI runners are 15-20× slower than local); vitest test timeout raised to 30s to absorb the warm-up + iter × per-call walltime. - Stale "88 glyphs" + "8 glyphs drawn" copy on home page's "See it in action" Hn section — last of the 88→162 surface sweep that earlier passes missed.
Performance
- Production cold-load multi-run baseline confirms no perf regressions. 3 cold runs against
patens.design(Chromium viascripts/profile-cold-load.mjs):/612ms FCP (warm 342ms);/project/demo/edit1056ms LCP cold (warm 658ms);/share/demo671ms FCP (warm 522ms). Zero long tasks ≥50ms anywhere. All three routes well inside Web Vitals "good" (LCP ≤ 2.5s). A single-run +346ms "editor LCP regression" turned out to be CDN cold-warming noise; streaming-SSR-on-editor demoted from must-have to nice-to-have. Numbers + verdict captured indocs/launch/lighthouse-baseline.md. - *
/project/[id]/critical path: −243 KB raw (−68 KB gzipped) by deferring opentype.js out of the project layout's eager chain.previewStore(mounted by the project layout for every sub-route) statically importedbuildFontfrom$lib/font/export.ts, which in turn statically importedopentype.js. That made the 243 KB opentype chunk land on the audit / spacing / features / release / share sub-routes — none of which immediately render the live preview. MovedbuildFont+applyColorFontTablesto dynamic imports insidePreviewStore.build(); opentype now lands ~180 ms after layout mount when the rebuild debounce fires, well after canvas paint. Static imports ofmzzzXzJM.mjsacross the manifest: 3 → 0.** - Editor cold-load: 593 KB → 486 KB (−106 KB / −17.9%). Four-stage defer pass:
- Audit module dynamically imported on requestIdleCallback; the 94-check × every-glyph badge computation no longer blocks first paint. - SettingsDialog (5KB) + ShortcutsDialog (5KB) + StatsPopover (19KB) lazy-mount on first open. - CommandPalette (15KB) lazy-mounted in the root layout — saves 15 KB on every cold load across every route, marketing pages included. - EditorTour (7KB) lazy on tour activation. - 5 right-sidebar panels (CompositeEditor 15KB + ReferenceImagePanel 11KB + RevisionsPanel 8KB + MetricsInspector 5KB + StemsPanel 3KB) batched into a single Promise.all import on requestIdleCallback ~200ms after canvas mount.
- Pyodide lazy-loaded off the home-page eager chain. UFO import now dynamic-imports
$lib/font/pythonon first use; first-visit home no longer ships the Python runtime in the preload graph.
[1.5.2] — 2026-05-25
Security
- Closed open-redirect via
?returnToon/auth/login,/auth/callback/github,/auth/logout.safeReturnTo()helper validates the redirect target resolves to the same origin; off-origin / scheme-relative /javascript:/data:URLs fall back to/. 13 unit tests cover the helper + the encode/decode/tamper paths. - Replaced
===token comparison withtimingSafeEqualin share POST + DELETE (constantTimeEqualinsrc/lib/share-blob.ts). Defense-in-depth against timing attacks.
Fixed
- Pointer-listener leak in the 2D variation explorer. Inline-template handler was recreated on every reactive update + added new listeners on every pointerdown with no cleanup if pointerup was missed (pointercancel / lostpointercapture / ESC during drag). Hoisted to script block; added re-entrancy guard + listens for all drag-end events.
Added
- Family kerning UI panel on
/family/[id]— completes the v1.5.0 Day 9 data-only work. Table of family-wide pairs (left + right + value + delete) + inline add form. Pairs apply to every sibling at resolution time viaresolveKerning(). - e2e API smoke tests (
e2e/api-smoke.spec.ts) — 12 tests covering health version, sitemap + RSS XML validity, OG byte size with PNG magic-number check, manifest PWA shape, share endpoints 503/404 paths, auth gates. Catches the regression classes thatdocs/qa-checklist.mdexists for, in CI instead of post-deploy. session.test.ts— 13 unit tests for the signed-cookie session helpers + the newsafeReturnToopen-redirect defense.
Changed
- Lint baseline ratcheted 22 → 2 (CI gate at 5). Cleaned vestigial vars across
svg-path.ts(cx/cy/startX/startY current-point trackers that were never read),audit.ts(unused depth counter),path.ts(dead pts[] + last in reverseContour),export.ts(orphan effectiveContours wrapper),GlyphBrowser.svelte(orphan handleDelete), and a few placeholder/escape-character tightenings. - Cold-load baseline refresh at
patens.design(post-rebrand + post-preload-mjs): home FCP 1057ms (was 1319ms in v1.4.0 — 20% faster), editor FCP 906ms, share FCP 1356ms. 0 long tasks anywhere.
[1.5.1] — 2026-05-25
Changed
- Rebrand: Font Studio → Patens. Canonical domain
patens.design. Latin for "lying open, accessible" — root of patent, the word for openness before lawyers got to it. Open-source positioning made explicit in the name. - Canonical-domain 301 redirects in
vercel.json—patens.app/patens.studio/font-studio.vercel.appall permanently redirect topatens.design. One source of truth for SEO + OG previews. - Storage namespaces deliberately preserved as
font-studio-*(IndexedDB, localStorage, cookies, service-worker cache, PartyKit room) — backward compatibility with existing browser data.
[1.5.0] — 2026-05-25
Added
- WCAG 2.0 + 2.1 + 2.2 A/AA test coverage. axe-core via Playwright now runs the full WCAG A/AA tag set across 19 routes. Caught + fixed: scrollable-region-focusable on /share, link-in-text-block on 6 files (13 sites), label-content-name-mismatch on 4 editor toolbar buttons, and target-size on the 9 GlyphBrowser filter chips (now
min-h-[24px]). - Cold-load profiler.
scripts/profile-cold-load.mjsdrives headless Chromium via CDP, captures the Performance trace, and digests long tasks + layout events + FCP/LCP markers. Wired aspnpm profile [url]. v1.4.0 baselines documented indocs/qa-checklist.md. - Post-deploy QA checklist.
docs/qa-checklist.md— 9 sections covering URL smoke tests, OG image byte verification, health version drift, end-to-end editor walk, share + cloud, RSS validation, perf profile, lighthouse + axe, and CI status. Triggered by 2 production bugs caught during v1.4.0 deploy that the checklist now catches in 30 seconds. - CODE_OF_CONDUCT.md — Contributor Covenant 2.1, linked from CONTRIBUTING.
Fixed
/og/[id]500 in production —@resvg/resvg-js-linux-x64-gnunative binding wasn't bundled when prebuilt-deploying from Mac. Fix:pnpm.supportedArchitecturesconfig + deploy without--prebuiltso Vercel installs the right native bindings on Linux./og/[id]500 after first fix — Lora + Inter fonts understatic/og-fonts/aren't bundled with serverless functions. Moved tosrc/lib/og-fonts/and read via$app/server'sread()so the binaries embed in the function bundle.- Lighthouse CI broken on every push — the
lighthouse:no-pwapreset's strict insight assertions were erroring onforced-reflow-insight,network-dependency-tree-insight,target-size,uses-passive-event-listeners,uses-rel-preconnect,bf-cache. Relaxed towarn; ratcheted category-score thresholds (perf 0.85, a11y 0.90) toerrorto gate on real regressions.
Changed
- Network preload strategy →
preload-mjs. Default'modulepreload'only emits inside the inline bootstrap script; switched to'preload-mjs'which puts<link rel="preload" as="script">in<head>. Home page went from 0 → 45 preload tags; the full chunk graph fetches in parallel with HTML parsing. Closesnetwork-dependency-tree-insight+ transitively eliminates the long-task waterfall behindmax-potential-fid. - EditorTour scroll listener → passive (
{ capture: true, passive: true }). Closesuses-passive-event-listeners. - Editor toolbar buttons — moved descriptive aria-label text to
title=so the visible button text becomes the accessible name. Voice-control users can now say "click Undo stroke" / "click Copy path" / etc. Closeslabel-content-name-mismatch. - describeAuditCode() — added the 43 missing descriptions; full 94-code coverage. New entries span brief completeness, coverage subsets, glyph count, UPM, naming, OS/2 metadata, vertical-metric mismatches, designspace orphans, kerning classes, and anchor coverage.
- /changelog — RSS 2.0 feed at
/changelog/rss.xml(autodiscovered + visible link); h2 deep-link anchors; visible Subscribe-via-RSS affordance. - /help — h2 deep-link anchors on each section; FAQPage schema.org JSON-LD for Google rich results; per-page OG + Twitter meta with
og:image=/og/brand. - /about — per-page OG + Twitter meta +
og:image=/og/brand. Version read frompackage.jsonso it can't drift. - /health — version read from
package.json(was hardcoded). - Home page footer — Help / Changelog / About / GitHub nav added.
- Sitemap — adds /help, /changelog, /about, /changelog/rss.xml.
docs/next-90-days.md+docs/phase-c-sprint-plan.md— frozen as archival snapshots.
Deferred
forced-reflow-insight— closed via profiler measurement (max 35ms layout on editor cold-load, all normal first-render territory).max-potential-fid— closed via profiler measurement (0 long tasks ≥50ms across all profiled routes).uses-rel-preconnect— false positive (Vercel auto-injected analytics, not site code).
[1.4.0] — 2026-05-24
Added
- PWA install support.
static/manifest.jsonwith name, short_name, description, start_url, scope, display: standalone, theme + background colors, 3 icons (svg + 192/512 PNG, "any maskable" purpose), categories. Linked fromapp.html. Installable on Chrome/Edge desktop and iOS home-screen "Add to Home Screen". - Favicon variants. Rasterized
static/icon-192.pngandicon-512.pngfrom the favicon.svg (light-mode H-on-#fafaf9), wired as<link rel="icon" type="image/png">andapple-touch-icon. - /help page. FAQ across six sections (getting started, sharing, export, editor, performance, broken). 17 questions answered. Static, prerendered.
Fixed
- Share page JSON-LD parsing. Escaped the inline
</script>literal in the{@html}block as<\/script>so the svelte-eslint-parser doesn't terminate the script context prematurely. Functionally identical at runtime. - Changelog
{@html}lint. Moved the eslint-disable-next-line comment to the same block as the actual{@html}paragraph render.
[1.2.0] — 2026-05-24
Added
- Multi-script starter set. 17 Cyrillic look-alike glyphs (А В Е К М Н О Р С Т Х uppercase + а е о р с х lowercase) and 14 Greek look-alike glyphs (Α Β Ε Ζ Η Ι Κ Μ Ν Ο Ρ Τ Υ Χ uppercase). All reuse Latin builders where the shape is identical in a geometric sans; bespoke Cyrillic/Greek shapes (Я Ж Ф + entire Greek lowercase) are explicit future work.
- 5 new audit codes:
color-layer-no-palette,color-layer-out-of-range,kerning-pair-self,master-empty,feature-kern-disabled-with-pairs. Audit code count: 88 → 93. - Demo
brief.differentiation+decisionsupdated to acknowledge the multi-script starter and frame the trade-off honestly.
[1.1.0] — 2026-05-24
Added
- Cloud share. New
POST /api/share+GET /api/share/[id]endpoints back by Vercel Blob; share URLs now work for recipients in any browser (not just the originator's). Link-as-capability auth — the share URL contains an unguessable UUID. Upload-on-share wired into the editor header + home-page card Share buttons; clear 503 fallback whenBLOB_READ_WRITE_TOKENisn't configured. - Per-project OG image.
/og/[id]route renders a 1200×630 PNG via satori + resvg-js. Family name in Lora serif, designer + version + glyph count below. Demo OG renders on the fly; uploaded projects render from cloud storage; brand variant at/og/homefor the home-page unfurl. - Launch copy drafts.
docs/launch/copy.mdwith Show HN, Twitter/Bluesky thread, Reddit, DM, and email templates. Anti-patterns section to avoid in launch copy.
Changed
- Share-404 page copy: "This link is browser-local" → "Project not found" (cloud sharing exists; the 404 now means the upload didn't happen or this deployment doesn't have cloud configured).
- README "Project status" bumped from
v1.0.0-betatov1.0.0with a "Configuring cloud share" section. - ROADMAP M3 section marked ✅ SHIPPED with the precise list of what landed.
[1.0.0] — 2026-05-24
Added
- v1.0.0 milestone. Production-grade.
Fixed
- Critical: app.html slot bug. The literal string
%sveltekit.body%inside an HTML comment was substituted by SvelteKit's templating, breaking the actual body slot. Every E2E test that interacted with the page failed because the JS bundle never loaded. - designspace infinite
$effectloop. The previewLocation initializer effect read AND wrotepreviewLocation. Wrapped the write inuntrack()so the effect tracks only the axes array. This was the root cause of the entire 16-test CI failure cliff. - Cmd+Shift+V paste-glyph regression. The bare-V key handler in the editor's keydown chain caught Cmd+Shift+V before the paste-glyph branch. Added
!metaKey && !ctrlKeyguards. Found by ESLint's no-dupe-else-if rule on first run. - Vercel Lighthouse false positives. Vercel's audit was running against per-deployment URLs behind Deployment Protection, scoring an auth wall page. Documented the fix in ROADMAP known-issues section.
Changed
- Welcome dialog → non-blocking strip. First-visit modal blocking the home page replaced with an inline strip at the top. Visitors see the actual product immediately.
- Microcopy sweep. "Copied path (1842 chars)" → "Copied SVG path for {name}"; "Clipboard write failed." → "Could not copy — try Cmd+Shift+C"; "[ ] navigation" tooltip spelled out.
- Pre-hydration shim.
app.htmlpaints html/body backgrounds in canvas-token colors before SvelteKit hydrates so the cold-load flash matches the eventual theme. - Share-404 detects share links and shows specific recovery copy (open demo + import
.font.json). - Brand serif consistency. All home-page section headers use Hoefler Text.
- LoadingPanel italics rule. Removed
italicentries from the type-flicker cycle per project's no-italics-in-UI rule.
Repository hygiene
- Real README replacing the SvelteKit boilerplate.
- LICENSE (MIT) added.
package.jsonmetadata: name →font-studio, description, author, license, homepage, repository, bugs, keywords.CONTRIBUTING.mdwith setup, branch model, commit conventions, PR flow.docs/architecture.md— one-page mental-model loading.ROADMAP.md— deferred M3 work + known issues.- GitHub
ISSUE_TEMPLATE(bug / feature / question) andpull_request_template.md.
Engineering
- ESLint flat config with typescript-eslint + eslint-plugin-svelte. Real-bug rules at error (caught the Cmd+Shift+V conflict on first run). Stylistic rules at warn (52-warning baseline).
- Vitest coverage (
@vitest/coverage-v8). Baseline: 77.58% statements, 79.16% lines, 80.46% functions, 64.56% branches. CI uploads as artifact. - Bundle analyzer (
rollup-plugin-visualizer).pnpm run analyzewritesbundle-report.htmlto repo root. - WAI-ARIA disclosure on Accordion (the right-sidebar collapsible sections). Header + region linked via aria-controls + aria-labelledby.
- Focus traps on all major modals (ShortcutsDialog, Settings, Storage, Welcome, CreateFont).
[0.6.0] — content-complete
13 new glyphs ($, ©, ®, ™, §, [, ], {, }, °, ±, ×, ÷), 10 .onum digits, 2 .case parens, g.ss02 alternate. Liga ligatures (fi, fl). Demo project at 119 glyphs.
[0.5.0] — finished
ß as drawn shape, fi/fl ligature GSUB, focus traps, accordion a11y, math glyphs, denser kerning.
[0.4.0] — share-complete
Foundry-style tester (master pills + size + tracking sliders), live OpenType feature toggles, reading specimens (Display / Body / Caption), glyph inspector with metric guides + sidebearings + anchors + master overlay, deep-linkable inspector, glyph grid filter, shareable tester URL, palette switcher, print specimen sheet, sample-text presets, OT features grouped, in-context mockups, coverage heatmap, anchor visualizer.
Earlier
See git history for prior 153 commits. The pre-0.4.0 commits built out the foundational editor (sketch + trace pipeline, opentype.js export, audit module, IndexedDB project store, COLR + variable font support, harfbuzz live shaping).