Skip to content

Rebuild tablepro.app: positioning, IA, English + Vietnamese, design system, placeholders - #38

Merged
datlechin merged 126 commits into
mainfrom
rebuild
Oct 4, 2026
Merged

datlechin merged 126 commits into
mainfrom
rebuild

Conversation

@datlechin

@datlechin datlechin commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Draft. The account half (TableProApp/license#87 and #88) is already merged; this PR is the remaining half. main deploys itself when its tests pass. 126 of the 147 images and cards are supplied; the other 21 (P2 and P3) render as described placeholders (see docs/visual-assets.md).

Rebuild delivery summary

The website rebuild of tablepro.app, delivered on the rebuild branch of both web
repositories: this one (TableProApp/web, the public site) and TableProApp/license
(the account, checkout and transactional pages on the same origin). This document answers
the rebuild brief's delivery checklist point by point for this repository. It points to
the design documents rather than repeating them.

Section references such as "architecture §1.7" or "positioning §11" point to
docs/rebuild/design/{architecture,design-system,positioning,sitemap}.md. The rebuild
brief, the research notes, evidence matrix, stale-claim list, baseline and other working
records behind those documents are kept privately and are not part of this repository.

State. The account half is merged (TableProApp/license#87 and #88). This PR is still a
draft and nothing in it is merged or deployed. No provider dashboard, mailbox or production
database was touched. The page controllers live directly in app/Http/Controllers (the
Landing/ folder came from the platform app and grouped nothing here).


1. Positioning and headline

Final copy: positioning §1. The category-first proposal won; two grafts came from the
durable-identity and workflow-first proposals.

English Tiếng Việt
H1 A native database client for developers. Database client native dành cho lập trình viên.
<title> TablePro: native database client for {deviceList} TablePro: database client native cho {deviceList}
Primary action Download for Mac (macOS 13 Ventura or later · Apple silicon or Intel) Tải về cho Mac
Secondary action App Store badge (iPhone and iPad · iOS and iPadOS 18 or later)

Why it fits today and later. The headline names the category and the audience, not a
platform. The platforms live in a separate availability layer that is rendered from
resources/data/platforms.json: the title's {deviceList}, the captions under each
action, the header and footer platform links, and the structured data. Adding Windows,
Linux or Android means adding a platform entry. The headline does not change, and no
sentence claims parity between apps (positioning §3, §13 guard tests). Every claim holds
on the Mac builds being served when the copy was finished (0.77.1 on GitHub and Sparkle,
0.77.0 on Homebrew) and on App Store 1.0 (build 22). Copy contains no typed counts: counts are derived from data
(NoTypedCountsTest).

2. Sitemap and dispositions

Final sitemap: sitemap §A (51 paired pages, 102 URLs; the English-only blog set; legal;
system responses). Keep, rewrite, merge, archive, redirect and remove decisions for every
existing, historical and inbound URL: sitemap §C.

  • 4 database pages merged into their family page with a 301 (MariaDB → MySQL,
    CockroachDB and PGlite → PostgreSQL, ScyllaDB → Cassandra). Kafka was added.
  • /compare/azimutt answers a branded 410.
  • 4 off-topic blog posts redirect to the page that now answers them. Release posts stay
    English only and are listed on /vi/blog as English articles (noindex).
  • resources/data/redirects.json holds every 301 and 410, read by RedirectMap
    (architecture §1.7). Each redirect lands on a 200 in one hop and keeps ref and
    utm_*.
  • public/sitemap.xml: 113 URLs, alternates on the 102 paired URLs. The sitemap and the
    head tags come from one registry (PageRegistry).

3. Product evidence, release applicability and factual corrections

  • Every product claim was checked against the app at the stated release, its CHANGELOG or
    the store listing, and its release applicability (0.77.x against 0.76.1 against iOS
    1.0) was reconciled. The copy floor (platforms.mac.floorVersion) is 0.77.0, the
    lowest version any channel serves, so no page needs a version label today. A feature
    newer than the floor would get one from the data. 0.77.1 (2026-10-03) adds New… for
    SQLite, DuckDB and libSQL files plus editor and Oracle fixes; no engine fact changed.
  • Corrections to the old site. The significant ones:
    • The old site called the Mac app entirely free. Ten features are paid (8 Starter,
      2 Team). The site now names them from paid-features.json everywhere.
    • Privacy, Terms and Refund policy were rewritten in English and Vietnamese to
      describe current behaviour. Polar is named as the merchant, Cloudflare Web Analytics
      and GA4 are disclosed, and the refund policy states 7 days on every plan.
    • The iPhone and iPad app claims were reconciled to App Store 1.0. Jump hosts, Redis
      key browsing and the iPad sidebar are not claimed, and the blog post carries a dated
      correction.
    • Sponsors were reduced to the four verified as current.

4. Research references and the principles applied

The reference set (Zed, Raycast, Linear, Proxyman, 1Password, Things, Tower, Notion,
Obsidian, TablePlus, Beekeeper Studio, DataGrip, DbVisualizer, Postico, DBeaver, Navicat;
Apple, Google and W3C guidance) produced a set of synthesized principles. The principles
were all applied; the design documents state the rules they decided (design-system §1).

  • Identity: product and category first, with availability stated once from
    data. Each destination has its own verb. The header stays short and conventional.
  • Homepage: one workflow per section and one real window per image slot.
    Platform scope is described positively, with no parity claim.
  • Download: the page leads with the live release and server-renders both
    Apple silicon and Intel links. It gives the requirement, Homebrew and honest
    after-click steps, and falls back to the releases page.
  • Pricing and account: each card carries its unit, cycle, minimum, total and
    currency from pricing.json. The pages say what happens when payment stops. Paid
    features are a matrix with one line each. Account is in the header and footer.
  • Database pages: engine-specific or merged into the family page.
  • Comparisons: a dated, two-sided template with sources.
  • Language: the URL is the language. The switcher uses endonyms and links
    the equivalent page without ever redirecting. No Vietnamese shells for English
    content. Money and product terms are consistent in Vietnamese. Translation
    completeness is tracked as data (ContentParityTest).

5. Screens and languages

  • Launch locales: English (/) and Vietnamese (/vi). The locale allowlist is
    resources/data/locales.json, mirrored in the account app.
  • Public: every page family in §2 in both languages, plus branded 404, 410, 500 and
    503 pages in the path's language. A Vietnamese URL for an English-only page explains
    this and links the English page. A Vietnamese account or checkout URL links
    /account?locale=vi.
  • Account and transactional (license app): sign-in and the magic-link states,
    Overview, Machines, Team, Library, thank-you, and newsletter
    confirm/unsubscribe. Language is a stored preference set by an explicit ?locale= or
    form field, and transactional mail follows it. The cross-app contract is architecture §2.
  • Archival-language exceptions (honest): the 10 release posts are English only.
    /vi/blog lists them labelled "(tiếng Anh)" and is noindex. The documentation
    (docs.tablepro.app) is English only, and each link to it says so. The legal pages
    in Vietnamese state that the English version prevails.

6. Visual assets and placeholders

  • Handoff: docs/visual-assets.md, generated by php artisan assets:handoff from
    resources/data/assets.json and docs/rebuild/assets/*.md. It contains a brief per
    asset with the scene, dataset, framing, light/dark, locale, export sizes, file names
    and the manifest fields to change, with a Vietnamese summary for each.
  • Supplied and remaining: there are 147 entries (146 page slots and 1 bespoke
    social card): 126 supplied and 21 placeholder. All five P1 entries are supplied:
    the homepage hero window and its phone crop, the iPhone shot in the homepage platforms
    band, and the /ios header iPad capture and its crop. The social card is supplied
    too. The 21 placeholders are P2 and P3: seven database-page details with their phone
    crops (Redshift, Cloudflare D1, Turso, BigQuery, Snowflake, Elasticsearch, Teradata),
    mac-agent-mode and mac-handoff-ios with their crops, mac-team-library,
    ios-live-activity and ipad-two-windows. Kinds across all entries: 15 Mac windows
    (with 39 phone crops), 41 detail crops, 11 iPhone, 2 iPad, 36 release-post figures,
    1 diagram, 1 illustration.
  • Placeholders request nothing. They are labelled boxes with the asset id and an English
    or Vietnamese description (asset-slot.test.ts, and imgInsidePlaceholders: 0 in
    every capture). The supplied-image path (<picture>, AVIF/WebP, the LCP preload per
    theme with the phone crop under its own media) is tested against an isolated
    fixture (AssetManifestTest, RootTemplateTest), and every supplied entry's files
    are checked for presence, pixel size and byte budget (AssetManifestTest).
  • OG cards: generated by og:generate for every feature, database and compare page
    in both locales, plus the release posts (91 generated PNGs under public/og). The
    bespoke site card (public/og/bespoke/og-site-{en,vi}.png) is the default card in
    both locales; the generated /og.png (EN) and /og/vi/default.png (VI) stay as the
    fallback.
  • Identity assets: Apple's Vietnamese App Store badge (light and dark) ships at
    public/images/app-store-{light,dark}-vi.svg.

7. Changed files by repository and purpose

TableProApp/web (main...rebuild: 1,589 files, 988 of them images and other
binaries, and about +103k/−18k lines of text; the integration fixes are below):

Purpose Where
Locale routing, URL helpers, canonical/redirect middleware, page registry, SEO, sitemap app/Http/Middleware, app/Support/{Localization,Seo}, routes/web.php, bootstrap/app.php
Data files (single sources) and their validation resources/data/*.json (platforms, pricing, engines, comparisons, paid-features, sponsors, facts, redirects, assets, locales), tests/Feature/Data
Content in both languages resources/data/content/{en,vi}, resources/data/legal/{en,vi}, resources/js/i18n/messages/{en,vi}
Design system and shared primitives resources/css/{tokens,fonts,app}.css, resources/js/components/{ui,shared}
Page families resources/js/pages/*, resources/js/components/{home,features,databases,compare,pricing,download,blog,legal,site}
Release data and download robustness app/Services/Releases, app/Console/Commands/ReleaseCheckCommand.php
Assets, handoff and OG app/Support/Assets, resources/views/og, public/og, docs/visual-assets.md, docs/rebuild/assets
Local preview and shared-file tooling scripts/dev-proxy.mjs, scripts/check-shared-files.sh, docs/shared-files.md
Tests tests/Feature/**, tests/js/*.test.ts
Design records and image briefs docs/rebuild/design, docs/rebuild/assets

The companion account-app changes (locale handling, localized mail, shared chrome) are
described in TableProApp/license#87.

Integration fixes, this repository:

  • Tables: DataTable keeps hidden words inside its scroll region, which ended the
    page-wide sideways scroll on phones. It shows its visible caption above the region and
    paints the sticky column with its section's ground. Priority columns are used below
    640px on the plan matrix, availability, paid-features, engine and comparison tables.
    /databases gets fixed column widths shared by every category.
  • Layout: every section sits on the grid's left edge (no zigzag), and sections are one
    rhythm apart instead of two. The platforms band, the /ios slots, the engine page
    header and the pricing cards were rebalanced. All download bands are built from one
    ActionPair row.
  • Motion: the global scroll-behavior: smooth was removed. In-page anchors glide one
    jump at a time (lib/anchor-glide.ts), so Inertia's scroll reset is instant again.
  • Interaction and accessibility:
    • The theme menu follows the menu-button keyboard pattern.
    • The header menus close on focus-out.
    • A busy button keeps its focus.
    • The mobile menu marks the current section.
    • The header focus ring wraps the label.
    • Dark segmented controls show the selected segment above its track.
  • Typography: shortcut chips use a face that has ⌘⇧⌥⌃⏎. Arrows, separators (DotList),
    dates, inline code and fixed compounds no longer strand at line breaks. File names are
    set in mono.
  • Correctness:
    • The LCP preload script reads the per-image list (it requested /undefined once an
      image was supplied) and takes the placement's sizes.
    • One EnginePaths rule replaces five copies.
    • The release refresh is single-flight.
    • The checkout overlay wait is bounded.
    • Stale discount answers are dropped.
    • /vi/account* 404s link the account.
    • Related posts are listed newest first.
  • Clean-up: unused code removed (GitHubRepoService, tabs, footnote, runtime halves
    of engines.ts and comparisons.ts, the reversed absoluteUrl, the SEOHead
    breadcrumbs prop, legacy banner props, unused catalog keys). Obsolete skip guards were
    removed from tests. Tests no longer depend on a running composer dev (public/hot).

8. Validation

Results for this repository, after the integration fixes (2026-10-03):

Check Result
npm run typecheck pass
npm run test:js 176 of 176 pass
npm run build (client and SSR) pass
REQUIRE_SSR=1 php artisan test --compact, with inertia:start-ssr on 13715 1405 passed, 0 skipped (before the integration fixes, 1370 passed and 1 skipped)
vendor/bin/pint --test pass
php artisan assets:handoff --check both generated files up to date
scripts/check-shared-files.sh ../license every pair same, and docs/shared-files.md is identical
Journeys (15), on production assets through the proxy 15 of 15 pass
Public captures (358) on production assets 0 errors, 0 unexpected statuses, 0 horizontal overflow, 0 clipped text, 0 console, page or hydration errors, 0 cookies, and no third-party request but the consent-mode gtag.js. Layout shift: no public page above 0.003. LCP median 72–76 ms (localhost: a regression check, not a field number). SEO: one H1 per page; every public page has a self canonical with en/vi/x-default, except /vi/blog, which is noindex by design
release:check every channel matches platforms.json after the 0.77.1 update (GitHub and Sparkle 0.77.1, Homebrew 0.77.0 as expected)
  • Visual inspection: about 780 full-page captures (every public page family, the
    account and transactional screens, the 404 and 410 pages, and long articles), in
    English and Vietnamese, light and dark, at 375, 768, 1280 and 1440. Captures are taken
    through the local proxy with third parties blocked. Reviewed by tile against
    design-system §8. Every public page was re-captured after the fixes.
  • Automated per capture: status, lang, canonical and hreflang, horizontal overflow,
    clipped text, console and hydration errors, cookies, third-party requests, LCP and
    CLS.
  • Journeys: language switching and history across locales, SSR deep links, theme and
    consent shared between the two apps, the cross-app account link, account locale,
    download device handling, mocked checkout and newsletter, the redirect table, keyboard
    and reduced motion, platform-to-public links, and the sitemap crawl.

Not verified, and why:

  • Final-image performance: these checks ran on 2026-10-03, before the images were
    supplied.
  • Lighthouse runs: not run. Bundle cost was measured instead (below).
  • Real checkout, mail and provider webhooks: mocked by design.
  • The /index.php normalisation behind production nginx: it needs a local nginx with
    the production vhost, and the server was not touched.

Bundle cost against main (Vite client build of git archive main against
rebuild; JS and CSS that a page loads, gzip):

Page main rebuild Change
/ 158.4 KB 167.1 KB +5.5%
/download 129.1 KB 141.9 KB +9.9%
Database page 156.8 KB 160.6 KB +2.5%
/blog 128.0 KB 137.1 KB +7.1%
/faq 131.4 KB 138.3 KB +5.3%

The increase is mostly the second language's UI catalogs, which every page bundles
(architecture §1.4 asked for this to be measured), and the slot manifest (asset-slot
chunk, 14.7 KB gzip on pages with images). The shared app entry is unchanged
(98.9 → 99.8 KB). Splitting the slot descriptions per locale, or passing each page's
slots as props, is a follow-up.

Failures already on main: none on the public side.

9. Local preview

Architecture §4 has the full list. In short, for this repository:

composer install && npm ci && npm run build
php artisan inertia:start-ssr &                       # SSR on 13715
WEB_DOMAIN=localhost php artisan serve --port=8000
# the account app: php artisan serve --port=8001, inertia:start-ssr on 13714
npm run dev:proxy                                     # http://localhost:8080 serves both apps on one origin

Local settings: WEB_DOMAIN=localhost in both apps; in the account app, a throwaway
database, mail on the log driver and payments mocked (its own README has the settings).
Use built assets. While composer dev runs, public/hot makes pages load from the Vite
dev server: the test suite ignores it, but a browser check of production behaviour (CLS,
hydration timing) needs it gone. Connected journeys to try: the header Account link to
/account?locale=; a guest checkout from /vi/pricing that carries vi into the order;
theme and consent set on / showing on /account; and the account's links back to the
public pages in the reader's language.

10. Routing and release, for the maintainer

  • Order:
    1. TableProApp/license#87 and #88 are merged.
    2. Merge this PR next. main deploys itself when its tests pass.
    • Both apps share theme, tablepro:analytics-consent and the visual system, so the
      two should go out the same day. A mismatch between them is cosmetic, not
      functional.
  • Shared files: run scripts/check-shared-files.sh ../license (a checkout of
    TableProApp/license) before marking either PR ready. Every pair must report same.
  • nginx: unchanged. Account routes keep no locale prefix, and /vi/account* is a
    public 404 that links /account?locale=vi.
  • After the deploy: the deploy script regenerates the sitemap, and the download page
    reads the live release, so a new app version needs no copy change.

Still open

  • 21 P2 and P3 images are still described placeholders (§6).
  • Merge this PR to finish the release (§10); the account half is already merged.

…s audit

Evidence-led inventory of the product (Mac v0.77.0, iOS 1.0), both web
surfaces, references, competitors and localization, written before the
rebuild so every later copy decision can cite it.
…and account plan

The decision documents for the rebuild: final positioning and copy rules,
sitemap with content dispositions and redirects, file-level architecture
for both repositories, the visual system, and the account experience plan.
…the social card brief

resources/data/assets.json holds every sitemap §A.8 slot, a phone crop for each Mac window, every release-post figure and the bespoke og-site card, with the kinds design-system §6.3 pins. AssetManifest validates supplied files (widths, formats, true pixel sizes, byte budgets, light/dark parity, crop before window) and builds the lcpAsset descriptor app.blade.php reads. AssetReferences finds slot uses in TSX, content JSON and markdown. AssetManifestTest checks the real manifest and drives the supplied path on an isolated fixture with GD-drawn images.
…ctures

<AssetSlot id> renders a labelled, correctly proportioned placeholder (type label, mono id, localized brief, accessible name, no image request) while the manifest says placeholder, and <picture> with srcset, sizes, dimensions, theme variants and loading priority once supplied, hiding the id and brief. The mode switches on the manifest status alone.
…fest and brief fragments

php artisan assets:handoff writes docs/visual-assets.md (Vietnamese replacement steps, capture rules, kinds table, production order, per-page asset briefs with file names, the social-card state and the legacy source list with a cleanup proposal); --check fails on a stale document. Adds the fragment template and the og fragment.
…sTest

The srcset and existence checks now include the manifest's supplied srcsets and legacySource files, the ladder check covers AVIF and the manifest naming, and a parser self-check replaces a count that would go red once the legacy components are deleted.
tokens.css gains the default hairline border colour, the forced-colours focus ring and the ThemeControl icon and segment states. app.css keeps only aliases the old pages still name, the banner offsets, the app-plate shadow, the article typography with a gutter permalink, and print. ColorContrastTest now parses tokens.css and asserts every design-system §2.3 pair in both themes, the gamut, hue 55 and the theme-color backgrounds.
The fifteen shared primitives (button, text-link, container, badge, status-badge, field, select, stepper, dialog, data-table, description-list, callout, copy-button, empty-state, notice-page) plus public-only section, page-header, breadcrumbs, card, disclosure, segmented-control, tabs and prose-article. The existing code, kbd, faq-list, footnote, glyph and database-mark are restyled to spec with their old exports kept. FullLine stops bleeding 100vw, SectionShell paints its band directly, and ThemedImage switches on the theme class.
…with the account app

lib/theme.ts reads, applies and syncs the shared `theme` key (light by default); ThemeControl offers Light, Dark and System as a menu and a segmented control with labels as props. The consent bar moves to components/shared with one short question and equal Allow and Decline. Crisp loads only from a chat button click. Theme and third-party tests pin the head script, the absence of OS-switched images and of third-party loads in the document.
…t in both languages

LandingLayout renders the skip link, the sticky banner and SiteHeader, main, SiteFooter and the consent bar itself, so every page, the error page included, gets the same chrome. The header carries Features, Databases, Pricing, Docs, Blog, the language switcher, the theme control, Account (/account?locale=) and Download; the footer has five groups, a newsletter form through useEmailForm with the page locale and no subscriber count, and Cookie settings. config/banner.php keeps only the switch (off by default), the link and the version; the words move to the banner catalog.
…ount app

Nine foundation files and all fifteen design-system primitives are byte-shared; the doc lists their sha256, what a shared file may import, the call-site changes the account app needs to adopt them, and what is deliberately not shared. SharedFilesTest checks presence, header, hash, that every headed file is listed, and that shared modules import nothing app-specific.
…cast fallback and a last-good cache

MacReleaseService accepts a release only if it is neither draft nor prerelease, is tagged vX.Y.Z and carries both DMGs, so a plugin release or a half-uploaded app release is rejected. When that fails it falls back to the Sparkle appcast's top item, building the DMG names and URLs from platforms.json. A fresh copy is cached for 15 minutes and a last-good copy forever, and a 5-minute failure marker stops a broken API being retried on every request. With nothing ever fetched, the release is 'unavailable' and both buttons point at GitHub's releases/latest. GitHubRepoService::stars() follows the same pattern. Every test fakes HTTP.
…orms.json

Run by hand, never scheduled. It compares GitHub latest (version and publish date), the appcast (version and minimum macOS), the Homebrew cask, the lowest served version against floorVersion, and the iTunes lookup for us (version, minimum iOS, price) and de (against storefrontExclusions). It exits non-zero on any drift or unreadable channel.
…for a Mac

Pure rules for the download page: iPhone, iPod and iPadOS's desktop user agent are 'ios'. Only Chromium's architecture hint picks a build, and only to highlight it. Safari and Firefox get no guess. Nothing here starts a download.
…Homebrew and the App Store card

Both DMG links are server-rendered from the release service, with sizes when GitHub provides them, a version badge, and the requirement taken from platforms.json through the platforms catalog. The page also has a 'Which Mac do I have?' disclosure, the Homebrew command with a copy button and a lag note driven by floorVersion, and an iPhone and iPad card that leads on iOS devices. It includes install and first-run steps (notarized, drivers on first pick, sample database), Sparkle updates, other platforms (not available, no date) and older versions. Nothing auto-starts; a post-click panel offers the file again. JSON-LD comes from the shared structured-data builders (#app, #ios-app, WebPage) with no rating, size or breadcrumb. download_click{location, platform} is unchanged.
…ase service

The legacy /ios page still renders until its phase-C rewrite. Its header links now come from MacReleaseService and GitHubRepoService (cached, last-good, failure marker) instead of LandingController's full releases list.
…captures phone crops, and load the /ios hero with priority
…ges, close the guards that could not fail, and fix the copy they caught
…d secondary columns on phones

The DataTable scroll region is now position: relative. Without it, the sr-only words of an off-screen column escaped the overflow clip, and the document grew to 532px at 375px wide on /pricing, the comparisons and two feature pages. The visible caption now sits above the region, so it no longer scrolls or clips. The sticky column is painted with --table-ground, so it no longer shows a raised stripe in dark mode or a two-tone table on a band.

Priority columns below 640px: the plan matrix fits all four columns in 343px; availability, the paid-features hub table, the engine tables and the comparison tables fold their secondary columns into the row header. The compare hub's five-column table folds below 1024px. /databases now uses one fixed column layout shared by every category. Monogram marks take the same footprint as vendor marks. Source markers are 12px. A marked cell shows its yes or no word, and TablePro-only rows show an explicit "Does not apply". The hub's Sources section moves to the end of the page, as its captions say.
…s, one download row, anchor-only smooth scroll

- Sections: a text-width section is now a left-aligned measure inside the wide grid, so headings no longer zigzag. A plain section that follows another drops its top padding (data-rhythm), so neighbours are one --space-section apart instead of two.
- Rebalanced: the platforms band, the /ios phone rows and header facts, the engine page header (the detail lead sits beside the facts card, with a 32px gap), and the pricing cards (activation directly under the price; two columns inside a card at md).
- Download: the page uses PageHeader. DMG names are set in mono, and the UA status line has its room reserved.
- Download bands: every band is one ActionPair row, whose captions share a line.
- Sponsors: a 2x2 grid on phones; logos load eagerly at low priority; the Nimbus mark is cropped to its bounds.
- Placeholders: the icon stays with its label.
- Scrolling: the global scroll-behavior: smooth is removed. In-page anchors glide one jump at a time (lib/anchor-glide.ts), and Inertia's scroll reset is instant again.
…current section on mobile, label focus ring, dark selected segments

- Theme menu: Enter and Space open it with focus on the checked option, and Escape closes it from the button too. The Features and Language menus close on focus-out, and Escape is scoped to each menu.
- Busy button: it is aria-disabled and swallows clicks instead of using a native disabled, so focus stays on it. Email fields are readOnly while submitting.
- Mobile menu: marks the current row with aria-current and a 2px bar.
- Header nav: the focus ring is drawn around the label instead of the 64px link.
- New --segment-selected token: a step above the track in dark (1.36:1). The disabled Stepper fill sinks below an enabled one.
- Language switcher fallback: names where it leads ("Bài viết này chỉ có bằng tiếng Anh · Xem danh sách Blog").
… are legible

- Standalone links, footer links and external links are inline text, so the arrow stays with the last word.
- DotList draws separators that never start or end a line.
- keepTogether holds dates and "Apple silicon or Intel" on one line. Fixed compounds use a no-break hyphen or space (in-app, Read-Only, mã nguồn mở).
- Kbd uses the system UI face, which has ⌘⇧⌥⌃⏎, as an inline box.
- Inline code of 32 characters or fewer never breaks.
…release refresh, bounded checkout wait, untrusted X-Forwarded-Host

- LCP preload: the head script now loops over each theme's list and sets media. It requested /undefined once an image was supplied. The /ios preload takes the iPad slot's sizes (IosController::IPAD_SIZES).
- EnginePaths: replaces five disagreeing copies of the engine link rule.
- MacReleaseService: refreshes under a short lock and serves the last good copy meanwhile.
- Checkout: gives up on the overlay script after 4s and falls back to the hosted page.
- Discount field: drops answers for a code that was edited.
- trustProxies: no longer trusts X-Forwarded-Host.
…ted posts by date, one newsletter form on /blog, no repeated legal contact line

- /vi/account and /vi/checkout 404s link /account?locale=vi.
- The English-only fallback: the post title shows as text, and the Button carries the page's own words.
- The 500 page reads the support address from facts.json.
- Related posts are listed newest first.
- The blog index turns off the footer newsletter.
- Privacy and Terms no longer repeat the contact line under their own Contact section.
- The VI FAQ docs sentence is reworded.
…ependent of composer dev

- Removed: GitHubRepoService and its test; ui/tabs and ui/footnote; the runtime halves of lib/data/engines.ts and comparisons.ts; sponsorLogoSrc and formatPrice; the reversed i18n/paths absoluteUrl; the SEOHead breadcrumbs prop and buildBreadcrumbJsonLd; the legacy banner copy props; unused catalog keys in both languages.
- Tests: skip guards that turned a missing data file into a silent skip are gone.
- TestCase points the fake Vite's hot file at a path that never exists. While public/hot exists, every SSR page test used to fail with a 500.
v0.77.1 (build 135) shipped on GitHub and Sparkle on 2026-10-03.
Homebrew still serves 0.77.0, so the copy floor stays there. The
release adds New… for SQLite, DuckDB and libSQL files and fixes the
editor and Oracle; no engine fact changed, so the engine data is
re-dated to the new tag.
@datlechin

Copy link
Copy Markdown
Member Author

Companion PR: TableProApp/license#87 (account and transactional half). Merge it first, on the same day, and only after the P1 imagery is supplied.

…les exists

The php CI job installs no npm packages, so the check now skips there
and runs in the ssr job, which does. With REQUIRE_SSR set it fails
instead of skipping, so the ssr job cannot pass it by accident.
Vietnamese pages now use Apple's own Vietnamese badge ("Tải về trên App Store"),
black for the light theme and white for the dark one, labelled with that text
and without lang="en". The badge reads the page language itself, so a caller
can no longer pair a label with the wrong artwork.
…m and the site social card

The 34 release-post figures (AVIF and WebP, with personal details covered) and the four iCloud sync diagrams
(per locale and theme) are now supplied. So is the bespoke og-site card, which
replaces the generated card on every page that has none of its own. The
manifest, the slot data and the handoff document are regenerated.

Tests that found a slot by data-asset-id now use renderedSlotSelector(), which
matches a placeholder by its id and a supplied slot by the file it renders.
…lice for real images

The placeholder walk now accepts a supplied slot as a <picture> with no
placeholder box, and the gzip ceiling on the bundled slot data moves to
18 KB, measured with the Mac captures in place.
…and database pages

64 entries move from placeholder to supplied: the hero window, the
feature workflows, every engine page that can run on a Mac (PostgreSQL,
MySQL, SQLite, MongoDB, Redis, SQL Server, Oracle, ClickHouse, DuckDB,
Cassandra, DynamoDB, etcd, Elasticsearch, SurrealDB, Trino, Beancount,
Kafka) and the three 0.70 figures, each with its phone crop where the
kind has one. Captured from TablePro 0.77.1 on sample data in an
isolated build, light and dark from one state, exported as AVIF and WebP
in sRGB within each kind's byte cap.

Captures that failed review (a focus ring, an editor diagnostic, cut
statements in phone crops, a missing production tag, a scrolled Query
Insights view, the Copy To review and the Export dialog) stay
placeholders until they are reshot. The Oracle brief now names 23ai
Free, the image that runs on Apple silicon.
…ots and six new Mac captures

The 140 new AVIF/WebP files that the next commit's manifest entries reference: all 11 iOS and iPadOS slots (phones at 280/560/840 from 840x1821, the iPad at 720/1008/2016 from 2016x1511, its crop at 343/686), and Copy To, the Elasticsearch console and its crop, Export, Import mapping, Query Insights and its crop, and the Touch ID prompt and its crop. Nothing references them yet, so this commit renders exactly like its parent.
…es, and redo 20 flawed ones

Flips 20 slots to supplied (all 11 iOS and iPadOS slots and 9 Mac) and replaces the files of 20 that were already supplied, fixing the flaws QA found: focus rings, the editor's error underline, Query Insights scrolled past its chart, Export reading '3 table', no production colour on Touch ID, mid-token wraps in the AI chat. The 0.70 figure 1 becomes 1408:1068. Alt text is corrected in English and Vietnamese for 10 entries. Regenerates the handoff and the bundled slot slice (17.1 KB gzipped).

Tests: the icon-and-label check renders ios-connection-list as a placeholder whatever its status, and the /ios test expects the manifest's iPad preload instead of null.
The review lists ENUM columns that reach PostgreSQL as VARCHAR without
their allowed values. It treats TINYINT(1) to BOOLEAN as lossless and
never lists it, so the old example described something the app does
not do and the capture beside it showed otherwise.
…one crops

Supplies mac-connection-ssh-form, mac-engine-picker, mac-edit-preview-sql and mac-structure-ddl-preview, with their phone crops (72 AVIF/WebP files). In 0.77.1 no native 343 pt cut holds what their briefs ask for. Per the owner's decision of 2026-10-04, the crops are scaled down uniformly: the SSH form at 0.667x (4:5), and the other three at 0.635x with the 1:1 aspect. The mobile-crop kind note now allows a scale down to 0.6x, and each crop's brief records its scale.

The SSH form's alt now says key authentication instead of a private key. The page shows the detail's alt over both images, and the phone crop uses SSH Agent. The engine picker crop's alt describes the 0.77.1 list instead of tiles. Regenerates the handoff and the bundled slot data.
Landing/ came with the site when it was split out of the platform app,
where it set the public pages apart from the account, API and webhook
controllers. Every controller here serves a public page, so the folder
grouped nothing. Route names are unchanged.
Homebrew now serves 0.77.1, so every Mac channel's lowest served version
is 0.77.1 and release:check reported drift. Raising the floor drops the
"since 0.77" labels that only mattered while Homebrew lagged.
@datlechin
datlechin marked this pull request as ready for review October 4, 2026 18:53
@datlechin
datlechin merged commit b44ea0a into main Oct 4, 2026
5 checks passed
@datlechin
datlechin deleted the rebuild branch October 4, 2026 18:53
datlechin added a commit that referenced this pull request Oct 4, 2026
…40)

changed() piped the changed-path list into grep -q. grep exits on its first
match, and under set -o pipefail the printf still writing into the closed
pipe failed the whole test once the list passed 64 KB. The 1,584-file
rebuild (#38) therefore reported php=false and content=false: it shipped
without rebuilding the caches, reloading PHP-FPM or regenerating the
sitemap, and the smoke test answered 500. A here-string has no pipe.

The new test runs the script's own changed() under its shell options
against a list larger than the buffer; it fails on the old definition.
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.

1 participant