30899de5a3
Bundles the pending native-UI session (dark shell, style overhaul, audio page) with review fixes: - native Row: set_subtitle_lines + set_activatable_widget (store and online-accounts pages crashed with AttributeError under TANINUX_UI=native) - wine page routed through the ui seam (last direct Adw import removed) - console: delete no-op log_expander/collapsible_log and .clear() call sites - ui/__init__ docstring now states the actual default backend (native) - tests/test_pages_native.py builds every registered page under the native backend (29 pass; skips headless) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
294 lines
16 KiB
Markdown
294 lines
16 KiB
Markdown
# TANINUX — Handover (Session 2026-06-23)
|
|
|
|
Context for a fresh instance. Repo: `~/projects/taninux` (distro codename **camel**,
|
|
niri-only GTK4/libadwaita + eww desktop). User: Karim (German; iterates visually;
|
|
hates UI jank; blanket perms in `~/projects`). UI strings are **English**.
|
|
|
|
---
|
|
|
|
## THIS SESSION (2026-06-23) — Native GTK4 shell, CSS overhaul
|
|
|
|
`TANINUX_UI=native` means the app uses `src/taninux/gui/ui/native.py` widgets and
|
|
loads `src/taninux/gui/ui/style_apple.css` instead of libadwaita. This session
|
|
fixed the native theme comprehensively.
|
|
|
|
### What was done
|
|
|
|
**`src/taninux/gui/ui/style_apple.css`**
|
|
- All hardcoded blue accents (`#007aff`, `#0a84ff`, `rgba(0,122,255…)`) replaced
|
|
with `@accent_color` + `alpha(@accent_color, 0.10)`.
|
|
- Dark headerbar: `rgba(20,20,22,0.97)` with `box-shadow: none` (matches sidebar,
|
|
no GNOME "lighter band" effect).
|
|
- Light headerbar: `#ebebeb`. Light sidebar: `#ebebeb`. Light page bg: `#f6f6f6`
|
|
(matches libadwaita white).
|
|
- Explicit light-mode text colors (`color: #1d1d1f`) for all nav rows, nav header,
|
|
group titles — because GTK4 Adwaita can leave text light-colored in light mode.
|
|
- `button.suggested-action` → `background-color: @accent_color !important` (was blue).
|
|
- `progress, trough > progress` → `background-color: @accent_color !important`.
|
|
- Nav selection dark: glass wash `alpha(@accent_color, 0.18)` + border.
|
|
|
|
**`src/taninux/gui/style.py`**
|
|
- `apply()` (native backend): now calls
|
|
`Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", dark)`
|
|
so GTK4 Adwaita widgets (buttons, sliders) follow the chosen scheme.
|
|
- `_build_css()` swatch buttons: selector is now `button.fuji-swatch.swatch-{key}`
|
|
with `background-color: {hex} !important; background: {hex} !important;` — fixes
|
|
swatches appearing black (Adwaita overrode them without !important).
|
|
- Swatch checked state: uses `outline: 2px solid {hex}; outline-offset: 3px`
|
|
(removed `box-shadow: inset 0 0 0 2px @window_bg_color` which was undefined in
|
|
native mode).
|
|
- `_set_gsettings()`: writes `TANINUX-dark`/`TANINUX` as the GTK3 theme
|
|
(for greetd/GTK3 apps). Also sets `color-scheme=prefer-dark/prefer-light`.
|
|
⚠ This is SYSTEM-WIDE — affects eww and all other GTK4 apps.
|
|
|
|
**`src/taninux/gui/pages/settings.py`**
|
|
- `persist()`: after saving the color scheme, now also calls `native.set_dark(root, …)`
|
|
live so the theme switches immediately without restart.
|
|
|
|
**`src/taninux/gui/console.py`** — complete rewrite
|
|
- `LogConsole` is now a plain Python class (NO GTK widget). Writes to
|
|
`~/.local/share/taninux/taninux.log` with timestamps.
|
|
- `RunnerCard`: button only (no embedded log widget). Process output goes to
|
|
`_write_log()`.
|
|
- `log_expander()` and `collapsible_log()`: kept as no-ops returning `None` —
|
|
all callers already cleaned up.
|
|
|
|
**Log expanders removed from all settings pages:**
|
|
`backup.py`, `update.py`, `kernel.py`, `maintain.py`, `network.py`,
|
|
`bluetooth.py`, `shares.py`, `bundles.py`, `appdetail.py`, `store.py`,
|
|
`printers.py` — all formerly called `log_expander(…)` which is now a no-op;
|
|
the UI no longer has a "Log" section on any page.
|
|
|
|
**PKGBUILDs** (`packaging/taninux/PKGBUILD`, `packaging/tanin-desktop/PKGBUILD`)
|
|
- networkmanager, bluez-utils, wireplumber, brightnessctl, wlsunset, cups,
|
|
pacman-contrib moved from `optdepends` → `depends` in `taninux/PKGBUILD`.
|
|
- wlsunset, cups moved from `optdepends` → `depends` in `tanin-desktop/PKGBUILD`.
|
|
- paru, flatpak, timeshift remain optdepends.
|
|
|
|
### Files touched this session
|
|
```
|
|
src/taninux/gui/ui/style_apple.css
|
|
src/taninux/gui/style.py
|
|
src/taninux/gui/pages/settings.py
|
|
src/taninux/gui/console.py
|
|
src/taninux/gui/pages/{backup,update,kernel,maintain,network,bluetooth,shares,
|
|
bundles,appdetail,store,printers}.py
|
|
packaging/taninux/PKGBUILD
|
|
packaging/tanin-desktop/PKGBUILD
|
|
```
|
|
|
|
### Current state
|
|
| Thing | State |
|
|
|---|---|
|
|
| Native CSS theme (dark + light) | ✅ fixed, live |
|
|
| Accent colors correct (`@accent_color`) | ✅ |
|
|
| Live dark/light switch in Settings | ✅ |
|
|
| Log expanders removed everywhere | ✅ |
|
|
| PKGBUILDs updated | ✅ source only |
|
|
| taninux package rebuilt | ❌ needs `./packaging/finish-tanin-repo.sh` |
|
|
| tanin-icons built+committed (c5b0848) | ✅ from previous session |
|
|
| `[tanin]` repo published | ❌ blocked on `TANIN_PKG_TOKEN` |
|
|
|
|
---
|
|
|
|
## THE CRITICAL BUG THIS SESSION — `awww-daemon` black screen
|
|
|
|
**Symptom:** Entire desktop goes black (no wallpaper, no windows visible).
|
|
Opening any eww panel made it worse (visible as black backdrop).
|
|
|
|
**Root cause:** `awww` (the blurred-wallpaper daemon used for niri's Overview
|
|
backdrop) creates a surface at the Wayland **Background layer**. After suspend/
|
|
resume, awww's cached blurred image goes stale or the surface loses its content
|
|
→ the surface renders as **solid black**. Because awww-daemon sits ABOVE swaybg
|
|
in the Background layer stack, it covers the wallpaper entirely.
|
|
|
|
The transparent panel-backdrop was fine — what appeared "black" was the awww
|
|
surface showing through the transparent backdrop, covering the wallpaper behind it.
|
|
|
|
**Fix (immediate):** `pkill awww`
|
|
- This kills the awww daemon, the black surface disappears from niri's Background
|
|
layer, swaybg's wallpaper shows through again.
|
|
|
|
**Permanent fix needed (TODO for next session):**
|
|
Option A — Regenerate blur cache on resume:
|
|
Add a systemd user sleep hook that calls `~/projects/eww/scripts/overview-backdrop.sh sync`
|
|
on `post-resume`. File: `~/.config/systemd/user/awww-refresh.service` + `.path`.
|
|
Option B — Make awww not start at login (only at Overview open):
|
|
Remove awww from `launch.sh`; call `overview-backdrop.sh start` only when niri's
|
|
Overview is about to open. niri has no "overview-opening" IPC event yet → tricky.
|
|
Option C — Restart awww after resume with a fresh cache:
|
|
In the sleep hook: `pkill awww; sleep 0.5; overview-backdrop.sh start`.
|
|
|
|
**Diagnosis dead-ends (don't re-investigate):**
|
|
- ❌ NOT caused by our `~/.config/gtk-4.0/libadwaita.css` (tested: removed → still black)
|
|
- ❌ NOT caused by `color-scheme=prefer-dark` gsettings (tested: default → still black)
|
|
- ❌ NOT caused by GSK renderer (`GSK_RENDERER=cairo` → still black)
|
|
- ✅ CONFIRMED: `pkill awww` → wallpaper returns instantly
|
|
|
|
---
|
|
|
|
## CURRENT SYSTEM STATE (after this session's debugging)
|
|
|
|
⚠ **gsettings were modified during debugging.** Restore if needed:
|
|
```bash
|
|
gsettings set org.gnome.desktop.interface color-scheme prefer-dark
|
|
gsettings set org.gnome.desktop.interface gtk-theme TANINUX-dark
|
|
```
|
|
The awww daemon was killed (`pkill awww`). The desktop now shows swaybg's
|
|
wallpaper correctly. Restarting eww (`~/projects/eww/launch.sh`) will restart
|
|
awww via launch.sh — which may make the black screen return. If it does: `pkill awww`.
|
|
|
|
---
|
|
|
|
## PREVIOUS SESSION (2026-06-22) — Icon theme
|
|
|
|
### A. New packages in the `[tanin]` repo
|
|
- `librewolf-bin`, `arch-update`, `timeshift` (+ `timeshift-autosnap`),
|
|
`xdg-terminal-exec` added to AUR rebuild list.
|
|
- `paru` built into repo but stays optdepend.
|
|
- `papirus-folders` removed.
|
|
|
|
### B. Icon theme: `tanin-icons`
|
|
Custom package `packaging/tanin-icons/` — 5 thin Adwaita-based themes (`Tanin-<accent>`)
|
|
that recolour only folder/place icons in Fuji accent hues. Built + committed as c5b0848.
|
|
Installed to `~/.local/share/icons/Tanin-*` (user dir, not pacman).
|
|
`style.py` → `_set_gsettings()` sets `icon-theme=Tanin-<accent_key>` on accent change.
|
|
`tanin-desktop/PKGBUILD`: depends `+tanin-icons`, removed `papirus-icon-theme`.
|
|
⚠ NOT yet in the published `[tanin]` repo (needs `TANIN_PKG_TOKEN`).
|
|
|
|
---
|
|
|
|
## Gotchas to respect
|
|
- **NEVER** run lock/swaylock/suspend commands to test (locked Karim out once).
|
|
Exception: Karim explicitly authorised `sudo systemctl suspend` in the 2026-06-23
|
|
session — that's not a standing permission.
|
|
- `write_global_theme()` writes `~/.config/gtk-4.0/libadwaita.css` which is read
|
|
by ALL GTK4 apps including eww (USER priority 800 — above GTK theme). It only
|
|
defines `@define-color` vars and button border-radius; it does NOT define
|
|
window backgrounds. The globals are safe.
|
|
- TANINUX tree has unrelated uncommitted files (tanin-calendar, online_accounts,
|
|
gtklock, iso/) — NOT from this session; do NOT `git add -A`.
|
|
- GTK3 can't exact-Fuji recolour at runtime (parse-time `@define-color` limit).
|
|
|
|
## Open / not done
|
|
- Permanent fix for awww black screen after suspend (see above).
|
|
- Rebuild + publish `[tanin]` repo (needs `TANIN_PKG_TOKEN` env).
|
|
- Rebuild ISO.
|
|
- `camel.toml` `rebuilt_aur` list is stale (missing librewolf-bin, arch-update,
|
|
timeshift-autosnap, xdg-terminal-exec).
|
|
- Manual icon picker in `desktop.py` still lists all 5 `Tanin-*` themes (minor).
|
|
|
|
---
|
|
|
|
## SESSION 2026-06-27 — TANINUX Music scaffold (native TIDAL client, "Weg 3")
|
|
|
|
New package `src/taninux/music/` — a native Apple-Music-style TIDAL client. Strategy:
|
|
**reuse High Tide's UI-free backend, build our own native shell** (no libadwaita fork).
|
|
High Tide's shell is built on heavy Adw layout widgets (NavigationView, OverlaySplitView,
|
|
MultiLayoutView, Breakpoints, Carousel) that have no plain-GTK4 equivalent and that our
|
|
`native.py` does NOT cover — so forking its UI was rejected. Its *backend* (~1650 LOC:
|
|
GStreamer player, tidalapi session, token store) is cleanly decoupled and worth reusing.
|
|
|
|
### Structure
|
|
- `backend/player.py` — copied verbatim from High Tide's `lib/player_object.py` (GPL-3.0),
|
|
except ONE edit: `from . import discord_rpc, utils` → `from . import discord_rpc` +
|
|
`from . import state as utils`. Player's only coupling to the old god-`utils` was 4 names
|
|
(`CACHE_DIR`, `MUSIC_DIR`, `session`, `send_toast`) — all provided by `state.py`.
|
|
- `backend/state.py` — slim shim providing those 4 names + `init()`/`set_session()`/`set_toast_sink()`.
|
|
- `backend/secret_store.py` — copied from High Tide (LGPL-3.0), schema/key renamed to TANINUX.
|
|
- `backend/auth.py` — extracted PKCE flow (was in an Adw.Dialog): `Auth.try_restore()` /
|
|
`pkce_login_url()` / `finish_pkce_login()` + `login_async()`.
|
|
- `backend/discord_rpc.py` — no-op stub (player calls `set_activity`/`disconnect`).
|
|
- `ui/transport.py` — bottom now-playing bar, binds to player GObject signals
|
|
(`song-changed`, `update-slider`, `notify::playing`, `can-go-*`). Async cover fetch.
|
|
- `ui/library.py` — favorites-tracks list; row-activate → `player.play_this(tracks, i)`.
|
|
- `ui/login_dialog.py` — native `ui.Dialog` PKCE login (tidalapi imported lazily inside).
|
|
- `window.py` — shell: sidebar (nav) + `Gtk.Stack` content + transport bar. `tn-dark`.
|
|
- `app.py` — entry `tmusic`. Lazy-imports player/auth (so app loads without tidalapi → shows
|
|
"backend missing" StatusPage). Flow: CSS → state.init → Auth → try_restore? → shell ±login.
|
|
- CSS appended to `gui/ui/style_apple.css` (section "TANINUX Music"): `.tn-transport`,
|
|
`.tn-content`, `.tn-track-list`, `.tn-music-sidebar`. Reuses existing `.tn-sidebar`.
|
|
- Entry point added to `pyproject.toml`: `tmusic = "taninux.music.app:main"`.
|
|
|
|
### Verified
|
|
- All new files `py_compile` clean. Backend `state`/`discord_rpc` import without tidalapi/Adw.
|
|
- All UI+app modules import without tidalapi (lazy imports hold).
|
|
|
|
### NOT verified (needs real env)
|
|
- End-to-end playback: `tidalapi` is NOT installed here, and there's no TIDAL account/display
|
|
in the sandbox. **To run:** `pacman -S python-tidalapi` (or `pip install tidalapi`), then
|
|
`PYTHONPATH=src python -m taninux.music` (or `tmusic` after install). Expect: login dialog →
|
|
paste redirect URL → library loads → click track → GStreamer plays → transport updates.
|
|
|
|
### Next building blocks (all go into the content `Gtk.Stack`)
|
|
- Home page (mixes/recommendations carousels) and Search page (currently placeholders).
|
|
- The "few missing native layout widgets" if richer nav is wanted: a NavigationStack
|
|
(push/pop with back button) and horizontal carousels. v1 deliberately uses a flat `Gtk.Stack`.
|
|
- Toast sink: `state.set_toast_sink(...)` is wired but app currently leaves the default (logs).
|
|
|
|
---
|
|
|
|
## SESSION 2026-06-27 (cont.) — TANINUX Music FULL build + playback verified
|
|
|
|
User gave full autonomy ("entwickle die app komplett aus"). The scaffold above is now a
|
|
working app. **Playback verified end-to-end** via a headless test (logged-in PKCE session →
|
|
favorite track → GStreamer pipeline reached PLAYING, position advanced to 8.2s).
|
|
|
|
### Login: must be PKCE, not device-flow (IMPORTANT)
|
|
- tidalapi has TWO clients. The OAuth **device-flow** (`session.login_oauth()`, the smooth
|
|
`link.tidal.com/XXXXX` flow) logs in and can BROWSE, but **cannot stream** — playback hits
|
|
`429 Too Many Requests` on `playbackinfopostpaywall`. tidalapi docstring: PKCE "is the only
|
|
way" to stream.
|
|
- So login uses **PKCE** (`auth.pkce_login_url()` → browser → copy the redirect/"Oops" URL →
|
|
`finish_pkce_login`). `LoginDialog` auto-opens the browser, shows a clickable link, AND
|
|
watches the clipboard (`Gdk.Clipboard.read_text_async` poll) so pasting is automatic.
|
|
- `auth.try_restore()` discards any stored NON-pkce token (`is-pkce` flag in keyring) to force
|
|
a PKCE re-login. Tokens persist in libsecret → normally no dialog on subsequent launches.
|
|
|
|
### Runtime deps installed this session (pacman)
|
|
- `python-tidalapi` (extra) — the API/player lib.
|
|
- `gst-libav` + `gst-plugins-bad` + `gst-plugins-ugly` — **required for audio**. Without them
|
|
GStreamer can't decode TIDAL's AAC/DASH streams ("No suitable plugins found", no sound).
|
|
Key elements now present: `avdec_aac`, `avdec_flac`, `faad`, `dashdemux2`.
|
|
- These are NOT expressible in pyproject; package them as distro deps for the [tanin] repo.
|
|
|
|
### Architecture (all under src/taninux/music/)
|
|
- `backend/api.py` — thin tidalapi data layer (UI-free): `home/explore/for_you_categories`,
|
|
`search`, favorites/playlists, `album_tracks`/`playlist_tracks`/`mix_items`/`artist_*`,
|
|
item classification (`item_kind/title/subtitle`), `IdCache`. NOTE: `session.home()` returns
|
|
400 sometimes → `home_categories()` falls back to legacy endpoint, and HomePage also pulls
|
|
`explore()` + `for_you()`.
|
|
- `ui/images.py` — async cover loader, disk cache under `state.IMG_DIR`, sets `Gdk.Texture`
|
|
on `Gtk.Picture`.
|
|
- `ui/nav.py` — `NavigationStack` (back button + title + content; set_root/push/pop). Replaces
|
|
Adw.NavigationView.
|
|
- `ui/context.py` — `MusicContext(session, player, nav, toast)`; `open_item(item)` classifies +
|
|
pushes the right detail page; `play/shuffle`. Passed to all pages/widgets (no globals).
|
|
- `ui/widgets.py` — `Card` (cover+title+sub, click→open_item), `Carousel` (titled h-scroll),
|
|
`TrackList` (row-activate → play).
|
|
- `ui/pages_base.py` — `LoadablePage` (spinner → thread fetch() → main-thread build()).
|
|
- `ui/pages_home.py`, `pages_search.py` (live clipboard-less SearchEntry → debounced search),
|
|
`pages_detail.py` (Album/Playlist/Mix share `_TrackDetailPage`; ArtistPage), `pages_collection.py`
|
|
(Bibliothek: playlists/albums/artists/tracks), `pages_queue.py`.
|
|
- `window.py` — shell: sidebar (Home/Suchen/Bibliothek) → `nav.set_root`; Gtk.Overlay for
|
|
toasts; `TransportBar` at bottom. `reload_after_login()`.
|
|
- `ui/transport.py` — now-playing bar: cover, prev/play/next, seek, shuffle/repeat/queue, volume.
|
|
- `backend/mpris.py` — ported from High Tide, DECOUPLED: `MPRIS(player, app_id, raise_cb, quit_cb)`,
|
|
cover path via `state.IMG_DIR`. Verified on bus as `org.mpris.MediaPlayer2.ch.gabrielevarano.Taninux.Music`.
|
|
- `app.py` — entry `tmusic`. CSS → state.init → Auth → try_restore? → shell ±PKCE login; wires
|
|
toast sink + starts MPRIS (both non-fatal).
|
|
- CSS: "TANINUX Music" section in `gui/ui/style_apple.css` (cards/covers/carousel/transport/
|
|
toast/nav + title-1/title-3/dim-label/error fallbacks).
|
|
|
|
### Known minor issues / TODO
|
|
- Cosmetic GTK warning "GtkGizmo (slider) reported min width -4" from a `Gtk.Scale` — harmless.
|
|
- Per-section nav stacks are not preserved: switching sidebar resets that section to its root
|
|
(Apple keeps a stack per tab). Acceptable for v1.
|
|
- Queue page is read-only (no reorder/remove). No lyrics page. `discord_rpc` is a no-op stub.
|
|
- DONE (2026-06-27 cont.): track→album/artist navigation — TrackRow has a "⋯" MenuButton +
|
|
right-click popover (Zum Album / Zum Interpreten / Als Nächstes / Zur Warteschlange); the
|
|
transport bar's title→album and artist→artist are clickable (`.tn-link`). Carousel cards
|
|
already opened detail pages. Sidebar switched to the Settings `.tn-nav` look (glassy accent
|
|
wash + a frost gradient on `window.tn-dark .tn-sidebar`).
|
|
- Search is debounced via SearchEntry's `search-changed`; could add a small delay/throttle.
|