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>
16 KiB
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)withbox-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 callsGtk.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 nowbutton.fuji-swatch.swatch-{key}withbackground-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(removedbox-shadow: inset 0 0 0 2px @window_bg_colorwhich was undefined in native mode). _set_gsettings(): writesTANINUX-dark/TANINUXas the GTK3 theme (for greetd/GTK3 apps). Also setscolor-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 callsnative.set_dark(root, …)live so the theme switches immediately without restart.
src/taninux/gui/console.py — complete rewrite
LogConsoleis now a plain Python class (NO GTK widget). Writes to~/.local/share/taninux/taninux.logwith timestamps.RunnerCard: button only (no embedded log widget). Process output goes to_write_log().log_expander()andcollapsible_log(): kept as no-ops returningNone— 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→dependsintaninux/PKGBUILD. - wlsunset, cups moved from
optdepends→dependsintanin-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-darkgsettings (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:
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-execadded to AUR rebuild list.parubuilt into repo but stays optdepend.papirus-foldersremoved.
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 suspendin the 2026-06-23 session — that's not a standing permission. write_global_theme()writes~/.config/gtk-4.0/libadwaita.csswhich is read by ALL GTK4 apps including eww (USER priority 800 — above GTK theme). It only defines@define-colorvars 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-colorlimit).
Open / not done
- Permanent fix for awww black screen after suspend (see above).
- Rebuild + publish
[tanin]repo (needsTANIN_PKG_TOKENenv). - Rebuild ISO.
camel.tomlrebuilt_aurlist is stale (missing librewolf-bin, arch-update, timeshift-autosnap, xdg-terminal-exec).- Manual icon picker in
desktop.pystill lists all 5Tanin-*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'slib/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-utilswas 4 names (CACHE_DIR,MUSIC_DIR,session,send_toast) — all provided bystate.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 callsset_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— nativeui.DialogPKCE login (tidalapi imported lazily inside).window.py— shell: sidebar (nav) +Gtk.Stackcontent + transport bar.tn-dark.app.py— entrytmusic. 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_compileclean. Backendstate/discord_rpcimport without tidalapi/Adw. - All UI+app modules import without tidalapi (lazy imports hold).
NOT verified (needs real env)
- End-to-end playback:
tidalapiis NOT installed here, and there's no TIDAL account/display in the sandbox. To run:pacman -S python-tidalapi(orpip install tidalapi), thenPYTHONPATH=src python -m taninux.music(ortmusicafter 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 smoothlink.tidal.com/XXXXXflow) logs in and can BROWSE, but cannot stream — playback hits429 Too Many Requestsonplaybackinfopostpaywall. 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).LoginDialogauto-opens the browser, shows a clickable link, AND watches the clipboard (Gdk.Clipboard.read_text_asyncpoll) so pasting is automatic. auth.try_restore()discards any stored NON-pkce token (is-pkceflag 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 pullsexplore()+for_you().ui/images.py— async cover loader, disk cache understate.IMG_DIR, setsGdk.TextureonGtk.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;TransportBarat 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 viastate.IMG_DIR. Verified on bus asorg.mpris.MediaPlayer2.ch.gabrielevarano.Taninux.Music.app.py— entrytmusic. 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_rpcis 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-navlook (glassy accent wash + a frost gradient onwindow.tn-dark .tn-sidebar). - Search is debounced via SearchEntry's
search-changed; could add a small delay/throttle.