This is a demo project. For demonstration only.

Release Notes

Changelog

Latest updates and release notes.

0.10

A Built-In Admin Area for Operators

AdminAuthOperationsSecurity

An analytics dashboard interface with charts and data panels

  • A guarded admin area at /admin — overview counters, a contact inbox, newsletter management, payment snapshots, AI usage, and user management, every server function passing one shared fail-closed permission gate
  • Operator identity without a migration chicken-and-egg — ADMIN_EMAILS promotes a verified account to the admin role at its next session check, landing a bootstrap_admin_promotion audit row; operator emails are permanent super users until removed from the list
  • Payments stay read-only on purpose — order, subscription, and customer snapshots mirror what verified webhooks landed in D1; refunds and plan changes belong to the provider dashboard, so admin never writes payment state
  • Audit trail for every privileged action — role changes, bans, newsletter unsubscribes, and bootstrap promotions land one row each, with a dedicated error code when a completed action fails its audit write so operators can reconcile

  • Added the Better Auth admin plugin with D1-backed roles on the user model, plus migration 0008 (role, banReason, banExpires, the session impersonatedBy field, the admin_audit_log table, and a created-at index for the roster query).

  • Built the decision core with a ban-first fail-closed order: an active ban rejects before every grant branch, including the bootstrap path, so a hand-written ban cannot be walked over by an allowlisted email.

  • Shipped the six admin screens: overview statistics with the newest audit rows, a read-only contact inbox with per-channel delivery badges and an expandable body, newsletter subscriptions with the audited operator unsubscribe, read-only payments snapshots across orders/subscriptions/customers, AI credit periods and recent requests, and user management with role and ban dialogs.

  • Added ADMIN_EMAILS as an optional public environment variable with the full sync chain, email-verified promotion, and normalized address matching. Demotions always re-promote at the next session check; removing the email from the list is the only way to retire an operator.

  • Architecture guards scan the admin area in CI: every server function entry passes the shared request guard, read-only repositories carry no write paths, route heads declare themselves unindexable, and the enumeration maps answer the raw value for unknown enum values instead of routing a fallback key's translation.

  • Moved the TanStack Start cookie bridge to the end of the Better Auth plugin array per upstream guidance, so plugin hooks that run after it cannot write cookies the framework cookie store misses.

0.9

Deploys Run on the Cloudflare CLI

DeploymentCloudflareToolingConfiguration

A terminal window with deployment commands over a dark code editor

  • Deploys run on the official Cloudflare CLI — cf deploy uploads each version with its secrets attached, so one command ships code and configuration together
  • Typed project configuration — cloudflare.config.ts replaces wrangler.jsonc: bindings are TypeScript values with editor and agent autocomplete, and the managed environment variables it imports live in a generated cloudflare.vars.json
  • One CLI for the whole Cloudflare surface — project commands and account/resource commands share one login, and Wrangler is no longer a project dependency; live-log tailing still runs through npx wrangler tail because cf cannot stream logs yet
  • The same gates, new engine — pnpm deploy:check still refuses an out-of-sync environment or a test Turnstile key before anything ships, and the local D1 database lives under .cloudflare/state, the one store the dev simulator and local migrations share

  • Migrated the deployment chain from Wrangler to the official Cloudflare CLI. cf migrate converted the Worker configuration, keeping the Worker name, every binding, the D1 database ID, and the R2 bucket; the build now runs through the Cloudflare Vite plugin's new Wrangler-independent line, and cf deploy --prebuilt publishes the Build Output pnpm build already produced instead of building a second time.

  • Replaced the JSONC environment surgery with a JSON sidecar: pnpm cloudflare:sync-env renders cloudflare.vars.json from .env, cloudflare.config.ts imports it and maps each entry to a text binding, and the build and preflight scripts read the same file. Secrets are no longer uploaded by the sync step -- they attach to each Worker version at deploy time, which is how the CLI models them.

  • Moved the D1 migration commands to cf d1 migrations apply, which takes the database ID and applies to the remote database by default; local migrations add --local --persist-to .cloudflare/state, sharing the dev simulator's database; unattended runs answer the migration confirmation with its default yes.

0.8

Dashboard Routing, One Tap, and Two Homepages

RoutingAuthenticationLandingThemingTypographyInternationalization

Copper traces and chips across a circuit board in close-up

  • Two homepages, one route — / serves either the marketing page or the product showcase, chosen in the theme customizer, and each keeps its own background rhythm
  • Every dashboard view is its own route — the sidebar, command search, and billing links resolve real URLs instead of one page carrying a view parameter
  • Google One Tap is opt-in on public pages, and stays out of the sign-in and sign-up flow
  • The factory theme is Orange with square corners, Space Grotesk over Fira Code, and the Compact reading rhythm
  • Translations load per namespace and per route — a page downloads only the copy it renders, the namespaces the shared chrome needs ride along on every route, the namespaces a route's head() reads are loaded from hydration on, and a dashboard no longer downloads the landing page's prose
  • Quieter, lighter chrome — the navigation pending bar, the pricing "All Plans Include" block, and four closing blocks on the homepage are gone, and the showcase homepage now loads on demand

  • Split the dashboard into one route per view. /dashboard/apps/mail, /dashboard/settings/billing, and the rest are addressable paths now, and each route builds only the view it renders, so opening one dashboard surface no longer ships the others with it. Legacy /dashboard/?view=... links redirect permanently with the locale intact, and settings-user stays as an alias for the account view.

  • Added Google One Tap as an opt-in. Setting VITE_GOOGLE_CLIENT_ID offers the browser prompt to signed-out visitors on public pages, waits for the session to settle before it asks, and returns the visitor to the localized dashboard afterwards. It stays out of the auth flow, which keeps its own "Sign in with Google" button.

  • Shipped a new factory theme: Orange, square corners, Space Grotesk over Fira Code, and the Compact reading rhythm. A stored configuration keeps whatever the reader already had, so the new defaults reach a first visit and a reset rather than every returning reader.

  • Grew both curated lists: the font pairings from ten to thirteen (a native pair that asks for no web font, a literary serif, and a grotesque drawn from monospace bones with a ligature mono), and the typography presets from six to eight, adding Essay for a narrow long-read column and Easy read for the largest size at a moderate rhythm. Guard tests reject a preset or pairing that repeats another one's values.

  • Added a second homepage. The home route renders a variant picked in the theme customizer's Custom region: the original Showcase page, with its interactive hero and its tour of what ships in the box, or a Marketing page migrated from the @7ovr/saas-landing registry block. Both use the semantic tokens, so every theme, font, radius, and typography choice reaches them, and the reset action and the dice leave the choice alone because it is a layout decision rather than a skin.

  • Rewrote that marketing page for this template. Its copy describes the accounts, billing, trust, data, locale, and theming features that actually ship, its pricing table uses the catalogue's real plans, and every number on it is countable in the repository. Its hotlinked photos and avatars became token-built mocks and initials, its fake demo video and support form became links to the real guides and contact pages, and the brand wall now shares one set of marks with the showcase page. The default homepage is the marketing page, and it is prerendered, so a first visit carries the real content.

  • Made the default homepage cheap to serve. Its variant is imported statically so the prerendered HTML holds the real page, while the showcase variant loads on demand in its own chunk: the home route's script went from 83 KB to 34 KB, and a reader who never switches never downloads the other one.

  • Removed the navigation pending bar, the pricing "All Plans Include" block, and four closing blocks from the homepage, so a navigation no longer flashes a loading strip and the pricing page no longer repeats a shared feature list that every plan card already covers.

  • Fixed raw translation keys on almost every page. Splitting the locale bundle per route only covered the route a reader lands on: the router's hydration hook runs once, so a client-side navigation never loaded the next route's copy, and the instance built for the first route could not see it arriving. A first attempt fixed the shared chrome's namespaces in the route table; the general fix loads each route's namespaces as the router moves and rebuilds the instance from that state, pushes a namespace registered later into the instance already in use, and reads translated arrays without trusting them -- a missing bundle now renders less rather than the key names.

  • Fixed the same class of fallback in the places that build output rather than render it: each route's head() is projected before the page renders, so the copy it reads now lives in a namespace loaded on every route, and the Open Graph generator -- which runs outside the app and whose cards no test read -- takes its keys from that namespace too.

0.7

Theme Precision

ThemingTypographyOpen Graph

Colour swatches and interface sketches on a designer's desk

  • Every radius follows the control — the corner scale derives from the customizer value, so zero means square everywhere while the preset geometry stays
  • Body and headings share one family — the third font slot is gone, and code keeps its own
  • Open Graph images read the theme mode instead of shipping the dark mark in every build
  • The reading position holds still across a reload, and the long font list scrolls

  • Rebuilt the corner scale as a ratio of the customizer value and pulled the remaining fixed radii into it. Checkboxes, charts, tooltips, input groups, long-form marks, search highlights, scrollbars, page-header badges, blog tags, and the pricing monthly/yearly switch now all follow the control, and a guard test keeps a hard-coded radius from creeping back in.

  • Merged the body and heading font slots into one. A reader picks a body family and a code family, and the curated pairings describe that pair instead of inventing a heading family for every body.

  • Open Graph images now take their logo from the resolved theme mode, so the light and dark marks stay with the theme they belong to.

  • Stopped the reading position from drifting on reload. The restore now waits for the page height to settle instead of aiming at a layout that is still growing.

  • Made the long font list scrollable. The default alignment translated the list rather than giving it a scrollable area, so a family near the end could not be reached at all.

  • Realigned the pricing FAQ. Both columns read from one shared grid, so a taller answer no longer pushes the next question out of line.

  • Put the Open Graph brand line and headline on the semibold face, since full bold read too heavy at display sizes.

  • Removed the page-load reveal. Content no longer starts hidden and fades in, so a fast navigation does not show an empty frame first.

0.6

English-Only Locale

i18nContentSEO

A connected world of regions and routes

  • One locale ships — English — while the locale registry, per-locale lazy loading, the URL rewrite, and the formatters stay in place for the next language
  • The Chinese bundle, 44 localized MDX entries, and 52 Open Graph images are gone, and a removed locale prefix like /zh/** answers 404 instead of serving a translation
  • SEO follows the locale list: the sitemap, hreflang alternates, Open Graph locale, and structured data emit the default locale plus x-default only
  • Social previews, emails, and search render from the one registered bundle, so the build no longer produces or ships localized assets nothing links to

  • Locale registry: one row per locale drives the supported list, the HTML language, hreflang, Open Graph, and Intl formatting
  • Single-locale content model: blog, guides, and changelog read one set of sources through the same locale parameters as before
  • English-only Open Graph output with a check that fails the build if a stale locale folder survives
0.5

Typography and Font System

ThemingTypographyPerformance

Typography controls in the theme manager

  • One typesetting engine replaces the Tailwind Typography plugin, so articles, guides, changelog entries, and legal pages share a system that reads the theme tokens directly
  • A three-slot font system gives body text, headings, and code their own family, self-hosted and fetched only for the family actually selected
  • Typography controls in the theme manager add presets plus size, line height, block spacing, and reading width
  • Reader choices survive theme presets, imports, and randomization instead of being reset with the theme
  • Reading width reaches the shells: the blog and guides columns follow the measure while their side rails stay on screen

  • Vendored shadcn Typeset as the only typesetting engine and removed the Tailwind Typography plugin, its token bridge, and the hand-written prose overrides. Heading sizes, list indents, and block spacing now derive from three rhythm values.

  • Added a font system with a slot each for body text, headings, and code, plus curated pairings. Families are self-hosted and loaded on demand, and the heading slot accepts serif faces for an editorial tone.

  • Added typography presets to the theme manager, with controls for size, line height, block spacing, and reading width, and individual values for each measurement.

  • Tuned the reading defaults per surface: Chinese pages and dark surfaces loosen the line height slightly, and any value chosen in the customizer overrides both.

  • Wired the reading width through the blog and guides shells. A wider column narrows the side rails instead of pushing them past the viewport, and every shell keeps its previous geometry at the default measure.

  • Kept wide tables inside long-form content scrollable instead of widening the page on mobile, and gave interactive widgets an explicit way to opt out of the engine.

  • Restored the site heading scale across long-form content. Page titles and section headings follow the shared roles again, so an article or legal page reads at the same size as every other page instead of one step below it. Headings inside the text keep their em ladder, so the typography size control still scales those, while the page header stays fixed.

  • Restored the font tokens across the whole app. Utilities compile to the theme variables again, so the selected family reaches the site chrome, the dashboard, and article content alike.

  • Kept typography values and font slots out of the theme preset, imported theme, and randomization paths, so switching themes no longer discards a reader's choices.

0.4

Launch Readiness and SEO

MarketingSEOLicensing

Launch readiness and analytics

  • Interactive landing experience now includes a 3D Hero Showcase, component gallery, and localized typography across light and dark themes
  • Live chat support is available through Chatwoot with dark mode and full localization
  • Search visibility improved with structured data for Blog, homepage, pricing, and public pages, plus a dedicated Service page
  • Commercial licensing introduced with the TanShip Commercial License and a dedicated license page
  • Performance polish replaces feature imagery with WebP and uses OFL-licensed fonts for Open Graph rendering

  • Added an interactive Hero Showcase with 3D story cards, pricing promo, animated backdrop, and logo carousel, fully localized in EN and ZH.

  • Integrated Chatwoot live chat with a dark-mode toggle and localized launcher and chat UI.

  • Added JSON-LD structured data for Blog posts, homepage, pricing, and public pages, with refined FAQ and article rendering.

  • Added the Service page, blog cover images, and updated navigation for the expanded public site.

  • Introduced the TanShip Commercial License page and replaced Open Graph fonts with OFL-licensed Fredoka and Smiley Sans.

  • Converted feature showcase images to WebP and added Vite chunk size warnings for leaner deployments.

  • Stabilized table-of-contents highlighting during initial page load.
  • Prevented duplicate analytics events from double-counting.
  • Refined mobile navigation and guide layouts across small screens.
  • Derived guide pagination from a single source to prevent navigation drift.
0.3

Cloud Platform and Experience

CloudflareLocalizationUX

Cloud platform and global network

  • Cloudflare platform integration now covers Workers AI, D1, R2, ASSETS, and deployment validation
  • Localized documentation adds EN/ZH Guides content, search, highlights, navigation, and table of contents
  • Personalization and identity include global themes, Dashboard layouts, shared account menus, and profile avatars
  • Content experience now provides Open Graph images, responsive Blog and Guides layouts, unified typography, and reusable MDX code blocks

  • Added authenticated Workers AI tools with monthly credit management and Cloudflare binding-based execution.

  • Added deployment scripts, environment preflight checks, and Cloudflare configuration for production Workers.

  • Expanded Guides with Chinese content, localized full-text search, snippets, highlights, icons, and responsive navigation.

  • Added global theme presets, radius controls, and Dashboard layout settings.

  • Added Open Graph image generation, R2-backed profile avatars, and unified account identity across public and Dashboard surfaces.

  • Introduced shared typography roles and reusable MDX code blocks with syntax highlighting, copy actions, and bounded scrolling.

  • Restored page scroll positions and stabilized table-of-contents highlighting.
  • Prevented duplicate checkouts while verified payment webhooks are pending.
  • Preserved paid access correctly during Creem and Stripe cancellation states.
  • Removed Dashboard view-loading gaps during navigation and refreshes.
  • Corrected Blog, Guides, Read More, and TOC alignment across responsive sizes.
  • Improved profile avatar loading and identity consistency during hydration.
0.2

Authentication and Commerce

AuthenticationPaymentsBilling

Online payment and commerce

  • Better Auth and D1 now provide account creation, sessions, email verification, and password recovery
  • Multi-provider payments support Creem, Stripe, and PayPal behind a provider-neutral catalog
  • Billing workflows cover subscriptions, lifetime purchases, cancellation, recovery, and account history
  • Customer communication includes D1-backed Newsletter and Contact flows with transactional email delivery

  • Integrated Better Auth with Cloudflare D1, including email verification, password reset, and localized authentication redirects.

  • Added Creem, Stripe, and PayPal adapters with verified webhook processing and provider-specific checkout flows.

  • Added subscription recovery, cancellation, renewal status, and lifetime purchase handling.

  • Expanded the authenticated Billing page with membership summaries and paginated payment history.

  • Added double-opt-in Newsletter subscriptions and a persisted Contact form with localized email delivery.

  • Reused payment customers safely during concurrent checkout requests.
  • Improved webhook idempotency and duplicate event handling.
  • Corrected Stripe renewal and scheduled cancellation state handling.
  • Hardened PayPal subscription cancellation and recovery behavior.
  • Improved password validation and payment diagnostics for failed flows.
0.1

Foundation and Content Platform

FoundationDashboardContent

Software development workspace

  • Application foundation built with TanStack Start, React 19, TypeScript, and a Cloudflare Workers deployment target
  • Dashboard workspace established with responsive navigation and multiple application views
  • Content platform introduced for Blog, Guides, and Changelog entries backed by MDX
  • Public site structure expanded with About, Team, Privacy, and Terms pages

  • Added the initial TanStack Router structure for public, authentication, and dashboard experiences.

  • Created responsive Dashboard navigation and workspace views for core application workflows.

  • Added Guides navigation, search, table of contents, and responsive documentation layouts.

  • Introduced Blog listing, pagination, article rendering, and shared MDX content registries.

  • Added shared long-form typography through the ArticleContent component.

  • Simplified Dashboard routing and removed obsolete demo destinations.
  • Corrected responsive overflow and scrolling in Dashboard workspaces.
  • Improved mobile navigation behavior across public and Guides pages.
  • Aligned shared page headers and article typography across content pages.