Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 13 additions & 7 deletions app/Http/Controllers/CompareController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Controllers;

use App\Services\Releases\LinuxReleaseService;
use App\Services\Releases\PlatformCatalog;
use App\Support\Content\ContentRepository;
use Illuminate\Support\Arr;
Expand Down Expand Up @@ -29,13 +30,13 @@
*/
class CompareController extends Controller
{
public function index(ContentRepository $content, PlatformCatalog $platforms): Response
public function index(ContentRepository $content, PlatformCatalog $platforms, LinuxReleaseService $linux): Response
{
$locale = App::getLocale();
$data = $this->comparisons();
$products = $this->comparedProducts($data);
$checkedAt = is_string($data['checkedAt'] ?? null) ? $data['checkedAt'] : null;
$tablepro = $this->tablepro($platforms);
$tablepro = $this->tablepro($platforms, $linux);

return Inertia::render('Compare/Index', [
'content' => $content->page('compare/index', $locale),
Expand All @@ -48,7 +49,7 @@ public function index(ContentRepository $content, PlatformCatalog $platforms): R
]);
}

public function show(ContentRepository $content, PlatformCatalog $platforms, string $slug): Response
public function show(ContentRepository $content, PlatformCatalog $platforms, LinuxReleaseService $linux, string $slug): Response
{
$locale = App::getLocale();
$data = $this->comparisons();
Expand All @@ -57,7 +58,7 @@ public function show(ContentRepository $content, PlatformCatalog $platforms, str

abort_if($product === null, 404);

$tablepro = $this->tablepro($platforms);
$tablepro = $this->tablepro($platforms, $linux);

return Inertia::render('Compare/Show', [
'slug' => $slug,
Expand Down Expand Up @@ -169,16 +170,21 @@ private function others(ContentRepository $content, array $products, string $cur
/**
* The TablePro facts the page states but should not bundle: the featured
* engine names, and the Mac and iPhone and iPad releases the column
* describes (`platforms.json` → `release`).
* describes (`platforms.json` → `release`). Linux reports its newest
* release on its channel, and is null until platforms.json releases it.
*
* @return array{featuredEngines: list<string>, mac: array{version: string, date: string}|null, ios: array{version: string, date: string}|null}
* @return array{featuredEngines: list<string>, mac: array{version: string, date: string}|null, ios: array{version: string, date: string}|null, linux: array{version: string|null, date: string|null, deviceNames: list<string>, channel: string, distributions: list<array{id: string, name: string|null, versions: list<string>, via: string}>}|null}
*/
private function tablepro(PlatformCatalog $platforms): array
private function tablepro(PlatformCatalog $platforms, LinuxReleaseService $linux): array
{
$summary = $platforms->linuxSummary();
$release = $summary !== null ? $linux->latest() : null;

return [
'featuredEngines' => $this->featuredEngines(),
'mac' => $this->release($platforms, 'mac'),
'ios' => $this->release($platforms, 'ios'),
'linux' => $summary === null ? null : ['version' => $release?->version, 'date' => $release?->publishedAt, ...$summary],
];
}

Expand Down
19 changes: 18 additions & 1 deletion app/Http/Controllers/DatabaseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ private function summary(array $engine, bool $detailed = false): array
'inPicker' => ($engine['ios']['inPicker'] ?? false) === true,
'openable' => ($engine['ios']['openable'] ?? false) === true,
],
'linux' => $this->linuxSupport($engine),
'limits' => array_map(
fn(array $limit): array => ['id' => (string) $limit['id'], 'value' => is_int($limit['value'] ?? null) ? $limit['value'] : null],
is_array($engine['limits'] ?? null) ? $engine['limits'] : [],
Expand Down Expand Up @@ -342,13 +343,29 @@ private function product(): array
* list and the closing band's requirement line. Null for an app that is
* not released.
*
* @return array{mac: array{deviceNames: list<string>, requirements: array{systems: list<string>, minVersion: string, displayVersion: string, releaseName: string|null}}|null, ios: array{deviceNames: list<string>, requirements: array{systems: list<string>, minVersion: string, displayVersion: string, releaseName: string|null}}|null}
* @return array{mac: array{deviceNames: list<string>, requirements: array{systems: list<string>, minVersion: string, displayVersion: string, releaseName: string|null}}|null, ios: array{deviceNames: list<string>, requirements: array{systems: list<string>, minVersion: string, displayVersion: string, releaseName: string|null}}|null, linux: array{deviceNames: list<string>, channel: string, distributions: list<array{id: string, name: string|null, versions: list<string>, via: string}>}|null}
*/
private function platformSummaries(): array
{
return [
'mac' => $this->platforms->summary('mac'),
'ios' => $this->platforms->summary('ios'),
'linux' => $this->platforms->linuxSummary(),
];
}

/**
* @param array<string, mixed> $engine
* @return array{support: string, tested: list<string>}
*/
private function linuxSupport(array $engine): array
{
$linux = is_array($engine['linux'] ?? null) ? $engine['linux'] : [];
$support = in_array($linux['support'] ?? null, ['tested', 'untested', 'blocked', 'none'], true) ? $linux['support'] : 'none';

return [
'support' => $support,
'tested' => $support === 'tested' ? array_values(array_filter(is_array($linux['tested'] ?? null) ? $linux['tested'] : [], 'is_string')) : [],
];
}

Expand Down
19 changes: 4 additions & 15 deletions app/Http/Controllers/DownloadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,11 @@ private function ios(PlatformCatalog $platforms, SiteFacts $facts, string $local
private function linux(PlatformCatalog $platforms, ?LinuxRelease $release, string $locale): array
{
$platform = $platforms->find('linux') ?? [];
$summary = $platforms->linuxSummary() ?? ['deviceNames' => [], 'channel' => $platforms->linuxChannel(), 'distributions' => []];
$packages = $platforms->linuxPackages();
$architectures = $this->strings($platform['architectures'] ?? []);
$desktop = $platform['desktop'] ?? null;

$distributions = [];

foreach (is_array($platform['distributions'] ?? null) ? $platform['distributions'] : [] as $distribution) {
if (is_array($distribution) && is_string($distribution['id'] ?? null) && is_string($distribution['via'] ?? null)) {
$distributions[] = [
'id' => $distribution['id'],
'name' => $this->string($distribution['name'] ?? null),
'versions' => $this->strings($distribution['versions'] ?? []),
'via' => $distribution['via'],
];
}
}
$distributions = $summary['distributions'];

$methods = [];

Expand Down Expand Up @@ -134,8 +123,8 @@ private function linux(PlatformCatalog $platforms, ?LinuxRelease $release, strin
}

return [
'deviceNames' => $this->strings($platform['deviceNames'] ?? []),
'channel' => $platforms->linuxChannel(),
'deviceNames' => $summary['deviceNames'],
'channel' => $summary['channel'],
'distributions' => $distributions,
'desktop' => is_array($desktop) && is_string($desktop['name'] ?? null) && is_string($desktop['version'] ?? null) ? ['name' => $desktop['name'], 'version' => $desktop['version']] : null,
'architectures' => $architectures,
Expand Down
4 changes: 3 additions & 1 deletion app/Http/Controllers/FeatureController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Controllers;

use App\Services\Releases\PlatformCatalog;
use App\Support\Content\ContentRepository;
use App\Support\Content\Slugs\FeatureSlugs;
use App\Support\Features\FeatureFacts;
Expand Down Expand Up @@ -38,7 +39,7 @@ public function index(ContentRepository $content, FeatureFacts $facts): Response
]);
}

public function show(ContentRepository $content, FeatureFacts $facts, string $slug): Response
public function show(ContentRepository $content, FeatureFacts $facts, PlatformCatalog $platforms, string $slug): Response
{
$locale = App::getLocale();
$page = $content->page("features/{$slug}", $locale);
Expand All @@ -49,6 +50,7 @@ public function show(ContentRepository $content, FeatureFacts $facts, string $sl
'content' => $page,
'labels' => $labels,
'facts' => $facts->for($page),
'linux' => $platforms->linuxSummary(),
]);
}

Expand Down
39 changes: 33 additions & 6 deletions app/Services/Releases/PlatformCatalog.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,6 @@ public function macFloorVersion(): ?string
return is_string($floor) && $floor !== '' ? $floor : null;
}

/**
* The facts the download page states about a released platform, or null
* when the platform is not released.
*
* @return array{deviceNames: list<string>, requirements: array{systems: list<string>, minVersion: string, displayVersion: string, releaseName: string|null}}|null
*/
/**
* The Linux packages destination: GitHub release file templates, the tag prefix and how to verify a file.
*
Expand All @@ -184,6 +178,36 @@ public function linuxChannel(): string
return ($this->find('linux')['channel'] ?? null) === 'stable' ? 'stable' : 'beta';
}

/**
* @return array{deviceNames: list<string>, channel: string, distributions: list<array{id: string, name: string|null, versions: list<string>, via: string}>}|null
*/
public function linuxSummary(): ?array
{
if (! $this->isReleased('linux')) {
return null;
}

$platform = $this->find('linux') ?? [];
$distributions = [];

foreach (is_array($platform['distributions'] ?? null) ? $platform['distributions'] : [] as $distribution) {
if (is_array($distribution) && is_string($distribution['id'] ?? null) && is_string($distribution['via'] ?? null)) {
$distributions[] = [
'id' => $distribution['id'],
'name' => is_string($distribution['name'] ?? null) && $distribution['name'] !== '' ? $distribution['name'] : null,
'versions' => array_values(array_filter(is_array($distribution['versions'] ?? null) ? $distribution['versions'] : [], 'is_string')),
'via' => $distribution['via'],
];
}
}

return [
'deviceNames' => array_values(array_filter(is_array($platform['deviceNames'] ?? null) ? $platform['deviceNames'] : [], 'is_string')),
'channel' => $this->linuxChannel(),
'distributions' => $distributions,
];
}

/**
* Every file a Linux release carries for each format, series and architecture, in data order.
*
Expand Down Expand Up @@ -232,6 +256,9 @@ public function linuxDataRelease(): ?array
];
}

/**
* @return array{deviceNames: list<string>, requirements: array{systems: list<string>, minVersion: string, displayVersion: string, releaseName: string|null}}|null
*/
public function summary(string $id): ?array
{
$platform = $this->find($id);
Expand Down
7 changes: 4 additions & 3 deletions docs/rebuild/design/positioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ promote the badge on an iPhone or iPad. The labels do not change, nothing auto-d
- A file size.
- A star count or "#1 trending".
- "No account".
- Any Linux or Windows line.
- Any Windows line. Linux appears in the hero only as the visitor's own system's action, decided in the browser
(the Home redesign), never as a list of platforms.
- "View source". Open source is in the line under both actions. GitHub is linked from the header, the footer and the
homepage open-source section (sitemap §B.1, §B.3, §D).

Expand Down Expand Up @@ -725,7 +726,7 @@ Structured data and OG cards take their text from those sources, so they need no
| Class | About | Banned (EN) | Banned (VI) | Allowed form |
|---|---|---|---|---|
| Universals | TablePro | every database, all databases, any database, all platforms, every platform, cross-platform, one app everywhere, works everywhere | mọi cơ sở dữ liệu, tất cả cơ sở dữ liệu, mọi nền tảng, đa nền tảng, trên mọi thiết bị | "{featuredEngines} and more"; "each platform it supports". A link to the hub says "Supported databases" / "Cơ sở dữ liệu được hỗ trợ" |
| Platform futures | TablePro | coming soon, waitlist, release dates for unreleased platforms; any Windows or Linux availability (a reviewer check, because comparison pages name competitors' Windows and Linux builds) | sắp ra mắt, danh sách chờ | TablePro's Windows and Linux status appears only in the FAQ ("a prototype exists, nothing to install, no date") and on `/download#other-platforms` ("not available, no date", sitemap §A.1) |
| Platform futures | TablePro | coming soon, waitlist, release dates for unreleased platforms; any Windows availability (a reviewer check, because comparison pages name competitors' Windows builds) | sắp ra mắt, danh sách chờ | TablePro's Windows status appears only in the FAQ and on `/download` ("not available, no date"). Linux shipped as a beta on 2026-11-06: say "beta" beside it, name its engines from `engines.json`, and never claim a Mac-only feature or a paid feature for it (the feature pages' "Where it works" tables and `/databases#linux` hold what runs where) |
| Brand defined by Mac | TablePro | "Mac database client", or "for Mac" as the identity (tested on identity keys by §13; reviewers check the rest); "TablePro Mobile"; Universal, Universal Binary; Mac App Store; Setapp; visionOS app | TablePro Mobile | "Download for Mac" on Mac actions; platform-specific page titles; "separate builds for Apple silicon and Intel". On `/ios`, "one app for iPhone and iPad" rather than "universal app" |
| Unscoped free | Any | the whole app is free, all of it, no feature gating, no per-feature gate, no paywall, free forever, completely free, 100% free, free software as a price | hoàn toàn miễn phí, miễn phí hoàn toàn, miễn phí mãi mãi, miễn phí trọn đời | "free to use" next to "paid plans add optional features"; "free, with no in-app purchases" for iPhone |
| Unlock framing | Any | unlock, unlocks | mở khóa | "adds" / "bổ sung" for paid features. iPhone App Lock is written "lock the app with Face ID" / "khóa ứng dụng bằng Face ID", as in the App Store listing |
Expand Down Expand Up @@ -773,7 +774,7 @@ Structured data and OG cards take their text from those sources, so they need no
hits ordinary prose. A reviewer treats an "anywhere" claim about TablePro as a Universal.
- **Reviewers check these by hand, not the test:**
- safety-overreach paraphrases, against the allowed form;
- TablePro's Windows and Linux availability;
- TablePro's Windows availability, and any Linux claim against the feature pages' "Where it works" tables and `/databases#linux`;
- "fast" as a claim;
- "for Mac" as the identity outside the identity keys;
- counts that `Content/NoTypedCountsTest` cannot see.
Expand Down
4 changes: 2 additions & 2 deletions resources/data/content/de/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"maker": {
"title": "Wer TablePro entwickelt",
"body": "{maker} entwickelt die Mac-App mit ihren Plugins, die iPhone- und iPad-App und diese Website.",
"body": "{maker} entwickelt die Mac-App mit ihren Plugins, die iPhone- und iPad-App, die Linux-App und diese Website.",
"identity": "Derselbe Entwickler veröffentlicht TablePro im <appStore>App Store</appStore> und erhält Unterstützung über <sponsorsProgram>GitHub Sponsors</sponsorsProgram>.",
"facts": {
"maker": "Entwickler",
Expand All @@ -32,7 +32,7 @@
},
"source": {
"title": "Quellcode",
"body": "Beide Apps sind quelloffen unter der AGPLv3, einschließlich des Codes der Bezahlfunktionen, in einem <source>Repository auf GitHub</source>.",
"body": "Die Apps sind quelloffen unter der AGPLv3, einschließlich des Codes der Bezahlfunktionen, in einem <source>Repository auf GitHub</source>.",
"license": "Der <agpl>Lizenztext</agpl> liegt im selben Repository."
},
"contact": {
Expand Down
6 changes: 3 additions & 3 deletions resources/data/content/de/black-friday.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
"lifetime": {
"name": "Pro einmalig",
"unit": "einmalige Zahlung",
"detail": "Kein Ablaufdatum. {activations} Macs."
"detail": "Kein Ablaufdatum. {activations} Computer, Mac oder Linux."
},
"yearly": {
"name": "Pro jährlich",
"unit": "im ersten Jahr",
"detail": "Danach {yearlyList} pro Jahr. {activations} Macs."
"detail": "Danach {yearlyList} pro Jahr. {activations} Computer, Mac oder Linux."
},
"buy": "Für {price} kaufen",
"soon": "Ab dem {startsOn}",
Expand All @@ -39,7 +39,7 @@
},
"includes": {
"title": "Was Pro ergänzt",
"lead": "TablePro ist kostenlos, auch bei der Arbeit. Pro ergänzt diese Funktionen auf {activations} Macs.",
"lead": "TablePro ist kostenlos, auch bei der Arbeit. Pro ergänzt die Mac-App um diese Funktionen.",
"more": "Außerdem in Pro",
"link": "Mehr zu {feature}"
},
Expand Down
4 changes: 2 additions & 2 deletions resources/data/content/de/compare/beekeeper-studio.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"Eine native App und eine separate kostenlose App für iPhone und iPad."
],
"competitor": [
"Derselbe Client auf Windows, macOS und Linux.",
"Unterstützung für Windows, mit denselben Funktionen auf macOS und Linux.",
"Verbindungen zu Firebird, SQL Anywhere oder GreengageDB.",
"Cloud-Arbeitsbereiche zum Teilen von Verbindungen und Abfragen, ab {cells.sync.edition}."
]
Expand Down Expand Up @@ -69,7 +69,7 @@
"intro": "",
"items": [
{
"text": "TablePro ist eine in Swift geschriebene Mac-App mit nativen Datenbanktreibern. Beekeeper Studio basiert auf {technology}. Beide bieten separate Builds für Apple silicon und Intel-Macs.",
"text": "Die Mac-App von TablePro ist in Swift geschrieben, mit nativen Datenbanktreibern. Beekeeper Studio basiert auf {technology}. Beide bieten separate Builds für Apple silicon und Intel-Macs.",
"cite": [
"technology",
"mac"
Expand Down
4 changes: 2 additions & 2 deletions resources/data/content/de/compare/datagrip.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"competitor": [
"Codeprüfungen und Versionskontrolle neben Abfragen.",
"Eine vollständige IDE, kostenlos für nichtkommerzielle Nutzung.",
"Windows oder Linux oder JDBC-Engines wie Firebird, Informix und Databricks."
"Windows oder JDBC-Engines wie Firebird, Informix und Databricks."
]
},
"glance": {
Expand Down Expand Up @@ -78,7 +78,7 @@
"href": "/privacy#mac-usage-report"
},
{
"text": "TablePro ist eine in Swift geschriebene Mac-App mit nativen Datenbanktreibern. DataGrip basiert auf {technology}.",
"text": "Die Mac-App von TablePro ist in Swift geschrieben, mit nativen Datenbanktreibern. DataGrip basiert auf {technology}.",
"cite": [
"technology"
],
Expand Down
Loading