Launcher2 — Tray Widget & Background Mode¶
Spec pro fázi L2.9 — změna chování launcher2 tak, aby aplikace běžela primárně na pozadí a hlavní okno bylo jen jedna z forem zobrazení. Křížek hl. okna už neodhlašuje, místo toho zůstává session + sync agent + chat aktivní v tray. K nim přibývá mini widget — kompaktní okno vpravo dole nad taskbarem.
Status: draft (2026-05-13)
1. Cíle¶
- Mini widget v pravém dolním rohu se souhrnným stavem záloh + chat notifikacemi + tlačítkem pro otevření hlavní app. Otevírá se z tray ikonky a sám se objevuje při klíčových událostech.
- Křížek (X) na hlavním okně schová okno do tray, nikoli odhlášení. Session, sync agent, chat běží dál.
- Auto-login při startu Windows rozhoduje co zobrazit podle toho, v jakém stavu byla aplikace před vypnutím PC.
2. Komponenty (high-level)¶
┌─────────────────────────────────────┐
│ customtkinter root (skrytý CTk()) │
└────────────────┬────────────────────┘
│
┌─────────────────────────┼──────────────────────┐
▼ ▼ ▼
Hlavní okno Mini widget Chat okna
(MainWindow, (TrayWidget, (ChatWindow,
CTkToplevel) CTkToplevel, plovoucí)
borderless)
▲
│ otevřít / skrýt
┌─────┴─────┐
│ Tray ikona│ ◄── pystray (existující)
└───────────┘
Všechno v jednom procesu (potvrzeno). Hlavní okno je další toplevel vedle widgetu, ne root.
3. Mini widget¶
3.1 Vzhled a pozice¶
- Borderless CTkToplevel (
overrideredirect(True)), bez systémového titulu i křížku. - Pevná pozice: vpravo dole, kotvení k pravému dolnímu rohu pracovní plochy (mimo taskbar). Offset 12 px od kraje obrazovky.
- Šířka: 360 px. Výška dynamická podle obsahu (max 480 px, jinak scrollable).
- Always-on-top (
-topmost,True). - Skrývání: klik mimo widget (focus-out event) →
withdraw(). Při auto-pop-up (viz 3.4) navíc timer na 20 s →withdraw(). - Žádný křížek na widgetu (borderless), ukončení procesu jen přes tray menu → "Ukončit".
- Multi-monitor: widget se kotví k monitoru, na kterém je kurzor / aktivní okno v čase zobrazení.
3.2 Obsah widgetu¶
Zhora dolů:
┌─────────────────────────────────────────────┐
│ AVAX [☰ menu] │ ← hlavička, jméno user
│ Firma: AVAXIS.CZ [👤 přepnout] │ ← jen pokud má 2+ firem
├─────────────────────────────────────────────┤
│ Zálohy (souhrnný progress) │
│ ████████████░░░░░░ 47 % 1 240/2 632 │ ← jen pokud sync běží
│ Příští plánovaná: 19:42 (za 12 min) │
│ │
│ [🔴 D:\Faktury — selhalo 14:22 (3 chyby)] │ ← jen složky s chybou
│ [🔴 E:\Sklad — selhalo 14:23] │ ← klik = otevře detail
├─────────────────────────────────────────────┤
│ Sekce 'Firma 2' (DALŠÍ FIRMA, s.r.o.) │ ← jen multi-firma
│ ... totéž ... │
├─────────────────────────────────────────────┤
│ 💬 Chat (3 nepřečtené) │
│ • Petr Novák — 2 zprávy │ ← klik = otevře ChatWindow
│ • support — 1 zpráva │ na konkrétní konverzaci
├─────────────────────────────────────────────┤
│ [Synchronizovat] [AVAX aplikace] │
└─────────────────────────────────────────────┘
Pravidla zobrazení¶
| Sekce | Kdy zobrazit |
|---|---|
| Progress bar souhrnný | Pouze pokud sync právě běží |
| "Příští plánovaná" | Vždy (formát relativní + absolutní) |
| Řádky složek s chybou | Pouze složky se stavem failed (nejnovější selhání). OK složky se nezobrazují. |
| Sekce per firma | Vždy, pokud má uživatel 2+ firem. Při 1 firmě bez sekce. |
| Chat sekce | Pouze pokud má aspoň 1 nepřečtenou zprávu. |
[Synchronizovat] |
Schovaný/disabled během běžící synchronizace, jinak vždy. |
[AVAX aplikace] |
Vždy. |
3.3 Akce¶
[Synchronizovat]— spustí okamžitou synchronizaci VŠECH sync složek aktivní firmy (volá existující sync agent, ignoruje plánovaný interval, resetuje plánovač).[AVAX aplikace]— otevře hlavní okno. PokudMainWindowinstance existuje a jewithdrawn→deiconify()+lift(). Pokud neexistuje (ještě nebyl login) → spustí přihlašovací flow (otevřeLoginScreen).- Klik na řádek chyby — otevře hl. okno přepnuté na obrazovku Zálohy s detailem dané složky a chybovým logem.
- Klik na řádek chatu — otevře samostatné plovoucí
ChatWindow(jak funguje dnes v v0.3.9) na konkrétní konverzaci. Hlavní okno se NEOTEVÍRÁ. [👤 přepnout]— popup s výběrem firmy. Změna se promítne do hl. app i widgetu (sdílený company context).[☰ menu]— to samé co pravoklik na tray ikoně (viz 6).
3.4 Auto-pop-up scénáře¶
Widget se sám zobrazí v těchto případech (jinak je skrytý a otevírá se jen přes tray):
- Auto-login při startu Windows (viz 5). Zůstává 20 s, pak
withdraw(). - Začátek "velké zálohy" — záloha s >= 50 souborů nebo total size
= 100 MB v jedné operaci. Malé sync změny (1-2 soubory) nepop-upují. Konstanty
WIDGET_AUTO_POPUP_FILES = 50,WIDGET_AUTO_POPUP_BYTES = 100*1024*1024. - Dokončení zálohy s chybou — pop-up na 20 s.
- Nová příchozí chat zpráva — pop-up na 6 s (toast-like). Pokud
uživatel během toho zaktivuje widget kurzorem (
<Enter>), timer se zruší dokud kurzor neopustí widget (focus-out).
Pop-up logika nesmí překrývat: pokud widget už je viditelný, novou událost jen aktualizuje obsah a zresetuje 20 s timer (vyjma chat toast 6 s který nesnižuje delší timer).
4. Křížek (X) hlavního okna¶
Dnes: X = process exit (ukončení).
Nově: X = withdraw() hlavního okna. Proces dál běží:
- sync agent jede dál (plánované zálohy + watcher)
- chat WebSocket zůstává připojený
- tray ikona zůstává
- session token v paměti (auto-login disk state nezměněn)
Hlavní okno lze znovu otevřít: - klikem na tray ikonu (jednoduchý LMB) — pravoklik = tray menu - tlačítkem AVAX aplikace v mini widgetu
Ukončit aplikaci úplně lze jen: - tray menu → Ukončit (sync agent se zastaví, chat WS odpojí, proces končí) - z hl. okna menu → Soubor → Ukončit (totožné chování)
4.1 Stav last_main_window_visible¶
Při každé změně viditelnosti hlavního okna se zapíše do
settings.json:
Triggery:
- MainWindow.deiconify() / <Map> event → true
- MainWindow.withdraw() / <Unmap> event (i přes X i přes minimalizaci) → false
- Při Ukončit se stav NEAKTUALIZUJE (zachová poslední živý stav, aby
start po vypnutí věděl jak to bylo)
Důležité: minimalizace do taskbaru (iconify()) se počítá jako
neviditelné (false). Tj. pokud uživatel minimalizuje a vypne PC,
příští start zobrazí widget, ne hl. okno.
4.2 Logout¶
- Tray menu: Odhlásit (vedle Ukončit)
- Hl. okno menu: Soubor → Odhlásit (existující, zachovat)
- Akce: vyčistit session token (paměť + perzistentní store pokud byl
remember_password=False), odpojit WS, zastavit sync agent, schovat widget, otevřítLoginScreen(proces běží dál ale ve stavu "nepřihlášený launcher").
5. Auto-login flow při startu Windows¶
Předpoklad: registry entry HKCU \…\Run\AVAX spustí
pythonw.exe main.py (bez konzole — opraveno 2026-05-13).
[Start Windows]
│
▼
main.py
│
├── Načti settings.json
│
├── auto_login_enabled? ──── NE ──► LoginScreen (klasický flow)
│ │
│ ANO
│ │
│ ▼
│ Proveď silent login (existující flow z v0.3.1)
│ │
│ ├── selhání → LoginScreen (klasický flow)
│ │
│ ▼ úspěch
│
▼
Načti last_main_window_visible
│
├── true ──► Otevři MainWindow (normal), widget skrytý
│
└── false ──► MainWindow zůstává withdrawn,
zobraz Mini widget pop-up
po 20 s widget.withdraw()
5.1 Detail "20 s pak schovat"¶
- Widget se zobrazí (
deiconify()) s plným obsahem. widget.after(20_000, widget.withdraw)naplánovaný timer.- Pokud uživatel během 20 s klikne na widget / hover nad ním → timer se zruší a widget zůstává dokud uživatel nekliknul mimo.
- Pokud uživatel klikne mimo widget před uplynutím 20 s → standardní
focus-out →
withdraw()(timer se přeskočí).
6. Tray menu¶
Pravoklik na tray ikoně:
┌──────────────────────────────┐
│ Otevřít AVAX │ ← LMB klik = stejné jako "Otevřít AVAX"
│ Zobrazit widget │
│ ─────────────────────────── │
│ Synchronizovat teď │
│ ─────────────────────────── │
│ Nastavení │
│ Odhlásit │
│ Ukončit │
└──────────────────────────────┘
LMB klik na tray = Zobrazit widget (otevírá widget, ne hl. okno). Dvojklik (volitelně) = Otevřít AVAX hl. okno.
7. Perzistence stavu¶
Klíče v settings.json (rozšíření existujícího):
| Klíč | Typ | Default | Význam |
|---|---|---|---|
last_main_window_visible |
bool | false |
Stav hl. okna při posledním změně viditelnosti |
last_main_window_visible_ts |
str (ISO) | null |
Časová značka poslední změny |
widget_enabled |
bool | true |
Zda widget vůbec používat (master switch v Nastavení) |
widget_auto_popup_files |
int | 50 |
Práh pro auto-popup při velké záloze |
widget_auto_popup_bytes |
int | 104857600 |
Práh velikosti (B) pro auto-popup |
widget_startup_visible_seconds |
int | 20 |
Timeout pro auto-login pop-up |
8. Změny v existujícím kódu (hrubě)¶
| Soubor / komponenta | Změna |
|---|---|
desktop/launcher2/main.py |
Nový boot flow (viz 5), instanciace TrayWidget před MainWindow |
desktop/launcher2/screens.py (MainWindow) |
protocol("WM_DELETE_WINDOW") → withdraw() místo destroy(). <Map>/<Unmap> handlery pro last_main_window_visible. |
desktop/launcher2/settings.py |
Nové klíče v DEFAULTS, gettery/settery |
desktop/launcher2/tray.py (existující) |
LMB klik → otevři widget. Rozšíření menu (Odhlásit, Zobrazit widget). |
desktop/launcher2/tray_widget.py (nové) |
TrayWidget(ctk.CTkToplevel) — borderless, position bottom-right, sekce per firma, chat sekce, auto-popup logika, focus-out hide. |
desktop/launcher2/sync_agent.py |
Při startu / dokončení / chybě zálohy publikovat event do widgetu (existující event bus nebo přímé widget.on_sync_event(...)). Detekce "velké zálohy" pro auto-popup. |
desktop/launcher2/chat.py (existující) |
Příchozí zpráva → notifikace do widgetu (počet nepřečtených per peer). |
9. Otevřené body / pozdější iterace¶
- Multi-monitor preferovaný monitor: aktuálně "kde je kurzor". Možno rozšířit o per-monitor pinning v nastavení.
- Drag-to-move widget: zatím pevná pozice, později volitelně.
- Notifikace Windows native (
win10toast/winrt) jako alternativa / doplněk widgetu pro toast chování — zvážit po prvním uživatelském testu. - Snooze sync v widgetu (odložit příští zálohu o 10/30 min) — nice-to-have.
10. Akceptační kritéria¶
- Po
pip install + python main.pyaplikace běží, tray ikona viditelná, hlavní okno otevřené (po login). - Křížek hl. okna → okno zmizí, tray ikona zůstane, sync agent loguje plánované zálohy, chat zprávy přicházejí.
- LMB klik na tray → mini widget se otevře vpravo dole, zobrazuje plánovanou zálohu (čas) a "AVAX aplikace" tlačítko.
- Klik mimo widget → widget zmizí (tray zůstává).
- Spuštění sync (manuálně i plánovaně) s 60+ soubory → widget se objeví s progress barem, zmizí po dokončení (s 20s pop-up oknem ak selhalo).
- Příchozí chat zpráva, když je hl. okno zavřené → widget toastem 6 s. Klik na řádek → ChatWindow té konverzace.
- Restart PC s
auto_login_enabled=True+ zavřeným hl. oknem před shutdown → po loginu se objeví widget na 20 s, pak zmizí. - Restart PC se stejným nastavením, ale s viditelným hl. oknem před shutdown → po loginu se otevře hl. okno (widget se neukáže).
- Tray menu → Odhlásit → widget zmizí, otevře se LoginScreen.
- Tray menu → Ukončit → proces končí, tray ikona zmizí.