# 020 — Architecture

Status: approved 2026-09-30.

## 1. Layers

```
┌──────────────────────── skin.milionar (Kodi skin addon) ────────────────────────┐
│ Estuary 21.3 fork: every window 1:1, plus                                       │
│  Home.xml ─ Milionář menu items + widget groups (050)                           │
│  Custom_1108_MilionarSettingsMenu.xml ─ settings menu (050 §4)                  │
│  Custom_1120_MilionarSearch.xml ─ search results window (070)                   │
│  DialogVideoInfo.xml ─ one extra InfoDialogButton (080)                         │
│  Custom_1121_MilionarActions.xml ─ library action menu (080 §2)                 │
│  Custom_1122_MilionarGame.xml ─ game topic picker (050 §4a)                     │
│  SkinSettings.xml ─ "Milionář" section (050 §5)                                 │
└───────────────┬──────────────────────────────────────────────────┬──────────────┘
                │ <content>plugin://…?action=widget_*</content>     │ RunPlugin(…action=…)
                ▼                                                   ▼
┌──────────────────────── plugin.video.milionar ──────────────────────────────────┐
│ main.py fast path → wcs/skin/widgets.py (cached listings, 060)                  │
│ router.py new actions: skin_search, skin_browse, skin_game, skin_switch, lib_*  │
│ context.py (kodi.context.item, 080)                                             │
│ service.py → wcs/skin/installer.py (install/update/offer switch, 040)           │
│            → widget cache refresher + Home property refresh token (060 §4)      │
│ resources/skin.milionar/  ← the skin source, shipped in the addon ZIP           │
└─────────────────────────────────────────────────────────────────────────────────┘
```

Rules:

- The skin never reads addon files directly; it only uses `plugin://` content,
  `RunPlugin`/`RunScript` actions, and `Window(Home).Property(milionar.*)`
  values set by the addon.
- Every Milionář element in the skin is guarded by
  `System.AddonIsEnabled(plugin.video.milionar)`; without the addon the skin is
  plain Estuary with the Milionář logo.
- The addon must keep working in any skin; skin-specific addon code lives in
  `wcs/skin/` and is only reached through new actions.

## 2. File layout

```
plugin.video.milionar/
  resources/skin.milionar/          skin source (full Estuary fork, loose PNGs)
    addon.xml  LICENSE.txt  changelog.txt  colors/ extras/ fonts/ language/
    media/  playlists/  resources/  themes/  xml/
  wcs/skin/__init__.py
  wcs/skin/installer.py             040
  wcs/skin/widgets.py               060 (imports nothing heavy at module level)
  wcs/skin/search.py                070
  wcs/skin/library_actions.py       080
  wcs/skin/game.py                  050 §4a
  wcs/ai/RecommendationHistory.py   060 §6
  context.py                        080 (kodi.context.item entry)
  scripts/skin_harness/             100
  docs/plan/skin-milionar/          this plan
```

Installed copy: `special://home/addons/skin.milionar/`. In development it is
a **symlink** to `plugin.video.milionar/resources/skin.milionar` so edits are
live after `ReloadSkin()` (040 §5).

## 3. Why the skin must be a separate addon

Kodi treats the first extension point as the addon type; a skin is loaded
from `special://home/addons/<id>/` only, never from inside another addon.
The addon therefore carries the skin as data and installs it (040).

## 4. What the addon provides to the skin

| Contract | Direction | Chapter |
|----------|-----------|---------|
| `plugin://plugin.video.milionar/?action=widget_<name>&reload=<token>` listings | skin → addon | 060 |
| `Window(Home).Property(milionar.widgets.token)` refresh token | addon → skin | 060 §4 |
| `Window(Home).Property(milionar.search.query)` + `…search.token` | addon → skin | 070 |
| `Window(Home).Property(milionar.provider.kraska)`, `…ai`, `…webshare` (`1` when configured) | addon → skin | 050 §3 |
| `RunPlugin(plugin://plugin.video.milionar/?action=…)` actions | skin → addon | 050, 070, 080 |

Property names and action names are fixed here; renaming one means updating
this table and both sides.
