Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ log found in one line what two rounds of guessing had missed.

Never enable the inspector outside `-debug`.

`make debug` asks which OS to look like, so the Windows or Linux theme can be checked from a Mac. It sets `ADMINER_DESKTOP_SIMULATE_OS`, ignored without `-debug`. It changes the look and the window only (`Os::look()`): the engine stays WKWebView, and without Segoe the font falls back.

PHP fatal errors reach `~/Library/Logs/Adminer Desktop/adminer-desktop.log`, but only
because `app/php/desktop.ini` turns `log_errors` on — frankenphp's default sends them to
the page and nowhere else. `make logs` or the Open Logs menu item opens the folder.
Expand Down Expand Up @@ -176,7 +178,7 @@ into components pulled in by `@import`: `tokens`, `base`, `tables`, `forms`, `si
`settings`. `light-dark()` is colour-only, so the one non-colour scheme difference —
inverting adminer's sprite icons on the dark surface — lives in `base.css` and keys off the
`theme-auto`/`theme-dark` body class instead. `system-ui` gives the native OS font with no
branching; the `theme-*`, `os-mac`/`os-windows`/`os-linux` and `density-compact`/`-cozy`/
branching; the `theme-*`, `mac-os`/`windows-os`/`linux-os` and `density-compact`/`-cozy`/
`-comfortable` body classes come from `AdminerDesktop::bodyClass()` and are the hooks for
appearance, per-OS and per-density tweaks. oxfmt owns the formatting — one declaration per
line.
Expand Down
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ qa: fetch app/vendor i18n mcp-bridge-data ## Run every static check (php, go, j
|| { echo "launcher: no non-darwin definition of $$f() (menu_other.go stub, or *_linux.go + *_other.go or *_windows.go split)"; exit 1; }; \
done && echo "platform stubs ok"
@command -v shellcheck >/dev/null \
&& { shellcheck check.sh cli/compile-adminer.sh cli/dresscode.sh cli/kill-orphans.sh cli/demo-mysql.sh tests/cli/dresscode-test.sh tests/cli/dresscode-hook-test.sh tests/cli/kill-orphans-test.sh tests/launcher/downloads.sh && echo "shellcheck ok"; } \
|| { sh -n check.sh && sh -n cli/compile-adminer.sh && sh -n cli/dresscode.sh && sh -n cli/kill-orphans.sh && sh -n cli/demo-mysql.sh && echo "sh ok (shellcheck not installed)"; }
&& { shellcheck check.sh cli/pick-os.sh tests/cli/pick-os-test.sh cli/compile-adminer.sh cli/dresscode.sh cli/kill-orphans.sh cli/demo-mysql.sh tests/cli/dresscode-test.sh tests/cli/dresscode-hook-test.sh tests/cli/kill-orphans-test.sh tests/launcher/downloads.sh && echo "shellcheck ok"; } \
|| { sh -n check.sh && sh -n cli/pick-os.sh && sh -n tests/cli/pick-os-test.sh && sh -n cli/compile-adminer.sh && sh -n cli/dresscode.sh && sh -n cli/kill-orphans.sh && sh -n cli/demo-mysql.sh && echo "sh ok (shellcheck not installed)"; }
@# Native-shell strings: the `i18n` prerequisite already generated the real files (which is
@# also what makes i18n_gen.h exist before `go vet` compiles the cgo launcher above). Assert the
@# generator's output quality and lint the emitted plists. `make i18n-check` reports coverage.
Expand All @@ -338,6 +338,7 @@ qa: fetch app/vendor i18n mcp-bridge-data ## Run every static check (php, go, j
./bin/frankenphp$(EXE) php-cli tests/shortcuts/ShortcutsTest.php
./bin/frankenphp$(EXE) php-cli tests/cli/WindowsIcoTest.php
./bin/frankenphp$(EXE) php-cli tests/cli/I18nTest.php
@sh tests/cli/pick-os-test.sh
@sh tests/cli/kill-orphans-test.sh
@sh tests/cli/dresscode-test.sh
@sh tests/cli/dresscode-hook-test.sh
Expand Down Expand Up @@ -459,8 +460,9 @@ editor: build ## Run in editor mode
# Brings up the e2e's seeded mysql beside it, so there is a database to point it at; no
# docker skips it rather than refusing to debug.
debug: build ## Run with Safari's Web Inspector attached, and a seeded mysql (docker)
@./cli/demo-mysql.sh $(DEMO_MYSQL) || echo "no mysql: docker is not running"
./build.noindex/adminer-desktop$(EXE) -debug
@os=$$(./cli/pick-os.sh); \
./cli/demo-mysql.sh $(DEMO_MYSQL) || echo "no mysql: docker is not running"; \
ADMINER_DESKTOP_SIMULATE_OS=$$os ./build.noindex/adminer-desktop$(EXE) -debug

# The app in dev mode against seeded demo data, for clicking around by hand. Brings up
# (or reuses) the same throwaway postgres the e2e uses, reseeds it, and opens the app
Expand Down
2 changes: 1 addition & 1 deletion app/AdminerDesktop.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ function csp(array &$csp): void {
* @return void
*/
function bodyClass(): void {
echo " " . Os::current()->bodyClass();
echo " " . Os::look()->bodyClass();
// The launcher sets this under -debug; the desktop scripts read it to stand down so
// the web inspector's own behaviour is unobstructed.
if (Env::Debug->get()) {
Expand Down
6 changes: 4 additions & 2 deletions app/src/Assets/javascript/scrollers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
* launcher marks <html> with ad-legacy-scrollers) the panels' thumbs are ours too, only always on
* screen and as thick as Mail's: WebKit's own took a column of the panel, and the toolbar inside
* #content, its hairline and its search, stopped that column short of the window's edge. The
* inner scrollers keep WebKit's legacy ones. macOS only, as base.css's rules are.
* inner scrollers keep WebKit's legacy ones. macOS and Windows, whose own bars fade the same way
* (windows-os.css); Linux keeps its own.
*/

{
Expand All @@ -29,7 +30,8 @@
// Our design only, which hides WebKit's bars and styles the thumb (base.css); --ad-accent is
// defined by it alone, as table-nav.js tells it.
const ours = () =>
document.body?.classList.contains("os-mac") &&
(document.body?.classList.contains("mac-os") ||
document.body?.classList.contains("windows-os")) &&
getComputedStyle(document.documentElement)
.getPropertyValue("--ad-accent")
.trim() !== "";
Expand Down
2 changes: 2 additions & 0 deletions app/src/Env.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ enum Env: string {
case DataDir = 'ADMINER_DESKTOP_DATA';
/** Set under -debug: turns on Tracy and the web inspector's own affordances. */
case Debug = 'ADMINER_DESKTOP_DEBUG';
/** Which OS the UI dresses as under -debug, an Os case (Os::look). Ignored without -debug. */
case SimulateOs = 'ADMINER_DESKTOP_SIMULATE_OS';
/** `make demo` only: the throwaway connection demo-login.js fills and submits. */
case Demo = 'ADMINER_DESKTOP_DEMO';
/** The launcher's own path (Go's os.Executable), so the MCP panel can print the exact
Expand Down
9 changes: 8 additions & 1 deletion app/src/Os.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,16 @@ static function current(): self {
};
}

/** The OS the UI dresses as: the real one, unless -debug asks for another (Env::SimulateOs).
* Only for how the page looks. Paths and printed commands stay on current(). */
static function look(): self {
$simulated = Env::Debug->get() ? self::tryFrom((string) Env::SimulateOs->get()) : null;
return $simulated ?? self::current();
}

/** The class on <body> the theme keys off (AdminerDesktop::bodyClass). */
function bodyClass(): string {
return 'os-' . $this->value;
return $this->value . '-os';
}

/** The name a person recognises, for the settings UI. */
Expand Down
26 changes: 25 additions & 1 deletion app/src/Settings/Theme/Theme.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Desktop\Settings\Theme;

use Desktop\Latte;
use Desktop\Os;
use Desktop\SettingKey;
use Desktop\UserSettings;

Expand All @@ -15,6 +16,9 @@
* (include/plugins.inc.php:33), so a helper called AdminerTheme would quietly become one.
*/
class Theme {
private const string OWN_DESIGN = "src/Settings/Theme/designs/adminer-desktop/adminer.css";
private const string WINDOWS_SHEET = "src/Settings/Theme/designs/adminer-desktop/windows-os.css";

private \AdminerDesktop $desktop;
private UserSettings $settings;

Expand Down Expand Up @@ -60,7 +64,27 @@ function designs(Mode $mode): array {
* @return array<string,string>
*/
function cssMap(): array {
$self = "src/Settings/Theme/designs/adminer-desktop/adminer.css";
$map = $this->designMap();
if (Os::look() !== Os::Windows) {
return $map;
}
// Right after ours and tagged the same, so it applies wherever ours does and never over a
// gallery design.
$return = [];
foreach ($map as $path => $mode) {
$return[$path] = $mode;
if ($path === self::OWN_DESIGN) {
$return[self::WINDOWS_SHEET] = $mode;
}
}
return $return;
}

/** Which design each side uses, before anything per-OS is added to it.
* @return array<string,string>
*/
private function designMap(): array {
$self = self::OWN_DESIGN;
$sides = [];
foreach (Mode::cases() as $mode) {
$design = (string) $this->settings->get($mode->designKey(), "");
Expand Down
5 changes: 3 additions & 2 deletions app/src/Settings/Theme/designs/adminer-desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pulled in by native `@import` from `adminer.css`:
- `tables.css` — the data grid
- `forms.css` — inputs and buttons
- `sidebar.css` — the menu and breadcrumb
- `windows-os.css` — Windows 11's tokens and controls under `windows-os`, with values from WinUI's resources; `Theme::cssMap()` loads it on Windows only, not this file

One file carrying both schemes: every scheme difference is a `light-dark(light, dark)`
token, and the used `color-scheme` picks the side. Adminer sets that from its
Expand All @@ -19,7 +20,7 @@ dark surface, keys off the `theme-auto` / `theme-dark` body class instead (`base
It is excluded from the gallery (`Theme::designs()`) because it is the default, offered as
the empty "Adminer Desktop" option on each side rather than one entry among the rest.

Per-OS adaptation is mostly `system-ui` (the OS UI font, no branching); the `os-mac` /
`os-windows` / `os-linux` body class from `AdminerDesktop::bodyClass()` is the hook for
Per-OS adaptation is mostly `system-ui` (the OS UI font, no branching); the `mac-os` /
`windows-os` / `linux-os` body class from `AdminerDesktop::bodyClass()` is the hook for
the rest. Row density (`density-compact` / `-cozy` / `-comfortable`) comes from the same
place.
12 changes: 6 additions & 6 deletions app/src/Settings/Theme/designs/adminer-desktop/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ kbd,

/* macOS renders system-ui (San Francisco) best antialiased; Windows and Linux stay on
the OS default, where forcing this only thins the text. .os-* comes from bodyClass. */
.os-mac body {
.mac-os body {
-webkit-font-smoothing: antialiased;
}

Expand Down Expand Up @@ -206,28 +206,28 @@ body.theme-dark .icon {
* system set to hide them — so the panels wore two grey bars at all times. Transparent until
* hovered (`:hover` holds for every element under the pointer, so the scroller it is in), and
* thin, as an overlay scrollbar is, and while it scrolls (.ad-scrolling, scrollers.js), as an
* overlay one appears then. macOS only: Windows and Linux keep theirs on screen natively.
* overlay one appears then. Windows has the same in windows-os.css; Linux keeps its own on screen.
*
* Only while the system draws overlay scrollbars. With a mouse plugged in, or System Settings set
* to Always, macOS draws legacy ones that stay on screen, and so does every native app — Mail
* included; the launcher says which (ad-legacy-scrollers on <html>, installScrollerStyle), and then
* WebKit's own are left as they are, except the panels' (below). A plain browser is never told,
* and keeps the overlay behaviour. */
:root:not(.ad-legacy-scrollers) .os-mac * {
:root:not(.ad-legacy-scrollers) .mac-os * {
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}

:root:not(.ad-legacy-scrollers) .os-mac :is(:hover, .ad-scrolling) {
:root:not(.ad-legacy-scrollers) .mac-os :is(:hover, .ad-scrolling) {
scrollbar-color: color-mix(in srgb, var(--fg) 35%, transparent) transparent;
}

/* Even transparent, that bar keeps its width, so every rule in the two panels stopped one
scrollbar short of the right edge. A legacy one takes the same column, and the toolbar's
hairline and search stopped short of it. So the panels' bars are scrollers.js's in both modes;
the scrollers inside them keep theirs. */
.os-mac #menu,
.os-mac #content {
.mac-os #menu,
.mac-os #content {
scrollbar-width: none;
}

Expand Down
20 changes: 10 additions & 10 deletions app/src/Settings/Theme/designs/adminer-desktop/tables.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,30 @@ th,
* finds the true first and last row whether or not the table has a header.
*
* macOS only, because a rounded grid is a macOS idiom rather than a house style: Windows and
* Linux keep the square corners their own tables have, which is what os-mac not matching
* Linux keep the square corners their own tables have, which is what mac-os not matching
* leaves them with. */
.os-mac table {
.mac-os table {
border-radius: var(--ad-radius);
}

.os-mac table > :first-child > tr:first-child > :first-child {
.mac-os table > :first-child > tr:first-child > :first-child {
border-top-left-radius: var(--ad-radius);
}

.os-mac table > :first-child > tr:first-child > :last-child {
.mac-os table > :first-child > tr:first-child > :last-child {
border-top-right-radius: var(--ad-radius);
}

.os-mac table > :last-child > tr:last-child > :first-child {
.mac-os table > :last-child > tr:last-child > :first-child {
border-bottom-left-radius: var(--ad-radius);
}

.os-mac table > :last-child > tr:last-child > :last-child {
.mac-os table > :last-child > tr:last-child > :last-child {
border-bottom-right-radius: var(--ad-radius);
}

/* A settings table's corners, which everything drawing them reads from --ad-radius. */
.os-mac #desktop-panels table {
.mac-os #desktop-panels table {
--ad-radius: var(--ad-card-radius);
}

Expand Down Expand Up @@ -371,17 +371,17 @@ span.ad-page-step {
}

/* A notice inside the settings dialog, rounded as its tables are. */
.os-mac dialog#desktop-settings :is(.message, .error) {
.mac-os dialog#desktop-settings :is(.message, .error) {
border-radius: var(--ad-card-radius);
padding: 1.2em 1.5em;
}

/* A select in a dialog's body only: the sidebar's and the pages' keep the UI's tighter corners. */
.os-mac .ad-modal-body select {
.mac-os .ad-modal-body select {
border-radius: 10px;
}

/* The saved connections on the index. */
.os-mac :is(.ad-card, #ad-new-connection) {
.mac-os :is(.ad-card, #ad-new-connection) {
border-radius: var(--ad-card-radius);
}
2 changes: 1 addition & 1 deletion app/src/Settings/Theme/designs/adminer-desktop/tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ html {
/* macOS 26+ rounds a titled window with a continuous curve, not a circle: 16.5pt at its apex,
easing into the edges 24pt from the corner (measured off a screenshot of ours). A circular
corner cannot follow both; 20 sits between them and is the one that reads as parallel. */
.os-mac {
.mac-os {
--ad-window-radius: 20px;
/* What stands on a surface, a table or a card: as round as a message in Mail. */
--ad-card-radius: 12px;
Expand Down
Loading
Loading