Keynctr/CHECKPOINT-display-and-icons.md

6.1 KiB

Checkpoint — Linux display compatibility + icon alpha fixes (2026-09-09)

Where things are

  • Project: /home/avi/Projects/Keynctr
  • Branch: master @ d580139 ("fix(icons): true alpha channel, no white matte or white tile") on top of 0814a53 ("fix(linux): work on X11, Wayland, and Hyprland").
  • Working tree: clean for tracked files. Untracked leftovers are the pre-existing hygiene entries (.directory, .impeccable/, .opencode/, COSMIC_THEME.md, src/publish.rs.bak, src/signer/nip46_external.rs) plus deferred/SignerConnectionPanel.tsx.wip (a broken WIP component, moved out of the build — see below). Original white-background icons are preserved under deferred/original-icons/ (tracked).

What was completed (this session)

1. Linux display-server compatibility — 0814a53

The app did not start on a friend's Wayland machine. Root causes found and fixed:

  • No explicit platform choice. Hyprland (and any Wayland session with XWayland) exports both $DISPLAY and $WAYLAND_DISPLAY, so Electron must be told which backend to use before Chromium initializes. frontend/electron/main.ts now sets ozone-platform (wayland/x11) from XDG_SESSION_TYPE/WAYLAND_DISPLAY at module load, with KEYNCTR_FORCE_X11=1 / KEYNCTR_FORCE_WAYLAND=1 overrides.
  • GPU process crashes (SIGSEGV in eglCreateWindowSurface, Mesa libGLESv2). Reproduced on this box (Intel Iris Xe, Hyprland, mesa 26.2.1): with hardware GL the GPU helper died repeatedly and the window never appeared. Fix: software rendering by default on Linux (app.disableHardwareAcceleration()); hardware GL is opt-in via KEYNCTR_ENABLE_GPU=1.
  • Startup watchdog + bounded relaunch ladder. A marker file (<tmp>/keynctr-startup.json, stamped clean on deliberate quit) records each launch; if the previous process died before its window proved itself (painted and survived 8 s), the next launch advances one rung: detected platform → other platform → GPU opt-in → other+GPU, then stops with an error dialog listing the escape hatches. AppImage-safe relaunch via $APPIMAGE. No infinite cascades.
  • Sandbox pre-flight. Packaged builds check for a non-setuid chrome-sandbox combined with blocked unprivileged user namespaces (Ubuntu 24.04 AppArmor knob, unprivileged_userns_clone) and fall back to --no-sandbox instead of dying silently. Root also gets --no-sandbox as Chromium requires.
  • Window now uses show: false + ready-to-show (always shown, even with the watchdog disabled).

2. Icon white-fringe fix — d580139

The source icons were grayscale (mode L, no alpha at all): a black bird on a flat white field, which rendered as a white box/halo on every non-white surface (window icon, taskbar, sidebar, launchers).

  • frontend/public/icon.png and frontend/src/assets/logo.png regenerated as RGBA: alpha = ink coverage of the original artwork; RGB forced to 0 everywhere, so no white matte can bleed through semi-transparent edge pixels (verified: 0 pixels with RGB > 200 at alpha < 20). Artwork bbox/shape unchanged (IoU 1.0 vs originals).
  • frontend/src/styles.css: .sidebar-logo dropped its background: #fff white tile, border-radius, and object-fit: cover; the artwork now composites directly with contain. Dark-theme invert(1) kept (ink artwork must flip on dark sidebars).
  • Originals preserved: deferred/original-icons/icon-public-512-white.png, deferred/original-icons/logo-sidebar-338-white.png.

3. Build hygiene (uncommitted by design? no — landed with the fixes)

  • frontend/src/components/signer/SignerConnectionPanel.tsx was an untracked, non-compiling WIP (broken useCallback closures, APIs that don't exist on SignerManager, dependency on uninstalled react-router-dom) that blocked npm run typecheck. Moved intact to deferred/SignerConnectionPanel.tsx.wip (untracked) — nothing deleted; it needs a rewrite against the real hooks before returning.

Verification (all run this session)

  • Rust: cargo fmt --check clean, cargo clippy --all-targets clean, cargo test green, cargo build --release succeeded.
  • Frontend: npm run electron:build, npm run typecheck, npm run lint clean; npm test 116/116 passed; npx prettier --check electron/main.ts and src/styles.css clean; npm run build succeeded. (5 pre-existing Prettier warnings in untouched files — ExportSecretKeyModal.tsx, SignerModeScreen.tsx, AppProvider.tsx, ExportSecretKey.test.tsx, fakeBackend.ts — predate this session and were left alone.)
  • On-device (Hyprland/Wayland, this machine): app launched under a clean systemd user scope: Keynctr window mapped (class: keynectr), watchdog marker cleared (= config proven), no new Electron core dumps after 19:40 while multiple software-render launches ran. grim screenshot + visual inspection confirmed the sidebar bird sits directly on the sidebar with no white tile, border, or halo.
  • Icon proof: checkerboard composite of the new public/icon.png shows clean anti-aliased edges into transparency, no white fringe.

How to run / reproduce

  • GUI: cd frontend && npm start (or the packaged AppImage/deb once rebuilt via npm run dist).
  • Escape hatches: KEYNCTR_FORCE_X11=1, KEYNCTR_FORCE_WAYLAND=1, KEYNCTR_ENABLE_GPU=1, KEYNCTR_DISABLE_GPU=1, KEYNCTR_NO_RELAUNCH=1.
  • CLI: cargo run --release -- serve (JSON-lines IPC) as before.

Outstanding / next steps

  • Repackage for the friend: npm run dist (AppImage + deb) with the new icon and display fixes; the old frontend/release/ artifacts predate both commits.
  • deferred/SignerConnectionPanel.tsx.wip: rewrite against the real useSignerManager API (+ either add react-router-dom or drop the import) before reinstating.
  • Consider a taskbar-visible test on a pure-X11 session and on GNOME Wayland for the friend matrix (only Hyprland/Wayland was verifiable here).
  • Step 3 sub-step 2 (IPC reroute) is untouched and remains the next signer milestone.
  • The user's crash-reporter still holds old core dumps from pre-fix launches (coredumpctl rm clears them).