From f60693c341706538e17beb7f8a75d6f945e29172 Mon Sep 17 00:00:00 2001
From: Kubat <maelle.martin@proton.me>
Date: Tue, 1 Oct 2024 21:46:19 +0200
Subject: [PATCH] AMADEUS: Continue implementation, cargo update + clippy

---
 Cargo.lock                                    | 335 +++++------
 Cargo.toml                                    |   9 +-
 README.md                                     |   6 +-
 amadeus/Cargo.toml                            |  16 +-
 amadeus/i18n/en/amadeus.ftl                   |  16 +-
 amadeus/i18n/es-ES/amadeus.ftl                |  16 +-
 amadeus/rsc/icons/fontawesome/archive.svg     |   1 +
 amadeus/rsc/icons/fontawesome/barcodes.svg    |   1 +
 amadeus/rsc/icons/fontawesome/bug.svg         |   1 +
 amadeus/rsc/icons/fontawesome/edit.svg        |   1 +
 amadeus/rsc/icons/fontawesome/filter.svg      |   1 +
 amadeus/rsc/icons/fontawesome/folder.svg      |   1 +
 .../rsc/icons/fontawesome/folder_minus.svg    |   1 +
 amadeus/rsc/icons/fontawesome/folder_plus.svg |   1 +
 amadeus/rsc/icons/fontawesome/git.svg         |   1 +
 amadeus/rsc/icons/fontawesome/hashtag.svg     |   1 +
 amadeus/rsc/icons/fontawesome/history.svg     |   1 +
 amadeus/rsc/icons/fontawesome/home.svg        |   1 +
 amadeus/rsc/icons/fontawesome/link.svg        |   1 +
 amadeus/rsc/icons/fontawesome/menu.svg        |   1 +
 amadeus/rsc/icons/fontawesome/meteor.svg      |   1 +
 amadeus/rsc/icons/fontawesome/minus.svg       |   1 +
 amadeus/rsc/icons/fontawesome/next.svg        |   1 +
 amadeus/rsc/icons/fontawesome/pause.svg       |   1 +
 amadeus/rsc/icons/fontawesome/pin.svg         |   1 +
 amadeus/rsc/icons/fontawesome/play.svg        |   1 +
 amadeus/rsc/icons/fontawesome/plus.svg        |   1 +
 amadeus/rsc/icons/fontawesome/previous.svg    |   1 +
 amadeus/rsc/icons/fontawesome/queue.svg       |   1 +
 amadeus/rsc/icons/fontawesome/rust.svg        |   1 +
 amadeus/rsc/icons/fontawesome/satellite.svg   |   1 +
 amadeus/rsc/icons/fontawesome/save.svg        |   1 +
 amadeus/rsc/icons/fontawesome/search.svg      |   1 +
 amadeus/rsc/icons/fontawesome/shuffle.svg     |   1 +
 amadeus/rsc/icons/fontawesome/stop.svg        |   1 +
 amadeus/rsc/icons/fontawesome/tag.svg         |   1 +
 amadeus/rsc/icons/fontawesome/tags.svg        |   1 +
 amadeus/rsc/icons/fontawesome/unpin.svg       |   1 +
 amadeus/rsc/icons/fontawesome/user.svg        |   1 +
 amadeus/rsc/icons/fontawesome/web.svg         |   1 +
 amadeus/rsc/icons/iced.svg                    | 124 +++++
 amadeus/src/app.rs                            | 521 ++++++++++++------
 amadeus/src/app/bottom_bar.rs                 | 134 +++++
 amadeus/src/app/config_page.rs                |  83 ---
 .../app/{context_page.rs => context_pages.rs} |   5 +-
 .../{about_page.rs => context_pages/about.rs} |  20 +-
 amadeus/src/app/context_pages/config.rs       | 104 ++++
 amadeus/src/app/history_page.rs               |  19 -
 amadeus/src/app/home_page.rs                  |  18 -
 amadeus/src/app/kard.rs                       |  98 ++++
 amadeus/src/app/main_page.rs                  |  58 --
 amadeus/src/app/{menu_bar.rs => menu.rs}      |  55 +-
 amadeus/src/app/pages.rs                      | 120 ++++
 amadeus/src/app/pages/history.rs              |  16 +
 amadeus/src/app/pages/home.rs                 |   9 +
 amadeus/src/app/pages/playlist.rs             |  23 +
 amadeus/src/app/pages/playlists.rs            |  10 +
 amadeus/src/app/pages/queue.rs                |  35 ++
 amadeus/src/app/pages/search.rs               | 162 ++++++
 amadeus/src/app/playlist_page.rs              |  18 -
 amadeus/src/app/playlists_page.rs             |  18 -
 amadeus/src/app/progress_bar.rs               |  33 ++
 amadeus/src/app/queue_page.rs                 |  18 -
 amadeus/src/app/search_page.rs                |  18 -
 amadeus/src/config.rs                         |   9 +
 amadeus/src/icons.rs                          |  98 ++--
 amadeus/src/main.rs                           |   1 +
 amadeus/src/store.rs                          | 110 ++++
 amadeus/src/store/history.rs                  |  36 ++
 amadeus/src/store/playlist_content.rs         |  47 ++
 amadeus/src/store/playlists.rs                |  35 ++
 amadeus/src/store/queue.rs                    |  37 ++
 amadeus/src/store/queue_level.rs              |  37 ++
 amadeus/src/subscriptions.rs                  |   3 +-
 amadeus/src/subscriptions/lektord.rs          |  39 --
 amadeus/src/subscriptions/playback.rs         |  38 ++
 amadeus/src/subscriptions/updates.rs          |  40 ++
 lektor_nkdb/src/database/kara.rs              |  11 +
 lektor_nkdb/src/playlist/name.rs              |  25 +
 lektor_nkdb/src/queue/priority.rs             |  16 +
 lektor_nkdb/src/search/kara_by.rs             |   2 +-
 lektor_payloads/src/playlist_name.rs          |  25 +-
 lektord/src/c_wrapper/mod.rs                  |   2 +-
 83 files changed, 1957 insertions(+), 805 deletions(-)
 create mode 100644 amadeus/rsc/icons/fontawesome/archive.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/barcodes.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/bug.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/edit.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/filter.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/folder.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/folder_minus.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/folder_plus.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/git.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/hashtag.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/history.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/home.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/link.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/menu.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/meteor.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/minus.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/next.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/pause.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/pin.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/play.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/plus.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/previous.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/queue.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/rust.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/satellite.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/save.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/search.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/shuffle.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/stop.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/tag.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/tags.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/unpin.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/user.svg
 create mode 100644 amadeus/rsc/icons/fontawesome/web.svg
 create mode 100644 amadeus/rsc/icons/iced.svg
 create mode 100644 amadeus/src/app/bottom_bar.rs
 delete mode 100644 amadeus/src/app/config_page.rs
 rename amadeus/src/app/{context_page.rs => context_pages.rs} (83%)
 rename amadeus/src/app/{about_page.rs => context_pages/about.rs} (81%)
 create mode 100644 amadeus/src/app/context_pages/config.rs
 delete mode 100644 amadeus/src/app/history_page.rs
 delete mode 100644 amadeus/src/app/home_page.rs
 create mode 100644 amadeus/src/app/kard.rs
 delete mode 100644 amadeus/src/app/main_page.rs
 rename amadeus/src/app/{menu_bar.rs => menu.rs} (64%)
 create mode 100644 amadeus/src/app/pages.rs
 create mode 100644 amadeus/src/app/pages/history.rs
 create mode 100644 amadeus/src/app/pages/home.rs
 create mode 100644 amadeus/src/app/pages/playlist.rs
 create mode 100644 amadeus/src/app/pages/playlists.rs
 create mode 100644 amadeus/src/app/pages/queue.rs
 create mode 100644 amadeus/src/app/pages/search.rs
 delete mode 100644 amadeus/src/app/playlist_page.rs
 delete mode 100644 amadeus/src/app/playlists_page.rs
 create mode 100644 amadeus/src/app/progress_bar.rs
 delete mode 100644 amadeus/src/app/queue_page.rs
 delete mode 100644 amadeus/src/app/search_page.rs
 create mode 100644 amadeus/src/store.rs
 create mode 100644 amadeus/src/store/history.rs
 create mode 100644 amadeus/src/store/playlist_content.rs
 create mode 100644 amadeus/src/store/playlists.rs
 create mode 100644 amadeus/src/store/queue.rs
 create mode 100644 amadeus/src/store/queue_level.rs
 delete mode 100644 amadeus/src/subscriptions/lektord.rs
 create mode 100644 amadeus/src/subscriptions/playback.rs
 create mode 100644 amadeus/src/subscriptions/updates.rs

diff --git a/Cargo.lock b/Cargo.lock
index 57711d89..998566d4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -153,12 +153,13 @@ name = "amadeus"
 version = "0.0.1"
 dependencies = [
  "anyhow",
+ "async-channel",
  "async-trait",
  "chrono",
  "derive_more",
  "futures",
  "futures-util",
- "hashbrown",
+ "hashbrown 0.15.0",
  "i18n-embed",
  "i18n-embed-fl",
  "lektor_lib",
@@ -168,7 +169,6 @@ dependencies = [
  "lektor_utils",
  "libcosmic",
  "log",
- "open",
  "reqwest",
  "rust-embed",
  "serde",
@@ -498,7 +498,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -527,13 +527,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
 
 [[package]]
 name = "async-trait"
-version = "0.1.82"
+version = "0.1.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
+checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -602,15 +602,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
 name = "axum"
-version = "0.7.6"
+version = "0.7.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
+checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
 dependencies = [
  "async-trait",
  "axum-core",
@@ -634,16 +634,16 @@ dependencies = [
  "serde_path_to_error",
  "sync_wrapper 1.0.1",
  "tokio",
- "tower 0.5.1",
+ "tower",
  "tower-layer",
  "tower-service",
 ]
 
 [[package]]
 name = "axum-core"
-version = "0.4.4"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
+checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
 dependencies = [
  "async-trait",
  "bytes",
@@ -667,7 +667,7 @@ checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -828,7 +828,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -897,9 +897,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.1.21"
+version = "1.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
+checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938"
 dependencies = [
  "jobserver",
  "libc",
@@ -983,7 +983,7 @@ dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1198,7 +1198,7 @@ dependencies = [
 [[package]]
 name = "cosmic-config"
 version = "0.1.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "atomicwrites",
  "cosmic-config-derive",
@@ -1220,7 +1220,7 @@ dependencies = [
 [[package]]
 name = "cosmic-config-derive"
 version = "0.1.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "quote",
  "syn 1.0.109",
@@ -1229,7 +1229,7 @@ dependencies = [
 [[package]]
 name = "cosmic-settings-daemon"
 version = "0.1.0"
-source = "git+https://github.com/pop-os/dbus-settings-bindings#8059e6bdaa35fecd70d228a999ca342fb00d313b"
+source = "git+https://github.com/pop-os/dbus-settings-bindings#01ee80cd975ad3f41a47738ed21d778a7cd07552"
 dependencies = [
  "zbus 4.4.0",
 ]
@@ -1260,7 +1260,7 @@ dependencies = [
 [[package]]
 name = "cosmic-theme"
 version = "0.1.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "almost",
  "cosmic-config",
@@ -1401,7 +1401,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1412,7 +1412,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1423,7 +1423,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
- "hashbrown",
+ "hashbrown 0.14.5",
  "lock_api",
  "once_cell",
  "parking_lot_core 0.9.10",
@@ -1464,7 +1464,7 @@ dependencies = [
  "convert_case",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
  "unicode-xid",
 ]
 
@@ -1477,7 +1477,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1525,7 +1525,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1633,7 +1633,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -1754,9 +1754,9 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
 
 [[package]]
 name = "fdeflate"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
+checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab"
 dependencies = [
  "simd-adler32",
 ]
@@ -1784,9 +1784,9 @@ dependencies = [
 
 [[package]]
 name = "flate2"
-version = "1.0.33"
+version = "1.0.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
+checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
 dependencies = [
  "crc32fast",
  "miniz_oxide 0.8.0",
@@ -1863,6 +1863,12 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "foldhash"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33d5ac82bdbbd872ce0dfea4e848a678cfcfdc0d210a5b20549b8c659fec0392"
+
 [[package]]
 name = "font-types"
 version = "0.6.0"
@@ -1913,7 +1919,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -2048,7 +2054,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -2227,7 +2233,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
 dependencies = [
  "bitflags 2.6.0",
  "gpu-descriptor-types",
- "hashbrown",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
@@ -2292,6 +2298,17 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 dependencies = [
  "ahash",
  "allocator-api2",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
  "serde",
 ]
 
@@ -2382,9 +2399,9 @@ dependencies = [
 
 [[package]]
 name = "httparse"
-version = "1.9.4"
+version = "1.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
 
 [[package]]
 name = "httpdate"
@@ -2433,9 +2450,9 @@ dependencies = [
 
 [[package]]
 name = "hyper-util"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
+checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b"
 dependencies = [
  "bytes",
  "futures-channel",
@@ -2446,7 +2463,6 @@ dependencies = [
  "pin-project-lite",
  "socket2 0.5.7",
  "tokio",
- "tower 0.4.13",
  "tower-service",
  "tracing",
 ]
@@ -2504,7 +2520,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.77",
+ "syn 2.0.79",
  "unic-langid",
 ]
 
@@ -2518,7 +2534,7 @@ dependencies = [
  "i18n-config",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -2547,7 +2563,7 @@ dependencies = [
 [[package]]
 name = "iced"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "dnd",
  "iced_accessibility",
@@ -2565,7 +2581,7 @@ dependencies = [
 [[package]]
 name = "iced_accessibility"
 version = "0.1.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "accesskit",
  "accesskit_winit",
@@ -2574,7 +2590,7 @@ dependencies = [
 [[package]]
 name = "iced_core"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "bitflags 2.6.0",
  "dnd",
@@ -2594,7 +2610,7 @@ dependencies = [
 [[package]]
 name = "iced_futures"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "futures",
  "iced_core",
@@ -2607,7 +2623,7 @@ dependencies = [
 [[package]]
 name = "iced_graphics"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "bitflags 2.6.0",
  "bytemuck",
@@ -2631,7 +2647,7 @@ dependencies = [
 [[package]]
 name = "iced_renderer"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "iced_graphics",
  "iced_tiny_skia",
@@ -2643,7 +2659,7 @@ dependencies = [
 [[package]]
 name = "iced_runtime"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "dnd",
  "iced_core",
@@ -2655,7 +2671,7 @@ dependencies = [
 [[package]]
 name = "iced_style"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "iced_core",
  "once_cell",
@@ -2665,7 +2681,7 @@ dependencies = [
 [[package]]
 name = "iced_tiny_skia"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "bytemuck",
  "cosmic-text",
@@ -2682,7 +2698,7 @@ dependencies = [
 [[package]]
 name = "iced_wgpu"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "as-raw-xcb-connection",
  "bitflags 2.6.0",
@@ -2711,7 +2727,7 @@ dependencies = [
 [[package]]
 name = "iced_widget"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "dnd",
  "iced_renderer",
@@ -2727,7 +2743,7 @@ dependencies = [
 [[package]]
 name = "iced_winit"
 version = "0.12.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "dnd",
  "iced_graphics",
@@ -2810,7 +2826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
 dependencies = [
  "equivalent",
- "hashbrown",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
@@ -2878,25 +2894,6 @@ version = "2.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
 
-[[package]]
-name = "is-docker"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "is-wsl"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
-dependencies = [
- "is-docker",
- "once_cell",
-]
-
 [[package]]
 name = "itoa"
 version = "1.0.11"
@@ -3020,7 +3017,7 @@ dependencies = [
 name = "kurisu_api"
 version = "8.0.1"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.15.0",
  "lektor_utils",
  "serde",
  "serde_json",
@@ -3060,7 +3057,7 @@ name = "lektor_mpris"
 version = "8.0.1"
 dependencies = [
  "derive_more",
- "hashbrown",
+ "hashbrown 0.15.0",
  "lektor_procmacros",
  "lektor_utils",
  "log",
@@ -3077,7 +3074,7 @@ dependencies = [
  "async-trait",
  "chrono",
  "futures",
- "hashbrown",
+ "hashbrown 0.15.0",
  "kurisu_api",
  "lektor_procmacros",
  "lektor_utils",
@@ -3113,7 +3110,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -3122,7 +3119,7 @@ version = "8.0.1"
 dependencies = [
  "anyhow",
  "futures",
- "hashbrown",
+ "hashbrown 0.15.0",
  "kurisu_api",
  "lektor_nkdb",
  "lektor_utils",
@@ -3158,7 +3155,7 @@ dependencies = [
  "axum",
  "clap",
  "futures",
- "hashbrown",
+ "hashbrown 0.15.0",
  "hyper",
  "hyper-util",
  "lektor_mpris",
@@ -3172,19 +3169,19 @@ dependencies = [
  "serde_json",
  "tokio",
  "tokio-stream",
- "tower 0.5.1",
+ "tower",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.158"
+version = "0.2.159"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
 
 [[package]]
 name = "libcosmic"
 version = "0.1.0"
-source = "git+https://github.com/pop-os/libcosmic.git#701638009df09a254b7d077ddc4d1076cd87a147"
+source = "git+https://github.com/pop-os/libcosmic.git#228eb4d70d581be88bacb1e261106a58603d847b"
 dependencies = [
  "apply",
  "ashpd 0.9.1",
@@ -3266,7 +3263,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
 dependencies = [
  "bitflags 2.6.0",
  "libc",
- "redox_syscall 0.5.4",
+ "redox_syscall 0.5.7",
 ]
 
 [[package]]
@@ -3343,7 +3340,7 @@ version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
 dependencies = [
- "hashbrown",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
@@ -3491,7 +3488,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
 dependencies = [
  "adler",
- "simd-adler32",
 ]
 
 [[package]]
@@ -3501,6 +3497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
 dependencies = [
  "adler2",
+ "simd-adler32",
 ]
 
 [[package]]
@@ -3729,7 +3726,7 @@ dependencies = [
  "proc-macro-crate 3.2.0",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -3820,19 +3817,11 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-
-[[package]]
-name = "open"
-version = "5.3.0"
+version = "1.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3"
+checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1"
 dependencies = [
- "is-wsl",
- "libc",
- "pathdiff",
+ "portable-atomic",
 ]
 
 [[package]]
@@ -3857,7 +3846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
 dependencies = [
  "dlv-list",
- "hashbrown",
+ "hashbrown 0.14.5",
 ]
 
 [[package]]
@@ -3891,7 +3880,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -3925,7 +3914,7 @@ dependencies = [
  "by_address",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -3977,7 +3966,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.5.4",
+ "redox_syscall 0.5.7",
  "smallvec",
  "windows-targets 0.52.6",
 ]
@@ -3988,12 +3977,6 @@ version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
-[[package]]
-name = "pathdiff"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
-
 [[package]]
 name = "percent-encoding"
 version = "2.3.1"
@@ -4030,7 +4013,7 @@ dependencies = [
  "phf_shared",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -4048,26 +4031,6 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
 
-[[package]]
-name = "pin-project"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.77",
-]
-
 [[package]]
 name = "pin-project-lite"
 version = "0.2.14"
@@ -4099,15 +4062,15 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
 
 [[package]]
 name = "png"
-version = "0.17.13"
+version = "0.17.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
+checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
 dependencies = [
  "bitflags 1.3.2",
  "crc32fast",
  "fdeflate",
  "flate2",
- "miniz_oxide 0.7.4",
+ "miniz_oxide 0.8.0",
 ]
 
 [[package]]
@@ -4147,6 +4110,12 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
 
+[[package]]
+name = "portable-atomic"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.20"
@@ -4178,7 +4147,7 @@ version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
 dependencies = [
- "toml_edit 0.22.21",
+ "toml_edit 0.22.22",
 ]
 
 [[package]]
@@ -4408,9 +4377,9 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.4"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
+checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
 dependencies = [
  "bitflags 2.6.0",
 ]
@@ -4428,9 +4397,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.6"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -4440,9 +4409,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -4451,9 +4420,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "renderdoc-sys"
@@ -4463,9 +4432,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
 
 [[package]]
 name = "reqwest"
-version = "0.12.7"
+version = "0.12.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
+checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
 dependencies = [
  "base64 0.22.1",
  "bytes",
@@ -4617,7 +4586,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rust-embed-utils",
- "syn 2.0.77",
+ "syn 2.0.79",
  "walkdir",
 ]
 
@@ -4702,19 +4671,18 @@ dependencies = [
 
 [[package]]
 name = "rustls-pemfile"
-version = "2.1.3"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
 dependencies = [
- "base64 0.22.1",
  "rustls-pki-types",
 ]
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.8.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
+checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
 
 [[package]]
 name = "rustls-webpki"
@@ -4838,7 +4806,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -4872,14 +4840,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.7"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
 dependencies = [
  "serde",
 ]
@@ -5209,9 +5177,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.77"
+version = "2.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5255,9 +5223,9 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.12.0"
+version = "3.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
 dependencies = [
  "cfg-if",
  "fastrand 2.1.1",
@@ -5302,7 +5270,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -5415,7 +5383,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -5471,7 +5439,7 @@ dependencies = [
  "serde",
  "serde_spanned",
  "toml_datetime",
- "toml_edit 0.22.21",
+ "toml_edit 0.22.22",
 ]
 
 [[package]]
@@ -5496,30 +5464,15 @@ dependencies = [
 
 [[package]]
 name = "toml_edit"
-version = "0.22.21"
+version = "0.22.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
 dependencies = [
  "indexmap",
  "serde",
  "serde_spanned",
  "toml_datetime",
- "winnow 0.6.18",
-]
-
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project",
- "pin-project-lite",
- "tokio",
- "tower-layer",
- "tower-service",
+ "winnow 0.6.20",
 ]
 
 [[package]]
@@ -5568,7 +5521,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -5702,9 +5655,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-properties"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524"
+checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
 
 [[package]]
 name = "unicode-script"
@@ -5880,7 +5833,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
  "wasm-bindgen-shared",
 ]
 
@@ -5914,7 +5867,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -6617,9 +6570,9 @@ dependencies = [
 
 [[package]]
 name = "winnow"
-version = "0.6.18"
+version = "0.6.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
 dependencies = [
  "memchr",
 ]
@@ -6838,7 +6791,7 @@ dependencies = [
  "proc-macro-crate 3.2.0",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
  "zvariant_utils 2.1.0",
 ]
 
@@ -6888,7 +6841,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
 
 [[package]]
@@ -6956,7 +6909,7 @@ dependencies = [
  "proc-macro-crate 3.2.0",
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
  "zvariant_utils 2.1.0",
 ]
 
@@ -6979,5 +6932,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.77",
+ "syn 2.0.79",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 4c9cd820..693ef9df 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,7 +65,8 @@ lektor_payloads   = { path = "lektor_payloads" }
 lektor_procmacros = { path = "lektor_procmacros" }
 
 # Data Structures
-hashbrown = { version = "*", features = ["serde"] }
+hashbrown     = { version = "*", features = ["serde"] }
+async-channel = { version = "*", default-features = false } 
 
 # Serialization & Deserialization
 toml = "*"
@@ -80,14 +81,14 @@ serde = { version = "*", default-features = false, features = [
 ] }
 
 # Async stuff
-async-trait = "*"
+async-trait  = "*"
 futures-util = "*"
 futures = { version = "*", default-features = false, features = [
     "std",
     "async-await",
 ] }
-tokio = { version = "*", features = [ "full" ] }
-tokio-stream = { version = "*", default-features = false, features = ["net"] }
+tokio        = { version = "*", features = [ "full" ] }
+tokio-stream = { version = "*", features = [ "net" ], default-features = false }
 
 # Web stuff
 reqwest = { version = "*", default-features = false, features = [
diff --git a/README.md b/README.md
index 626c2116..d7020834 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ The lektord and related binaries and source code are under the MIT license. Plea
 
 ### Prerequisites
 
-- [rust](https://www.rust-lang.org) compiler with version >= 1.70
+- [rust](https://www.rust-lang.org) compiler with version >= 1.80
 - [cmake](https://cmake.org/) at least the version 3.18
 - C++ compiler with [C++17 support](https://en.cppreference.com/w/cpp/17)
 - [mpv](https://mpv.io/) development library
@@ -91,8 +91,8 @@ appimagetool --comp xz --sign --sign-key $YOUR_SIGN_KEY Amadeus
 
 ## How to use lektor
 
-> **Important note**: Upgrading the database from version 2.4 to version 3 is not possible. You need
-> to delete the database and re-dl all the karas from kurisu.
+> **Important note**: When upgrading lektor's database to version 8 is not possible from older
+> releases. You need to delete the database and re-dl all the karas from kurisu.
 
 ### Launch instructions
 
diff --git a/amadeus/Cargo.toml b/amadeus/Cargo.toml
index 272ba469..dc72b513 100644
--- a/amadeus/Cargo.toml
+++ b/amadeus/Cargo.toml
@@ -18,14 +18,14 @@ lektor_procmacros.workspace = true
 serde.workspace      = true
 serde_json.workspace = true
 
-log.workspace         = true
-chrono.workspace      = true
-anyhow.workspace      = true
-hashbrown.workspace   = true
-derive_more.workspace = true
+log.workspace           = true
+chrono.workspace        = true
+anyhow.workspace        = true
+hashbrown.workspace     = true
+derive_more.workspace   = true
+async-channel.workspace = true
 
 libcosmic.workspace = true
-open.workspace      = true
 
 futures-util.workspace = true
 tokio.workspace        = true
@@ -38,5 +38,5 @@ rust-embed.workspace    = true
 i18n-embed.workspace    = true
 
 [build-dependencies]
-anyhow.workspace = true
-lektor_utils = { path = "../lektor_utils" }
+anyhow.workspace       = true
+lektor_utils.workspace = true
diff --git a/amadeus/i18n/en/amadeus.ftl b/amadeus/i18n/en/amadeus.ftl
index 41c1e50a..570f09ac 100644
--- a/amadeus/i18n/en/amadeus.ftl
+++ b/amadeus/i18n/en/amadeus.ftl
@@ -29,15 +29,21 @@ search    = Search
 playlists = Playlists
 playlist  = Playlist { $name }
 
-app-config       = Application configuration
-remote-config    = Remote instance configuration
 log-level        = Log level
 icon-theme       = Icon theme
 dark-theme       = Dark theme
-instance-addr    = Address
-instance-port    = Port
-instance-scheme  = Scheme
+address-ip       = Address
+scheme           = Scheme
 kurisu-token     = Kurisu token
 open-url         = Open URL { $url }
 token            = Token
 port             = Port
+status           = Status
+connected        = Connected
+disconnected     = Disconnected
+version          = Version
+info-about       = { $what } information
+epoch            = { $what } epoch
+user             = User
+config           = { $what } configuration
+url-of           = { $what } URL
diff --git a/amadeus/i18n/es-ES/amadeus.ftl b/amadeus/i18n/es-ES/amadeus.ftl
index 6a104b11..4f30527f 100644
--- a/amadeus/i18n/es-ES/amadeus.ftl
+++ b/amadeus/i18n/es-ES/amadeus.ftl
@@ -29,15 +29,21 @@ search    = Buscar
 playlists = Listas de reproducción
 playlist  = Lista { $name }
 
-app-config       = Ajustes de la aplicación
-remote-config    = Ajustes de la instancia remota
 log-level        = Nivel de registro
 icon-theme       = Tema de icono
 dark-theme       = Tema oscuro
-instance-addr    = Dirección IP
-instance-port    = Puerto IP
-instance-scheme  = Esquema
+address-ip       = Dirección IP
+scheme           = Esquema
 kurisu-token     = Token por Kurisu
 open-url         = Abrir URL { $url }
 token            = Token
 port             = Puerto
+status           = Estado
+connected        = Conectado
+disconnected     = Disconectado
+version          = Versión
+info-about       = Información de { $what }
+epoch            = Época de { $what }
+user             = Usuario·a
+config           = Ajustes de { $what }
+url-of           = Dirección de { $what }
diff --git a/amadeus/rsc/icons/fontawesome/archive.svg b/amadeus/rsc/icons/fontawesome/archive.svg
new file mode 100644
index 00000000..d39a6503
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/archive.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64C0 46.3 14.3 32 32 32zm0 128l448 0 0 256c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-256zm128 80c0 8.8 7.2 16 16 16l160 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-160 0c-8.8 0-16 7.2-16 16z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/barcodes.svg b/amadeus/rsc/icons/fontawesome/barcodes.svg
new file mode 100644
index 00000000..3a18d4de
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/barcodes.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M24 32C10.7 32 0 42.7 0 56L0 456c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24L64 56c0-13.3-10.7-24-24-24L24 32zm88 0c-8.8 0-16 7.2-16 16l0 416c0 8.8 7.2 16 16 16s16-7.2 16-16l0-416c0-8.8-7.2-16-16-16zm72 0c-13.3 0-24 10.7-24 24l0 400c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24l0-400c0-13.3-10.7-24-24-24l-16 0zm96 0c-13.3 0-24 10.7-24 24l0 400c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24l0-400c0-13.3-10.7-24-24-24l-16 0zM448 56l0 400c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24l0-400c0-13.3-10.7-24-24-24l-16 0c-13.3 0-24 10.7-24 24zm-64-8l0 416c0 8.8 7.2 16 16 16s16-7.2 16-16l0-416c0-8.8-7.2-16-16-16s-16 7.2-16 16z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/bug.svg b/amadeus/rsc/icons/fontawesome/bug.svg
new file mode 100644
index 00000000..404c8114
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/bug.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c53 0 96 43 96 96l0 3.6c0 15.7-12.7 28.4-28.4 28.4l-135.1 0c-15.7 0-28.4-12.7-28.4-28.4l0-3.6c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4l112 0c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5l64.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-64 0c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6L272 240c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 239.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64.3 0c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/edit.svg b/amadeus/rsc/icons/fontawesome/edit.svg
new file mode 100644
index 00000000..281b9443
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/edit.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M368.4 18.3L312.7 74.1 437.9 199.3l55.7-55.7c21.9-21.9 21.9-57.3 0-79.2L447.6 18.3c-21.9-21.9-57.3-21.9-79.2 0zM288 94.6l-9.2 2.8L134.7 140.6c-19.9 6-35.7 21.2-42.3 41L3.8 445.8c-3.8 11.3-1 23.9 7.3 32.4L164.7 324.7c-3-6.3-4.7-13.3-4.7-20.7c0-26.5 21.5-48 48-48s48 21.5 48 48s-21.5 48-48 48c-7.4 0-14.4-1.7-20.7-4.7L33.7 500.9c8.6 8.3 21.1 11.2 32.4 7.3l264.3-88.6c19.7-6.6 35-22.4 41-42.3l43.2-144.1 2.7-9.2L288 94.6z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/filter.svg b/amadeus/rsc/icons/fontawesome/filter.svg
new file mode 100644
index 00000000..13ec3d87
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/filter.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M3.9 54.9C10.5 40.9 24.5 32 40 32l432 0c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9 320 448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6l0-79.1L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/folder.svg b/amadeus/rsc/icons/fontawesome/folder.svg
new file mode 100644
index 00000000..8534f3ea
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/folder.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/folder_minus.svg b/amadeus/rsc/icons/fontawesome/folder_minus.svg
new file mode 100644
index 00000000..a7d293ec
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/folder_minus.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M448 480L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l128 0c20.1 0 39.1 9.5 51.2 25.6l19.2 25.6c6 8.1 15.5 12.8 25.6 12.8l160 0c35.3 0 64 28.7 64 64l0 256c0 35.3-28.7 64-64 64zM184 272c-13.3 0-24 10.7-24 24s10.7 24 24 24l144 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-144 0z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/folder_plus.svg b/amadeus/rsc/icons/fontawesome/folder_plus.svg
new file mode 100644
index 00000000..cee2b2fd
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/folder_plus.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M512 416c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l128 0c20.1 0 39.1 9.5 51.2 25.6l19.2 25.6c6 8.1 15.5 12.8 25.6 12.8l160 0c35.3 0 64 28.7 64 64l0 256zM232 376c0 13.3 10.7 24 24 24s24-10.7 24-24l0-64 64 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-64 0 0-64c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 64-64 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l64 0 0 64z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/git.svg b/amadeus/rsc/icons/fontawesome/git.svg
new file mode 100644
index 00000000..276b7471
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/git.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M439.6 236.1L244 40.5a28.9 28.9 0 0 0 -40.8 0l-40.7 40.6 51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.2 199v121.9c25.3 12.5 22.3 41.9 9.1 55a34.3 34.3 0 0 1 -48.6 0c-17.6-17.6-11.1-46.9 11.3-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1a28.9 28.9 0 0 0 0 40.8l195.6 195.6a28.9 28.9 0 0 0 40.8 0l194.7-194.7a28.9 28.9 0 0 0 0-40.8z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/hashtag.svg b/amadeus/rsc/icons/fontawesome/hashtag.svg
new file mode 100644
index 00000000..c4969190
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/hashtag.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M181.3 32.4c17.4 2.9 29.2 19.4 26.3 36.8L197.8 128l95.1 0 11.5-69.3c2.9-17.4 19.4-29.2 36.8-26.3s29.2 19.4 26.3 36.8L357.8 128l58.2 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-68.9 0L325.8 320l58.2 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-68.9 0-11.5 69.3c-2.9 17.4-19.4 29.2-36.8 26.3s-29.2-19.4-26.3-36.8l9.8-58.7-95.1 0-11.5 69.3c-2.9 17.4-19.4 29.2-36.8 26.3s-29.2-19.4-26.3-36.8L90.2 384 32 384c-17.7 0-32-14.3-32-32s14.3-32 32-32l68.9 0 21.3-128L64 192c-17.7 0-32-14.3-32-32s14.3-32 32-32l68.9 0 11.5-69.3c2.9-17.4 19.4-29.2 36.8-26.3zM187.1 192L165.8 320l95.1 0 21.3-128-95.1 0z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/history.svg b/amadeus/rsc/icons/fontawesome/history.svg
new file mode 100644
index 00000000..57e646c4
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/history.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9L0 168c0 13.3 10.7 24 24 24l110.1 0c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24l0 104c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65 0-94.1c0-13.3-10.7-24-24-24z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/home.svg b/amadeus/rsc/icons/fontawesome/home.svg
new file mode 100644
index 00000000..def72ba2
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/home.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/link.svg b/amadeus/rsc/icons/fontawesome/link.svg
new file mode 100644
index 00000000..0e701db3
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/link.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/menu.svg b/amadeus/rsc/icons/fontawesome/menu.svg
new file mode 100644
index 00000000..d5edfefe
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/menu.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/meteor.svg b/amadeus/rsc/icons/fontawesome/meteor.svg
new file mode 100644
index 00000000..a4684e4a
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/meteor.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M493.7 .9L299.4 75.6l2.3-29.3c1-12.8-12.8-21.5-24-15.1L101.3 133.4C38.6 169.7 0 236.6 0 309C0 421.1 90.9 512 203 512c72.4 0 139.4-38.6 175.7-101.3L480.8 234.3c6.5-11.1-2.2-25-15.1-24l-29.3 2.3L511.1 18.3c.6-1.5 .9-3.2 .9-4.8C512 6 506 0 498.5 0c-1.7 0-3.3 .3-4.8 .9zM192 192a128 128 0 1 1 0 256 128 128 0 1 1 0-256zm0 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm16 96a16 16 0 1 0 0-32 16 16 0 1 0 0 32z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/minus.svg b/amadeus/rsc/icons/fontawesome/minus.svg
new file mode 100644
index 00000000..a7d293ec
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/minus.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M448 480L64 480c-35.3 0-64-28.7-64-64L0 96C0 60.7 28.7 32 64 32l128 0c20.1 0 39.1 9.5 51.2 25.6l19.2 25.6c6 8.1 15.5 12.8 25.6 12.8l160 0c35.3 0 64 28.7 64 64l0 256c0 35.3-28.7 64-64 64zM184 272c-13.3 0-24 10.7-24 24s10.7 24 24 24l144 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-144 0z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/next.svg b/amadeus/rsc/icons/fontawesome/next.svg
new file mode 100644
index 00000000..7370e1e2
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/next.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416L0 96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4L224 214.3l0 41.7 0 41.7L52.5 440.6zM256 352l0-96 0-128 0-32c0-12.4 7.2-23.7 18.4-29s24.5-3.6 34.1 4.4l192 160c7.3 6.1 11.5 15.1 11.5 24.6s-4.2 18.5-11.5 24.6l-192 160c-9.5 7.9-22.8 9.7-34.1 4.4s-18.4-16.6-18.4-29l0-64z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/pause.svg b/amadeus/rsc/icons/fontawesome/pause.svg
new file mode 100644
index 00000000..c97d01e1
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/pause.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M48 64C21.5 64 0 85.5 0 112L0 400c0 26.5 21.5 48 48 48l32 0c26.5 0 48-21.5 48-48l0-288c0-26.5-21.5-48-48-48L48 64zm192 0c-26.5 0-48 21.5-48 48l0 288c0 26.5 21.5 48 48 48l32 0c26.5 0 48-21.5 48-48l0-288c0-26.5-21.5-48-48-48l-32 0z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/pin.svg b/amadeus/rsc/icons/fontawesome/pin.svg
new file mode 100644
index 00000000..442bf1fc
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/pin.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M32 32C32 14.3 46.3 0 64 0L320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-29.5 0 11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3L32 352c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64 64 64C46.3 64 32 49.7 32 32zM160 384l64 0 0 96c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-96z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/play.svg b/amadeus/rsc/icons/fontawesome/play.svg
new file mode 100644
index 00000000..26253bb2
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/play.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/plus.svg b/amadeus/rsc/icons/fontawesome/plus.svg
new file mode 100644
index 00000000..f20b235f
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/plus.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/previous.svg b/amadeus/rsc/icons/fontawesome/previous.svg
new file mode 100644
index 00000000..ea6edb03
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/previous.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M459.5 440.6c9.5 7.9 22.8 9.7 34.1 4.4s18.4-16.6 18.4-29l0-320c0-12.4-7.2-23.7-18.4-29s-24.5-3.6-34.1 4.4L288 214.3l0 41.7 0 41.7L459.5 440.6zM256 352l0-96 0-128 0-32c0-12.4-7.2-23.7-18.4-29s-24.5-3.6-34.1 4.4l-192 160C4.2 237.5 0 246.5 0 256s4.2 18.5 11.5 24.6l192 160c9.5 7.9 22.8 9.7 34.1 4.4s18.4-16.6 18.4-29l0-64z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/queue.svg b/amadeus/rsc/icons/fontawesome/queue.svg
new file mode 100644
index 00000000..6be2a2ea
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/queue.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M24 56c0-13.3 10.7-24 24-24l32 0c13.3 0 24 10.7 24 24l0 120 16 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l16 0 0-96-8 0C34.7 80 24 69.3 24 56zM86.7 341.2c-6.5-7.4-18.3-6.9-24 1.2L51.5 357.9c-7.7 10.8-22.7 13.3-33.5 5.6s-13.3-22.7-5.6-33.5l11.1-15.6c23.7-33.2 72.3-35.6 99.2-4.9c21.3 24.4 20.8 60.9-1.1 84.7L86.8 432l33.2 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-88 0c-9.5 0-18.2-5.6-22-14.4s-2.1-18.9 4.3-25.9l72-78c5.3-5.8 5.4-14.6 .3-20.5zM224 64l256 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-256 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 160l256 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-256 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 160l256 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-256 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/rust.svg b/amadeus/rsc/icons/fontawesome/rust.svg
new file mode 100644
index 00000000..b65b98d9
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/rust.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M508.5 249.8 486.7 236.2c-.2-2-.3-3.9-.6-5.9l18.7-17.5a7.4 7.4 0 0 0 -2.4-12.3l-24-9c-.5-1.9-1.1-3.8-1.7-5.6l15-20.8a7.4 7.4 0 0 0 -4.8-11.5l-25.4-4.2c-.9-1.7-1.8-3.5-2.7-5.2l10.7-23.4a7.4 7.4 0 0 0 -7-10.4l-25.8 .9q-1.8-2.2-3.6-4.4L439 81.8A7.4 7.4 0 0 0 430.2 73L405 78.9q-2.2-1.8-4.4-3.6l.9-25.8a7.4 7.4 0 0 0 -10.4-7L367.7 53.2c-1.7-.9-3.4-1.8-5.2-2.7L358.4 25.1a7.4 7.4 0 0 0 -11.5-4.8L326 35.3c-1.9-.6-3.8-1.1-5.6-1.7l-9-24a7.4 7.4 0 0 0 -12.3-2.4l-17.5 18.7c-2-.2-3.9-.4-5.9-.6L262.3 3.5a7.4 7.4 0 0 0 -12.5 0L236.2 25.3c-2 .2-3.9 .3-5.9 .6L212.9 7.1a7.4 7.4 0 0 0 -12.3 2.4l-9 24c-1.9 .6-3.8 1.1-5.7 1.7l-20.8-15a7.4 7.4 0 0 0 -11.5 4.8l-4.2 25.4c-1.7 .9-3.5 1.8-5.2 2.7L120.9 42.6a7.4 7.4 0 0 0 -10.4 7l.9 25.8c-1.5 1.2-3 2.4-4.4 3.6L81.8 73A7.4 7.4 0 0 0 73 81.8L78.9 107c-1.2 1.5-2.4 2.9-3.6 4.4l-25.8-.9a7.4 7.4 0 0 0 -6.4 3.3 7.4 7.4 0 0 0 -.6 7.1l10.7 23.4c-.9 1.7-1.8 3.4-2.7 5.2L25.1 153.6a7.4 7.4 0 0 0 -4.8 11.5l15 20.8c-.6 1.9-1.1 3.8-1.7 5.7l-24 9a7.4 7.4 0 0 0 -2.4 12.3l18.7 17.5c-.2 2-.4 3.9-.6 5.9L3.5 249.8a7.4 7.4 0 0 0 0 12.5L25.3 275.8c.2 2 .3 3.9 .6 5.9L7.1 299.1a7.4 7.4 0 0 0 2.4 12.3l24 9c.6 1.9 1.1 3.8 1.7 5.7l-15 20.8a7.4 7.4 0 0 0 4.8 11.5l25.4 4.2c.9 1.7 1.8 3.5 2.7 5.1L42.6 391.1a7.4 7.4 0 0 0 .6 7.1 7.1 7.1 0 0 0 6.4 3.3l25.8-.9q1.8 2.2 3.6 4.4L73 430.2A7.4 7.4 0 0 0 81.8 439L107 433.1q2.2 1.8 4.4 3.6l-.9 25.8a7.4 7.4 0 0 0 10.4 7l23.4-10.7c1.7 .9 3.4 1.8 5.1 2.7l4.2 25.4a7.3 7.3 0 0 0 11.5 4.8l20.8-15c1.9 .6 3.8 1.1 5.7 1.7l9 24a7.4 7.4 0 0 0 12.3 2.4l17.5-18.7c2 .2 3.9 .4 5.9 .6l13.5 21.8a7.4 7.4 0 0 0 12.5 0l13.5-21.8c2-.2 3.9-.3 5.9-.6l17.5 18.7a7.4 7.4 0 0 0 12.3-2.4l9-24c1.9-.6 3.8-1.1 5.7-1.7l20.8 15a7.3 7.3 0 0 0 11.5-4.8l4.2-25.4c1.7-.9 3.5-1.8 5.2-2.7l23.4 10.7a7.4 7.4 0 0 0 10.4-7l-.9-25.8q2.2-1.8 4.4-3.6L430.2 439a7.4 7.4 0 0 0 8.8-8.8L433.1 405q1.8-2.2 3.6-4.4l25.8 .9a7.2 7.2 0 0 0 6.4-3.3 7.4 7.4 0 0 0 .6-7.1L458.8 367.7c.9-1.7 1.8-3.4 2.7-5.2l25.4-4.2a7.4 7.4 0 0 0 4.8-11.5l-15-20.8c.6-1.9 1.1-3.8 1.7-5.7l24-9a7.4 7.4 0 0 0 2.4-12.3l-18.7-17.5c.2-2 .4-3.9 .6-5.9l21.8-13.5a7.4 7.4 0 0 0 0-12.5zm-151 129.1A13.9 13.9 0 0 0 341 389.5l-7.6 35.7A187.5 187.5 0 0 1 177 424.4l-7.6-35.7a13.9 13.9 0 0 0 -16.5-10.7l-31.5 6.8a187.4 187.4 0 0 1 -16.3-19.2H258.3c1.7 0 2.9-.3 2.9-1.9V309.6c0-1.6-1.2-1.9-2.9-1.9H213.5l.1-34.4H262c4.4 0 23.7 1.3 29.8 25.9 1.9 7.6 6.2 32.1 9.1 40 2.9 8.8 14.6 26.5 27.1 26.5H407a187.3 187.3 0 0 1 -17.3 20.1zm25.8 34.5A15.2 15.2 0 1 1 368 398.1h.4A15.2 15.2 0 0 1 383.2 413.3zm-225.6-.7a15.2 15.2 0 1 1 -15.3-15.3h.5A15.3 15.3 0 0 1 157.6 412.6zM69.6 234.2l32.8-14.6a13.9 13.9 0 0 0 7.1-18.3L102.7 186h26.6V305.7H75.7A187.7 187.7 0 0 1 69.6 234.2zM58.3 198.1a15.2 15.2 0 0 1 15.2-15.3H74a15.2 15.2 0 1 1 -15.7 15.2zm155.2 24.5 .1-35.3h63.3c3.3 0 23.1 3.8 23.1 18.6 0 12.3-15.2 16.7-27.7 16.7zM399 306.7c-9.8 1.1-20.6-4.1-22-10.1-5.8-32.5-15.4-39.4-30.6-51.4 18.9-12 38.5-29.6 38.5-53.3 0-25.5-17.5-41.6-29.4-49.5-16.8-11-35.3-13.2-40.3-13.2H116.3A187.5 187.5 0 0 1 221.2 70.1l23.5 24.6a13.8 13.8 0 0 0 19.6 .4l26.3-25a187.5 187.5 0 0 1 128.4 91.4l-18 40.6A14 14 0 0 0 408 220.4l34.6 15.3a187.1 187.1 0 0 1 .4 32.5H423.7c-1.9 0-2.7 1.3-2.7 3.1v8.8C421 301 409.3 305.6 399 306.7zM240 60.2A15.2 15.2 0 0 1 255.2 45h.5A15.2 15.2 0 1 1 240 60.2zM436.8 214a15.2 15.2 0 1 1 0-30.5h.4a15.2 15.2 0 0 1 -.4 30.5z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/satellite.svg b/amadeus/rsc/icons/fontawesome/satellite.svg
new file mode 100644
index 00000000..93cc0442
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/satellite.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M192 32c0-17.7 14.3-32 32-32C383.1 0 512 128.9 512 288c0 17.7-14.3 32-32 32s-32-14.3-32-32C448 164.3 347.7 64 224 64c-17.7 0-32-14.3-32-32zM60.6 220.6L164.7 324.7l28.4-28.4c-.7-2.6-1.1-5.4-1.1-8.3c0-17.7 14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32c-2.9 0-5.6-.4-8.3-1.1l-28.4 28.4L291.4 451.4c14.5 14.5 11.8 38.8-7.3 46.3C260.5 506.9 234.9 512 208 512C93.1 512 0 418.9 0 304c0-26.9 5.1-52.5 14.4-76.1c7.5-19 31.8-21.8 46.3-7.3zM224 96c106 0 192 86 192 192c0 17.7-14.3 32-32 32s-32-14.3-32-32c0-70.7-57.3-128-128-128c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/save.svg b/amadeus/rsc/icons/fontawesome/save.svg
new file mode 100644
index 00000000..0fb7c648
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/save.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm0 96c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32L96 224c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/search.svg b/amadeus/rsc/icons/fontawesome/search.svg
new file mode 100644
index 00000000..f5770fcf
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/search.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/shuffle.svg b/amadeus/rsc/icons/fontawesome/shuffle.svg
new file mode 100644
index 00000000..47a40dce
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/shuffle.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M403.8 34.4c12-5 25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64c-9.2 9.2-22.9 11.9-34.9 6.9s-19.8-16.6-19.8-29.6l0-32-32 0c-10.1 0-19.6 4.7-25.6 12.8L284 229.3 244 176l31.2-41.6C293.3 110.2 321.8 96 352 96l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6zM164 282.7L204 336l-31.2 41.6C154.7 401.8 126.2 416 96 416l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c10.1 0 19.6-4.7 25.6-12.8L164 282.7zm274.6 188c-9.2 9.2-22.9 11.9-34.9 6.9s-19.8-16.6-19.8-29.6l0-32-32 0c-30.2 0-58.7-14.2-76.8-38.4L121.6 172.8c-6-8.1-15.5-12.8-25.6-12.8l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c30.2 0 58.7 14.2 76.8 38.4L326.4 339.2c6 8.1 15.5 12.8 25.6 12.8l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/stop.svg b/amadeus/rsc/icons/fontawesome/stop.svg
new file mode 100644
index 00000000..3925382c
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/stop.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/tag.svg b/amadeus/rsc/icons/fontawesome/tag.svg
new file mode 100644
index 00000000..d4d29469
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/tag.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 80L0 229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7L48 32C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/tags.svg b/amadeus/rsc/icons/fontawesome/tags.svg
new file mode 100644
index 00000000..3fa32c9e
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/tags.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M345 39.1L472.8 168.4c52.4 53 52.4 138.2 0 191.2L360.8 472.9c-9.3 9.4-24.5 9.5-33.9 .2s-9.5-24.5-.2-33.9L438.6 325.9c33.9-34.3 33.9-89.4 0-123.7L310.9 72.9c-9.3-9.4-9.2-24.6 .2-33.9s24.6-9.2 33.9 .2zM0 229.5L0 80C0 53.5 21.5 32 48 32l149.5 0c17 0 33.3 6.7 45.3 18.7l168 168c25 25 25 65.5 0 90.5L277.3 442.7c-25 25-65.5 25-90.5 0l-168-168C6.7 262.7 0 246.5 0 229.5zM144 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/unpin.svg b/amadeus/rsc/icons/fontawesome/unpin.svg
new file mode 100644
index 00000000..dc809686
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/unpin.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L481.4 352c9.8-.4 18.9-5.3 24.6-13.3c6-8.3 7.7-19.1 4.4-28.8l-1-3c-13.8-41.5-42.8-74.8-79.5-94.7L418.5 64 448 64c17.7 0 32-14.3 32-32s-14.3-32-32-32L192 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l29.5 0-6.1 79.5L38.8 5.1zM324.9 352L177.1 235.6c-20.9 18.9-37.2 43.3-46.5 71.3l-1 3c-3.3 9.8-1.6 20.5 4.4 28.8s15.7 13.3 26 13.3l164.9 0zM288 384l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96-64 0z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/user.svg b/amadeus/rsc/icons/fontawesome/user.svg
new file mode 100644
index 00000000..188f9cbb
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/user.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M370.7 96.1C346.1 39.5 289.7 0 224 0S101.9 39.5 77.3 96.1C60.9 97.5 48 111.2 48 128l0 64c0 16.8 12.9 30.5 29.3 31.9C101.9 280.5 158.3 320 224 320s122.1-39.5 146.7-96.1c16.4-1.4 29.3-15.1 29.3-31.9l0-64c0-16.8-12.9-30.5-29.3-31.9zM336 144l0 16c0 53-43 96-96 96l-32 0c-53 0-96-43-96-96l0-16c0-26.5 21.5-48 48-48l128 0c26.5 0 48 21.5 48 48zM189.3 162.7l-6-21.2c-.9-3.3-3.9-5.5-7.3-5.5s-6.4 2.2-7.3 5.5l-6 21.2-21.2 6c-3.3 .9-5.5 3.9-5.5 7.3s2.2 6.4 5.5 7.3l21.2 6 6 21.2c.9 3.3 3.9 5.5 7.3 5.5s6.4-2.2 7.3-5.5l6-21.2 21.2-6c3.3-.9 5.5-3.9 5.5-7.3s-2.2-6.4-5.5-7.3l-21.2-6zM112.7 316.5C46.7 342.6 0 407 0 482.3C0 498.7 13.3 512 29.7 512l98.3 0 0-64c0-17.7 14.3-32 32-32l128 0c17.7 0 32 14.3 32 32l0 64 98.3 0c16.4 0 29.7-13.3 29.7-29.7c0-75.3-46.7-139.7-112.7-165.8C303.9 338.8 265.5 352 224 352s-79.9-13.2-111.3-35.5zM176 448c-8.8 0-16 7.2-16 16l0 48 32 0 0-48c0-8.8-7.2-16-16-16zm96 32a16 16 0 1 0 0-32 16 16 0 1 0 0 32z"/></svg>
diff --git a/amadeus/rsc/icons/fontawesome/web.svg b/amadeus/rsc/icons/fontawesome/web.svg
new file mode 100644
index 00000000..c48c28e8
--- /dev/null
+++ b/amadeus/rsc/icons/fontawesome/web.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M352 256c0 22.2-1.2 43.6-3.3 64l-185.3 0c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64l185.3 0c2.2 20.4 3.3 41.8 3.3 64zm28.8-64l123.1 0c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64l-123.1 0c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32l-116.7 0c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0l-176.6 0c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0L18.6 160C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192l123.1 0c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64L8.1 320C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6l176.6 0c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352l116.7 0zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6l116.7 0z"/></svg>
diff --git a/amadeus/rsc/icons/iced.svg b/amadeus/rsc/icons/iced.svg
new file mode 100644
index 00000000..9f39c856
--- /dev/null
+++ b/amadeus/rsc/icons/iced.svg
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="169.895"
+   height="169.895"
+   viewBox="0 0 169.895 169.895"
+   fill="none"
+   version="1.1"
+   id="svg8"
+   sodipodi:docname="iced.svg"
+   inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg">
+  <sodipodi:namedview
+     id="namedview8"
+     pagecolor="#505050"
+     bordercolor="#eeeeee"
+     borderopacity="1"
+     inkscape:showpageshadow="0"
+     inkscape:pageopacity="0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#505050"
+     showgrid="false"
+     inkscape:zoom="2.6782765"
+     inkscape:cx="80.835568"
+     inkscape:cy="56.566229"
+     inkscape:window-width="1920"
+     inkscape:window-height="1054"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg8" />
+  <rect
+     x="0"
+     y="0"
+     width="169.895"
+     height="169.895"
+     rx="49.8148"
+     fill="url(#paint1_linear)"
+     id="rect2"
+     style="fill:url(#paint1_linear)"
+     ry="49.8148" />
+  <path
+     fill-rule="evenodd"
+     clip-rule="evenodd"
+     d="m 140.623,34.745901 -28.136,28.606 -6.13,-6.0291 28.136,-28.606 z m -26.344,0.218 -42.204,42.9091 -6.13,-6.029 42.204,-42.9092 z m -61.6476,23.9129 c 5.3254,-5.3831 10.6496,-10.7651 21.5686,-21.867 l 6.13,6.0291 c -10.927,11.1102 -16.258,16.4984 -21.587,21.8853 -4.4007,4.4488 -8.8009,8.8968 -16.3587,16.5728 l 31.9767,8.358 25.968,-26.4022 6.13,6.0292 -25.968,26.402 8.907,31.907999 42.138,-42.086999 6.076,6.083 L 88.503,140.838 42.6662,128.21 29.2725,82.564001 l 1.7714,-1.801 c 10.9276,-11.111 16.2585,-16.4994 21.5875,-21.8862 z M 81.05,129.867 72.204,98.178001 l -31.8307,-8.32 9.1945,31.334999 z m 47.734,-56.516999 7.122,-7.1221 -6.08,-6.0797 -7.147,7.1474 -30.171,30.6744 6.13,6.028999 z"
+     fill="url(#paint2_linear)"
+     id="path3"
+     style="fill:url(#paint2_linear)" />
+  <defs
+     id="defs8">
+    <filter
+       id="filter0_f"
+       x="55"
+       y="47.000999"
+       width="144"
+       height="168"
+       filterUnits="userSpaceOnUse"
+       color-interpolation-filters="sRGB">
+      <feFlood
+         flood-opacity="0"
+         result="BackgroundImageFix"
+         id="feFlood3" />
+      <feBlend
+         mode="normal"
+         in="SourceGraphic"
+         in2="BackgroundImageFix"
+         result="shape"
+         id="feBlend3" />
+      <feGaussianBlur
+         stdDeviation="2"
+         result="effect1_foregroundBlur"
+         id="feGaussianBlur3" />
+    </filter>
+    <linearGradient
+       id="paint0_linear"
+       x1="127"
+       y1="51.000999"
+       x2="127"
+       y2="211.00101"
+       gradientUnits="userSpaceOnUse">
+      <stop
+         offset="0.0520833"
+         id="stop3" />
+      <stop
+         offset="1"
+         stop-opacity="0.08"
+         id="stop4" />
+    </linearGradient>
+    <linearGradient
+       id="paint1_linear"
+       x1="212"
+       y1="31.000999"
+       x2="57.5"
+       y2="189.00101"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-42,-31.000999)">
+      <stop
+         stop-color="#00A3FF"
+         id="stop5" />
+      <stop
+         offset="1"
+         stop-color="#3300FF"
+         id="stop6" />
+    </linearGradient>
+    <linearGradient
+       id="paint2_linear"
+       x1="86.098099"
+       y1="158.278"
+       x2="206.01401"
+       y2="35.326599"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-42,-31.000999)">
+      <stop
+         stop-color="white"
+         id="stop7" />
+      <stop
+         offset="1"
+         stop-color="white"
+         id="stop8" />
+    </linearGradient>
+  </defs>
+</svg>
diff --git a/amadeus/src/app.rs b/amadeus/src/app.rs
index 8e544f6a..df1846dc 100644
--- a/amadeus/src/app.rs
+++ b/amadeus/src/app.rs
@@ -1,42 +1,38 @@
-mod about_page;
-mod config_page;
-mod context_page;
-mod history_page;
-mod home_page;
-mod main_page;
-mod menu_bar;
-mod playlist_page;
-mod playlists_page;
-mod queue_page;
-mod search_page;
+mod bottom_bar;
+mod context_pages;
+mod kard;
+mod menu;
+mod pages;
+mod progress_bar;
 
 use crate::{
     app::{
-        context_page::ContextPage,
-        main_page::Page,
-        menu_bar::{MenuAction, MenuHeaderCentralCommands},
+        context_pages::ContextPage,
+        menu::MenuAction,
+        pages::{search::Filter, Page},
     },
     config::{Config, LogLevel},
-    fl, subscriptions,
+    fl,
+    store::Store,
+    subscriptions,
 };
 use cosmic::{
     app::{Command, Core},
     iced::{
         alignment::{Horizontal, Vertical},
-        Alignment, Length, Subscription,
+        Length, Subscription,
     },
-    prelude::CollectionWidget as _,
-    theme,
-    widget::{
-        self,
-        menu::{self, Action},
-        nav_bar,
-    },
-    Application, ApplicationExt, Apply, Element,
+    prelude::*,
+    theme, widget, Application,
 };
 use lektor_lib::ConnectConfig;
-use lektor_utils::config::SocketScheme;
+use lektor_payloads::{
+    KId, PlayStateWithCurrent, Playlist, PlaylistName, Priority, PRIORITY_LENGTH, PRIORITY_VALUES,
+};
+use lektor_utils::{config::SocketScheme, open};
+use pages::search::{FilterAtom, FilterAtomId};
 use std::{
+    borrow::Cow,
     collections::HashMap,
     mem,
     net::{IpAddr, Ipv4Addr, SocketAddr},
@@ -56,14 +52,11 @@ pub struct AppModel {
     /// Display a context drawer with the designated page if defined.
     context_page: ContextPage,
 
-    /// The central buttons to command the playback.
-    header_controls: MenuHeaderCentralCommands,
-
     /// Contains items assigned to the nav bar panel.
-    nav: nav_bar::Model,
+    nav: widget::nav_bar::Model,
 
     /// Key bindings for the application's menu bar.
-    key_binds: HashMap<menu::KeyBind, MenuAction>,
+    key_binds: HashMap<widget::menu::KeyBind, MenuAction>,
 
     /// Configuration data that persists between application runs.
     config: Config,
@@ -77,6 +70,15 @@ pub struct AppModel {
     /// Informations about the lektord server.
     lektord_state: LektordState,
 
+    /// The store where we cache the playlists, queue, history, etc.
+    store: Store,
+
+    /// The search filter, from the [search page](pages::search).
+    search_filter: pages::search::Filter,
+
+    /// The search results, stored in the application and not in the filter.
+    search_results: Vec<KId>,
+
     // Temporary things for the remote instance
     tmp_remote_addr: String,
     tmp_remote_port: String,
@@ -96,9 +98,35 @@ enum LektordState {
     },
 }
 
+impl LektordState {
+    /// Get the current kara we are playing, if any.
+    pub fn current_kid(&self) -> Option<&KId> {
+        match self {
+            LektordState::Disconnected => None,
+            LektordState::Connected { state, .. } => {
+                (state.as_ref()).and_then(|PlayStateWithCurrent { current, .. }| {
+                    current.as_ref().map(|(kid, ..)| kid)
+                })
+            }
+        }
+    }
+
+    /// Get the times, first the current time in the kara, then the duration of the current kara,
+    /// if any.
+    pub fn current_times(&self) -> Option<(f32, f32)> {
+        match self {
+            LektordState::Disconnected => None,
+            LektordState::Connected { state, .. } => {
+                (state.as_ref()).and_then(|PlayStateWithCurrent { current, .. }| {
+                    (current.as_ref()).map(|(_, elapse, duration)| (*elapse, *duration))
+                })
+            }
+        }
+    }
+}
+
 /// A command to send to the lektord instance.
 #[derive(Debug, Clone, Copy)]
-#[non_exhaustive]
 pub enum LektordCommand {
     PlaybackToggle,
     PlaybackPlay,
@@ -112,31 +140,55 @@ pub enum LektordCommand {
     QueueCrop,
 }
 
-/// Messages emitted by the application and its widgets.
+/// Something changed with the config.
 #[derive(Debug, Clone)]
-pub enum Message {
-    OpenUrl(&'static str),
-    ToggleContextPage(ContextPage),
-
-    UpdateConfig(Config),
+pub enum ConfigMessage {
+    Written(Config),
     ChangeTheme(theme::Theme),
     ChangeLogLevel(LogLevel),
-    ChangeKurisuToken(String),
+    ChangeKurisuToken(Cow<'static, str>),
     ChangeURLScheme(SocketScheme),
-    ChangeRemoteAddr(String),
-    ChangeRemotePort(String),
-    ChangeUserName(String),
-    ChangeUserToken(String),
+    ChangeRemoteAddr(Cow<'static, str>),
+    ChangeRemotePort(Cow<'static, str>),
+    ChangeUserName(Cow<'static, str>),
+    ChangeUserToken(Cow<'static, str>),
+    ChangeKurisuURL(Cow<'static, str>),
+}
+
+/// Something changed with the lektord instance we are connected to.
+#[derive(Debug, Clone)]
+pub enum LektordMessage {
+    Disconnected,
+    Connected(lektor_payloads::Infos),
+    PlaybackUpdate(lektor_payloads::PlayStateWithCurrent),
 
-    LektordDisconnected,
-    LektordConnected(lektor_payloads::Infos),
-    LektordUpdate(lektor_payloads::PlayStateWithCurrent),
+    DownloadKaraInfo(KId),
+    DownloadKarasInfo(Vec<KId>),
+
+    ChangedQueue([Vec<KId>; PRIORITY_LENGTH]),
+    ChangedQueueLevel(Priority, Vec<KId>),
+    ChangedHistory(Vec<KId>),
+    ChangedAvailablePlaylists(Vec<PlaylistName>),
+    ChangedPlaylistContent(PlaylistName, Playlist),
+}
+
+/// Messages emitted by the application and its widgets.
+#[derive(Debug, Clone)]
+pub enum Message {
+    OpenUrl(&'static str),
+    OpenKaraInfo(KId),
+    ToggleContextPage(ContextPage),
 
-    /// A button in the header is clicked.
-    HeaderCommand(widget::segmented_button::Entity),
+    UpdateConfig(ConfigMessage),
 
-    /// Send a command to the lektord instance.
     SendCommand(LektordCommand),
+    Lektord(LektordMessage),
+
+    ChangeFilterStageBuffer(String),
+    AddFilterAtomFromStageBuffer,
+    RemoveFilterAtom(FilterAtomId),
+    ClearFilters,
+    QueryWithFilters,
 }
 
 /// Create a COSMIC application from the app model
@@ -164,12 +216,14 @@ impl Application for AppModel {
         let host = config.host().1;
 
         let mut app = AppModel {
-            nav: main_page::nav_bar_model(),
+            nav: pages::nav_bar_model(),
             key_binds: Default::default(),
             context_page: Default::default(),
-            header_controls: Default::default(),
 
             lektord_state: LektordState::Disconnected,
+            store: Store::default(),
+            search_filter: Filter::default(),
+            search_results: Vec::default(),
 
             tmp_remote_addr: host.ip().to_string(),
             tmp_remote_port: host.port().to_string(),
@@ -189,87 +243,235 @@ impl Application for AppModel {
 
     /// Elements to pack at the start of the header bar.
     fn header_start(&self) -> Vec<Element<Self::Message>> {
-        vec![menu_bar::header_menu(&self.key_binds)]
+        vec![menu::header_menu(&self.key_binds)]
+    }
+
+    /// Elements to pack at the end of the header bar.
+    fn header_end(&self) -> Vec<Element<Self::Message>> {
+        vec![progress_bar::view(self)]
     }
 
     /// Elements to place at the center of the header bar.
     fn header_center(&self) -> Vec<Element<Self::Message>> {
-        vec![
-            widget::horizontal_space(Length::Fill).into(),
-            self.header_controls.view(),
-            widget::horizontal_space(Length::Fill).into(),
-        ]
+        let is_connected = !matches!(self.lektord_state, LektordState::Disconnected);
+        macro_rules! icon {
+            ($icon:ident => $action:ident) => {{
+                let button = widget::icon::from_svg_bytes(crate::icons::$icon)
+                    .symbolic(true)
+                    .apply(widget::button::icon);
+                Element::from(match is_connected {
+                    true => button.on_press(Message::SendCommand(LektordCommand::$action)),
+                    false => button,
+                })
+            }};
+        }
+
+        let pre = [
+            Element::from(widget::horizontal_space(Length::Fill)),
+            icon!(PREVIOUS => PlaybackPrevious),
+        ];
+
+        let post = [
+            icon!(NEXT    => PlaybackNext),
+            icon!(SHUFFLE => QueueShuffle),
+            icon!(METEOR  => QueueClear),
+            Element::from(widget::horizontal_space(Length::Fill)),
+        ];
+
+        let infix = match &self.lektord_state {
+            LektordState::Connected { state: None, .. } | LektordState::Disconnected => {
+                [Some(icon!(PLAY => PlaybackPlay)), None]
+            }
+
+            LektordState::Connected {
+                state: Some(state), ..
+            } if state.state == lektor_payloads::PlayState::Stop => {
+                [Some(icon!(PLAY => PlaybackPlay)), None]
+            }
+
+            LektordState::Connected {
+                state: Some(state), ..
+            } if state.state == lektor_payloads::PlayState::Pause => [
+                Some(icon!(PLAY => PlaybackPlay)),
+                Some(icon!(STOP => PlaybackStop)),
+            ],
+
+            LektordState::Connected {
+                state: Some(state), ..
+            } if state.state == lektor_payloads::PlayState::Play => [
+                Some(icon!(PAUSE => PlaybackPause)),
+                Some(icon!(STOP  => PlaybackStop)),
+            ],
+
+            _ => [None, None],
+        };
+
+        pre.into_iter()
+            .chain(infix.into_iter().flatten())
+            .chain(post)
+            .collect()
     }
 
     /// Enables the COSMIC application to create a nav bar with this model.
-    fn nav_model(&self) -> Option<&nav_bar::Model> {
+    fn nav_model(&self) -> Option<&widget::nav_bar::Model> {
         Some(&self.nav)
     }
 
     /// Display a context drawer if the context page is requested.
     fn context_drawer(&self) -> Option<Element<Self::Message>> {
         (self.core.window.show_context).then(|| match self.context_page {
-            ContextPage::About => about_page::view(self),
-            ContextPage::Settings => config_page::view(self),
+            ContextPage::About => context_pages::about::view(self),
+            ContextPage::Settings => context_pages::config::view(self),
         })
     }
 
     /// Describes the interface based on the current state of the application model.
     fn view(&self) -> Element<Self::Message> {
-        match self.nav.active_data::<Page>() {
-            Some(Page::Home) => home_page::view(),
-            Some(Page::Queue) => queue_page::view(),
-            Some(Page::History) => history_page::view(),
-            Some(Page::Search) => search_page::view(),
-            Some(Page::Playlists) => playlists_page::view(),
-            Some(Page::Playlist(name)) => playlist_page::view(name),
-
-            // A page was not implemented, or nothing is selected, which is rare.
-            page => widget::column()
-                .push(widget::text::title1(fl!("error-found")))
-                .push_maybe(page.map(|page| {
-                    widget::text::title3(fl!("unimplemented", what = page.to_string()))
-                }))
-                .align_items(Alignment::Center)
-                .apply(widget::container)
-                .width(Length::Fill)
-                .height(Length::Fill)
-                .align_x(Horizontal::Center)
-                .align_y(Vertical::Center)
-                .into(),
+        let page = match self.nav.active_data::<Page>() {
+            Some(Page::Home) => pages::home::view(),
+            Some(Page::Queue) => pages::queue::view(&self.store),
+            Some(Page::History) => pages::history::view(&self.store),
+            Some(Page::Search) => pages::search::view(self),
+            Some(Page::Playlists) => pages::playlists::view(&self.store),
+            Some(Page::Playlist(name)) => pages::playlist::view(&self.store, name),
+            page => pages::not_found(page),
         }
+        .apply(widget::container)
+        .width(Length::Fill)
+        .height(Length::Fill)
+        .align_x(Horizontal::Center)
+        .align_y(Vertical::Top);
+
+        widget::column()
+            .push(page)
+            .push(bottom_bar::view(self))
+            .apply(widget::container)
+            .width(Length::Fill)
+            .height(Length::Fill)
+            .into()
     }
 
     /// Register subscriptions for this application.
     fn subscription(&self) -> Subscription<Self::Message> {
         Subscription::batch([
-            theme::subscription(theme::is_dark()).map(Message::ChangeTheme),
+            theme::subscription(theme::is_dark())
+                .map(|theme| Message::UpdateConfig(ConfigMessage::ChangeTheme(theme))),
             self.core().watch_config::<Config>(Self::APP_ID).map(
                 |cosmic::cosmic_config::Update { errors, config, .. }| {
                     (errors.into_iter()).for_each(|e| log::error!("config error: {e}"));
-                    Message::UpdateConfig(config)
+                    Message::UpdateConfig(ConfigMessage::Written(config))
                 },
             ),
-            subscriptions::lektord::Suscription {
-                config: self.connect_config.clone(),
-            }
-            .run(),
+            subscriptions::playback::Suscription::new(self.connect_config.clone()).run(),
+            subscriptions::updates::Suscription::new(self.connect_config.clone()).run(),
         ])
     }
 
     /// Handles messages emitted by the application and its widgets.
     fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
+        match message {
+            Message::UpdateConfig(message) => self.update_config(message),
+
+            Message::Lektord(message) => self.handle_lektord_message(message),
+            Message::OpenUrl(url) => self.open_url(url),
+            Message::ToggleContextPage(context_page) => self.toggle_context_page(context_page),
+
+            Message::SendCommand(cmd) => self.send_command(cmd),
+            Message::OpenKaraInfo(kid) => {
+                log::error!("open kara info {kid}");
+                Command::none()
+            }
+
+            Message::ChangeFilterStageBuffer(buffer) => {
+                self.search_filter.modify_stage_buffer(buffer);
+                Command::none()
+            }
+            Message::AddFilterAtomFromStageBuffer => {
+                self.search_filter.commit();
+                Command::none()
+            }
+            Message::RemoveFilterAtom(id) => {
+                self.search_filter.remove(id);
+                Command::none()
+            }
+            Message::ClearFilters => {
+                self.search_filter.clear();
+                self.search_results.clear();;
+                Command::none()
+            }
+            Message::QueryWithFilters => {
+                log::error!("query with filters");
+                Command::none()
+            }
+        }
+    }
+
+    /// Called when a nav item is selected.
+    fn on_nav_select(&mut self, id: widget::nav_bar::Id) -> Command<Self::Message> {
+        self.nav.activate(id);
+        self.update_title()
+    }
+}
+
+impl AppModel {
+    /// Updates the header and window titles.
+    fn update_title(&mut self) -> Command<Message> {
+        let mut window_title = fl!("app-title");
+        if let Some(page) = self.nav.text(self.nav.active()) {
+            window_title.push_str(" — ");
+            window_title.push_str(page);
+        }
+        self.set_window_title(window_title)
+    }
+
+    /// Update the connect config with a set config in the application. Because communications may
+    /// be in progress, we must wait to write the config pointer…
+    fn update_connect_config(&self) -> Command<Message> {
+        let config = self.config.get_connect_config();
+        let connect_config = self.connect_config.clone();
+        cosmic::command::future(async {
+            *connect_config.write_owned().await = config;
+            cosmic::app::message::none()
+        })
+    }
+
+    /// Toggle the context page to the correct one.
+    fn toggle_context_page(&mut self, context_page: ContextPage) -> Command<Message> {
+        if self.context_page == context_page {
+            self.core.window.show_context = !self.core.window.show_context;
+        } else {
+            self.context_page = context_page;
+            self.core.window.show_context = true;
+        }
+        self.set_context_title(context_page.title());
+        Command::none()
+    }
+
+    /// Open an url in the user's browser.
+    fn open_url(&self, url: &str) -> Command<Message> {
+        match open::that_detached(url) {
+            Err(err) => log::error!("failed to open \"{url}\": {err}"),
+            Ok(()) => log::info!("opened link: \"{url}\""),
+        }
+        Command::none()
+    }
+
+    /// Handle the update config message.
+    fn update_config(&mut self, message: ConfigMessage) -> Command<Message> {
         macro_rules! save_config {
-            ($setter:ident ($arg:expr) => $on_ok:expr) => {
+            ($setter:ident ($arg:expr) $(=> $on_ok:expr)?) => {
                 match self.config.$setter(&self.cosmic_config, $arg) {
-                    Ok(_) => $on_ok,
+                    Ok(_) => save_config!(@on-ok $($on_ok)?),
                     Err(err) => {
                         log::error!("failed to save config: {err}");
                         Command::none()
                     }
                 }
             };
+            (@on-ok $arg:expr) => {{ $arg }};
+            (@on-ok          ) => {{ Command::none() }};
         }
+
         macro_rules! update_remote_addr {
             ($pre_update:stmt) => {{
                 $pre_update
@@ -289,8 +491,7 @@ impl Application for AppModel {
         }
 
         match message {
-            // Misc config updates.
-            Message::UpdateConfig(config) => {
+            ConfigMessage::Written(config) => {
                 self.tmp_remote_addr = config.host().1.ip().to_string();
                 self.tmp_remote_port = config.host().1.port().to_string();
                 self.tmp_remote_user = config.user().user.clone();
@@ -300,37 +501,61 @@ impl Application for AppModel {
                 lektor_utils::logger::set_level(self.config.log_level());
                 self.update_connect_config()
             }
-            Message::ChangeTheme(arg) => cosmic::app::command::set_theme(arg),
-            Message::ChangeRemoteAddr(arg) => update_remote_addr!(self.tmp_remote_addr = arg),
-            Message::ChangeRemotePort(arg) => match arg.is_empty() {
-                true => update_remote_addr!(self.tmp_remote_port = "0".to_string()),
-                false => update_remote_addr!(self.tmp_remote_port = arg),
+
+            ConfigMessage::ChangeTheme(theme) => cosmic::app::command::set_theme(theme),
+            ConfigMessage::ChangeLogLevel(level) => save_config! {
+                set_log_level(level) => {
+                    lektor_utils::logger::set_level(level.into());
+                    Command::none()
+                }
             },
-            Message::ChangeUserName(arg) => save_config! {
-                set_user(self.config.user().clone().with_user(arg)) => self.update_connect_config()
+
+            ConfigMessage::ChangeRemoteAddr(addr) => {
+                update_remote_addr!(self.tmp_remote_addr = addr.to_string())
+            }
+            ConfigMessage::ChangeRemotePort(port) => match port.is_empty() {
+                true => update_remote_addr!(self.tmp_remote_port = "0".to_string()),
+                false => update_remote_addr!(self.tmp_remote_port = port.to_string()),
             },
-            Message::ChangeUserToken(arg) => save_config! {
-                set_user(self.config.user().clone().with_token(arg)) => self.update_connect_config()
+            ConfigMessage::ChangeURLScheme(scheme) => save_config! {
+                set_scheme(scheme)
             },
-            Message::ChangeKurisuToken(arg) => save_config! {
-                set_kurisu_token((!arg.is_empty()).then_some(arg)) => Command::none()
+            ConfigMessage::ChangeUserName(user) => {
+                let user = self.config.user().clone().with_user(user.to_string());
+                save_config! { set_user(user) => self.update_connect_config() }
+            }
+            ConfigMessage::ChangeUserToken(token) => {
+                let user = self.config.user().clone().with_token(token.to_string());
+                save_config! { set_user(user) => self.update_connect_config() }
+            }
+
+            ConfigMessage::ChangeKurisuURL(url) => save_config! {
+                set_kurisu_url(url.to_string())
             },
-            Message::ChangeURLScheme(arg) => save_config! { set_scheme(arg) => Command::none() },
-            Message::ChangeLogLevel(arg) => save_config! {
-                set_log_level(arg) => {
-                    lektor_utils::logger::set_level(arg.into());
-                    Command::none()
-                }
+            ConfigMessage::ChangeKurisuToken(token) => save_config! {
+                set_kurisu_token((!token.is_empty()).then(|| token.to_string()))
             },
+        }
+    }
 
-            // Messages from the lektord suscription.
-            Message::LektordDisconnected => {
+    /// Handle updates from lektord.
+    fn handle_lektord_message(&mut self, message: LektordMessage) -> Command<Message> {
+        use LektordMessage::*;
+        match message {
+            // Asked to download metadata informations.
+            DownloadKaraInfo(kid) => log::error!("download kara info {kid}"),
+            DownloadKarasInfo(kids) => log::error!("download kara info {kids:?}"),
+
+            // Disconnected, if any query failed we set the disconnected status
+            Disconnected => {
                 if let LektordState::Connected { .. } = mem::take(&mut self.lektord_state) {
                     log::error!("disconnected from lektord instance");
                 }
-                Command::none()
             }
-            Message::LektordConnected(lektor_payloads::Infos {
+
+            // Initial connection, done by the update subscription. It is needed for most update to
+            // be taken into account.
+            Connected(lektor_payloads::Infos {
                 version,
                 last_epoch,
             }) => {
@@ -340,74 +565,30 @@ impl Application for AppModel {
                     last_epoch,
                     state: None,
                 };
-                Command::none()
             }
-            Message::LektordUpdate(state) => {
+
+            // Playback update messages from subscription. Will be ignored while the update
+            // subscription don't send us the connect message.
+            PlaybackUpdate(state) => {
                 if let LektordState::Connected { state: ptr, .. } = &mut self.lektord_state {
                     *ptr = Some(state)
                 }
-                Command::none()
-            }
-
-            // Open an URL...
-            Message::OpenUrl(url) => {
-                match open::that_detached(url) {
-                    Err(err) => log::error!("failed to open \"{url}\": {err}"),
-                    Ok(()) => log::info!("opened link: \"{url}\""),
-                }
-                Command::none()
-            }
-
-            // Context page & header button pressed.
-            Message::ToggleContextPage(context_page) => {
-                if self.context_page == context_page {
-                    self.core.window.show_context = !self.core.window.show_context;
-                } else {
-                    self.context_page = context_page;
-                    self.core.window.show_context = true;
-                }
-                self.set_context_title(context_page.title());
-                Command::none()
             }
-            Message::HeaderCommand(id) => match self.header_controls.action(id) {
-                Some(action) => cosmic::command::message(action.message()),
-                None => Command::none(),
-            },
-
-            // Send commands to lektord.
-            Message::SendCommand(cmd) => self.send_command(cmd),
-        }
-    }
-
-    /// Called when a nav item is selected.
-    fn on_nav_select(&mut self, id: nav_bar::Id) -> Command<Self::Message> {
-        self.nav.activate(id);
-        self.update_title()
-    }
-}
 
-impl AppModel {
-    /// Updates the header and window titles.
-    fn update_title(&mut self) -> Command<Message> {
-        let mut window_title = fl!("app-title");
-        if let Some(page) = self.nav.text(self.nav.active()) {
-            window_title.push_str(" — ");
-            window_title.push_str(page);
+            // Down here, got updates from lektord.
+            ChangedAvailablePlaylists(names) => self.store.keep_playlists(names),
+            ChangedPlaylistContent(name, plt) => self.store.set_playlist(name, plt),
+            ChangedHistory(kids) => self.store.set_history(kids),
+            ChangedQueueLevel(lvl, kids) => self.store.set_queue_level(lvl, kids),
+            ChangedQueue(mut queue) => PRIORITY_VALUES.iter().for_each(|&level| {
+                let kids = mem::take(&mut queue[level.index()]);
+                self.store.set_queue_level(level, kids);
+            }),
         }
-        self.set_window_title(window_title)
-    }
-
-    /// Update the connect config with a set config in the application. Because communications may
-    /// be in progress, we must wait to write the config pointer…
-    fn update_connect_config(&self) -> Command<Message> {
-        let config = self.config.get_connect_config();
-        let connect_config = self.connect_config.clone();
-        cosmic::command::future(async {
-            *connect_config.write_owned().await = config;
-            cosmic::app::message::none()
-        })
+        Command::none()
     }
 
+    /// Send commands to lektord.
     fn send_command(&self, cmd: LektordCommand) -> Command<Message> {
         let config = self.connect_config.clone();
         use lektor_payloads::*;
diff --git a/amadeus/src/app/bottom_bar.rs b/amadeus/src/app/bottom_bar.rs
new file mode 100644
index 00000000..e9f55288
--- /dev/null
+++ b/amadeus/src/app/bottom_bar.rs
@@ -0,0 +1,134 @@
+use crate::{
+    app::{AppModel, LektordMessage, Message},
+    store::KaraOrId,
+};
+use cosmic::{
+    cosmic_theme::Spacing,
+    font,
+    iced::{
+        alignment::{Horizontal, Vertical},
+        Alignment, Length,
+    },
+    iced_core::text::Wrap,
+    style, theme, widget, Apply, Element,
+};
+use lektor_payloads::{KId, Kara};
+
+fn view_right_part<'a>(kara: &Kara) -> Element<'a, Message> {
+    let Spacing {
+        space_xxs,
+        space_xxxs,
+        ..
+    } = theme::active().cosmic().spacing;
+
+    macro_rules! tag {
+        ($str:expr, $color:ident) => {
+            widget::text(format!(" {} ", $str))
+                .style(style::Text::Color(theme::active().cosmic().$color().into()))
+                .apply(widget::container)
+                .padding(space_xxxs)
+                .style(style::Container::Card)
+                .apply(Element::<Message>::from)
+        };
+    }
+
+    let row_1 = Vec::from_iter(
+        kara.kara_makers
+            .iter()
+            .map(|author| tag!(author, destructive_text_color)),
+    )
+    .apply(widget::row::with_children)
+    .spacing(space_xxs);
+
+    let row_2 = [tag!(kara.song_type, success_text_color)]
+        .into_iter()
+        .chain(
+            kara.language
+                .iter()
+                .map(|lang| tag!(lang, success_text_color)),
+        )
+        .chain([tag!(kara.song_origin, success_text_color)])
+        .collect::<Vec<Element<Message>>>()
+        .apply(widget::row::with_children)
+        .spacing(space_xxs);
+
+    widget::column()
+        .push(row_1)
+        .push(row_2)
+        .spacing(space_xxs)
+        .align_items(Alignment::End)
+        .width(Length::Shrink)
+        .apply(widget::container)
+        .align_x(Horizontal::Right)
+        .align_y(Vertical::Center)
+        .height(Length::Fill)
+        .into()
+}
+
+fn view_left_part<'a>(kara: &Kara) -> Element<'a, Message> {
+    let title = widget::text::title2(kara.song_title.clone())
+        .style(theme::Text::Color(
+            theme::active().cosmic().accent_text_color().into(),
+        ))
+        .wrap(Wrap::None);
+
+    let source = (kara.tags.get(Kara::TAG_NUMBER).and_then(|num| num.first()))
+        .map(|num| format!("{}{num} - {}", kara.song_type, kara.song_source))
+        .unwrap_or_else(|| format!("{} - {}", kara.song_type, kara.song_source))
+        .apply(widget::text::title4)
+        .style(theme::Text::Color(
+            theme::active().cosmic().accent_text_color().into(),
+        ))
+        .font(font::FONT_LIGHT)
+        .wrap(Wrap::None);
+
+    widget::column()
+        .push(title)
+        .push(source)
+        .apply(widget::button::custom)
+        .style(style::Button::Transparent)
+        .on_press(Message::OpenKaraInfo(kara.id.clone()))
+        .apply(widget::container)
+        .align_x(Horizontal::Left)
+        .align_y(Vertical::Center)
+        .width(Length::Fill)
+        .height(Length::Fill)
+        .into()
+}
+
+fn view_kara_id<'a>(kid: KId) -> Element<'a, Message> {
+    widget::text::title1(kid.to_string())
+        .style(theme::Text::Color(
+            theme::active().cosmic().accent_text_color().into(),
+        ))
+        .wrap(Wrap::None)
+        .apply(widget::button::custom)
+        .style(style::Button::Transparent)
+        .on_press(Message::Lektord(LektordMessage::DownloadKaraInfo(kid)))
+        .apply(widget::container)
+        .align_x(Horizontal::Left)
+        .align_y(Vertical::Center)
+        .width(Length::Fill)
+        .height(Length::Fill)
+        .into()
+}
+
+/// This is just a modified [crate::app::kara_card] to display at the bottom of the screen if a
+/// kara is playing.
+pub fn view<'a>(app: &AppModel) -> Element<'a, Message> {
+    match app.lektord_state.current_kid() {
+        None => return widget::row().into(),
+        Some(kid) => match app.store.get(kid) {
+            KaraOrId::Kara(kara) => vec![view_left_part(kara), view_right_part(kara)],
+            KaraOrId::Id(kid) => vec![view_kara_id(kid)],
+        }
+        .apply(widget::row::with_children)
+        .width(Length::Fill)
+        .height(Length::Fixed(100.0))
+        .padding(theme::active().cosmic().space_xs())
+        .apply(widget::container)
+        .align_y(Vertical::Bottom)
+        .style(style::Container::Primary)
+        .into(),
+    }
+}
diff --git a/amadeus/src/app/config_page.rs b/amadeus/src/app/config_page.rs
deleted file mode 100644
index f5f64fba..00000000
--- a/amadeus/src/app/config_page.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use crate::{
-    app::{AppModel, Message},
-    fl,
-};
-use cosmic::{theme, widget, Element};
-use lektor_utils::config::{SocketScheme, UserConfig};
-
-pub fn view(app: &AppModel) -> Element<Message> {
-    macro_rules! dropdown {
-        ($array:expr, $val:expr => $msg:ident) => {{
-            let idx = ($array.iter().enumerate()).find_map(|(i, a)| (*a == $val).then_some(i));
-            widget::dropdown($array, idx, |idx| Message::$msg($array[idx]))
-        }};
-    }
-
-    // --- top section
-    let log_levels = dropdown!(
-        crate::config::LOG_LEVELS, lektor_utils::logger::get().into() => ChangeLogLevel
-    );
-    let dark_theme = widget::toggler(None, theme::active_type().is_dark(), |is_dark| {
-        Message::ChangeTheme(match is_dark {
-            true => theme::system_dark(),
-            false => theme::system_light(),
-        })
-    });
-
-    let top_section = widget::settings::section()
-        .title(fl!("app-config"))
-        .add(widget::settings::item(fl!("log-level"), log_levels))
-        .add(widget::settings::item(fl!("dark-theme"), dark_theme))
-        .into();
-
-    // --- lektord section
-    let url_schemes = dropdown!(
-        &[SocketScheme::Http, SocketScheme::Https], app.config.host().0 => ChangeURLScheme
-    );
-    let mut instance_addr = widget::text_input("IPv4", &app.tmp_remote_addr)
-        .width(150)
-        .on_clear(Message::ChangeRemoteAddr("127.0.0.1".to_string()))
-        .on_input(Message::ChangeRemoteAddr);
-    let mut instance_port = widget::text_input(fl!("port"), &app.tmp_remote_port)
-        .width(150)
-        .on_clear(Message::ChangeRemotePort("6600".to_string()))
-        .on_input(Message::ChangeRemotePort);
-    if !app.tmp_remote_valid {
-        instance_addr = instance_addr.error("invalid");
-        instance_port = instance_port.error("invalid");
-    }
-    let instance_user = widget::text_input("Viieux", &app.tmp_remote_user)
-        .width(150)
-        .on_clear(Message::ChangeUserName(UserConfig::default().user))
-        .on_input(Message::ChangeUserName);
-    let instance_token = widget::text_input(fl!("token"), &app.tmp_remote_token)
-        .password()
-        .width(150)
-        .on_clear(Message::ChangeUserToken(UserConfig::default().token))
-        .on_input(Message::ChangeUserToken);
-
-    let remote_section = widget::settings::section()
-        .title(fl!("remote-config"))
-        .add(widget::settings::item(fl!("instance-scheme"), url_schemes))
-        .add(widget::settings::item(fl!("instance-addr"), instance_addr))
-        .add(widget::settings::item(fl!("instance-port"), instance_port))
-        .add(widget::settings::item("User", instance_user))
-        .add(widget::settings::item(fl!("token"), instance_token))
-        .into();
-
-    // --- kurisu section
-    let kurisu_token =
-        widget::text_input::text_input(fl!("token"), app.config.kurisu_token().unwrap_or_default())
-            .password()
-            .width(150)
-            .on_clear(Message::ChangeKurisuToken("".to_string()))
-            .on_input(Message::ChangeKurisuToken);
-
-    let kurisu_section = widget::settings::section()
-        .title("Kurisu configuration")
-        .add(widget::settings::item(fl!("kurisu-token"), kurisu_token))
-        .into();
-
-    // --- total settings
-    widget::settings::view_column(vec![top_section, remote_section, kurisu_section]).into()
-}
diff --git a/amadeus/src/app/context_page.rs b/amadeus/src/app/context_pages.rs
similarity index 83%
rename from amadeus/src/app/context_page.rs
rename to amadeus/src/app/context_pages.rs
index 11a54dea..e22a40fa 100644
--- a/amadeus/src/app/context_page.rs
+++ b/amadeus/src/app/context_pages.rs
@@ -1,5 +1,8 @@
 use crate::fl;
 
+pub mod about;
+pub mod config;
+
 /// The context page to display in the context drawer. This is the pane on the right that can be
 /// hidden or shown.
 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
@@ -14,7 +17,7 @@ impl ContextPage {
     pub fn title(&self) -> String {
         match self {
             Self::About => fl!("about"),
-            Self::Settings=> fl!("settings"),
+            Self::Settings => fl!("settings"),
         }
     }
 }
diff --git a/amadeus/src/app/about_page.rs b/amadeus/src/app/context_pages/about.rs
similarity index 81%
rename from amadeus/src/app/about_page.rs
rename to amadeus/src/app/context_pages/about.rs
index cefd37f6..7b8e6153 100644
--- a/amadeus/src/app/about_page.rs
+++ b/amadeus/src/app/context_pages/about.rs
@@ -23,24 +23,24 @@ pub fn view(app: &AppModel) -> Element<Message> {
         .width(128);
 
     let link_rows = widget::row()
-        .push(url!(WEB => "https://kurisu.iiens.net/"))
+        .push(url!(WEB => "https://kurisu.iiens.net"))
         .push(url!(GIT => "https://git.iiens.net/martin2018/lektor"))
         .push(url!(BUG => "https://git.iiens.net/martin2018/lektor/-/issues/new"))
         .push(url!(BARCODES => "https://kurisu.iiens.net/api/download.php?token"))
         .spacing(theme::active().cosmic().space_xs());
 
     let amadeus_section = widget::settings::section()
-        .title("Amadeus informations")
+        .title(fl!("info-about", what = fl!("app-title")))
         .add(widget::settings::item(
-            "Version",
+            fl!("version"),
             crate::version().apply(widget::text::body),
         ));
 
-    let lektord_section = widget::settings::section().title("Lektord informations");
+    let lektord_section = widget::settings::section().title(fl!("info-about", what = "Lektord"));
     let lektord_section = match &app.lektord_state {
         LektordState::Disconnected => lektord_section.add(widget::settings::item(
-            "Status",
-            widget::text::body("Disconnected"),
+            fl!("status"),
+            widget::text::body(fl!("disconnected")),
         )),
         LektordState::Connected {
             version,
@@ -48,15 +48,15 @@ pub fn view(app: &AppModel) -> Element<Message> {
             ..
         } => lektord_section
             .add(widget::settings::item(
-                "Status",
-                widget::text::body("Connected"),
+                fl!("status"),
+                widget::text::body(fl!("connected")),
             ))
             .add(widget::settings::item(
-                "Version",
+                fl!("version"),
                 widget::text::body(version),
             ))
             .add(widget::settings::item(
-                "DB Epoch",
+                fl!("epoch", what = "DB"),
                 widget::text::body(last_epoch.unwrap_or_default().to_string()),
             )),
     };
diff --git a/amadeus/src/app/context_pages/config.rs b/amadeus/src/app/context_pages/config.rs
new file mode 100644
index 00000000..6452dbe4
--- /dev/null
+++ b/amadeus/src/app/context_pages/config.rs
@@ -0,0 +1,104 @@
+use crate::{
+    app::{AppModel, ConfigMessage, Message},
+    config::KURISU_URL,
+    fl,
+};
+use cosmic::{iced::Length, theme, widget, Element};
+use lektor_utils::config::{SocketScheme, UserConfig};
+use std::sync::LazyLock;
+
+pub fn view(app: &AppModel) -> Element<Message> {
+    use {std::borrow::Cow::*, ConfigMessage::*, Message::*};
+
+    let default_instance_user = UpdateConfig(ChangeUserName(Borrowed({
+        static STRING: LazyLock<&'static str> = LazyLock::new(|| UserConfig::default().user.leak());
+        *STRING
+    })));
+
+    let default_instance_token = UpdateConfig(ChangeUserToken(Borrowed({
+        static STRING: LazyLock<&'static str> =
+            LazyLock::new(|| UserConfig::default().token.leak());
+        *STRING
+    })));
+
+    const TEXT_ENTRY_WIDTH: Length = Length::Fixed(150.0);
+
+    macro_rules! dropdown {
+        ($array:expr, $val:expr => $msg:ident) => {{
+            let idx = ($array.iter().enumerate()).find_map(|(i, a)| (*a == $val).then_some(i));
+            widget::dropdown($array, idx, |idx| UpdateConfig($msg($array[idx])))
+        }};
+    }
+
+    // --- top section
+    let log_levels = dropdown!(
+        crate::config::LOG_LEVELS, lektor_utils::logger::get().into() => ChangeLogLevel
+    );
+    let dark_theme = widget::toggler(None, theme::active_type().is_dark(), |is_dark| {
+        UpdateConfig(ChangeTheme(match is_dark {
+            true => theme::system_dark(),
+            false => theme::system_light(),
+        }))
+    });
+
+    let top_section = widget::settings::section()
+        .title(fl!("config", what = fl!("app-title")))
+        .add(widget::settings::item(fl!("log-level"), log_levels))
+        .add(widget::settings::item(fl!("dark-theme"), dark_theme))
+        .into();
+
+    // --- lektord section
+    let schemes = dropdown!(
+        &[SocketScheme::Http, SocketScheme::Https], app.config.host().0 => ChangeURLScheme
+    );
+    let mut address = widget::text_input("IPv4", &app.tmp_remote_addr)
+        .width(TEXT_ENTRY_WIDTH)
+        .on_clear(UpdateConfig(ChangeRemoteAddr(Borrowed("127.0.0.1"))))
+        .on_input(|x| UpdateConfig(ChangeRemoteAddr(Owned(x))));
+    let mut port = widget::text_input(fl!("port"), &app.tmp_remote_port)
+        .width(TEXT_ENTRY_WIDTH)
+        .on_clear(UpdateConfig(ChangeRemotePort(Borrowed("6600"))))
+        .on_input(|x| UpdateConfig(ChangeRemotePort(Owned(x))));
+    if !app.tmp_remote_valid {
+        address = address.error("invalid");
+        port = port.error("invalid");
+    }
+    let user = widget::text_input("Viieux", &app.tmp_remote_user)
+        .width(TEXT_ENTRY_WIDTH)
+        .on_clear(default_instance_user)
+        .on_input(|x| UpdateConfig(ChangeUserName(Owned(x))));
+    let token = widget::text_input(fl!("token"), &app.tmp_remote_token)
+        .password()
+        .width(TEXT_ENTRY_WIDTH)
+        .on_clear(default_instance_token)
+        .on_input(|x| UpdateConfig(ChangeUserToken(Owned(x))));
+
+    let remote_section = widget::settings::section()
+        .title(fl!("config", what = "Lektord"))
+        .add(widget::settings::item(fl!("scheme"), schemes))
+        .add(widget::settings::item(fl!("address-ip"), address))
+        .add(widget::settings::item(fl!("port"), port))
+        .add(widget::settings::item(fl!("user"), user))
+        .add(widget::settings::item(fl!("token"), token))
+        .into();
+
+    // --- kurisu section
+    let url = widget::text_input(KURISU_URL, app.config.kurisu_url())
+        .width(TEXT_ENTRY_WIDTH)
+        .on_clear(UpdateConfig(ChangeKurisuURL(Borrowed(KURISU_URL))))
+        .on_input(|x| UpdateConfig(ChangeKurisuURL(Owned(x))));
+    let token = widget::text_input(fl!("token"), app.config.kurisu_token().unwrap_or_default())
+        .password()
+        .width(TEXT_ENTRY_WIDTH)
+        .on_clear(UpdateConfig(ChangeKurisuToken(Borrowed(""))))
+        .on_input(|x| UpdateConfig(ChangeKurisuToken(Owned(x))));
+
+    let kurisu_section = widget::settings::section()
+        .title(fl!("config", what = "Kurisu"))
+        .add(widget::settings::item(fl!("url-of", what = "Kurisu"), url))
+        .add(widget::settings::item(fl!("kurisu-token"), token))
+        .into();
+
+    // --- total settings
+    widget::settings::view_column(vec![top_section, remote_section, kurisu_section]).into()
+}
diff --git a/amadeus/src/app/history_page.rs b/amadeus/src/app/history_page.rs
deleted file mode 100644
index a191e2d6..00000000
--- a/amadeus/src/app/history_page.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use crate::{app::Message, fl};
-use cosmic::{
-    iced::{
-        alignment::{Horizontal, Vertical},
-        Length,
-    },
-    widget, Apply as _, Element,
-};
-
-pub fn view<'a>() -> Element<'a, Message> {
-    widget::text::title1(fl!("history"))
-        .apply(widget::container)
-        .width(Length::Fill)
-        .height(Length::Fill)
-        .align_x(Horizontal::Center)
-        .align_y(Vertical::Center)
-        .into()
-}
-
diff --git a/amadeus/src/app/home_page.rs b/amadeus/src/app/home_page.rs
deleted file mode 100644
index ebfc6b18..00000000
--- a/amadeus/src/app/home_page.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::{app::Message, fl};
-use cosmic::{
-    iced::{
-        alignment::{Horizontal, Vertical},
-        Length,
-    },
-    widget, Apply as _, Element,
-};
-
-pub fn view<'a>() -> Element<'a, Message> {
-    widget::text::title1(fl!("home"))
-        .apply(widget::container)
-        .width(Length::Fill)
-        .height(Length::Fill)
-        .align_x(Horizontal::Center)
-        .align_y(Vertical::Center)
-        .into()
-}
diff --git a/amadeus/src/app/kard.rs b/amadeus/src/app/kard.rs
new file mode 100644
index 00000000..a2d3e307
--- /dev/null
+++ b/amadeus/src/app/kard.rs
@@ -0,0 +1,98 @@
+use crate::{
+    app::{LektordMessage, Message},
+    store::KaraOrId,
+};
+use cosmic::{
+    cosmic_theme::Spacing,
+    font,
+    iced::{Alignment, Length},
+    style, theme, widget, Apply, Element,
+};
+use lektor_payloads::Kara;
+
+const KARD_HEIGHT: f32 = 50.0;
+
+fn kara_title<'a>(kara: &Kara) -> Element<'a, Message> {
+    widget::column::with_children(vec![
+        widget::text::title4(kara.song_title.clone()).into(),
+        (kara.tags.get(Kara::TAG_NUMBER).and_then(|num| num.first()))
+            .map(|num| format!("{}{num} - {}", kara.song_type, kara.song_source))
+            .unwrap_or_else(|| format!("{} - {}", kara.song_type, kara.song_source))
+            .apply(widget::text::text)
+            .font(font::FONT_LIGHT)
+            .into(),
+    ])
+    .apply(widget::button::custom)
+    .style(style::Button::Transparent)
+    .on_press(Message::OpenKaraInfo(kara.id.clone()))
+    .apply(Element::from)
+}
+
+fn kara_tags<'a>(kara: &Kara) -> Element<'a, Message> {
+    let Spacing {
+        space_xxs,
+        space_xxxs,
+        ..
+    } = theme::active().cosmic().spacing;
+
+    macro_rules! tag {
+        ($str:expr, $color:ident) => {
+            widget::text(format!(" {} ", $str))
+                .style(style::Text::Color(theme::active().cosmic().$color().into()))
+                .apply(widget::container)
+                .padding(space_xxxs)
+                .style(style::Container::Card)
+                .apply(Element::<Message>::from)
+        };
+    }
+
+    let row_1 = Vec::from_iter(
+        kara.kara_makers
+            .iter()
+            .map(|author| tag!(author, destructive_text_color)),
+    )
+    .apply(widget::row::with_children)
+    .spacing(space_xxs)
+    .into();
+
+    let row_2 = std::iter::once(tag!(kara.song_type, success_text_color))
+        .chain((kara.language.iter()).map(|lang| tag!(lang, success_text_color)))
+        .chain(std::iter::once(tag!(kara.song_origin, success_text_color)))
+        .collect::<Vec<Element<Message>>>()
+        .apply(widget::row::with_children)
+        .spacing(space_xxs)
+        .into();
+
+    widget::column::with_children(vec![row_1, row_2])
+        .spacing(space_xxs)
+        .align_items(Alignment::End)
+        .width(Length::Shrink)
+        .into()
+}
+
+/// View the kara (or the id) in a card. (kard = Kara cARD).
+#[allow(dead_code)]
+pub fn view<'a>(kara_or_id: KaraOrId) -> Element<'a, Message> {
+    match kara_or_id {
+        KaraOrId::Id(kid) => vec![widget::text::title3(kid.to_string())
+            .style(style::Text::Color(
+                theme::active().cosmic().destructive_text_color().into(),
+            ))
+            .apply(widget::button::custom)
+            .style(style::Button::Transparent)
+            .on_press(Message::Lektord(LektordMessage::DownloadKaraInfo(
+                kid.clone(),
+            )))
+            .apply(Element::from)],
+        KaraOrId::Kara(kara) => vec![
+            kara_title(kara),
+            widget::horizontal_space(Length::Fill).apply(Element::from),
+            kara_tags(kara),
+            // TODO: Add the controls here...
+        ],
+    }
+    .apply(widget::row::with_children)
+    .width(Length::Fill)
+    .height(KARD_HEIGHT)
+    .into()
+}
diff --git a/amadeus/src/app/main_page.rs b/amadeus/src/app/main_page.rs
deleted file mode 100644
index 8788a36c..00000000
--- a/amadeus/src/app/main_page.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use crate::fl;
-use cosmic::widget::{icon, nav_bar, Icon};
-use derive_more::Display;
-use std::sync::Arc;
-
-/// The page to display in the application.
-#[derive(Default, Debug, Eq, PartialEq, Clone, Display)]
-#[non_exhaustive]
-pub enum Page {
-    #[default]
-    #[display("{}", fl!("home"))]
-    Home,
-
-    #[display("{}", fl!("queue"))]
-    Queue,
-
-    #[display("{}", fl!("history"))]
-    History,
-
-    #[display("{}", fl!("search"))]
-    Search,
-
-    #[display("{}", fl!("playlists"))]
-    Playlists,
-
-    #[display("{}", fl!("playlist", name = _0.as_ref()))]
-    Playlist(Arc<str>),
-}
-
-impl Page {
-    pub fn icon(&self) -> Icon {
-        let icon = match self {
-            Page::Home => crate::icons::HOME,
-            Page::Queue => crate::icons::QUEUE,
-            Page::History => crate::icons::HISTORY,
-            Page::Search => crate::icons::SEARCH,
-            Page::Playlists | Page::Playlist(_) => crate::icons::ARCHIVE,
-        };
-        icon::icon(icon::from_svg_bytes(icon).symbolic(true))
-    }
-}
-
-pub fn nav_bar_model() -> nav_bar::Model {
-    macro_rules! insert {
-        ($b:expr, $page:ident) => {
-            $b.text(Page::$page.to_string())
-                .icon(Page::$page.icon())
-                .data(Page::$page)
-        };
-    }
-    nav_bar::Model::builder()
-        .insert(|b| insert!(b, Home).activate())
-        .insert(|b| insert!(b, Queue))
-        .insert(|b| insert!(b, History))
-        .insert(|b| insert!(b, Search))
-        .insert(|b| insert!(b, Playlists))
-        .build()
-}
diff --git a/amadeus/src/app/menu_bar.rs b/amadeus/src/app/menu.rs
similarity index 64%
rename from amadeus/src/app/menu_bar.rs
rename to amadeus/src/app/menu.rs
index 9508f83b..4e78b684 100644
--- a/amadeus/src/app/menu_bar.rs
+++ b/amadeus/src/app/menu.rs
@@ -1,12 +1,8 @@
 use crate::{
-    app::{context_page::ContextPage, LektordCommand, Message},
+    app::{context_pages::ContextPage, LektordCommand, Message},
     fl,
 };
-use cosmic::{
-    iced::{Alignment, Length},
-    prelude::*,
-    widget::{icon, menu, segmented_button},
-};
+use cosmic::{prelude::*, widget::menu};
 use derive_more::Display;
 use std::collections::HashMap;
 
@@ -27,11 +23,6 @@ pub enum MenuAction {
     #[display("{}", fl!("prev-kara"))]        PlaybackPrevious,
 }
 
-/// The group of buttons at the center of the header bar of the window.
-pub struct MenuHeaderCentralCommands {
-    buttons: segmented_button::Model<segmented_button::SingleSelect>,
-}
-
 impl menu::action::MenuAction for MenuAction {
     type Message = Message;
 
@@ -54,48 +45,6 @@ impl menu::action::MenuAction for MenuAction {
     }
 }
 
-impl Default for MenuHeaderCentralCommands {
-    fn default() -> Self {
-        macro_rules! add {
-            ($b:ident, $icon:ident, $action:ident) => {{
-                let icon = icon::from_svg_bytes(crate::icons::$icon)
-                    .symbolic(true)
-                    .apply(icon::icon);
-                $b.data(MenuAction::$action).icon(icon)
-            }};
-        }
-        let buttons = segmented_button::Model::builder()
-            .insert(|b| add!(b, PREVIOUS, PlaybackPrevious))
-            .insert(|b| add!(b, STOP, PlaybackStop))
-            .insert(|b| add!(b, NEXT, PlaybackNext))
-            .insert(|b| add!(b, SHUFFLE, QueueShuffle))
-            .insert(|b| add!(b, METEOR, QueueClear))
-            .build();
-        Self { buttons }
-    }
-}
-
-impl MenuHeaderCentralCommands {
-    pub fn view(&self) -> Element<Message> {
-        segmented_button::horizontal(&self.buttons)
-            .style(cosmic::theme::SegmentedButton::Control)
-            .button_alignment(Alignment::Center)
-            .button_height(32)
-            .button_padding([16, 10, 16, 10])
-            .button_spacing(8)
-            .spacing(cosmic::theme::active().cosmic().space_xxs())
-            .width(Length::Shrink)
-            .on_activate(Message::HeaderCommand)
-            .into()
-    }
-
-    /// Get the action associated with an action.
-    pub fn action(&mut self, id: segmented_button::Entity) -> Option<MenuAction> {
-        self.buttons.deactivate();
-        self.buttons.data(id).copied()
-    }
-}
-
 /// Get the menu from the header bar, are at the start of the header.
 pub fn header_menu(key_binds: &HashMap<menu::KeyBind, MenuAction>) -> Element<Message> {
     macro_rules! button {
diff --git a/amadeus/src/app/pages.rs b/amadeus/src/app/pages.rs
new file mode 100644
index 00000000..0e1e03f7
--- /dev/null
+++ b/amadeus/src/app/pages.rs
@@ -0,0 +1,120 @@
+use crate::{app::Message, fl};
+use cosmic::{
+    font,
+    iced::{
+        alignment::{Horizontal, Vertical},
+        Alignment, Length,
+    },
+    prelude::CollectionWidget as _,
+    style, theme,
+    widget::{self, icon, nav_bar, Icon},
+    Apply as _, Element,
+};
+use derive_more::Display;
+use std::sync::Arc;
+
+pub mod history;
+pub mod home;
+pub mod playlist;
+pub mod playlists;
+pub mod queue;
+pub mod search;
+
+/// The page to display in the application.
+#[derive(Default, Debug, Eq, PartialEq, Clone, Display)]
+#[non_exhaustive]
+pub enum Page {
+    #[default]
+    #[display("{}", fl!("home"))]
+    Home,
+
+    #[display("{}", fl!("queue"))]
+    Queue,
+
+    #[display("{}", fl!("history"))]
+    History,
+
+    #[display("{}", fl!("search"))]
+    Search,
+
+    #[display("{}", fl!("playlists"))]
+    Playlists,
+
+    #[display("{}", fl!("playlist", name = _0.as_ref()))]
+    Playlist(Arc<str>),
+}
+
+impl Page {
+    pub fn icon(&self) -> Icon {
+        let icon = match self {
+            Page::Home => crate::icons::USER,
+            Page::Queue => crate::icons::QUEUE,
+            Page::History => crate::icons::HISTORY,
+            Page::Search => crate::icons::SEARCH,
+            Page::Playlists | Page::Playlist(_) => crate::icons::ARCHIVE,
+        };
+        icon::icon(icon::from_svg_bytes(icon).symbolic(true))
+    }
+}
+
+pub fn nav_bar_model() -> nav_bar::Model {
+    macro_rules! insert {
+        ($b:expr, $page:ident) => {
+            $b.text(Page::$page.to_string())
+                .icon(Page::$page.icon())
+                .data(Page::$page)
+        };
+    }
+    nav_bar::Model::builder()
+        .insert(|b| insert!(b, Home).activate())
+        .insert(|b| insert!(b, Queue))
+        .insert(|b| insert!(b, History))
+        .insert(|b| insert!(b, Search))
+        .insert(|b| insert!(b, Playlists))
+        .build()
+}
+
+pub fn not_found(page: Option<&Page>) -> Element<Message> {
+    widget::column()
+        .push(widget::text::title1(fl!("error-found")))
+        .push_maybe(
+            page.map(|page| widget::text::title3(fl!("unimplemented", what = page.to_string()))),
+        )
+        .align_items(Alignment::Center)
+        .apply(widget::container)
+        .width(Length::Fill)
+        .height(Length::Fill)
+        .align_x(Horizontal::Center)
+        .align_y(Vertical::Center)
+        .into()
+}
+
+pub fn empty_page_label<'a>(title: impl AsRef<str>) -> widget::Row<'a, Message> {
+    vec![
+        (title.as_ref().to_string())
+            .apply(widget::text::title2)
+            .style(style::Text::Color(
+                theme::active().cosmic().warning_text_color().into(),
+            ))
+            .font(font::FONT_LIGHT)
+            .into(),
+        widget::horizontal_space(Length::Fill).into(),
+    ]
+    .apply(widget::row::with_children)
+    .width(Length::Fill)
+    .padding(theme::active().cosmic().space_m())
+}
+
+pub fn page_label<'a>(title: impl AsRef<str>) -> widget::Row<'a, Message> {
+    vec![
+        (title.as_ref().to_string())
+            .apply(widget::text::title2)
+            .style(style::Text::Default)
+            // .font(font::FONT_LIGHT)
+            .into(),
+        widget::horizontal_space(Length::Fill).into(),
+    ]
+    .apply(widget::row::with_children)
+    .width(Length::Fill)
+    .padding(theme::active().cosmic().space_m())
+}
diff --git a/amadeus/src/app/pages/history.rs b/amadeus/src/app/pages/history.rs
new file mode 100644
index 00000000..459628a7
--- /dev/null
+++ b/amadeus/src/app/pages/history.rs
@@ -0,0 +1,16 @@
+use crate::{
+    app::{kard, pages, Message},
+    store::Store,
+};
+use cosmic::{widget, Apply as _, Element};
+
+pub fn view(store: &Store) -> Element<Message> {
+    match store.iter_history().count() == 0 {
+        true => pages::empty_page_label("The history is empty").apply(Element::<Message>::from),
+        false => (store.iter_history())
+            .fold(widget::list_column(), |list, kid| {
+                list.add(kard::view(store.get(kid)))
+            })
+            .apply(Element::<Message>::from),
+    }
+}
diff --git a/amadeus/src/app/pages/home.rs b/amadeus/src/app/pages/home.rs
new file mode 100644
index 00000000..9bf742d7
--- /dev/null
+++ b/amadeus/src/app/pages/home.rs
@@ -0,0 +1,9 @@
+use crate::{
+    app::{pages, Message},
+    fl,
+};
+use cosmic::{Apply as _, Element};
+
+pub fn view<'a>() -> Element<'a, Message> {
+    pages::page_label(fl!("home")).apply(Element::<Message>::from)
+}
diff --git a/amadeus/src/app/pages/playlist.rs b/amadeus/src/app/pages/playlist.rs
new file mode 100644
index 00000000..4151fff2
--- /dev/null
+++ b/amadeus/src/app/pages/playlist.rs
@@ -0,0 +1,23 @@
+use crate::{
+    app::{kard, pages, Message},
+    fl,
+    store::Store,
+};
+use cosmic::{widget, Apply as _, Element};
+
+pub fn view<'a>(store: &'a Store, playlist: &str) -> Element<'a, Message> {
+    match store.iter_playlist_content(playlist).count() == 0 {
+        true => pages::empty_page_label(format!("The playlist {playlist} is empty"))
+            .apply(Element::<Message>::from),
+        false => vec![
+            pages::page_label(fl!("playlist", name = playlist)).apply(Element::<Message>::from),
+            (store.iter_playlist_content(playlist))
+                .fold(widget::list_column(), |list, kid| {
+                    list.add(kard::view(store.get(kid)))
+                })
+                .apply(Element::<Message>::from),
+        ]
+        .apply(widget::row::with_children)
+        .apply(Element::<Message>::from),
+    }
+}
diff --git a/amadeus/src/app/pages/playlists.rs b/amadeus/src/app/pages/playlists.rs
new file mode 100644
index 00000000..839f0db9
--- /dev/null
+++ b/amadeus/src/app/pages/playlists.rs
@@ -0,0 +1,10 @@
+use crate::{
+    app::{pages, Message},
+    fl,
+    store::Store,
+};
+use cosmic::{Apply as _, Element};
+
+pub fn view(_store: &Store) -> Element<Message> {
+    pages::page_label(fl!("playlists")).apply(Element::<Message>::from)
+}
diff --git a/amadeus/src/app/pages/queue.rs b/amadeus/src/app/pages/queue.rs
new file mode 100644
index 00000000..86f851c6
--- /dev/null
+++ b/amadeus/src/app/pages/queue.rs
@@ -0,0 +1,35 @@
+use crate::{
+    app::{kard, pages, Message},
+    fl,
+    store::Store,
+};
+use cosmic::{widget, Apply as _, Element};
+use lektor_payloads::{Priority, PRIORITY_VALUES};
+
+fn view_queue_level(store: &Store, level: Priority) -> Element<Message> {
+    // TODO: Add the controls
+    let header = widget::text::title1(format!("{} {level}", fl!("queue")));
+
+    let content = store
+        .iter_queue_level(level)
+        .fold(widget::list_column(), |list, kid| {
+            list.add(kard::view(store.get(kid)))
+        });
+
+    vec![header.into(), content.into()]
+        .apply(widget::column::with_children)
+        .into()
+}
+
+pub fn view(store: &Store) -> Element<Message> {
+    match store.iter_queue().count() == 0 {
+        true => pages::empty_page_label("The queue is empty").apply(Element::<Message>::from),
+        false => (PRIORITY_VALUES.iter().rev().copied())
+            .flat_map(|level| {
+                (store.iter_queue_level(level).count() != 0).then(|| view_queue_level(store, level))
+            })
+            .collect::<Vec<Element<Message>>>()
+            .apply(widget::column::with_children)
+            .into(),
+    }
+}
diff --git a/amadeus/src/app/pages/search.rs b/amadeus/src/app/pages/search.rs
new file mode 100644
index 00000000..df9f9067
--- /dev/null
+++ b/amadeus/src/app/pages/search.rs
@@ -0,0 +1,162 @@
+use crate::{
+    app::{kard, pages, AppModel, Message},
+    fl,
+};
+use cosmic::{iced::Alignment, prelude::*, style, theme, widget};
+use lektor_payloads::KaraBy;
+use std::{
+    convert::Infallible,
+    str::FromStr,
+    sync::atomic::{AtomicUsize, Ordering},
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FilterAtom(FilterAtomId, KaraBy);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct FilterAtomId(usize);
+
+#[derive(Debug, Default)]
+pub struct Filter(String, Vec<FilterAtom>);
+
+fn vertical_space<'a>() -> Element<'a, Message> {
+    widget::vertical_space(theme::active().cosmic().space_m()).into()
+}
+
+macro_rules! icon {
+    ($icon:ident) => {
+        widget::icon::from_svg_bytes(crate::icons::$icon)
+            .symbolic(true)
+            .apply(widget::icon)
+    };
+}
+
+impl FromStr for FilterAtom {
+    type Err = Infallible;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(Self(FilterAtomId::new(), s.parse()?))
+    }
+}
+
+impl FilterAtomId {
+    fn new() -> Self {
+        static FILTER_ATOM_ID: AtomicUsize = AtomicUsize::new(0);
+        Self(FILTER_ATOM_ID.fetch_add(1, Ordering::Relaxed))
+    }
+}
+
+impl FilterAtom {
+    fn view(&self) -> Element<Message> {
+        let (maybe_icon, text) = match &self.1 {
+            KaraBy::Id(id) => (Some(icon!(HASHTAG)), id.to_string()),
+            KaraBy::Query(query) => (None, query.clone()),
+            KaraBy::Tag((name, None)) => (Some(icon!(TAG)), name.clone()),
+            KaraBy::Tag((name, Some(value))) => (Some(icon!(TAGS)), format!("{name}:{value}")),
+            KaraBy::SongType(song_type) => (None, song_type.to_string()),
+            KaraBy::SongOrigin(song_origin) => (None, song_origin.to_string()),
+            KaraBy::Author(author) => (Some(icon!(USER)), author.clone()),
+            KaraBy::Playlist(playlist) => (Some(icon!(FOLDER)), playlist.clone()),
+        };
+
+        widget::row::with_capacity(2)
+            .push_maybe(maybe_icon)
+            .push(widget::text(text))
+            .align_items(Alignment::Center)
+            .spacing(theme::active().cosmic().space_xxxs())
+            .apply(widget::button::custom)
+            .on_press(Message::RemoveFilterAtom(self.0))
+            .style(style::Button::Destructive)
+            .into()
+    }
+}
+
+impl Filter {
+    fn can_be_clear(&self) -> bool {
+        !self.1.is_empty() || !self.0.is_empty()
+    }
+
+    fn can_be_submited(&self) -> bool {
+        !self.1.is_empty()
+    }
+
+    fn view(&self) -> Element<Message> {
+        let space_xxs = theme::active().cosmic().space_xxs();
+
+        let staging_text_input = widget::text_input(
+            "@author | tag:value | tag: | #playlist | OP | anime | ...",
+            &self.0,
+        )
+        .on_submit(Message::AddFilterAtomFromStageBuffer)
+        .on_input(Message::ChangeFilterStageBuffer)
+        .on_clear(Message::ChangeFilterStageBuffer(String::default()));
+
+        let button_commit_filters = widget::button::custom(icon!(FILTER))
+            .on_press_maybe(self.can_be_submited().then_some(Message::QueryWithFilters))
+            .padding(space_xxs)
+            .height(32)
+            .width(32);
+
+        let button_clear_filters = widget::button::custom(icon!(METEOR))
+            .on_press_maybe(self.can_be_clear().then_some(Message::ClearFilters))
+            .style(style::Button::Destructive)
+            .padding(space_xxs)
+            .height(32)
+            .width(32);
+
+        let staging_row = widget::row::with_capacity(3)
+            .push(staging_text_input)
+            .push(button_commit_filters)
+            .push(button_clear_filters)
+            .align_items(Alignment::Center)
+            .spacing(space_xxs)
+            .into();
+
+        Element::from(widget::column::with_children(vec![
+            staging_row,
+            vertical_space(),
+            (self.1.iter().map(FilterAtom::view).collect::<Vec<_>>())
+                .apply(widget::flex_row)
+                .spacing(space_xxs)
+                .apply(Element::<Message>::from),
+        ]))
+    }
+
+    pub fn clear(&mut self) {
+        self.0.clear();
+        self.1.clear();
+    }
+
+    pub fn modify_stage_buffer(&mut self, search: String) {
+        self.0 = search;
+    }
+
+    pub fn commit(&mut self) {
+        let atom: FilterAtom = self.0.parse().expect("infallible");
+        if !(self.1.iter()).any(|commited| commited.0 == atom.0 || commited.1 == atom.1) {
+            self.1.push(atom);
+        }
+        self.0.clear();
+    }
+
+    pub fn remove(&mut self, id: FilterAtomId) {
+        if let Some(idx) = (self.1.iter().enumerate())
+            .find_map(|(idx, FilterAtom(atom, _))| (*atom == id).then_some(idx))
+        {
+            self.1.remove(idx);
+        }
+    }
+}
+
+pub fn view(app: &AppModel) -> Element<Message> {
+    widget::column::with_capacity(4)
+        .push(pages::page_label(fl!("search")))
+        .push(app.search_filter.view())
+        .push(vertical_space())
+        .push_maybe((!app.search_results.is_empty()).then(|| {
+            (app.search_results.iter()).fold(widget::list_column(), |list, kid| {
+                list.add(kard::view(app.store.get(kid)))
+            })
+        }))
+        .into()
+}
diff --git a/amadeus/src/app/playlist_page.rs b/amadeus/src/app/playlist_page.rs
deleted file mode 100644
index aaf8cd23..00000000
--- a/amadeus/src/app/playlist_page.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::{app::Message, fl};
-use cosmic::{
-    iced::{
-        alignment::{Horizontal, Vertical},
-        Length,
-    },
-    widget, Apply as _, Element,
-};
-
-pub fn view<'a>(playlist: &str) -> Element<'a, Message> {
-    widget::text::title1(fl!("playlist", name = playlist))
-        .apply(widget::container)
-        .width(Length::Fill)
-        .height(Length::Fill)
-        .align_x(Horizontal::Center)
-        .align_y(Vertical::Center)
-        .into()
-}
diff --git a/amadeus/src/app/playlists_page.rs b/amadeus/src/app/playlists_page.rs
deleted file mode 100644
index dabcc76e..00000000
--- a/amadeus/src/app/playlists_page.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::{app::Message, fl};
-use cosmic::{
-    iced::{
-        alignment::{Horizontal, Vertical},
-        Length,
-    },
-    widget, Apply as _, Element,
-};
-
-pub fn view<'a>() -> Element<'a, Message> {
-    widget::text::title1(fl!("playlists"))
-        .apply(widget::container)
-        .width(Length::Fill)
-        .height(Length::Fill)
-        .align_x(Horizontal::Center)
-        .align_y(Vertical::Center)
-        .into()
-}
diff --git a/amadeus/src/app/progress_bar.rs b/amadeus/src/app/progress_bar.rs
new file mode 100644
index 00000000..67ad1d34
--- /dev/null
+++ b/amadeus/src/app/progress_bar.rs
@@ -0,0 +1,33 @@
+use crate::app::{AppModel, Message};
+use cosmic::{iced::Alignment, style, theme, widget, Apply as _, Element};
+
+fn format_time(secs: f32) -> String {
+    format!("{}:{}", secs / 60.0, secs % 60.0)
+}
+
+pub fn view(app: &AppModel) -> Element<Message> {
+    let Some((time, duration)) = app.lektord_state.current_times() else {
+        return widget::row().into();
+    };
+
+    widget::row::with_children(vec![
+        format_time(time)
+            .apply(widget::text::monotext)
+            .font(cosmic::font::FONT_LIGHT)
+            .style(style::Text::Accent)
+            .into(),
+        widget::progress_bar(0.0..=duration, time)
+            .height(10.0)
+            .width(100.0)
+            .style(style::ProgressBar::Primary)
+            .into(),
+        format_time(duration)
+            .apply(widget::text::monotext)
+            .font(cosmic::font::FONT_LIGHT)
+            .style(style::Text::Accent)
+            .into(),
+    ])
+    .align_items(Alignment::Center)
+    .spacing(theme::active().cosmic().space_xxs())
+    .into()
+}
diff --git a/amadeus/src/app/queue_page.rs b/amadeus/src/app/queue_page.rs
deleted file mode 100644
index e1daae7a..00000000
--- a/amadeus/src/app/queue_page.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::{app::Message, fl};
-use cosmic::{
-    iced::{
-        alignment::{Horizontal, Vertical},
-        Length,
-    },
-    widget, Apply as _, Element,
-};
-
-pub fn view<'a>() -> Element<'a, Message> {
-    widget::text::title1(fl!("queue"))
-        .apply(widget::container)
-        .width(Length::Fill)
-        .height(Length::Fill)
-        .align_x(Horizontal::Center)
-        .align_y(Vertical::Center)
-        .into()
-}
diff --git a/amadeus/src/app/search_page.rs b/amadeus/src/app/search_page.rs
deleted file mode 100644
index 03db263b..00000000
--- a/amadeus/src/app/search_page.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use crate::{app::Message, fl};
-use cosmic::{
-    iced::{
-        alignment::{Horizontal, Vertical},
-        Length,
-    },
-    widget, Apply as _, Element,
-};
-
-pub fn view<'a>() -> Element<'a, Message> {
-    widget::text::title1(fl!("search"))
-        .apply(widget::container)
-        .width(Length::Fill)
-        .height(Length::Fill)
-        .align_x(Horizontal::Center)
-        .align_y(Vertical::Center)
-        .into()
-}
diff --git a/amadeus/src/config.rs b/amadeus/src/config.rs
index 96a85567..ba840ea8 100644
--- a/amadeus/src/config.rs
+++ b/amadeus/src/config.rs
@@ -19,8 +19,11 @@ pub struct Config {
     retry_interval: Duration,
 
     kurisu_token: Option<String>,
+    kurisu_url: String,
 }
 
+pub const KURISU_URL: &str = "https://kurisu.iiens.net";
+
 impl Config {
     pub fn log_level(&self) -> log::Level {
         self.log_level.into()
@@ -38,6 +41,10 @@ impl Config {
         self.kurisu_token.as_deref()
     }
 
+    pub fn kurisu_url(&self) -> &str {
+        &self.kurisu_url
+    }
+
     pub fn get_connect_config(&self) -> ConnectConfig {
         ConnectConfig {
             host: self.host.into(),
@@ -53,7 +60,9 @@ impl Default for Config {
     fn default() -> Self {
         Self {
             retry_interval: Duration::from_secs(10),
+
             kurisu_token: None,
+            kurisu_url: KURISU_URL.into(),
 
             log_level: Default::default(),
             host: Default::default(),
diff --git a/amadeus/src/icons.rs b/amadeus/src/icons.rs
index 733cbcfc..26396513 100644
--- a/amadeus/src/icons.rs
+++ b/amadeus/src/icons.rs
@@ -1,58 +1,46 @@
 //! Custom icons used, picked from: https://fontawesome.com. The license can be found here:
 //! https://fontawesome.com/license/free. Copyright 2024 Fonticons, Inc.
 
-#![allow(unused)]
-
-pub const LINK: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"/></svg>"#.as_bytes();
-
-pub const MINUS: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z"/></svg>"#.as_bytes();
-
-pub const PLUS: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"/></svg>"#.as_bytes();
-
-pub const HISTORY: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9L0 168c0 13.3 10.7 24 24 24l110.1 0c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24l0 104c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65 0-94.1c0-13.3-10.7-24-24-24z"/></svg>"#.as_bytes();
-
-pub const BARCODES: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M24 32C10.7 32 0 42.7 0 56L0 456c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24L64 56c0-13.3-10.7-24-24-24L24 32zm88 0c-8.8 0-16 7.2-16 16l0 416c0 8.8 7.2 16 16 16s16-7.2 16-16l0-416c0-8.8-7.2-16-16-16zm72 0c-13.3 0-24 10.7-24 24l0 400c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24l0-400c0-13.3-10.7-24-24-24l-16 0zm96 0c-13.3 0-24 10.7-24 24l0 400c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24l0-400c0-13.3-10.7-24-24-24l-16 0zM448 56l0 400c0 13.3 10.7 24 24 24l16 0c13.3 0 24-10.7 24-24l0-400c0-13.3-10.7-24-24-24l-16 0c-13.3 0-24 10.7-24 24zm-64-8l0 416c0 8.8 7.2 16 16 16s16-7.2 16-16l0-416c0-8.8-7.2-16-16-16s-16 7.2-16 16z"/></svg>"#.as_bytes();
-
-pub const WEB: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M352 256c0 22.2-1.2 43.6-3.3 64l-185.3 0c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64l185.3 0c2.2 20.4 3.3 41.8 3.3 64zm28.8-64l123.1 0c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64l-123.1 0c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32l-116.7 0c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0l-176.6 0c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0L18.6 160C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192l123.1 0c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64L8.1 320C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6l176.6 0c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352l116.7 0zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6l116.7 0z"/></svg>"#.as_bytes();
-
-pub const USER: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M370.7 96.1C346.1 39.5 289.7 0 224 0S101.9 39.5 77.3 96.1C60.9 97.5 48 111.2 48 128l0 64c0 16.8 12.9 30.5 29.3 31.9C101.9 280.5 158.3 320 224 320s122.1-39.5 146.7-96.1c16.4-1.4 29.3-15.1 29.3-31.9l0-64c0-16.8-12.9-30.5-29.3-31.9zM336 144l0 16c0 53-43 96-96 96l-32 0c-53 0-96-43-96-96l0-16c0-26.5 21.5-48 48-48l128 0c26.5 0 48 21.5 48 48zM189.3 162.7l-6-21.2c-.9-3.3-3.9-5.5-7.3-5.5s-6.4 2.2-7.3 5.5l-6 21.2-21.2 6c-3.3 .9-5.5 3.9-5.5 7.3s2.2 6.4 5.5 7.3l21.2 6 6 21.2c.9 3.3 3.9 5.5 7.3 5.5s6.4-2.2 7.3-5.5l6-21.2 21.2-6c3.3-.9 5.5-3.9 5.5-7.3s-2.2-6.4-5.5-7.3l-21.2-6zM112.7 316.5C46.7 342.6 0 407 0 482.3C0 498.7 13.3 512 29.7 512l98.3 0 0-64c0-17.7 14.3-32 32-32l128 0c17.7 0 32 14.3 32 32l0 64 98.3 0c16.4 0 29.7-13.3 29.7-29.7c0-75.3-46.7-139.7-112.7-165.8C303.9 338.8 265.5 352 224 352s-79.9-13.2-111.3-35.5zM176 448c-8.8 0-16 7.2-16 16l0 48 32 0 0-48c0-8.8-7.2-16-16-16zm96 32a16 16 0 1 0 0-32 16 16 0 1 0 0 32z"/></svg>"#.as_bytes();
-
-pub const GIT: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M439.6 236.1L244 40.5a28.9 28.9 0 0 0 -40.8 0l-40.7 40.6 51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.2 199v121.9c25.3 12.5 22.3 41.9 9.1 55a34.3 34.3 0 0 1 -48.6 0c-17.6-17.6-11.1-46.9 11.3-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1a28.9 28.9 0 0 0 0 40.8l195.6 195.6a28.9 28.9 0 0 0 40.8 0l194.7-194.7a28.9 28.9 0 0 0 0-40.8z"/></svg>"#.as_bytes();
-
-pub const MENU: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 96C0 78.3 14.3 64 32 64l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32l384 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 288c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32L32 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l384 0c17.7 0 32 14.3 32 32z"/></svg>"#.as_bytes();
-
-pub const FILTER: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M3.9 54.9C10.5 40.9 24.5 32 40 32l432 0c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9 320 448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6l0-79.1L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z"/></svg>"#.as_bytes();
-
-pub const BUG: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 0c53 0 96 43 96 96l0 3.6c0 15.7-12.7 28.4-28.4 28.4l-135.1 0c-15.7 0-28.4-12.7-28.4-28.4l0-3.6c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4l112 0c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5l64.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-64 0c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6L272 240c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 239.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64.3 0c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"/></svg>"#.as_bytes();
-
-pub const UNPIN: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L481.4 352c9.8-.4 18.9-5.3 24.6-13.3c6-8.3 7.7-19.1 4.4-28.8l-1-3c-13.8-41.5-42.8-74.8-79.5-94.7L418.5 64 448 64c17.7 0 32-14.3 32-32s-14.3-32-32-32L192 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l29.5 0-6.1 79.5L38.8 5.1zM324.9 352L177.1 235.6c-20.9 18.9-37.2 43.3-46.5 71.3l-1 3c-3.3 9.8-1.6 20.5 4.4 28.8s15.7 13.3 26 13.3l164.9 0zM288 384l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-96-64 0z"/></svg>"#.as_bytes();
-
-pub const PIN: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M32 32C32 14.3 46.3 0 64 0L320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-29.5 0 11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3L32 352c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64 64 64C46.3 64 32 49.7 32 32zM160 384l64 0 0 96c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-96z"/></svg>"#.as_bytes();
-
-pub const SAVE: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96L0 416c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-242.7c0-17-6.7-33.3-18.7-45.3L352 50.7C340 38.7 323.7 32 306.7 32L64 32zm0 96c0-17.7 14.3-32 32-32l192 0c17.7 0 32 14.3 32 32l0 64c0 17.7-14.3 32-32 32L96 224c-17.7 0-32-14.3-32-32l0-64zM224 288a64 64 0 1 1 0 128 64 64 0 1 1 0-128z"/></svg>"#.as_bytes();
-
-pub const EDIT: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M368.4 18.3L312.7 74.1 437.9 199.3l55.7-55.7c21.9-21.9 21.9-57.3 0-79.2L447.6 18.3c-21.9-21.9-57.3-21.9-79.2 0zM288 94.6l-9.2 2.8L134.7 140.6c-19.9 6-35.7 21.2-42.3 41L3.8 445.8c-3.8 11.3-1 23.9 7.3 32.4L164.7 324.7c-3-6.3-4.7-13.3-4.7-20.7c0-26.5 21.5-48 48-48s48 21.5 48 48s-21.5 48-48 48c-7.4 0-14.4-1.7-20.7-4.7L33.7 500.9c8.6 8.3 21.1 11.2 32.4 7.3l264.3-88.6c19.7-6.6 35-22.4 41-42.3l43.2-144.1 2.7-9.2L288 94.6z"/></svg>"#.as_bytes();
-
-pub const METEOR: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M493.7 .9L299.4 75.6l2.3-29.3c1-12.8-12.8-21.5-24-15.1L101.3 133.4C38.6 169.7 0 236.6 0 309C0 421.1 90.9 512 203 512c72.4 0 139.4-38.6 175.7-101.3L480.8 234.3c6.5-11.1-2.2-25-15.1-24l-29.3 2.3L511.1 18.3c.6-1.5 .9-3.2 .9-4.8C512 6 506 0 498.5 0c-1.7 0-3.3 .3-4.8 .9zM192 192a128 128 0 1 1 0 256 128 128 0 1 1 0-256zm0 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm16 96a16 16 0 1 0 0-32 16 16 0 1 0 0 32z"/></svg>"#.as_bytes();
-
-pub const SATELLITE: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M192 32c0-17.7 14.3-32 32-32C383.1 0 512 128.9 512 288c0 17.7-14.3 32-32 32s-32-14.3-32-32C448 164.3 347.7 64 224 64c-17.7 0-32-14.3-32-32zM60.6 220.6L164.7 324.7l28.4-28.4c-.7-2.6-1.1-5.4-1.1-8.3c0-17.7 14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32c-2.9 0-5.6-.4-8.3-1.1l-28.4 28.4L291.4 451.4c14.5 14.5 11.8 38.8-7.3 46.3C260.5 506.9 234.9 512 208 512C93.1 512 0 418.9 0 304c0-26.9 5.1-52.5 14.4-76.1c7.5-19 31.8-21.8 46.3-7.3zM224 96c106 0 192 86 192 192c0 17.7-14.3 32-32 32s-32-14.3-32-32c0-70.7-57.3-128-128-128c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg>"#.as_bytes();
-
-pub const HOME: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"/></svg>"#.as_bytes();
-
-pub const QUEUE: &[u8]= r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M24 56c0-13.3 10.7-24 24-24l32 0c13.3 0 24 10.7 24 24l0 120 16 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l16 0 0-96-8 0C34.7 80 24 69.3 24 56zM86.7 341.2c-6.5-7.4-18.3-6.9-24 1.2L51.5 357.9c-7.7 10.8-22.7 13.3-33.5 5.6s-13.3-22.7-5.6-33.5l11.1-15.6c23.7-33.2 72.3-35.6 99.2-4.9c21.3 24.4 20.8 60.9-1.1 84.7L86.8 432l33.2 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-88 0c-9.5 0-18.2-5.6-22-14.4s-2.1-18.9 4.3-25.9l72-78c5.3-5.8 5.4-14.6 .3-20.5zM224 64l256 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-256 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 160l256 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-256 0c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 160l256 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-256 0c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg>"#.as_bytes();
-
-pub const ARCHIVE: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64C0 46.3 14.3 32 32 32zm0 128l448 0 0 256c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-256zm128 80c0 8.8 7.2 16 16 16l160 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-160 0c-8.8 0-16 7.2-16 16z"/></svg>"#.as_bytes();
-
-pub const SEARCH: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>"#.as_bytes();
-
-pub const PREVIOUS: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M459.5 440.6c9.5 7.9 22.8 9.7 34.1 4.4s18.4-16.6 18.4-29l0-320c0-12.4-7.2-23.7-18.4-29s-24.5-3.6-34.1 4.4L288 214.3l0 41.7 0 41.7L459.5 440.6zM256 352l0-96 0-128 0-32c0-12.4-7.2-23.7-18.4-29s-24.5-3.6-34.1 4.4l-192 160C4.2 237.5 0 246.5 0 256s4.2 18.5 11.5 24.6l192 160c9.5 7.9 22.8 9.7 34.1 4.4s18.4-16.6 18.4-29l0-64z"/></svg>"#.as_bytes();
-
-pub const NEXT: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416L0 96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4L224 214.3l0 41.7 0 41.7L52.5 440.6zM256 352l0-96 0-128 0-32c0-12.4 7.2-23.7 18.4-29s24.5-3.6 34.1 4.4l192 160c7.3 6.1 11.5 15.1 11.5 24.6s-4.2 18.5-11.5 24.6l-192 160c-9.5 7.9-22.8 9.7-34.1 4.4s-18.4-16.6-18.4-29l0-64z"/></svg>"#.as_bytes();
-
-pub const STOP: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128z"/></svg>"#.as_bytes();
-
-pub const SHUFFLE: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M403.8 34.4c12-5 25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64c-9.2 9.2-22.9 11.9-34.9 6.9s-19.8-16.6-19.8-29.6l0-32-32 0c-10.1 0-19.6 4.7-25.6 12.8L284 229.3 244 176l31.2-41.6C293.3 110.2 321.8 96 352 96l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6zM164 282.7L204 336l-31.2 41.6C154.7 401.8 126.2 416 96 416l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c10.1 0 19.6-4.7 25.6-12.8L164 282.7zm274.6 188c-9.2 9.2-22.9 11.9-34.9 6.9s-19.8-16.6-19.8-29.6l0-32-32 0c-30.2 0-58.7-14.2-76.8-38.4L121.6 172.8c-6-8.1-15.5-12.8-25.6-12.8l-64 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l64 0c30.2 0 58.7 14.2 76.8 38.4L326.4 339.2c6 8.1 15.5 12.8 25.6 12.8l32 0 0-32c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l64 64c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-64 64z"/></svg>"#.as_bytes();
-
-pub const PLAY: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80L0 432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg>"#.as_bytes();
-
-pub const PAUSE: &[u8] = r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M48 64C21.5 64 0 85.5 0 112L0 400c0 26.5 21.5 48 48 48l32 0c26.5 0 48-21.5 48-48l0-288c0-26.5-21.5-48-48-48L48 64zm192 0c-26.5 0-48 21.5-48 48l0 288c0 26.5 21.5 48 48 48l32 0c26.5 0 48-21.5 48-48l0-288c0-26.5-21.5-48-48-48l-32 0z"/></svg>"#.as_bytes();
+macro_rules! icon {
+    ($icon:ident : $file:literal) => {
+        #[allow(unused)]
+        pub const $icon: &[u8] = include_bytes!(concat!("../rsc/icons/", $file, ".svg"));
+    };
+}
+
+icon!(ICED: "iced");
+icon!(RUST: "fontawesome/rust");
+icon!(GIT:  "fontawesome/git");
+
+icon!(FOLDER_PLUS:  "fontawesome/folder_plus");
+icon!(FOLDER_MINUS: "fontawesome/folder_minus");
+icon!(FOLDER:       "fontawesome/folder");
+icon!(HASHTAG:      "fontawesome/hashtag");
+icon!(TAG:          "fontawesome/tag");
+icon!(TAGS:         "fontawesome/tags");
+icon!(LINK:         "fontawesome/link");
+icon!(MINUS:        "fontawesome/minus");
+icon!(PLUS:         "fontawesome/plus");
+icon!(HISTORY:      "fontawesome/history");
+icon!(BARCODES:     "fontawesome/barcodes");
+icon!(WEB:          "fontawesome/web");
+icon!(USER:         "fontawesome/user");
+icon!(MENU:         "fontawesome/menu");
+icon!(FILTER:       "fontawesome/filter");
+icon!(BUG:          "fontawesome/bug");
+icon!(HOME:         "fontawesome/home");
+icon!(QUEUE:        "fontawesome/queue");
+icon!(ARCHIVE:      "fontawesome/archive");
+icon!(SEARCH:       "fontawesome/search");
+icon!(UNPIN:        "fontawesome/unpin");
+icon!(PIN:          "fontawesome/pin");
+icon!(SAVE:         "fontawesome/save");
+icon!(EDIT:         "fontawesome/edit");
+icon!(METEOR:       "fontawesome/meteor");
+icon!(SATELLITE:    "fontawesome/satellite");
+icon!(PREVIOUS:     "fontawesome/previous");
+icon!(NEXT:         "fontawesome/next");
+icon!(STOP:         "fontawesome/stop");
+icon!(SHUFFLE:      "fontawesome/shuffle");
+icon!(PLAY:         "fontawesome/play");
+icon!(PAUSE:        "fontawesome/pause");
diff --git a/amadeus/src/main.rs b/amadeus/src/main.rs
index efb28eaa..0978fba8 100644
--- a/amadeus/src/main.rs
+++ b/amadeus/src/main.rs
@@ -2,6 +2,7 @@ mod app;
 mod config;
 mod i18n;
 mod icons;
+mod store;
 mod subscriptions;
 
 include!(concat!(env!("OUT_DIR"), "/amadeus_build_infos.rs"));
diff --git a/amadeus/src/store.rs b/amadeus/src/store.rs
new file mode 100644
index 00000000..c6df4c90
--- /dev/null
+++ b/amadeus/src/store.rs
@@ -0,0 +1,110 @@
+//! Contains all the structures to store and get the state of the lektord instance. Must be updated
+//! in the iced-update phase, and queried in the iced-view phase.
+
+mod history;
+mod playlist_content;
+mod playlists;
+mod queue;
+mod queue_level;
+
+use hashbrown::HashMap;
+use lektor_payloads::{KId, Kara, Playlist, PlaylistName, Priority, PRIORITY_LENGTH};
+use std::{fmt, mem};
+
+/// Stores the kara or its id if the [Kara] struct was not already cached in the [Store].
+#[derive(Debug, Clone)]
+pub enum KaraOrId<'a> {
+    Kara(&'a Kara),
+    Id(KId),
+}
+
+/// Contains informations about playlists, karas, the queue, the history, etc. All the operations
+/// are sync thanks to the way iced/libcosmic handle the things. All the getters are done in a lazy
+/// way.
+///
+/// Note that all updates are reset operations. We may want to do updates, but said updates must be
+/// stored in lektord and then queried… we will see latter if we do that…
+#[derive(Debug, Default)]
+pub struct Store {
+    /// All the karas, see latter if we do a hashmap or an LRU cache, or something else…
+    karas: HashMap<KId, Kara>,
+
+    /// All the informations about the playlists.
+    playlists: HashMap<PlaylistName, Playlist>,
+
+    /// The history, insert at the begin, remove at the end.
+    history: Vec<KId>,
+
+    /// The queue, we have a multilevel queue here.
+    queue: [Vec<KId>; PRIORITY_LENGTH],
+}
+
+impl Store {
+    /// Reset the state of a level of the queue to something.
+    pub fn set_queue_level(&mut self, level: Priority, kids: Vec<KId>) {
+        _ = mem::replace(&mut self.queue[level.index()], kids);
+    }
+
+    /// Reset the state of the history.
+    pub fn set_history(&mut self, kids: Vec<KId>) {
+        _ = mem::replace(&mut self.history, kids);
+    }
+
+    /// Set the content of a playlist. Insert it if it didn't exists. Update it if the entry
+    /// existed in the store.
+    pub fn set_playlist(&mut self, name: PlaylistName, plt: Playlist) {
+        _ = self.playlists.insert(name, plt);
+    }
+
+    /// Keep playlists only if their name is specified in the passed [Vec].
+    pub fn keep_playlists(&mut self, names: Vec<PlaylistName>) {
+        self.playlists.retain(|key, _| names.contains(key));
+    }
+}
+
+impl Store {
+    /// Get the iterator to query all the levels of the queue.
+    pub fn iter_queue(&self) -> queue::QueueIter {
+        queue::QueueIter::new(self)
+    }
+
+    /// Get the iterator to query a specific level of the queue.
+    pub fn iter_queue_level(&self, level: Priority) -> queue_level::QueueLevelIter {
+        queue_level::QueueLevelIter::new(self, level)
+    }
+
+    /// Get the iterator to query the history.
+    pub fn iter_history(&self) -> history::HistoryIter {
+        history::HistoryIter::new(self)
+    }
+
+    /// Get the iterator to list all the playlists' info.
+    pub fn iter_playlists(&self) -> playlists::PlaylistsIter {
+        playlists::PlaylistsIter::new(self)
+    }
+
+    /// Get the iterator to list the playlist's content.
+    pub fn iter_playlist_content<S>(&self, name: S) -> playlist_content::PlaylistContentIter
+    where
+        S: TryInto<PlaylistName>,
+        <S as TryInto<PlaylistName>>::Error: fmt::Display,
+    {
+        match name.try_into() {
+            Ok(name) => playlist_content::PlaylistContentIter::new(self, name),
+            Err(err) => {
+                log::error!("{err}");
+                playlist_content::PlaylistContentIter::empty(self)
+            }
+        }
+    }
+
+    /// Get a kara from the store. If the [Kara] was not already cached in the store, returns its
+    /// [KId]... Note that if the passed [KId] is not valid, then we return a [KId] that doesn't
+    /// really exists as we can't know if it's valid or not…
+    pub fn get(&self, kid: &KId) -> KaraOrId {
+        self.karas
+            .get(kid)
+            .map(KaraOrId::Kara)
+            .unwrap_or_else(|| KaraOrId::Id(kid.clone()))
+    }
+}
diff --git a/amadeus/src/store/history.rs b/amadeus/src/store/history.rs
new file mode 100644
index 00000000..b7e18576
--- /dev/null
+++ b/amadeus/src/store/history.rs
@@ -0,0 +1,36 @@
+use super::Store;
+use lektor_payloads::KId;
+
+/// Wrapper struct to get the content of the history. Returns the kara in the most recent to the
+/// oldest one. Implements [Iterator], use the [Iterator::next] function to query the state.
+#[derive(Debug, Clone, Copy)]
+pub struct HistoryIter<'a>(&'a Store, usize);
+
+impl<'a> HistoryIter<'a> {
+    pub(super) fn new(store: &'a Store) -> Self {
+        Self(store, 0)
+    }
+}
+
+impl<'a> Iterator for HistoryIter<'a> {
+    type Item = &'a KId;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.0.history)
+            .get(self.1)
+            .inspect(|_| self.1 += 1)
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        self.0.history.last()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let size = self.0.history.len() - self.1;
+        (size, Some(size))
+    }
+
+    fn count(self) -> usize {
+        self.size_hint().1.unwrap()
+    }
+}
diff --git a/amadeus/src/store/playlist_content.rs b/amadeus/src/store/playlist_content.rs
new file mode 100644
index 00000000..8f389bf1
--- /dev/null
+++ b/amadeus/src/store/playlist_content.rs
@@ -0,0 +1,47 @@
+use super::Store;
+use lektor_payloads::{KId, PlaylistName};
+
+/// Wrapper struct to get the content of a playlist.
+#[derive(Debug, Clone)]
+pub struct PlaylistContentIter<'a>(&'a Store, Option<PlaylistName>, usize);
+
+impl<'a> PlaylistContentIter<'a> {
+    pub(super) fn empty(store: &'a Store) -> Self {
+        Self(store, None, 0)
+    }
+
+    pub(super) fn new(store: &'a Store, name: PlaylistName) -> Self {
+        Self(store, Some(name), 0)
+    }
+}
+
+impl<'a> Iterator for PlaylistContentIter<'a> {
+    type Item = &'a KId;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.0.playlists)
+            .get(self.1.as_ref()?)?
+            .content()
+            .get(self.2)
+            .inspect(|_| self.2 += 1)
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        (self.0.playlists).get(self.1.as_ref()?)?.content().last()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let Some(name) = self.1.as_ref() else {
+            return (0, Some(0));
+        };
+        let size = (self.0.playlists)
+            .get(name)
+            .map(|plt| plt.content().len() - self.2)
+            .unwrap_or_default();
+        (size, Some(size))
+    }
+
+    fn count(self) -> usize {
+        self.size_hint().1.unwrap()
+    }
+}
diff --git a/amadeus/src/store/playlists.rs b/amadeus/src/store/playlists.rs
new file mode 100644
index 00000000..8734b713
--- /dev/null
+++ b/amadeus/src/store/playlists.rs
@@ -0,0 +1,35 @@
+use super::Store;
+use hashbrown::hash_map;
+use lektor_payloads::{Playlist, PlaylistName};
+
+/// Wrapper struct to get the playlists.
+#[derive(Debug, Clone)]
+pub struct PlaylistsIter<'a>(&'a Store, hash_map::Keys<'a, PlaylistName, Playlist>);
+
+impl<'a> PlaylistsIter<'a> {
+    pub(super) fn new(store: &'a Store) -> Self {
+        Self(store, store.playlists.keys())
+    }
+}
+
+impl<'a> Iterator for PlaylistsIter<'a> {
+    type Item = (PlaylistName, &'a Playlist);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let key = self.1.next()?;
+        self.0.playlists.get(key).map(|plt| (key.clone(), plt))
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        let key = self.1.last()?;
+        self.0.playlists.get(key).map(|plt| (key.clone(), plt))
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.1.size_hint()
+    }
+
+    fn count(self) -> usize {
+        self.1.count()
+    }
+}
diff --git a/amadeus/src/store/queue.rs b/amadeus/src/store/queue.rs
new file mode 100644
index 00000000..9805720e
--- /dev/null
+++ b/amadeus/src/store/queue.rs
@@ -0,0 +1,37 @@
+use super::Store;
+use lektor_payloads::{KId, Priority};
+
+/// Wrapper struct to get the content of the queue, any level. Implements [Iterator], use the
+/// [Iterator::next] functions to query the state. Returns kara from the one closest to being
+/// played to the farest.
+#[derive(Debug, Clone, Copy)]
+pub struct QueueIter<'a>(&'a Store, usize);
+
+impl<'a> QueueIter<'a> {
+    pub(super) fn new(store: &'a Store) -> Self {
+        Self(store, 0)
+    }
+}
+
+impl<'a> Iterator for QueueIter<'a> {
+    type Item = &'a KId;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.0.queue.iter().rev().flatten())
+            .nth(self.1)
+            .inspect(|_| self.1 += 1)
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        self.0.queue[Priority::min().index()].last()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let size = self.0.queue.iter().map(|lvl| lvl.len()).sum::<usize>() - self.1;
+        (size, Some(size))
+    }
+
+    fn count(self) -> usize {
+        self.size_hint().1.unwrap()
+    }
+}
diff --git a/amadeus/src/store/queue_level.rs b/amadeus/src/store/queue_level.rs
new file mode 100644
index 00000000..2cabf0cb
--- /dev/null
+++ b/amadeus/src/store/queue_level.rs
@@ -0,0 +1,37 @@
+use super::Store;
+use lektor_payloads::{KId, Priority};
+
+/// Wrapper struct to get the content of a specific level of the queue. Implements [Iterator], use
+/// the [Iterator::next] function to query the state. Returns kara from the one closest to being
+/// played to the farest.
+#[derive(Debug, Clone, Copy)]
+pub struct QueueLevelIter<'a>(&'a Store, Priority, usize);
+
+impl<'a> QueueLevelIter<'a> {
+    pub(super) fn new(store: &'a Store, level: Priority) -> Self {
+        Self(store, level, 0)
+    }
+}
+
+impl<'a> Iterator for QueueLevelIter<'a> {
+    type Item = &'a KId;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.0.queue[self.1.index()])
+            .get(self.2)
+            .inspect(|_| self.2 += 1)
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        (self.0.queue[self.1.index()]).last()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let size = (self.0.queue[self.1.index()]).len() - self.2;
+        (size, Some(size))
+    }
+
+    fn count(self) -> usize {
+        self.size_hint().1.unwrap()
+    }
+}
diff --git a/amadeus/src/subscriptions.rs b/amadeus/src/subscriptions.rs
index f33e8faa..e63c9824 100644
--- a/amadeus/src/subscriptions.rs
+++ b/amadeus/src/subscriptions.rs
@@ -1 +1,2 @@
-pub mod lektord;
+pub mod playback;
+pub mod updates;
diff --git a/amadeus/src/subscriptions/lektord.rs b/amadeus/src/subscriptions/lektord.rs
deleted file mode 100644
index 38cd9f07..00000000
--- a/amadeus/src/subscriptions/lektord.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use crate::app::Message;
-use cosmic::iced::{subscription, Subscription};
-use futures::SinkExt;
-use lektor_lib::{requests, ConnectConfig};
-use std::{any::TypeId, sync::Arc, time::Duration};
-use tokio::sync::RwLock;
-
-pub struct Suscription {
-    pub config: Arc<RwLock<ConnectConfig>>,
-}
-
-impl Suscription {
-    pub fn run(self) -> Subscription<Message> {
-        let Self { config } = self;
-        subscription::channel(TypeId::of::<Self>(), 10, move |mut channel| async move {
-            log::info!("lektord subscription launched");
-            'connect: loop {
-                macro_rules! disconnected {
-                    () => {{
-                        log::warn!("failed to connect to lektord");
-                        _ = channel.send(Message::LektordDisconnected);
-                        tokio::time::sleep(config.read().await.retry).await;
-                        continue 'connect;
-                    }};
-                }
-
-                match requests::get_infos(config.read().await.as_ref()).await {
-                    Ok(infos) => _ = channel.send(Message::LektordConnected(infos)).await,
-                    Err(_) => disconnected!(),
-                }
-
-                while let Ok(state) = requests::get_status(config.read().await.as_ref()).await {
-                    _ = channel.send(Message::LektordUpdate(state));
-                    tokio::time::sleep(Duration::from_secs(1)).await;
-                }
-            }
-        })
-    }
-}
diff --git a/amadeus/src/subscriptions/playback.rs b/amadeus/src/subscriptions/playback.rs
new file mode 100644
index 00000000..df1ad3b4
--- /dev/null
+++ b/amadeus/src/subscriptions/playback.rs
@@ -0,0 +1,38 @@
+use crate::app::{LektordMessage, Message};
+use cosmic::iced::{subscription, Subscription};
+use futures::SinkExt;
+use lektor_lib::{requests, ConnectConfig};
+use std::{any::TypeId, sync::Arc, time::Duration};
+use tokio::sync::RwLock;
+
+pub struct Suscription {
+    config: Arc<RwLock<ConnectConfig>>,
+}
+
+impl Suscription {
+    pub fn new(config: Arc<RwLock<ConnectConfig>>) -> Self {
+        Self { config }
+    }
+
+    pub fn run(self) -> Subscription<Message> {
+        use {LektordMessage::*, Message::*};
+        let Self { config } = self;
+
+        subscription::channel(TypeId::of::<Self>(), 10, move |mut channel| async move {
+            log::info!("lektord playback subscription launched");
+            loop {
+                match requests::get_status(config.read().await.as_ref()).await {
+                    Ok(state) => {
+                        _ = channel.send(Lektord(PlaybackUpdate(state))).await;
+                        tokio::time::sleep(Duration::from_secs(1)).await;
+                    }
+                    Err(err) => {
+                        log::debug!("failed to connect to lektord: {err}");
+                        _ = channel.send(Lektord(Disconnected)).await;
+                        tokio::time::sleep(config.read().await.retry).await;
+                    }
+                }
+            }
+        })
+    }
+}
diff --git a/amadeus/src/subscriptions/updates.rs b/amadeus/src/subscriptions/updates.rs
new file mode 100644
index 00000000..5a4b77ed
--- /dev/null
+++ b/amadeus/src/subscriptions/updates.rs
@@ -0,0 +1,40 @@
+use crate::app::{LektordMessage, Message};
+use cosmic::iced::{subscription, Subscription};
+use futures::SinkExt;
+use lektor_lib::{requests, ConnectConfig};
+use std::{any::TypeId, sync::Arc, time::Duration};
+use tokio::sync::RwLock;
+
+pub struct Suscription {
+    config: Arc<RwLock<ConnectConfig>>,
+}
+
+impl Suscription {
+    pub fn new(config: Arc<RwLock<ConnectConfig>>) -> Self {
+        Self { config }
+    }
+
+    pub fn run(self) -> Subscription<Message> {
+        use {LektordMessage::*, Message::*};
+        let Self { config } = self;
+
+        subscription::channel(TypeId::of::<Self>(), 10, move |mut channel| async move {
+            log::info!("lektord updates subscription launched");
+            'connect: loop {
+                log::debug!("try initial connection");
+                let Ok(infos) = requests::get_infos(config.read().await.as_ref()).await else {
+                    _ = channel.send(Lektord(Disconnected)).await;
+                    tokio::time::sleep(config.read().await.retry).await;
+                    continue 'connect;
+                };
+                _ = channel.send(Lektord(Connected(infos))).await;
+
+                loop {
+                    log::debug!("here we want to query updates for the queue, history, etc…");
+                    log::debug!("on comm error we want to loop to 'connect here");
+                    tokio::time::sleep(Duration::from_secs(5)).await;
+                }
+            }
+        })
+    }
+}
diff --git a/lektor_nkdb/src/database/kara.rs b/lektor_nkdb/src/database/kara.rs
index 79f3f3e6..aeab4641 100644
--- a/lektor_nkdb/src/database/kara.rs
+++ b/lektor_nkdb/src/database/kara.rs
@@ -45,6 +45,8 @@ pub struct KaraTimeStamps {
 
 /// The kara's data. To ensure backward compatibility with databases versions, every kara should be
 /// constructible from this struct definition.
+///
+/// TODO: Replace things by short vecs, to avoid too much allocations…
 #[derive(Debug, Serialize, Deserialize, Clone)]
 pub struct Kara {
     /// The local ID of the kara
@@ -60,6 +62,8 @@ pub struct Kara {
     pub song_source: String,
 
     /// A list of kara makers / authors and contributors to the kara.
+    ///
+    /// TODO: Replace by a short vec or a tri, or specialized structure.
     pub kara_makers: HashSet<Arc<str>>,
 
     /// The type of the song.
@@ -69,6 +73,8 @@ pub struct Kara {
     pub song_origin: SongOrigin,
 
     /// All languages present in the kara.
+    ///
+    /// TODO: Replace by a short vec, or tri, or specialized structure.
     pub language: HashSet<Arc<str>>,
 
     /// The status of the kara, can be usefull to decide if we can insert it into the queue or not.
@@ -78,6 +84,8 @@ pub struct Kara {
     pub timestamps: KaraTimeStamps,
 
     /// A list of tag. Tags can be value-less, have one value or multiple ones.
+    ///
+    /// TODO: Replace by a short vec. Better, replace by a specialized structure…
     pub tags: HashMap<Arc<str>, Vec<Arc<str>>>,
 }
 
@@ -99,6 +107,9 @@ impl Kara {
         ret.push_str(&self.song_title.to_lowercase());
         ret
     }
+
+    pub const TAG_NUMBER: &str = "number";
+    pub const TAG_VERSION: &str = "version";
 }
 
 impl std::fmt::Display for Kara {
diff --git a/lektor_nkdb/src/playlist/name.rs b/lektor_nkdb/src/playlist/name.rs
index d2b7757f..64037992 100644
--- a/lektor_nkdb/src/playlist/name.rs
+++ b/lektor_nkdb/src/playlist/name.rs
@@ -113,3 +113,28 @@ fn invalid_playlist_name() {
     assert_err!(PlaylistName::from_str("@"));
     assert_err!(PlaylistName::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
 }
+
+#[test]
+fn playlist_name_eq() {
+    use lektor_utils::assert_ok;
+    use std::{
+        hash::{DefaultHasher, Hash, Hasher},
+        ptr,
+    };
+    let a = assert_ok!(PlaylistName::from_str("abc"));
+    let b = assert_ok!(PlaylistName::from_str("abc"));
+    assert!(!ptr::addr_eq(a.0.as_ptr(), b.0.as_ptr()));
+    assert_eq!(a, b);
+    assert_eq!(
+        {
+            let mut s = DefaultHasher::new();
+            a.hash(&mut s);
+            s.finish()
+        },
+        {
+            let mut s = DefaultHasher::new();
+            b.hash(&mut s);
+            s.finish()
+        },
+    );
+}
diff --git a/lektor_nkdb/src/queue/priority.rs b/lektor_nkdb/src/queue/priority.rs
index b4aa903d..f8cd69ba 100644
--- a/lektor_nkdb/src/queue/priority.rs
+++ b/lektor_nkdb/src/queue/priority.rs
@@ -56,6 +56,14 @@ impl Priority {
     pub const fn index(&self) -> usize {
         (*self as usize) - 1
     }
+
+    pub const fn min() -> Self {
+        Self::Add
+    }
+
+    pub const fn max() -> Self {
+        Self::Enforce
+    }
 }
 
 impl std::fmt::Display for Priority {
@@ -94,11 +102,19 @@ fn test_priorities() {
     let into = <Priority as Into<usize>>::into;
 
     assert!(Add < Enforce);
+    assert!(into(Priority::min()) < PRIORITY_LENGTH);
+    assert!(into(Priority::max()) == PRIORITY_LENGTH);
     assert!(into(Add) < PRIORITY_LENGTH);
     assert!(into(Suggest) < PRIORITY_LENGTH);
     assert!(into(Insert) < PRIORITY_LENGTH);
     assert!(into(Enforce) == PRIORITY_LENGTH);
 
+    assert!(Priority::max() > Priority::min());
+    for prio in PRIORITY_VALUES {
+        assert!(*prio <= Priority::max());
+        assert!(*prio >= Priority::min());
+    }
+
     #[allow(dead_code)]
     fn test_4_variants(prio: Priority) {
         match prio {
diff --git a/lektor_nkdb/src/search/kara_by.rs b/lektor_nkdb/src/search/kara_by.rs
index 21fe3b15..ae375cbc 100644
--- a/lektor_nkdb/src/search/kara_by.rs
+++ b/lektor_nkdb/src/search/kara_by.rs
@@ -4,7 +4,7 @@ use regex::{Regex, RegexBuilder};
 use serde::{Deserialize, Serialize};
 use std::{borrow::Cow, convert::Infallible, fmt, str::FromStr};
 
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
 pub enum KaraBy {
     Id(u64),
     Query(String),
diff --git a/lektor_payloads/src/playlist_name.rs b/lektor_payloads/src/playlist_name.rs
index 822d9fbc..68c71ca4 100644
--- a/lektor_payloads/src/playlist_name.rs
+++ b/lektor_payloads/src/playlist_name.rs
@@ -12,7 +12,7 @@ use std::str::FromStr;
 ///
 /// ### Safety
 /// We check before builder the playlist name that the [`u8`] is a valide UTF-8 string.
-#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
+#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Hash)]
 #[repr(transparent)]
 pub struct PlaylistName(NKDBPlaylistName);
 
@@ -25,6 +25,22 @@ impl AsRef<str> for PlaylistName {
     }
 }
 
+impl FromStr for PlaylistName {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        NKDBPlaylistName::from_str(s).map(Self)
+    }
+}
+
+impl TryFrom<&str> for PlaylistName {
+    type Error = anyhow::Error;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        value.parse()
+    }
+}
+
 impl From<PlaylistName> for NKDBPlaylistName {
     fn from(value: PlaylistName) -> Self {
         value.0
@@ -43,13 +59,6 @@ impl std::fmt::Display for PlaylistName {
     }
 }
 
-impl FromStr for PlaylistName {
-    type Err = anyhow::Error;
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        Ok(Self(s.parse()?))
-    }
-}
-
 #[async_trait::async_trait]
 impl<S> FromRequestParts<S> for PlaylistName {
     type Rejection = (StatusCode, String);
diff --git a/lektord/src/c_wrapper/mod.rs b/lektord/src/c_wrapper/mod.rs
index f0ddb5fc..23c595e5 100644
--- a/lektord/src/c_wrapper/mod.rs
+++ b/lektord/src/c_wrapper/mod.rs
@@ -3,7 +3,7 @@
 use crate::LektorStatePtr;
 use anyhow::{bail, ensure, Context, Result};
 use lektor_nkdb::PlayState;
-use lektor_utils::{config::LektorPlayerConfig, *};
+use lektor_utils::config::LektorPlayerConfig;
 use std::{
     ffi::*,
     ptr::NonNull,
-- 
GitLab