avios-cli 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {avios_cli-0.2.0 → avios_cli-0.3.0}/CHANGELOG.md +36 -1
- {avios_cli-0.2.0 → avios_cli-0.3.0}/PKG-INFO +34 -17
- {avios_cli-0.2.0 → avios_cli-0.3.0}/README.md +33 -16
- {avios_cli-0.2.0 → avios_cli-0.3.0}/SECURITY.md +9 -10
- avios_cli-0.3.0/docs/dashboard.svg +190 -0
- avios_cli-0.3.0/scripts/screenshot.py +84 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/__init__.py +1 -1
- avios_cli-0.3.0/src/avios/accounts.py +174 -0
- avios_cli-0.3.0/src/avios/aggregate.py +118 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/auth.py +37 -35
- avios_cli-0.3.0/src/avios/cli.py +420 -0
- avios_cli-0.3.0/src/avios/finnair.py +236 -0
- avios_cli-0.3.0/src/avios/programmes.py +66 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/session.py +37 -10
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/tui/app.py +32 -33
- avios_cli-0.3.0/src/avios/tui/widgets.py +51 -0
- avios_cli-0.3.0/tests/test_accounts.py +136 -0
- avios_cli-0.3.0/tests/test_aggregate.py +140 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/test_auth.py +22 -24
- avios_cli-0.3.0/tests/test_cli.py +409 -0
- avios_cli-0.3.0/tests/test_finnair.py +220 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/test_session.py +29 -0
- avios_cli-0.3.0/tests/test_tui.py +137 -0
- avios_cli-0.2.0/docs/dashboard.svg +0 -190
- avios_cli-0.2.0/scripts/screenshot.py +0 -52
- avios_cli-0.2.0/src/avios/cli.py +0 -263
- avios_cli-0.2.0/src/avios/tui/widgets.py +0 -30
- avios_cli-0.2.0/tests/test_cli.py +0 -197
- avios_cli-0.2.0/tests/test_tui.py +0 -88
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.editorconfig +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.github/dependabot.yml +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.gitignore +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.pre-commit-config.yaml +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/.python-version +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/CONTRIBUTING.md +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/LICENSE +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/pyproject.toml +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/client.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/config.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/endpoints.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/models.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/tui/__init__.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/tui/art.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/src/avios/tui/styles.tcss +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/conftest.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/fixtures/balance.json +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/fixtures/transactions.json +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/test_client.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/tests/test_models.py +0 -0
- {avios_cli-0.2.0 → avios_cli-0.3.0}/uv.lock +0 -0
|
@@ -6,6 +6,39 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-07-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Multiple accounts, one per programme.** Log into several Avios programmes and
|
|
13
|
+
keep their sessions side by side: `avios login [programme]` (default `ba`),
|
|
14
|
+
`avios logout [programme]` (or `avios logout` for all), and a new `avios accounts`
|
|
15
|
+
roster showing each balance and status. Programmes: British Airways (`ba`),
|
|
16
|
+
Iberia (`iberia`), Aer Lingus (`aerlingus`), Finnair Plus (`finnair`).
|
|
17
|
+
- **Combined views.** `balance` sums every account into a combined total (a single
|
|
18
|
+
account keeps the individual/household detail); `transactions`/`pending` merge
|
|
19
|
+
across accounts newest-first with a Programme column. `--account/-a` filters any
|
|
20
|
+
data command to one programme.
|
|
21
|
+
- **Multi-account TUI.** `avios tui` shows a combined balance header (per-programme
|
|
22
|
+
breakdown) and a Programme column, loading every account concurrently.
|
|
23
|
+
- **Finnair Plus** via Finnair's CAS/OAuth browser login + loyalty API (balance,
|
|
24
|
+
profile, transactions); token-backed account storage alongside cookie accounts.
|
|
25
|
+
- Programme-aware browser login (per-programme login URL + isolated Chrome profile)
|
|
26
|
+
and a shared, backend-aware aggregation layer (`aggregate.py`) used by the CLI
|
|
27
|
+
and TUI. Per-account failures are captured, so one expired session never hides
|
|
28
|
+
the others.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- Sessions are now stored per programme at `~/.config/avios/accounts/<programme>.json`
|
|
32
|
+
(mode `600`); the old single `state.json` is migrated to `accounts/ba.json` on
|
|
33
|
+
first run.
|
|
34
|
+
|
|
35
|
+
## [0.2.1] - 2026-07-27
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- An expired session made avios.com hang the request, surfacing as a confusing
|
|
39
|
+
"read operation timed out". Request timeouts are now treated as an expired
|
|
40
|
+
session with a clear "run `avios login` again" message.
|
|
41
|
+
|
|
9
42
|
## [0.2.0] - 2026-07-24
|
|
10
43
|
|
|
11
44
|
### Added
|
|
@@ -120,7 +153,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
120
153
|
- Reward-flight **availability** search is not yet implemented (needs a British
|
|
121
154
|
Airways capture).
|
|
122
155
|
|
|
123
|
-
[Unreleased]: https://github.com/alexechoi/avios-cli/compare/v0.
|
|
156
|
+
[Unreleased]: https://github.com/alexechoi/avios-cli/compare/v0.3.0...HEAD
|
|
157
|
+
[0.3.0]: https://github.com/alexechoi/avios-cli/compare/v0.2.1...v0.3.0
|
|
158
|
+
[0.2.1]: https://github.com/alexechoi/avios-cli/compare/v0.2.0...v0.2.1
|
|
124
159
|
[0.2.0]: https://github.com/alexechoi/avios-cli/compare/v0.1.6...v0.2.0
|
|
125
160
|
[0.1.6]: https://github.com/alexechoi/avios-cli/compare/v0.1.5...v0.1.6
|
|
126
161
|
[0.1.5]: https://github.com/alexechoi/avios-cli/compare/v0.1.4...v0.1.5
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avios-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A CLI and TUI for avios.com — view your Avios balance and transactions from the terminal.
|
|
5
5
|
Project-URL: Homepage, https://github.com/alexechoi/avios-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/alexechoi/avios-cli
|
|
@@ -39,13 +39,14 @@ Description-Content-Type: text/markdown
|
|
|
39
39
|
[](LICENSE)
|
|
40
40
|
[](https://www.python.org/)
|
|
41
41
|
|
|
42
|
-
A **CLI and TUI for
|
|
43
|
-
|
|
42
|
+
A **CLI and TUI for your Avios programmes** — check balances and browse transactions
|
|
43
|
+
without leaving the terminal. British Airways and Iberia use avios.com; Finnair Plus
|
|
44
|
+
uses Finnair's own OAuth and loyalty API.
|
|
44
45
|
|
|
45
46
|
> ⚠️ **Unofficial.** This project is not affiliated with, authorised by, or endorsed by
|
|
46
|
-
> Avios, British Airways or IAG Loyalty. It drives the same private endpoints
|
|
47
|
-
>
|
|
48
|
-
> break at any time and may be against the provider's terms of service.
|
|
47
|
+
> Avios, British Airways, Finnair or IAG Loyalty. It drives the same private endpoints
|
|
48
|
+
> the providers' websites use, with your own logged-in session. Use at your own risk;
|
|
49
|
+
> it may break at any time and may be against the provider's terms of service.
|
|
49
50
|
|
|
50
51
|

|
|
51
52
|
|
|
@@ -84,6 +85,7 @@ on the dashboard. It needs the **`login` extra**:
|
|
|
84
85
|
|
|
85
86
|
```bash
|
|
86
87
|
uvx --from 'avios-cli[login]' avios login
|
|
88
|
+
uvx --from 'avios-cli[login]' avios login finnair
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
It launches your installed **Google Chrome** (no extra download). If you don't have
|
|
@@ -113,22 +115,34 @@ browser. Use `--from-browser` instead: log into avios.com in your normal Chrome
|
|
|
113
115
|
(you'll get one normal captcha), then run
|
|
114
116
|
`uvx --from 'avios-cli[login]' avios login --from-browser`.
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
Finnair uses a different CAS/OAuth flow. `avios login finnair` opens the Finnair Plus
|
|
119
|
+
balance page, lets you complete password and MFA in the real browser, and captures the
|
|
120
|
+
OAuth session from the authenticated loyalty request. `--from-browser` is not available
|
|
121
|
+
for Finnair because browser-cookie import cannot read that token.
|
|
122
|
+
|
|
123
|
+
Each programme session is stored at `~/.config/avios/accounts/<programme>.json`
|
|
124
|
+
(mode `600`). Sessions expire; just log in to that programme again. Use
|
|
125
|
+
`avios logout <programme>` for one account or `avios logout` for all accounts.
|
|
118
126
|
|
|
119
127
|
## Usage
|
|
120
128
|
|
|
129
|
+
Commands span **all logged-in accounts** by default and show a combined total; add
|
|
130
|
+
`--account/-a <programme>` to focus on one.
|
|
131
|
+
|
|
121
132
|
```bash
|
|
122
|
-
avios
|
|
123
|
-
avios
|
|
124
|
-
avios
|
|
133
|
+
avios accounts # every logged-in account: balance + status
|
|
134
|
+
avios balance # per-account balances + combined total
|
|
135
|
+
avios transactions --limit 20 # recent transactions, merged across accounts
|
|
136
|
+
avios pending # pending Avios, merged across accounts
|
|
137
|
+
avios balance --account iberia # just one programme
|
|
125
138
|
avios overview # dashboard summary
|
|
126
139
|
avios whoami # name, tier, membership, email
|
|
127
140
|
avios raw /shell/api/users/current/accounts # hit any endpoint directly
|
|
128
141
|
```
|
|
129
142
|
|
|
130
|
-
Add `--json` to `balance`, `transactions`, `pending` or `whoami` for
|
|
131
|
-
output.
|
|
143
|
+
Add `--json` to `accounts`, `balance`, `transactions`, `pending` or `whoami` for
|
|
144
|
+
scriptable output. With a single account, `balance` keeps the individual/household
|
|
145
|
+
breakdown.
|
|
132
146
|
|
|
133
147
|
## TUI
|
|
134
148
|
|
|
@@ -136,8 +150,9 @@ output. Non-BA Avios programmes can set `AVIOS_OPCO` (default `BAEC`).
|
|
|
136
150
|
avios tui
|
|
137
151
|
```
|
|
138
152
|
|
|
139
|
-
A full-screen dashboard
|
|
140
|
-
|
|
153
|
+
A full-screen dashboard across all your accounts: a combined balance header (with a
|
|
154
|
+
per-programme breakdown) and a scrollable transactions table with a Programme column,
|
|
155
|
+
merged newest-first. Press `r` to refresh, `q` to quit.
|
|
141
156
|
|
|
142
157
|
## Roadmap
|
|
143
158
|
|
|
@@ -147,6 +162,7 @@ table. Press `r` to refresh, `q` to quit.
|
|
|
147
162
|
- [x] Browser-assisted `avios login`
|
|
148
163
|
- [x] CLI commands
|
|
149
164
|
- [x] Textual TUI dashboard
|
|
165
|
+
- [x] Multiple accounts (BA, Iberia, Aer Lingus, Finnair) with combined views
|
|
150
166
|
- [ ] Reward-flight **availability** search _(coming soon — needs a British Airways capture)_
|
|
151
167
|
|
|
152
168
|
## Development
|
|
@@ -163,8 +179,9 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow and
|
|
|
163
179
|
|
|
164
180
|
## Security
|
|
165
181
|
|
|
166
|
-
No passwords are handled or stored — only
|
|
167
|
-
`~/.config/avios/
|
|
182
|
+
No passwords are handled or stored — only programme session cookies or OAuth tokens,
|
|
183
|
+
kept locally under `~/.config/avios/accounts/` (mode `600`). See
|
|
184
|
+
[SECURITY.md](SECURITY.md).
|
|
168
185
|
|
|
169
186
|
## License
|
|
170
187
|
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://www.python.org/)
|
|
6
6
|
|
|
7
|
-
A **CLI and TUI for
|
|
8
|
-
|
|
7
|
+
A **CLI and TUI for your Avios programmes** — check balances and browse transactions
|
|
8
|
+
without leaving the terminal. British Airways and Iberia use avios.com; Finnair Plus
|
|
9
|
+
uses Finnair's own OAuth and loyalty API.
|
|
9
10
|
|
|
10
11
|
> ⚠️ **Unofficial.** This project is not affiliated with, authorised by, or endorsed by
|
|
11
|
-
> Avios, British Airways or IAG Loyalty. It drives the same private endpoints
|
|
12
|
-
>
|
|
13
|
-
> break at any time and may be against the provider's terms of service.
|
|
12
|
+
> Avios, British Airways, Finnair or IAG Loyalty. It drives the same private endpoints
|
|
13
|
+
> the providers' websites use, with your own logged-in session. Use at your own risk;
|
|
14
|
+
> it may break at any time and may be against the provider's terms of service.
|
|
14
15
|
|
|
15
16
|

|
|
16
17
|
|
|
@@ -49,6 +50,7 @@ on the dashboard. It needs the **`login` extra**:
|
|
|
49
50
|
|
|
50
51
|
```bash
|
|
51
52
|
uvx --from 'avios-cli[login]' avios login
|
|
53
|
+
uvx --from 'avios-cli[login]' avios login finnair
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
It launches your installed **Google Chrome** (no extra download). If you don't have
|
|
@@ -78,22 +80,34 @@ browser. Use `--from-browser` instead: log into avios.com in your normal Chrome
|
|
|
78
80
|
(you'll get one normal captcha), then run
|
|
79
81
|
`uvx --from 'avios-cli[login]' avios login --from-browser`.
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
Finnair uses a different CAS/OAuth flow. `avios login finnair` opens the Finnair Plus
|
|
84
|
+
balance page, lets you complete password and MFA in the real browser, and captures the
|
|
85
|
+
OAuth session from the authenticated loyalty request. `--from-browser` is not available
|
|
86
|
+
for Finnair because browser-cookie import cannot read that token.
|
|
87
|
+
|
|
88
|
+
Each programme session is stored at `~/.config/avios/accounts/<programme>.json`
|
|
89
|
+
(mode `600`). Sessions expire; just log in to that programme again. Use
|
|
90
|
+
`avios logout <programme>` for one account or `avios logout` for all accounts.
|
|
83
91
|
|
|
84
92
|
## Usage
|
|
85
93
|
|
|
94
|
+
Commands span **all logged-in accounts** by default and show a combined total; add
|
|
95
|
+
`--account/-a <programme>` to focus on one.
|
|
96
|
+
|
|
86
97
|
```bash
|
|
87
|
-
avios
|
|
88
|
-
avios
|
|
89
|
-
avios
|
|
98
|
+
avios accounts # every logged-in account: balance + status
|
|
99
|
+
avios balance # per-account balances + combined total
|
|
100
|
+
avios transactions --limit 20 # recent transactions, merged across accounts
|
|
101
|
+
avios pending # pending Avios, merged across accounts
|
|
102
|
+
avios balance --account iberia # just one programme
|
|
90
103
|
avios overview # dashboard summary
|
|
91
104
|
avios whoami # name, tier, membership, email
|
|
92
105
|
avios raw /shell/api/users/current/accounts # hit any endpoint directly
|
|
93
106
|
```
|
|
94
107
|
|
|
95
|
-
Add `--json` to `balance`, `transactions`, `pending` or `whoami` for
|
|
96
|
-
output.
|
|
108
|
+
Add `--json` to `accounts`, `balance`, `transactions`, `pending` or `whoami` for
|
|
109
|
+
scriptable output. With a single account, `balance` keeps the individual/household
|
|
110
|
+
breakdown.
|
|
97
111
|
|
|
98
112
|
## TUI
|
|
99
113
|
|
|
@@ -101,8 +115,9 @@ output. Non-BA Avios programmes can set `AVIOS_OPCO` (default `BAEC`).
|
|
|
101
115
|
avios tui
|
|
102
116
|
```
|
|
103
117
|
|
|
104
|
-
A full-screen dashboard
|
|
105
|
-
|
|
118
|
+
A full-screen dashboard across all your accounts: a combined balance header (with a
|
|
119
|
+
per-programme breakdown) and a scrollable transactions table with a Programme column,
|
|
120
|
+
merged newest-first. Press `r` to refresh, `q` to quit.
|
|
106
121
|
|
|
107
122
|
## Roadmap
|
|
108
123
|
|
|
@@ -112,6 +127,7 @@ table. Press `r` to refresh, `q` to quit.
|
|
|
112
127
|
- [x] Browser-assisted `avios login`
|
|
113
128
|
- [x] CLI commands
|
|
114
129
|
- [x] Textual TUI dashboard
|
|
130
|
+
- [x] Multiple accounts (BA, Iberia, Aer Lingus, Finnair) with combined views
|
|
115
131
|
- [ ] Reward-flight **availability** search _(coming soon — needs a British Airways capture)_
|
|
116
132
|
|
|
117
133
|
## Development
|
|
@@ -128,8 +144,9 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow and
|
|
|
128
144
|
|
|
129
145
|
## Security
|
|
130
146
|
|
|
131
|
-
No passwords are handled or stored — only
|
|
132
|
-
`~/.config/avios/
|
|
147
|
+
No passwords are handled or stored — only programme session cookies or OAuth tokens,
|
|
148
|
+
kept locally under `~/.config/avios/accounts/` (mode `600`). See
|
|
149
|
+
[SECURITY.md](SECURITY.md).
|
|
133
150
|
|
|
134
151
|
## License
|
|
135
152
|
|
|
@@ -3,21 +3,20 @@
|
|
|
3
3
|
## How avios-cli handles your credentials
|
|
4
4
|
|
|
5
5
|
- **No passwords are ever handled or stored.** Login happens in a real browser
|
|
6
|
-
(
|
|
7
|
-
**session cookie**.
|
|
8
|
-
-
|
|
9
|
-
`600` (owner read/write only).
|
|
10
|
-
|
|
11
|
-
- `avios logout` deletes
|
|
12
|
-
server side after roughly a day.
|
|
6
|
+
(the programme's normal login and MFA); avios-cli only captures the resulting
|
|
7
|
+
**session cookie or OAuth token**.
|
|
8
|
+
- Sessions are stored locally under `~/.config/avios/accounts/` with file mode
|
|
9
|
+
`600` (owner read/write only). Each credential is sent only to its programme's
|
|
10
|
+
API in the requests you make.
|
|
11
|
+
- `avios logout` deletes stored sessions. They also expire on the server side.
|
|
13
12
|
- `AVIOS_COOKIE` may be set to supply a cookie via the environment instead of the
|
|
14
13
|
stored file — avoid committing it or storing it in shell history.
|
|
15
14
|
|
|
16
15
|
## Unofficial software
|
|
17
16
|
|
|
18
|
-
avios-cli is **not affiliated with Avios, British Airways or IAG
|
|
19
|
-
uses private, undocumented endpoints. Using it may breach the
|
|
20
|
-
service and could affect your account. Use at your own risk.
|
|
17
|
+
avios-cli is **not affiliated with Avios, British Airways, Finnair or IAG
|
|
18
|
+
Loyalty** and uses private, undocumented endpoints. Using it may breach the
|
|
19
|
+
provider's terms of service and could affect your account. Use at your own risk.
|
|
21
20
|
|
|
22
21
|
## Reporting a vulnerability
|
|
23
22
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<svg class="rich-terminal" viewBox="0 0 1190 782.0" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- Generated with Rich https://www.textualize.io -->
|
|
3
|
+
<style>
|
|
4
|
+
|
|
5
|
+
@font-face {
|
|
6
|
+
font-family: "Fira Code";
|
|
7
|
+
src: local("FiraCode-Regular"),
|
|
8
|
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
|
9
|
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
|
10
|
+
font-style: normal;
|
|
11
|
+
font-weight: 400;
|
|
12
|
+
}
|
|
13
|
+
@font-face {
|
|
14
|
+
font-family: "Fira Code";
|
|
15
|
+
src: local("FiraCode-Bold"),
|
|
16
|
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
|
17
|
+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
|
18
|
+
font-style: bold;
|
|
19
|
+
font-weight: 700;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.terminal-1749240985-matrix {
|
|
23
|
+
font-family: Fira Code, monospace;
|
|
24
|
+
font-size: 20px;
|
|
25
|
+
line-height: 24.4px;
|
|
26
|
+
font-variant-east-asian: full-width;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.terminal-1749240985-title {
|
|
30
|
+
font-size: 18px;
|
|
31
|
+
font-weight: bold;
|
|
32
|
+
font-family: arial;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.terminal-1749240985-r1 { fill: #c5c8c6 }
|
|
36
|
+
.terminal-1749240985-r2 { fill: #e0e0e0 }
|
|
37
|
+
.terminal-1749240985-r3 { fill: #a0a3a6 }
|
|
38
|
+
.terminal-1749240985-r4 { fill: #c5c6c7 }
|
|
39
|
+
.terminal-1749240985-r5 { fill: #00d7af;font-weight: bold }
|
|
40
|
+
.terminal-1749240985-r6 { fill: #00d7d7;font-weight: bold }
|
|
41
|
+
.terminal-1749240985-r7 { fill: #00afd7;font-weight: bold }
|
|
42
|
+
.terminal-1749240985-r8 { fill: #0087d7;font-weight: bold }
|
|
43
|
+
.terminal-1749240985-r9 { fill: #005fd7;font-weight: bold }
|
|
44
|
+
.terminal-1749240985-r10 { fill: #5f5fd7;font-weight: bold }
|
|
45
|
+
.terminal-1749240985-r11 { fill: #9e9e9e;font-style: italic; }
|
|
46
|
+
.terminal-1749240985-r12 { fill: #e0e0e0;font-weight: bold }
|
|
47
|
+
.terminal-1749240985-r13 { fill: #58d1eb;font-weight: bold }
|
|
48
|
+
.terminal-1749240985-r14 { fill: #9e9e9e }
|
|
49
|
+
.terminal-1749240985-r15 { fill: #0178d4 }
|
|
50
|
+
.terminal-1749240985-r16 { fill: #ddedf9;font-weight: bold }
|
|
51
|
+
.terminal-1749240985-r17 { fill: #ffa62b;font-weight: bold }
|
|
52
|
+
.terminal-1749240985-r18 { fill: #495259 }
|
|
53
|
+
</style>
|
|
54
|
+
|
|
55
|
+
<defs>
|
|
56
|
+
<clipPath id="terminal-1749240985-clip-terminal">
|
|
57
|
+
<rect x="0" y="0" width="1170.1999999999998" height="731.0" />
|
|
58
|
+
</clipPath>
|
|
59
|
+
<clipPath id="terminal-1749240985-line-0">
|
|
60
|
+
<rect x="0" y="1.5" width="1171.2" height="24.65"/>
|
|
61
|
+
</clipPath>
|
|
62
|
+
<clipPath id="terminal-1749240985-line-1">
|
|
63
|
+
<rect x="0" y="25.9" width="1171.2" height="24.65"/>
|
|
64
|
+
</clipPath>
|
|
65
|
+
<clipPath id="terminal-1749240985-line-2">
|
|
66
|
+
<rect x="0" y="50.3" width="1171.2" height="24.65"/>
|
|
67
|
+
</clipPath>
|
|
68
|
+
<clipPath id="terminal-1749240985-line-3">
|
|
69
|
+
<rect x="0" y="74.7" width="1171.2" height="24.65"/>
|
|
70
|
+
</clipPath>
|
|
71
|
+
<clipPath id="terminal-1749240985-line-4">
|
|
72
|
+
<rect x="0" y="99.1" width="1171.2" height="24.65"/>
|
|
73
|
+
</clipPath>
|
|
74
|
+
<clipPath id="terminal-1749240985-line-5">
|
|
75
|
+
<rect x="0" y="123.5" width="1171.2" height="24.65"/>
|
|
76
|
+
</clipPath>
|
|
77
|
+
<clipPath id="terminal-1749240985-line-6">
|
|
78
|
+
<rect x="0" y="147.9" width="1171.2" height="24.65"/>
|
|
79
|
+
</clipPath>
|
|
80
|
+
<clipPath id="terminal-1749240985-line-7">
|
|
81
|
+
<rect x="0" y="172.3" width="1171.2" height="24.65"/>
|
|
82
|
+
</clipPath>
|
|
83
|
+
<clipPath id="terminal-1749240985-line-8">
|
|
84
|
+
<rect x="0" y="196.7" width="1171.2" height="24.65"/>
|
|
85
|
+
</clipPath>
|
|
86
|
+
<clipPath id="terminal-1749240985-line-9">
|
|
87
|
+
<rect x="0" y="221.1" width="1171.2" height="24.65"/>
|
|
88
|
+
</clipPath>
|
|
89
|
+
<clipPath id="terminal-1749240985-line-10">
|
|
90
|
+
<rect x="0" y="245.5" width="1171.2" height="24.65"/>
|
|
91
|
+
</clipPath>
|
|
92
|
+
<clipPath id="terminal-1749240985-line-11">
|
|
93
|
+
<rect x="0" y="269.9" width="1171.2" height="24.65"/>
|
|
94
|
+
</clipPath>
|
|
95
|
+
<clipPath id="terminal-1749240985-line-12">
|
|
96
|
+
<rect x="0" y="294.3" width="1171.2" height="24.65"/>
|
|
97
|
+
</clipPath>
|
|
98
|
+
<clipPath id="terminal-1749240985-line-13">
|
|
99
|
+
<rect x="0" y="318.7" width="1171.2" height="24.65"/>
|
|
100
|
+
</clipPath>
|
|
101
|
+
<clipPath id="terminal-1749240985-line-14">
|
|
102
|
+
<rect x="0" y="343.1" width="1171.2" height="24.65"/>
|
|
103
|
+
</clipPath>
|
|
104
|
+
<clipPath id="terminal-1749240985-line-15">
|
|
105
|
+
<rect x="0" y="367.5" width="1171.2" height="24.65"/>
|
|
106
|
+
</clipPath>
|
|
107
|
+
<clipPath id="terminal-1749240985-line-16">
|
|
108
|
+
<rect x="0" y="391.9" width="1171.2" height="24.65"/>
|
|
109
|
+
</clipPath>
|
|
110
|
+
<clipPath id="terminal-1749240985-line-17">
|
|
111
|
+
<rect x="0" y="416.3" width="1171.2" height="24.65"/>
|
|
112
|
+
</clipPath>
|
|
113
|
+
<clipPath id="terminal-1749240985-line-18">
|
|
114
|
+
<rect x="0" y="440.7" width="1171.2" height="24.65"/>
|
|
115
|
+
</clipPath>
|
|
116
|
+
<clipPath id="terminal-1749240985-line-19">
|
|
117
|
+
<rect x="0" y="465.1" width="1171.2" height="24.65"/>
|
|
118
|
+
</clipPath>
|
|
119
|
+
<clipPath id="terminal-1749240985-line-20">
|
|
120
|
+
<rect x="0" y="489.5" width="1171.2" height="24.65"/>
|
|
121
|
+
</clipPath>
|
|
122
|
+
<clipPath id="terminal-1749240985-line-21">
|
|
123
|
+
<rect x="0" y="513.9" width="1171.2" height="24.65"/>
|
|
124
|
+
</clipPath>
|
|
125
|
+
<clipPath id="terminal-1749240985-line-22">
|
|
126
|
+
<rect x="0" y="538.3" width="1171.2" height="24.65"/>
|
|
127
|
+
</clipPath>
|
|
128
|
+
<clipPath id="terminal-1749240985-line-23">
|
|
129
|
+
<rect x="0" y="562.7" width="1171.2" height="24.65"/>
|
|
130
|
+
</clipPath>
|
|
131
|
+
<clipPath id="terminal-1749240985-line-24">
|
|
132
|
+
<rect x="0" y="587.1" width="1171.2" height="24.65"/>
|
|
133
|
+
</clipPath>
|
|
134
|
+
<clipPath id="terminal-1749240985-line-25">
|
|
135
|
+
<rect x="0" y="611.5" width="1171.2" height="24.65"/>
|
|
136
|
+
</clipPath>
|
|
137
|
+
<clipPath id="terminal-1749240985-line-26">
|
|
138
|
+
<rect x="0" y="635.9" width="1171.2" height="24.65"/>
|
|
139
|
+
</clipPath>
|
|
140
|
+
<clipPath id="terminal-1749240985-line-27">
|
|
141
|
+
<rect x="0" y="660.3" width="1171.2" height="24.65"/>
|
|
142
|
+
</clipPath>
|
|
143
|
+
<clipPath id="terminal-1749240985-line-28">
|
|
144
|
+
<rect x="0" y="684.7" width="1171.2" height="24.65"/>
|
|
145
|
+
</clipPath>
|
|
146
|
+
</defs>
|
|
147
|
+
|
|
148
|
+
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1188" height="780" rx="8"/><text class="terminal-1749240985-title" fill="#c5c8c6" text-anchor="middle" x="594" y="27">avios</text>
|
|
149
|
+
<g transform="translate(26,22)">
|
|
150
|
+
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
|
151
|
+
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
|
152
|
+
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
|
153
|
+
</g>
|
|
154
|
+
|
|
155
|
+
<g transform="translate(9, 41)" clip-path="url(#terminal-1749240985-clip-terminal)">
|
|
156
|
+
<rect fill="#242f38" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="24.4" y="1.5" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="85.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="97.6" y="1.5" width="256.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="353.8" y="1.5" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="414.8" y="1.5" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="451.4" y="1.5" width="329.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="780.8" y="1.5" width="268.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#2c363f" x="1049.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2c363f" x="1061.4" y="1.5" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#2c363f" x="1159" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="50.3" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="805.2" y="50.3" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="805.2" y="74.7" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="99.1" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="805.2" y="99.1" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="123.5" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="805.2" y="123.5" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="147.9" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="805.2" y="147.9" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="172.3" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="805.2" y="172.3" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="402.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="196.7" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="196.7" width="402.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="24.4" y="221.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="221.1" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="221.1" width="573.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="793" y="221.1" width="353.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="1146.8" y="221.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="24.4" y="245.5" width="1122.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="1146.8" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2d3740" x="0" y="294.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#2d3740" x="207.4" y="294.3" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#2d3740" x="353.8" y="294.3" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#2d3740" x="634.4" y="294.3" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#2d3740" x="744.2" y="294.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#2d3740" x="951.6" y="294.3" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="0" y="318.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="207.4" y="318.7" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="353.8" y="318.7" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="634.4" y="318.7" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="744.2" y="318.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="951.6" y="318.7" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="343.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="207.4" y="343.1" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="353.8" y="343.1" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="634.4" y="343.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="744.2" y="343.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="951.6" y="343.1" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="0" y="367.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="207.4" y="367.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="353.8" y="367.5" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="634.4" y="367.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="744.2" y="367.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="951.6" y="367.5" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="391.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="207.4" y="391.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="353.8" y="391.9" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="634.4" y="391.9" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="744.2" y="391.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="951.6" y="391.9" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="0" y="416.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="207.4" y="416.3" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="353.8" y="416.3" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="634.4" y="416.3" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="744.2" y="416.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1c1c1c" x="951.6" y="416.3" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="440.7" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="465.1" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="489.5" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="513.9" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="538.3" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="562.7" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="587.1" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="611.5" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="635.9" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="660.3" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#272727" x="0" y="684.7" width="1171.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="0" y="709.1" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="36.6" y="709.1" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="134.2" y="709.1" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="170.8" y="709.1" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="231.8" y="709.1" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="1024.8" y="709.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="1037" y="709.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="1061.4" y="709.1" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#242f38" x="1159" y="709.1" width="12.2" height="24.65" shape-rendering="crispEdges"/>
|
|
157
|
+
<g class="terminal-1749240985-matrix">
|
|
158
|
+
<text class="terminal-1749240985-r2" x="12.2" y="20" textLength="12.2" clip-path="url(#terminal-1749240985-line-0)">⭘</text><text class="terminal-1749240985-r2" x="353.8" y="20" textLength="61" clip-path="url(#terminal-1749240985-line-0)">avios</text><text class="terminal-1749240985-r3" x="414.8" y="20" textLength="36.6" clip-path="url(#terminal-1749240985-line-0)"> — </text><text class="terminal-1749240985-r3" x="451.4" y="20" textLength="329.4" clip-path="url(#terminal-1749240985-line-0)">your Avios, in the terminal</text><text class="terminal-1749240985-r4" x="1061.4" y="20" textLength="97.6" clip-path="url(#terminal-1749240985-line-0)">14:28:47</text><text class="terminal-1749240985-r1" x="1171.2" y="20" textLength="12.2" clip-path="url(#terminal-1749240985-line-0)">
|
|
159
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1749240985-line-1)">
|
|
160
|
+
</text><text class="terminal-1749240985-r5" x="353.8" y="68.8" textLength="451.4" clip-path="url(#terminal-1749240985-line-2)"> █████╗ ██╗   ██╗██╗ ██████╗ ███████╗</text><text class="terminal-1749240985-r1" x="1171.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1749240985-line-2)">
|
|
161
|
+
</text><text class="terminal-1749240985-r6" x="353.8" y="93.2" textLength="451.4" clip-path="url(#terminal-1749240985-line-3)">██╔══██╗██║   ██║██║██╔═══██╗██╔════╝</text><text class="terminal-1749240985-r1" x="1171.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1749240985-line-3)">
|
|
162
|
+
</text><text class="terminal-1749240985-r7" x="353.8" y="117.6" textLength="451.4" clip-path="url(#terminal-1749240985-line-4)">███████║██║   ██║██║██║   ██║███████╗</text><text class="terminal-1749240985-r1" x="1171.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1749240985-line-4)">
|
|
163
|
+
</text><text class="terminal-1749240985-r8" x="353.8" y="142" textLength="451.4" clip-path="url(#terminal-1749240985-line-5)">██╔══██║╚██╗ ██╔╝██║██║   ██║╚════██║</text><text class="terminal-1749240985-r1" x="1171.2" y="142" textLength="12.2" clip-path="url(#terminal-1749240985-line-5)">
|
|
164
|
+
</text><text class="terminal-1749240985-r9" x="353.8" y="166.4" textLength="451.4" clip-path="url(#terminal-1749240985-line-6)">██║  ██║ ╚████╔╝ ██║╚██████╔╝███████║</text><text class="terminal-1749240985-r1" x="1171.2" y="166.4" textLength="12.2" clip-path="url(#terminal-1749240985-line-6)">
|
|
165
|
+
</text><text class="terminal-1749240985-r10" x="353.8" y="190.8" textLength="451.4" clip-path="url(#terminal-1749240985-line-7)">╚═╝  ╚═╝  ╚═══╝  ╚═╝ ╚═════╝ ╚══════╝</text><text class="terminal-1749240985-r1" x="1171.2" y="190.8" textLength="12.2" clip-path="url(#terminal-1749240985-line-7)">
|
|
166
|
+
</text><text class="terminal-1749240985-r11" x="402.6" y="215.2" textLength="366" clip-path="url(#terminal-1749240985-line-8)">✈  your Avios, in the terminal</text><text class="terminal-1749240985-r1" x="1171.2" y="215.2" textLength="12.2" clip-path="url(#terminal-1749240985-line-8)">
|
|
167
|
+
</text><text class="terminal-1749240985-r12" x="24.4" y="239.6" textLength="122" clip-path="url(#terminal-1749240985-line-9)">Combined  </text><text class="terminal-1749240985-r13" x="146.4" y="239.6" textLength="73.2" clip-path="url(#terminal-1749240985-line-9)">97,791</text><text class="terminal-1749240985-r14" x="219.6" y="239.6" textLength="573.4" clip-path="url(#terminal-1749240985-line-9)">     British Airways 75,751   ·   Iberia 22,040</text><text class="terminal-1749240985-r1" x="1171.2" y="239.6" textLength="12.2" clip-path="url(#terminal-1749240985-line-9)">
|
|
168
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="264" textLength="12.2" clip-path="url(#terminal-1749240985-line-10)">
|
|
169
|
+
</text><text class="terminal-1749240985-r15" x="0" y="288.4" textLength="1171.2" clip-path="url(#terminal-1749240985-line-11)">────────────────────────────────────────────────────────────────────────────────────────────────</text><text class="terminal-1749240985-r1" x="1171.2" y="288.4" textLength="12.2" clip-path="url(#terminal-1749240985-line-11)">
|
|
170
|
+
</text><text class="terminal-1749240985-r12" x="0" y="312.8" textLength="207.4" clip-path="url(#terminal-1749240985-line-12)"> Programme       </text><text class="terminal-1749240985-r12" x="207.4" y="312.8" textLength="146.4" clip-path="url(#terminal-1749240985-line-12)"> Date       </text><text class="terminal-1749240985-r12" x="353.8" y="312.8" textLength="280.6" clip-path="url(#terminal-1749240985-line-12)"> Description           </text><text class="terminal-1749240985-r12" x="634.4" y="312.8" textLength="109.8" clip-path="url(#terminal-1749240985-line-12)"> Avios   </text><text class="terminal-1749240985-r12" x="744.2" y="312.8" textLength="207.4" clip-path="url(#terminal-1749240985-line-12)"> Type            </text><text class="terminal-1749240985-r1" x="1171.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1749240985-line-12)">
|
|
171
|
+
</text><text class="terminal-1749240985-r16" x="0" y="337.2" textLength="207.4" clip-path="url(#terminal-1749240985-line-13)"> British Airways </text><text class="terminal-1749240985-r16" x="207.4" y="337.2" textLength="146.4" clip-path="url(#terminal-1749240985-line-13)"> 2026-07-18 </text><text class="terminal-1749240985-r16" x="353.8" y="337.2" textLength="280.6" clip-path="url(#terminal-1749240985-line-13)"> Flight BA286 SFO-LHR  </text><text class="terminal-1749240985-r16" x="634.4" y="337.2" textLength="109.8" clip-path="url(#terminal-1749240985-line-13)"> +8,500  </text><text class="terminal-1749240985-r16" x="744.2" y="337.2" textLength="207.4" clip-path="url(#terminal-1749240985-line-13)"> Collection      </text><text class="terminal-1749240985-r1" x="1171.2" y="337.2" textLength="12.2" clip-path="url(#terminal-1749240985-line-13)">
|
|
172
|
+
</text><text class="terminal-1749240985-r2" x="0" y="361.6" textLength="207.4" clip-path="url(#terminal-1749240985-line-14)"> Iberia          </text><text class="terminal-1749240985-r2" x="207.4" y="361.6" textLength="146.4" clip-path="url(#terminal-1749240985-line-14)"> 2026-07-12 </text><text class="terminal-1749240985-r2" x="353.8" y="361.6" textLength="280.6" clip-path="url(#terminal-1749240985-line-14)"> Iberia IB3170 MAD-LHR </text><text class="terminal-1749240985-r2" x="634.4" y="361.6" textLength="109.8" clip-path="url(#terminal-1749240985-line-14)"> +4,200  </text><text class="terminal-1749240985-r2" x="744.2" y="361.6" textLength="207.4" clip-path="url(#terminal-1749240985-line-14)"> Reward Flight   </text><text class="terminal-1749240985-r1" x="1171.2" y="361.6" textLength="12.2" clip-path="url(#terminal-1749240985-line-14)">
|
|
173
|
+
</text><text class="terminal-1749240985-r2" x="0" y="386" textLength="207.4" clip-path="url(#terminal-1749240985-line-15)"> British Airways </text><text class="terminal-1749240985-r2" x="207.4" y="386" textLength="146.4" clip-path="url(#terminal-1749240985-line-15)"> 2026-07-02 </text><text class="terminal-1749240985-r2" x="353.8" y="386" textLength="280.6" clip-path="url(#terminal-1749240985-line-15)"> Tesco groceries       </text><text class="terminal-1749240985-r2" x="634.4" y="386" textLength="109.8" clip-path="url(#terminal-1749240985-line-15)"> +240    </text><text class="terminal-1749240985-r2" x="744.2" y="386" textLength="207.4" clip-path="url(#terminal-1749240985-line-15)"> BA Avios eStore </text><text class="terminal-1749240985-r1" x="1171.2" y="386" textLength="12.2" clip-path="url(#terminal-1749240985-line-15)">
|
|
174
|
+
</text><text class="terminal-1749240985-r2" x="0" y="410.4" textLength="207.4" clip-path="url(#terminal-1749240985-line-16)"> Iberia          </text><text class="terminal-1749240985-r2" x="207.4" y="410.4" textLength="146.4" clip-path="url(#terminal-1749240985-line-16)"> 2026-06-21 </text><text class="terminal-1749240985-r2" x="353.8" y="410.4" textLength="280.6" clip-path="url(#terminal-1749240985-line-16)"> NH Hotels Madrid      </text><text class="terminal-1749240985-r2" x="634.4" y="410.4" textLength="109.8" clip-path="url(#terminal-1749240985-line-16)"> +1,600  </text><text class="terminal-1749240985-r2" x="744.2" y="410.4" textLength="207.4" clip-path="url(#terminal-1749240985-line-16)"> Hotel           </text><text class="terminal-1749240985-r1" x="1171.2" y="410.4" textLength="12.2" clip-path="url(#terminal-1749240985-line-16)">
|
|
175
|
+
</text><text class="terminal-1749240985-r2" x="0" y="434.8" textLength="207.4" clip-path="url(#terminal-1749240985-line-17)"> British Airways </text><text class="terminal-1749240985-r2" x="207.4" y="434.8" textLength="146.4" clip-path="url(#terminal-1749240985-line-17)"> 2026-06-09 </text><text class="terminal-1749240985-r2" x="353.8" y="434.8" textLength="280.6" clip-path="url(#terminal-1749240985-line-17)"> Redemption LHR-JFK    </text><text class="terminal-1749240985-r2" x="634.4" y="434.8" textLength="109.8" clip-path="url(#terminal-1749240985-line-17)"> -50,000 </text><text class="terminal-1749240985-r2" x="744.2" y="434.8" textLength="207.4" clip-path="url(#terminal-1749240985-line-17)"> Reward Flight   </text><text class="terminal-1749240985-r1" x="1171.2" y="434.8" textLength="12.2" clip-path="url(#terminal-1749240985-line-17)">
|
|
176
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="459.2" textLength="12.2" clip-path="url(#terminal-1749240985-line-18)">
|
|
177
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="483.6" textLength="12.2" clip-path="url(#terminal-1749240985-line-19)">
|
|
178
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="508" textLength="12.2" clip-path="url(#terminal-1749240985-line-20)">
|
|
179
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="532.4" textLength="12.2" clip-path="url(#terminal-1749240985-line-21)">
|
|
180
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="556.8" textLength="12.2" clip-path="url(#terminal-1749240985-line-22)">
|
|
181
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="581.2" textLength="12.2" clip-path="url(#terminal-1749240985-line-23)">
|
|
182
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="605.6" textLength="12.2" clip-path="url(#terminal-1749240985-line-24)">
|
|
183
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="630" textLength="12.2" clip-path="url(#terminal-1749240985-line-25)">
|
|
184
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="654.4" textLength="12.2" clip-path="url(#terminal-1749240985-line-26)">
|
|
185
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="678.8" textLength="12.2" clip-path="url(#terminal-1749240985-line-27)">
|
|
186
|
+
</text><text class="terminal-1749240985-r1" x="1171.2" y="703.2" textLength="12.2" clip-path="url(#terminal-1749240985-line-28)">
|
|
187
|
+
</text><text class="terminal-1749240985-r17" x="0" y="727.6" textLength="36.6" clip-path="url(#terminal-1749240985-line-29)"> r </text><text class="terminal-1749240985-r2" x="36.6" y="727.6" textLength="97.6" clip-path="url(#terminal-1749240985-line-29)">Refresh </text><text class="terminal-1749240985-r17" x="134.2" y="727.6" textLength="36.6" clip-path="url(#terminal-1749240985-line-29)"> q </text><text class="terminal-1749240985-r2" x="170.8" y="727.6" textLength="61" clip-path="url(#terminal-1749240985-line-29)">Quit </text><text class="terminal-1749240985-r18" x="1024.8" y="727.6" textLength="12.2" clip-path="url(#terminal-1749240985-line-29)">▏</text><text class="terminal-1749240985-r17" x="1037" y="727.6" textLength="24.4" clip-path="url(#terminal-1749240985-line-29)">^p</text><text class="terminal-1749240985-r2" x="1061.4" y="727.6" textLength="97.6" clip-path="url(#terminal-1749240985-line-29)"> palette</text>
|
|
188
|
+
</g>
|
|
189
|
+
</g>
|
|
190
|
+
</svg>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Generate the TUI screenshot (SVG) used in the README.
|
|
2
|
+
|
|
3
|
+
Runs the multi-account dashboard headlessly with demo data (two programmes) and
|
|
4
|
+
exports an SVG.
|
|
5
|
+
|
|
6
|
+
uv run python scripts/screenshot.py
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from avios import aggregate
|
|
16
|
+
from avios.accounts import Account
|
|
17
|
+
from avios.aggregate import AccountBalance, TaggedTransaction
|
|
18
|
+
from avios.models import Balance, Transaction
|
|
19
|
+
from avios.programmes import get_programme
|
|
20
|
+
from avios.tui.app import AviosApp
|
|
21
|
+
|
|
22
|
+
DOCS = Path(__file__).resolve().parent.parent / "docs"
|
|
23
|
+
|
|
24
|
+
_BA = Account.from_programme(get_programme("ba"), cookies=[{"name": "s", "value": "ba"}])
|
|
25
|
+
_IB = Account.from_programme(get_programme("iberia"), cookies=[{"name": "s", "value": "ib"}])
|
|
26
|
+
|
|
27
|
+
_BALANCES = {"ba": 75751, "iberia": 22040}
|
|
28
|
+
_DEMO = [
|
|
29
|
+
(_BA, "2026-07-18", "Flight BA286 SFO-LHR", 8500, "Collection"),
|
|
30
|
+
(_IB, "2026-07-12", "Iberia IB3170 MAD-LHR", 4200, "Reward Flight"),
|
|
31
|
+
(_BA, "2026-07-02", "Tesco groceries", 240, "BA Avios eStore"),
|
|
32
|
+
(_IB, "2026-06-21", "NH Hotels Madrid", 1600, "Hotel"),
|
|
33
|
+
(_BA, "2026-06-09", "Redemption LHR-JFK", -50000, "Reward Flight"),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _demo_balances(accounts: list[Account], **_: Any) -> list[AccountBalance]:
|
|
38
|
+
return [
|
|
39
|
+
AccountBalance(
|
|
40
|
+
a,
|
|
41
|
+
Balance(
|
|
42
|
+
balance=_BALANCES[a.slug],
|
|
43
|
+
individual=_BALANCES[a.slug],
|
|
44
|
+
household=112430 if a.slug == "ba" else None,
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
for a in accounts
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _demo_transactions(accounts: list[Account], **_: Any) -> list[TaggedTransaction]:
|
|
52
|
+
items = [
|
|
53
|
+
TaggedTransaction(
|
|
54
|
+
acc,
|
|
55
|
+
Transaction.model_validate(
|
|
56
|
+
{
|
|
57
|
+
"dateProcessed": f"{date}T09:00:00Z",
|
|
58
|
+
"description": desc,
|
|
59
|
+
"amount": amount,
|
|
60
|
+
"type": {"value": kind},
|
|
61
|
+
}
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
for acc, date, desc, amount, kind in _DEMO
|
|
65
|
+
]
|
|
66
|
+
items.sort(key=lambda t: t.transaction.date_processed or "", reverse=True)
|
|
67
|
+
return items
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def main() -> None:
|
|
71
|
+
DOCS.mkdir(exist_ok=True)
|
|
72
|
+
# Swap the aggregation layer for demo data (no network) before the app loads.
|
|
73
|
+
aggregate.all_balances = _demo_balances # type: ignore[assignment]
|
|
74
|
+
aggregate.merged_transactions = _demo_transactions # type: ignore[assignment]
|
|
75
|
+
app = AviosApp(accounts=[_BA, _IB])
|
|
76
|
+
async with app.run_test(size=(96, 30)) as pilot:
|
|
77
|
+
await app._load()
|
|
78
|
+
await pilot.pause()
|
|
79
|
+
app.save_screenshot(filename="dashboard.svg", path=str(DOCS))
|
|
80
|
+
print(f"wrote {DOCS / 'dashboard.svg'}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
asyncio.run(main())
|