patches and low-level development discussion
 help / color / mirror / code / Atom feed
* [PATCH v2 0/5] Spectrum-installer
@ 2026-02-04 17:55 Johannes Süllner
  2026-02-04 17:55 ` [PATCH v2 1/5] tools: add spectrum-installer Johannes Süllner
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Johannes Süllner @ 2026-02-04 17:55 UTC (permalink / raw)
  To: devel; +Cc: Johannes Süllner

A custom graphical installer for Spectrum, written in Rust. It runs
within the live system and uses 'systemd-repart'. The user selects a
destination disk and confirms this selection. Then, 'systemd-repart'
first creates a partition table on the destination disk and then
copies the individual partitions of the live system to the destination
disk, what means they are kind of resized in the process. This allows
for having large A/B slots for Spectrum after the installation, while
keeping the live image at the minimum size.

The reason for writing a custom installer is that there are no
image-based distributions using an installer that does not require
an interpreter; something we want to avoid on the Spectrum host.
There is a promising new installer in development for Gnome OS
(https://gitlab.gnome.org/Teams/STF/setup), but it is still in an
early stage and the actual installation is not implemented yet.

Besides the boot partition and the A/B slots, the installer also
creates a Btrfs home partition, taking up the remaining disk space.
I included this for making it easier to test the update feature.

There are a few things I still want to add to the installer at some
future point. One thing is a check whether a possible destination
device is suitable for a Spectrum installation, or e.g. too small. I
currently plan to do so by running 'systemd-repart' in dry run mode on
all possible devices on launch of the installer, what would need to be
done asynchronously in a separate thread I think.

Also, running 'systemd-repart' in an embedded terminal, as it's done
right now, is something I'd like to change, as it feels somewhat hacky
to me.

v2:
• Updated to iced v0.14
• Replaced the error log on the 'completion' page with a QR code
  containing the log.
• Added the full discuss mailing list address to the error help
  message on the 'completion' page.
• Added '-o pipefail' to shell call for 'systemd-repart' invocation.
• Replaced 'lsblk' crate with 'blockdev' crate.
• Wrapped usage of 'drives' crate in 'DestinationDevice'.
• Replaced usage of 'libcosmicAppHook' in
  tools/spectrum-installer/default.nix
• Updated tools/spectrum-installer/shell.nix
• Updated cargo dependencies.
• Removed fixed size of verity partition in release/live/Makefile
• Added commit reverting the addition of the partition size parameter
  to make-gpt.sh

v1:
https://spectrum-os.org/lists/archives/spectrum-devel/20260104140102.106960-5-johannes.suellner@mailbox.org/

Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>

---

Johannes Süllner (5):
      tools: add spectrum-installer
      host/rootfs: integrate spectrum-installer
      release: drop combined and installer image
      release/live: remove B slot from live image and reduce
      Revert "scripts/make-gpt.sh: allow setting partition

 Documentation/development/uuid-reference.adoc      |   22 -
 host/rootfs/default.nix                            |    4 +-
 host/rootfs/file-list.mk                           |    3 +
 .../spectrum-installer-as-root/notification-fd     |    1 +
 .../notification-fd.license                        |    2 +
 .../service/spectrum-installer-as-root/run         |   17 +
 host/rootfs/image/etc/s6-rc/weston/run             |    1 +
 host/rootfs/image/etc/xdg/weston/weston.ini        |    5 +
 .../image/usr/bin/spectrum-installer-as-root       |    5 +
 pkgs/default.nix                                   |    3 +
 release/checks/default.nix                         |    2 -
 release/checks/installer.nix                       |   16 -
 release/combined/default.nix                       |  123 -
 release/combined/eosimages.nix                     |   21 -
 release/combined/grub.cfg.in                       |   22 -
 release/combined/run-vm.nix                        |   31 -
 ...-disable-gpt-partition-attribute-55-check.patch |   37 -
 ...002-gpt-disable-partition-table-CRC-check.patch |   36 -
 .../app/0003-install-remove-Endless-OS-ad.patch    |   99 -
 .../0004-finished-don-t-run-eos-diagnostics.patch  |   44 -
 ...ed-promote-spectrum-not-the-Endless-forum.patch |   32 -
 release/installer/app/default.nix                  |   26 -
 release/installer/app/vendor-customer-support.ini  |    5 -
 release/installer/configuration.nix                |   69 -
 release/installer/default.nix                      |   22 -
 release/installer/run-vm.nix                       |   47 -
 release/installer/seat.rules                       |    9 -
 release/live/Makefile                              |    6 +-
 scripts/make-gpt.sh                                |   31 +-
 scripts/sfdisk-field.awk                           |   22 +-
 tools/spectrum-installer/Cargo.lock                | 5182 ++++++++++++++++++++
 tools/spectrum-installer/Cargo.lock.license        |    2 +
 tools/spectrum-installer/Cargo.toml                |   14 +
 tools/spectrum-installer/default.nix               |   55 +
 .../spectrum-installer/res/installer_logo_mesh.svg |   85 +
 tools/spectrum-installer/res/repart.d/10-esp.conf  |    8 +
 .../res/repart.d/20-verity-A.conf                  |    9 +
 .../res/repart.d/21-rootfs-A.conf                  |    9 +
 .../res/repart.d/30-verity-B.conf                  |    8 +
 .../res/repart.d/31-rootfs-B.conf                  |    8 +
 tools/spectrum-installer/res/repart.d/40-home.conf |    7 +
 tools/spectrum-installer/shell.nix                 |   28 +
 tools/spectrum-installer/src/destination_device.rs |   53 +
 tools/spectrum-installer/src/main.rs               |   53 +
 tools/spectrum-installer/src/pages/completion.rs   |  138 +
 tools/spectrum-installer/src/pages/confirmation.rs |  108 +
 .../spectrum-installer/src/pages/disk_selection.rs |  123 +
 tools/spectrum-installer/src/pages/installation.rs |  186 +
 tools/spectrum-installer/src/pages/layout.rs       |   89 +
 tools/spectrum-installer/src/pages/mod.rs          |   43 +
 tools/spectrum-installer/src/pages/welcome.rs      |   62 +
 51 files changed, 6328 insertions(+), 705 deletions(-)
 create mode 100644 host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd
 create mode 100644 host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd.license
 create mode 100755 host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/run
 create mode 100755 host/rootfs/image/usr/bin/spectrum-installer-as-root
 delete mode 100644 release/checks/installer.nix
 delete mode 100644 release/combined/default.nix
 delete mode 100644 release/combined/eosimages.nix
 delete mode 100644 release/combined/grub.cfg.in
 delete mode 100644 release/combined/run-vm.nix
 delete mode 100644 release/installer/app/0001-gpt-disable-gpt-partition-attribute-55-check.patch
 delete mode 100644 release/installer/app/0002-gpt-disable-partition-table-CRC-check.patch
 delete mode 100644 release/installer/app/0003-install-remove-Endless-OS-ad.patch
 delete mode 100644 release/installer/app/0004-finished-don-t-run-eos-diagnostics.patch
 delete mode 100644 release/installer/app/0005-finished-promote-spectrum-not-the-Endless-forum.patch
 delete mode 100644 release/installer/app/default.nix
 delete mode 100644 release/installer/app/vendor-customer-support.ini
 delete mode 100644 release/installer/configuration.nix
 delete mode 100644 release/installer/default.nix
 delete mode 100644 release/installer/run-vm.nix
 delete mode 100644 release/installer/seat.rules
 create mode 100644 tools/spectrum-installer/Cargo.lock
 create mode 100644 tools/spectrum-installer/Cargo.lock.license
 create mode 100644 tools/spectrum-installer/Cargo.toml
 create mode 100644 tools/spectrum-installer/default.nix
 create mode 100644 tools/spectrum-installer/res/installer_logo_mesh.svg
 create mode 100644 tools/spectrum-installer/res/repart.d/10-esp.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/20-verity-A.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/21-rootfs-A.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/30-verity-B.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/31-rootfs-B.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/40-home.conf
 create mode 100644 tools/spectrum-installer/shell.nix
 create mode 100644 tools/spectrum-installer/src/destination_device.rs
 create mode 100644 tools/spectrum-installer/src/main.rs
 create mode 100644 tools/spectrum-installer/src/pages/completion.rs
 create mode 100644 tools/spectrum-installer/src/pages/confirmation.rs
 create mode 100644 tools/spectrum-installer/src/pages/disk_selection.rs
 create mode 100644 tools/spectrum-installer/src/pages/installation.rs
 create mode 100644 tools/spectrum-installer/src/pages/layout.rs
 create mode 100644 tools/spectrum-installer/src/pages/mod.rs
 create mode 100644 tools/spectrum-installer/src/pages/welcome.rs

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/5] tools: add spectrum-installer
  2026-02-04 17:55 [PATCH v2 0/5] Spectrum-installer Johannes Süllner
@ 2026-02-04 17:55 ` Johannes Süllner
  2026-02-04 17:55 ` [PATCH v2 2/5] host/rootfs: integrate spectrum-installer Johannes Süllner
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 13+ messages in thread
From: Johannes Süllner @ 2026-02-04 17:55 UTC (permalink / raw)
  To: devel; +Cc: Johannes Süllner

Written in Rust using the Iced GUI library. Uses the `drives` crate to
offer a list of disks where the user can choose one to install Spectrum
on.
Uses `systemd-repart` for the installation, which is running in an
embedded terminal window using `iced-term`.

Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
---
 pkgs/default.nix                              |    1 +
 tools/spectrum-installer/Cargo.lock           | 5182 +++++++++++++++++
 tools/spectrum-installer/Cargo.lock.license   |    2 +
 tools/spectrum-installer/Cargo.toml           |   14 +
 tools/spectrum-installer/default.nix          |   55 +
 .../res/installer_logo_mesh.svg               |   85 +
 .../res/repart.d/10-esp.conf                  |    8 +
 .../res/repart.d/20-verity-A.conf             |    9 +
 .../res/repart.d/21-rootfs-A.conf             |    9 +
 .../res/repart.d/30-verity-B.conf             |    8 +
 .../res/repart.d/31-rootfs-B.conf             |    8 +
 .../res/repart.d/40-home.conf                 |    7 +
 tools/spectrum-installer/shell.nix            |   28 +
 .../src/destination_device.rs                 |   53 +
 tools/spectrum-installer/src/main.rs          |   53 +
 .../src/pages/completion.rs                   |  138 +
 .../src/pages/confirmation.rs                 |  108 +
 .../src/pages/disk_selection.rs               |  123 +
 .../src/pages/installation.rs                 |  186 +
 tools/spectrum-installer/src/pages/layout.rs  |   89 +
 tools/spectrum-installer/src/pages/mod.rs     |   43 +
 tools/spectrum-installer/src/pages/welcome.rs |   62 +
 22 files changed, 6271 insertions(+)
 create mode 100644 tools/spectrum-installer/Cargo.lock
 create mode 100644 tools/spectrum-installer/Cargo.lock.license
 create mode 100644 tools/spectrum-installer/Cargo.toml
 create mode 100644 tools/spectrum-installer/default.nix
 create mode 100644 tools/spectrum-installer/res/installer_logo_mesh.svg
 create mode 100644 tools/spectrum-installer/res/repart.d/10-esp.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/20-verity-A.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/21-rootfs-A.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/30-verity-B.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/31-rootfs-B.conf
 create mode 100644 tools/spectrum-installer/res/repart.d/40-home.conf
 create mode 100644 tools/spectrum-installer/shell.nix
 create mode 100644 tools/spectrum-installer/src/destination_device.rs
 create mode 100644 tools/spectrum-installer/src/main.rs
 create mode 100644 tools/spectrum-installer/src/pages/completion.rs
 create mode 100644 tools/spectrum-installer/src/pages/confirmation.rs
 create mode 100644 tools/spectrum-installer/src/pages/disk_selection.rs
 create mode 100644 tools/spectrum-installer/src/pages/installation.rs
 create mode 100644 tools/spectrum-installer/src/pages/layout.rs
 create mode 100644 tools/spectrum-installer/src/pages/mod.rs
 create mode 100644 tools/spectrum-installer/src/pages/welcome.rs

diff --git a/pkgs/default.nix b/pkgs/default.nix
index 4dbdfee..a80c5b3 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -74,6 +74,7 @@ let
         ../release/live/build
         ../tools/mount-flatpak/target
         ../tools/router/target
+        ../tools/spectrum-installer/target
         ../tools/xdg-desktop-portal-spectrum-host/target
         ../vm/sys/net/build
       ]));
diff --git a/tools/spectrum-installer/Cargo.lock b/tools/spectrum-installer/Cargo.lock
new file mode 100644
index 0000000..1667f4a
--- /dev/null
+++ b/tools/spectrum-installer/Cargo.lock
@@ -0,0 +1,5182 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "ab_glyph"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alacritty_terminal"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46319972e74179d707445f64aaa2893bbf6a111de3a9af29b7eb382f8b39e282"
+dependencies = [
+ "base64",
+ "bitflags 2.10.0",
+ "home",
+ "libc",
+ "log",
+ "miow",
+ "parking_lot",
+ "piper",
+ "polling",
+ "regex-automata",
+ "rustix 1.1.3",
+ "rustix-openpty",
+ "serde",
+ "signal-hook",
+ "unicode-width",
+ "vte",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "aliasable"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
+
+[[package]]
+name = "aligned"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
+dependencies = [
+ "as-slice",
+]
+
+[[package]]
+name = "aligned-vec"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
+dependencies = [
+ "equator",
+]
+
+[[package]]
+name = "android-activity"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
+dependencies = [
+ "android-properties",
+ "bitflags 2.10.0",
+ "cc",
+ "cesu8",
+ "jni",
+ "jni-sys",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "android-build"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cac4c64175d504608cf239756339c07f6384a476f97f20a7043f92920b0b8fd"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
+name = "arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+
+[[package]]
+name = "arg_enum_proc_macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "as-raw-xcb-connection"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
+
+[[package]]
+name = "as-slice"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
+dependencies = [
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "ash"
+version = "0.38.0+1.3.281"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix 1.1.3",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix 1.1.3",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix 1.1.3",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
+[[package]]
+name = "async-trait"
+version = "0.1.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "av-scenechange"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
+dependencies = [
+ "aligned",
+ "anyhow",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "log",
+ "num-rational",
+ "num-traits",
+ "pastey",
+ "rayon",
+ "thiserror 2.0.18",
+ "v_frame",
+ "y4m",
+]
+
+[[package]]
+name = "av1-grain"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
+dependencies = [
+ "anyhow",
+ "arrayvec",
+ "log",
+ "nom",
+ "num-rational",
+ "v_frame",
+]
+
+[[package]]
+name = "avif-serialize"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
+[[package]]
+name = "bit_field"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "bitstream-io"
+version = "4.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
+dependencies = [
+ "core2",
+]
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "block2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
+dependencies = [
+ "objc2 0.6.3",
+]
+
+[[package]]
+name = "blockdev"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79bbf05d9fb1b7f01c3923d90c828ed7a37f97d8de1bca6e20ce13f3570273b8"
+dependencies = [
+ "serde",
+ "serde_json",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
+[[package]]
+name = "built"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
+
+[[package]]
+name = "bumpalo"
+version = "3.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
+[[package]]
+name = "bytes"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
+
+[[package]]
+name = "calloop"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
+dependencies = [
+ "bitflags 2.10.0",
+ "log",
+ "polling",
+ "rustix 0.38.44",
+ "slab",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "calloop"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb9f6e1368bd4621d2c86baa7e37de77a938adf5221e5dd3d6133340101b309e"
+dependencies = [
+ "bitflags 2.10.0",
+ "polling",
+ "rustix 1.1.3",
+ "slab",
+ "tracing",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
+dependencies = [
+ "calloop 0.13.0",
+ "rustix 0.38.44",
+ "wayland-backend",
+ "wayland-client",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
+dependencies = [
+ "calloop 0.14.3",
+ "rustix 1.1.3",
+ "wayland-backend",
+ "wayland-client",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
+dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "chrono"
+version = "0.4.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "clipboard-win"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
+dependencies = [
+ "error-code",
+]
+
+[[package]]
+name = "clipboard_macos"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f"
+dependencies = [
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "clipboard_wayland"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8"
+dependencies = [
+ "smithay-clipboard",
+]
+
+[[package]]
+name = "clipboard_x11"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd63e33452ffdafd39924c4f05a5dd1e94db646c779c6bd59148a3d95fff5ad4"
+dependencies = [
+ "thiserror 2.0.18",
+ "x11rb",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
+dependencies = [
+ "serde",
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "core-graphics-types 0.1.3",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
+dependencies = [
+ "bitflags 2.10.0",
+ "core-foundation 0.10.1",
+ "libc",
+]
+
+[[package]]
+name = "core2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "core_maths"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "cosmic-text"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737"
+dependencies = [
+ "bitflags 2.10.0",
+ "fontdb",
+ "harfrust",
+ "linebender_resource_handle",
+ "log",
+ "rangemap",
+ "rustc-hash 1.1.0",
+ "self_cell",
+ "skrifa",
+ "smol_str",
+ "swash",
+ "sys-locale",
+ "unicode-bidi",
+ "unicode-linebreak",
+ "unicode-script",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
+[[package]]
+name = "cryoglyph"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08bc795bdbccdbd461736fb163930a009da6597b226d6f6fce33e7a8eb6ec519"
+dependencies = [
+ "cosmic-text",
+ "etagere",
+ "lru",
+ "rustc-hash 2.1.1",
+ "wgpu",
+]
+
+[[package]]
+name = "ctor-lite"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b29fccfdaeb0f9bd90da5759b1d0eaa2f6cfcfe90960124cfc83141ed4e111fd"
+
+[[package]]
+name = "cursor-icon"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "dpi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "endi"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
+
+[[package]]
+name = "enumflags2"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equator"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
+dependencies = [
+ "equator-macro",
+]
+
+[[package]]
+name = "equator-macro"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "error-code"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
+
+[[package]]
+name = "etagere"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "euclid"
+version = "0.22.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "exr"
+version = "1.74.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "fax"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
+dependencies = [
+ "fax_derive",
+]
+
+[[package]]
+name = "fax_derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "flate2"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "float_next_after"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
+name = "font-types"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "fontconfig-parser"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646"
+dependencies = [
+ "roxmltree",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905"
+dependencies = [
+ "fontconfig-parser",
+ "log",
+ "memmap2",
+ "slotmap",
+ "tinyvec",
+ "ttf-parser",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-lite"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gethostname"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
+dependencies = [
+ "rustix 1.1.3",
+ "windows-link",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+]
+
+[[package]]
+name = "gif"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glam"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
+
+[[package]]
+name = "glow"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+dependencies = [
+ "bitflags 2.10.0",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+dependencies = [
+ "bitflags 2.10.0",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd"
+dependencies = [
+ "log",
+ "presser",
+ "thiserror 1.0.69",
+ "windows 0.58.0",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca"
+dependencies = [
+ "bitflags 2.10.0",
+ "gpu-descriptor-types",
+ "hashbrown 0.15.5",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91"
+dependencies = [
+ "bitflags 2.10.0",
+]
+
+[[package]]
+name = "guillotiere"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782"
+dependencies = [
+ "euclid",
+ "svg_fmt",
+]
+
+[[package]]
+name = "half"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+ "num-traits",
+ "zerocopy",
+]
+
+[[package]]
+name = "harfrust"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytemuck",
+ "core_maths",
+ "read-fonts",
+ "smallvec",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash 0.1.5",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "foldhash 0.2.0",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
+name = "home"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core 0.62.2",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "iced"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000e01026c93ba643f8357a3db3ada0e6555265a377f6f9291c472f6dd701fb3"
+dependencies = [
+ "iced_core",
+ "iced_debug",
+ "iced_futures",
+ "iced_renderer",
+ "iced_runtime",
+ "iced_widget",
+ "iced_winit",
+ "image",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "iced_aw"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cc84cc77dcb1c384c60792de025fb4a72e23c3d8c65c4a34691684875fc5403"
+dependencies = [
+ "cfg-if",
+ "chrono",
+ "iced_core",
+ "iced_fonts",
+ "iced_widget",
+ "num-format",
+ "num-traits",
+ "web-time",
+]
+
+[[package]]
+name = "iced_core"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ab1937d699403e7e69252ae743a902bcee9f4ab2052cc4c9a46fcf34729d85"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytes",
+ "glam",
+ "lilt",
+ "log",
+ "num-traits",
+ "rustc-hash 2.1.1",
+ "smol_str",
+ "thiserror 2.0.18",
+ "web-time",
+]
+
+[[package]]
+name = "iced_debug"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25035ab0215a620e53f4103e36fc4e59a1fb2817e4bfc38a30ad27b4202ea0be"
+dependencies = [
+ "iced_core",
+ "iced_futures",
+ "log",
+]
+
+[[package]]
+name = "iced_fonts"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "214cff7c8499e328774216690e58e315a1a5f8f6fdd1035aed6298e62ffc4c1d"
+dependencies = [
+ "iced_core",
+ "iced_fonts_macros",
+ "iced_widget",
+]
+
+[[package]]
+name = "iced_fonts_macros"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef5125e110cb19cd1910a28298661c98c5d9ab02eef43594968352940e8752e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "ttf-parser",
+]
+
+[[package]]
+name = "iced_futures"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c0c85ccad42dfbec7293c36c018af0ea0dbcc52d137a4a9a0b0f6822a3fdf0a"
+dependencies = [
+ "futures",
+ "iced_core",
+ "log",
+ "rustc-hash 2.1.1",
+ "wasm-bindgen-futures",
+ "wasmtimer",
+]
+
+[[package]]
+name = "iced_graphics"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234ca1c2cec4155055f68fa5fad1b5242c496ac8238d80a259bca382fb44a102"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytemuck",
+ "cosmic-text",
+ "half",
+ "iced_core",
+ "iced_futures",
+ "image",
+ "kamadak-exif",
+ "log",
+ "lyon_path",
+ "raw-window-handle",
+ "rustc-hash 2.1.1",
+ "thiserror 2.0.18",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "iced_program"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dfafec2947cda688d8eb00dac337ba11aa60f9ef6335aed343e189d26e4a673"
+dependencies = [
+ "iced_graphics",
+ "iced_runtime",
+]
+
+[[package]]
+name = "iced_renderer"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "250cc0802408e8c077986ec56c7d07c65f423ee658a4b9fd795a1f2aae5dac05"
+dependencies = [
+ "iced_graphics",
+ "iced_tiny_skia",
+ "iced_wgpu",
+ "log",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "iced_runtime"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1889b819ce4c06674183242e336c8d49465665441396914dc07cc86f44fa8d4"
+dependencies = [
+ "bytes",
+ "iced_core",
+ "iced_futures",
+ "raw-window-handle",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "iced_term"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f461d32a472194735c94901bf2fc70d493ee98a8b29e7e244f296c82826745"
+dependencies = [
+ "alacritty_terminal",
+ "anyhow",
+ "iced",
+ "iced_core",
+ "iced_graphics",
+ "open",
+ "tokio",
+]
+
+[[package]]
+name = "iced_tiny_skia"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe0acf8b75a3bc914aff5f2329fdffc1b36eeaea29dda0e4bd232f1c62e9cc3d"
+dependencies = [
+ "bytemuck",
+ "cosmic-text",
+ "iced_debug",
+ "iced_graphics",
+ "kurbo",
+ "log",
+ "rustc-hash 2.1.1",
+ "softbuffer",
+ "tiny-skia",
+]
+
+[[package]]
+name = "iced_wgpu"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff144a999b0ca0f8a10257934500060240825c42e950ec0ebee9c8ae30561c13"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytemuck",
+ "cryoglyph",
+ "futures",
+ "glam",
+ "guillotiere",
+ "iced_debug",
+ "iced_graphics",
+ "log",
+ "lyon",
+ "rustc-hash 2.1.1",
+ "thiserror 2.0.18",
+ "wgpu",
+]
+
+[[package]]
+name = "iced_widget"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1596afa0d3109c2618e8bc12bae6c11d3064df8f95c42dfce570397dbe957ab"
+dependencies = [
+ "iced_renderer",
+ "log",
+ "num-traits",
+ "ouroboros",
+ "qrcode",
+ "rustc-hash 2.1.1",
+ "thiserror 2.0.18",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "iced_winit"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b7dbedc47562d1de3b9707d939f678b88c382004b7ab5a18f7a7dd723162d75"
+dependencies = [
+ "iced_debug",
+ "iced_program",
+ "log",
+ "mundy",
+ "rustc-hash 2.1.1",
+ "thiserror 2.0.18",
+ "tracing",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "window_clipboard",
+ "winit",
+]
+
+[[package]]
+name = "image"
+version = "0.25.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "color_quant",
+ "exr",
+ "gif",
+ "image-webp",
+ "moxcms",
+ "num-traits",
+ "png",
+ "qoi",
+ "ravif",
+ "rayon",
+ "rgb",
+ "tiff",
+ "zune-core 0.5.1",
+ "zune-jpeg 0.5.12",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
+dependencies = [
+ "byteorder-lite",
+ "quick-error",
+]
+
+[[package]]
+name = "imgref"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
+
+[[package]]
+name = "indexmap"
+version = "2.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "interpolate_name"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[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 = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "kamadak-exif"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837"
+dependencies = [
+ "mutate_once",
+]
+
+[[package]]
+name = "khronos-egl"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
+dependencies = [
+ "libc",
+ "libloading",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "kurbo"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440"
+dependencies = [
+ "arrayvec",
+ "smallvec",
+]
+
+[[package]]
+name = "lebe"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
+
+[[package]]
+name = "libc"
+version = "0.2.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
+dependencies = [
+ "arbitrary",
+ "cc",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
+dependencies = [
+ "cfg-if",
+ "windows-link",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
+name = "libredox"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
+dependencies = [
+ "bitflags 2.10.0",
+ "libc",
+ "redox_syscall 0.7.0",
+]
+
+[[package]]
+name = "lilt"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad"
+dependencies = [
+ "web-time",
+]
+
+[[package]]
+name = "linebender_resource_handle"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
+name = "litrs"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "loop9"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
+dependencies = [
+ "imgref",
+]
+
+[[package]]
+name = "lru"
+version = "0.16.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
+
+[[package]]
+name = "lyon"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbcb7d54d54c8937364c9d41902d066656817dce1e03a44e5533afebd1ef4352"
+dependencies = [
+ "lyon_algorithms",
+ "lyon_tessellation",
+]
+
+[[package]]
+name = "lyon_algorithms"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c0829e28c4f336396f250d850c3987e16ce6db057ffe047ce0dd54aab6b647"
+dependencies = [
+ "lyon_path",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_geom"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e260b6de923e6e47adfedf6243013a7a874684165a6a277594ee3906021b2343"
+dependencies = [
+ "arrayvec",
+ "euclid",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_path"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aeca86bcfd632a15984ba029b539ffb811e0a70bf55e814ef8b0f54f506fdeb"
+dependencies = [
+ "lyon_geom",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_tessellation"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3f586142e1280335b1bc89539f7c97dd80f08fc43e9ab1b74ef0a42b04aa353"
+dependencies = [
+ "float_next_after",
+ "lyon_path",
+ "num-traits",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "maybe-rayon"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+dependencies = [
+ "cfg-if",
+ "rayon",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "memmap2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metal"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605"
+dependencies = [
+ "bitflags 2.10.0",
+ "block",
+ "core-graphics-types 0.2.0",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "mio"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "miow"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "moxcms"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
+[[package]]
+name = "mundy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523813c9e194ec43693805214eb112551f99382115b67f38600d724a692e7e8b"
+dependencies = [
+ "android-build",
+ "async-io",
+ "cfg-if",
+ "dispatch",
+ "futures-channel",
+ "futures-lite",
+ "jni",
+ "ndk-context",
+ "objc2 0.6.3",
+ "objc2-app-kit 0.3.2",
+ "objc2-foundation 0.3.2",
+ "pin-project-lite",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows 0.62.2",
+ "zbus",
+]
+
+[[package]]
+name = "mutate_once"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af"
+
+[[package]]
+name = "naga"
+version = "27.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8"
+dependencies = [
+ "arrayvec",
+ "bit-set",
+ "bitflags 2.10.0",
+ "cfg-if",
+ "cfg_aliases",
+ "codespan-reporting",
+ "half",
+ "hashbrown 0.16.1",
+ "hexf-parse",
+ "indexmap",
+ "libm",
+ "log",
+ "num-traits",
+ "once_cell",
+ "rustc-hash 1.1.0",
+ "spirv",
+ "thiserror 2.0.18",
+ "unicode-ident",
+]
+
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.10.0",
+ "jni-sys",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nom"
+version = "8.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "noop_proc_macro"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-format"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
+dependencies = [
+ "arrayvec",
+ "itoa",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "libc",
+ "objc2 0.5.2",
+ "objc2-core-data 0.2.2",
+ "objc2-core-image 0.2.2",
+ "objc2-foundation 0.2.2",
+ "objc2-quartz-core 0.2.2",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.6.2",
+ "libc",
+ "objc2 0.6.3",
+ "objc2-cloud-kit 0.3.2",
+ "objc2-core-data 0.3.2",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-core-image 0.3.2",
+ "objc2-core-text",
+ "objc2-core-video",
+ "objc2-foundation 0.3.2",
+ "objc2-quartz-core 0.3.2",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-contacts"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags 2.10.0",
+ "dispatch2",
+ "objc2 0.6.3",
+]
+
+[[package]]
+name = "objc2-core-graphics"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
+dependencies = [
+ "bitflags 2.10.0",
+ "dispatch2",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-io-surface",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
+dependencies = [
+ "objc2 0.6.3",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-contacts",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-text"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+]
+
+[[package]]
+name = "objc2-core-video"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-io-surface",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "dispatch",
+ "libc",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.6.2",
+ "libc",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-io-surface"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-link-presentation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
+dependencies = [
+ "bitflags 2.10.0",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-symbols"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
+dependencies = [
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-cloud-kit 0.2.2",
+ "objc2-core-data 0.2.2",
+ "objc2-core-image 0.2.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+ "objc2-link-presentation",
+ "objc2-quartz-core 0.2.2",
+ "objc2-symbols",
+ "objc2-uniform-type-identifiers",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-uniform-type-identifiers"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
+dependencies = [
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "open"
+version = "5.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc"
+dependencies = [
+ "is-wsl",
+ "libc",
+ "pathdiff",
+]
+
+[[package]]
+name = "orbclient"
+version = "0.3.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd"
+dependencies = [
+ "libc",
+ "libredox",
+]
+
+[[package]]
+name = "ordered-float"
+version = "5.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "ouroboros"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
+dependencies = [
+ "aliasable",
+ "ouroboros_macro",
+ "static_assertions",
+]
+
+[[package]]
+name = "ouroboros_macro"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.5.18",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pastey"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "piper"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "png"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
+dependencies = [
+ "bitflags 2.10.0",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix 1.1.3",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "presser"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+ "yansi",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
+dependencies = [
+ "profiling-procmacros",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pxfm"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "qrcode"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "166f136dfdb199f98186f3649cf7a0536534a61417a1a30221b492b4fb60ce3f"
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "quick-xml"
+version = "0.38.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "range-alloc"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
+
+[[package]]
+name = "rangemap"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
+
+[[package]]
+name = "rav1e"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
+dependencies = [
+ "aligned-vec",
+ "arbitrary",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "av-scenechange",
+ "av1-grain",
+ "bitstream-io",
+ "built",
+ "cfg-if",
+ "interpolate_name",
+ "itertools",
+ "libc",
+ "libfuzzer-sys",
+ "log",
+ "maybe-rayon",
+ "new_debug_unreachable",
+ "noop_proc_macro",
+ "num-derive",
+ "num-traits",
+ "paste",
+ "profiling",
+ "rand",
+ "rand_chacha",
+ "simd_helpers",
+ "thiserror 2.0.18",
+ "v_frame",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ravif"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
+dependencies = [
+ "avif-serialize",
+ "imgref",
+ "loop9",
+ "quick-error",
+ "rav1e",
+ "rayon",
+ "rgb",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "rayon"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "read-fonts"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358"
+dependencies = [
+ "bytemuck",
+ "core_maths",
+ "font-types",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.10.0",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
+dependencies = [
+ "bitflags 2.10.0",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+
+[[package]]
+name = "renderdoc-sys"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+
+[[package]]
+name = "rgb"
+version = "0.8.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
+
+[[package]]
+name = "roxmltree"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.10.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
+dependencies = [
+ "bitflags 2.10.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.11.0",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustix-openpty"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1de16c7c59892b870a6336f185dc10943517f1327447096bbb7bb32cd85e2393"
+dependencies = [
+ "errno",
+ "libc",
+ "rustix 1.1.3",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "sctk-adwaita"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
+dependencies = [
+ "ab_glyph",
+ "log",
+ "memmap2",
+ "smithay-client-toolkit 0.19.2",
+ "tiny-skia",
+]
+
+[[package]]
+name = "self_cell"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
+dependencies = [
+ "errno",
+ "libc",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
+
+[[package]]
+name = "simd_helpers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+dependencies = [
+ "quote",
+]
+
+[[package]]
+name = "skrifa"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841"
+dependencies = [
+ "bytemuck",
+ "read-fonts",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "slotmap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
+dependencies = [
+ "bitflags 2.10.0",
+ "calloop 0.13.0",
+ "calloop-wayland-source 0.3.0",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 0.38.44",
+ "thiserror 1.0.69",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0"
+dependencies = [
+ "bitflags 2.10.0",
+ "calloop 0.14.3",
+ "calloop-wayland-source 0.4.1",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 1.1.3",
+ "thiserror 2.0.18",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-experimental",
+ "wayland-protocols-misc",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smithay-clipboard"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226"
+dependencies = [
+ "libc",
+ "smithay-client-toolkit 0.20.0",
+ "wayland-backend",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "socket2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
+dependencies = [
+ "libc",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "softbuffer"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3"
+dependencies = [
+ "as-raw-xcb-connection",
+ "bytemuck",
+ "fastrand",
+ "js-sys",
+ "memmap2",
+ "ndk",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.2",
+ "objc2-quartz-core 0.3.2",
+ "raw-window-handle",
+ "redox_syscall 0.5.18",
+ "rustix 1.1.3",
+ "tiny-xlib",
+ "tracing",
+ "wasm-bindgen",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-sys",
+ "web-sys",
+ "windows-sys 0.61.2",
+ "x11rb",
+]
+
+[[package]]
+name = "spectrum-installer"
+version = "0.1.0"
+dependencies = [
+ "blockdev",
+ "iced",
+ "iced_aw",
+ "iced_fonts",
+ "iced_term",
+]
+
+[[package]]
+name = "spirv"
+version = "0.3.0+sdk-1.3.268.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
+dependencies = [
+ "bitflags 2.10.0",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strict-num"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
+
+[[package]]
+name = "svg_fmt"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
+
+[[package]]
+name = "swash"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47846491253e976bdd07d0f9cc24b7daf24720d11309302ccbbc6e6b6e53550a"
+dependencies = [
+ "skrifa",
+ "yazi",
+ "zeno",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sys-locale"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
+dependencies = [
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix 1.1.3",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tiff"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
+dependencies = [
+ "fax",
+ "flate2",
+ "half",
+ "quick-error",
+ "weezl",
+ "zune-jpeg 0.4.21",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "log",
+ "tiny-skia-path",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
+[[package]]
+name = "tiny-xlib"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e"
+dependencies = [
+ "as-raw-xcb-connection",
+ "ctor-lite",
+ "libloading",
+ "pkg-config",
+ "tracing",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.49.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.23.10+spec-1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.6+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+dependencies = [
+ "core_maths",
+]
+
+[[package]]
+name = "uds_windows"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "winapi",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-script"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+
+[[package]]
+name = "uuid"
+version = "1.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
+dependencies = [
+ "js-sys",
+ "serde_core",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "v_frame"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
+dependencies = [
+ "aligned-vec",
+ "num-traits",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "vte"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
+dependencies = [
+ "arrayvec",
+ "bitflags 2.10.0",
+ "cursor-icon",
+ "log",
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.2+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasmtimer"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b"
+dependencies = [
+ "futures",
+ "js-sys",
+ "parking_lot",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 1.1.3",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec"
+dependencies = [
+ "bitflags 2.10.0",
+ "rustix 1.1.3",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-csd-frame"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
+dependencies = [
+ "bitflags 2.10.0",
+ "cursor-icon",
+ "wayland-backend",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.31.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078"
+dependencies = [
+ "rustix 1.1.3",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3"
+dependencies = [
+ "bitflags 2.10.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-experimental"
+version = "20250721.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1"
+dependencies = [
+ "bitflags 2.10.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-misc"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791c58fdeec5406aa37169dd815327d1e47f334219b523444bc26d70ceb4c34e"
+dependencies = [
+ "bitflags 2.10.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b"
+dependencies = [
+ "bitflags 2.10.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3"
+dependencies = [
+ "bitflags 2.10.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd"
+dependencies = [
+ "dlib",
+ "log",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
+
+[[package]]
+name = "wgpu"
+version = "27.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77"
+dependencies = [
+ "arrayvec",
+ "bitflags 2.10.0",
+ "cfg-if",
+ "cfg_aliases",
+ "document-features",
+ "hashbrown 0.16.1",
+ "js-sys",
+ "log",
+ "naga",
+ "parking_lot",
+ "portable-atomic",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core"
+version = "27.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7"
+dependencies = [
+ "arrayvec",
+ "bit-set",
+ "bit-vec",
+ "bitflags 2.10.0",
+ "bytemuck",
+ "cfg_aliases",
+ "document-features",
+ "hashbrown 0.16.1",
+ "indexmap",
+ "log",
+ "naga",
+ "once_cell",
+ "parking_lot",
+ "portable-atomic",
+ "profiling",
+ "raw-window-handle",
+ "rustc-hash 1.1.0",
+ "smallvec",
+ "thiserror 2.0.18",
+ "wgpu-core-deps-apple",
+ "wgpu-core-deps-emscripten",
+ "wgpu-core-deps-windows-linux-android",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-core-deps-apple"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-emscripten"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-core-deps-windows-linux-android"
+version = "27.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3"
+dependencies = [
+ "wgpu-hal",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "27.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags 2.10.0",
+ "block",
+ "bytemuck",
+ "cfg-if",
+ "cfg_aliases",
+ "core-graphics-types 0.2.0",
+ "glow",
+ "glutin_wgl_sys",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "hashbrown 0.16.1",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading",
+ "log",
+ "metal",
+ "naga",
+ "ndk-sys",
+ "objc",
+ "once_cell",
+ "ordered-float",
+ "parking_lot",
+ "portable-atomic",
+ "portable-atomic-util",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "smallvec",
+ "thiserror 2.0.18",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "windows 0.58.0",
+ "windows-core 0.58.0",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "27.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytemuck",
+ "js-sys",
+ "log",
+ "thiserror 2.0.18",
+ "web-sys",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "window_clipboard"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5654226305eaf2dde8853fb482861d28e5dcecbbd40cb88e8393d94bb80d733"
+dependencies = [
+ "clipboard-win",
+ "clipboard_macos",
+ "clipboard_wayland",
+ "clipboard_x11",
+ "raw-window-handle",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "windows"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
+dependencies = [
+ "windows-core 0.58.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
+dependencies = [
+ "windows-collections",
+ "windows-core 0.62.2",
+ "windows-future",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
+dependencies = [
+ "windows-core 0.62.2",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
+dependencies = [
+ "windows-implement 0.58.0",
+ "windows-interface 0.58.0",
+ "windows-result 0.2.0",
+ "windows-strings 0.1.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement 0.60.2",
+ "windows-interface 0.59.3",
+ "windows-link",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link",
+ "windows-threading",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.58.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-numerics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result 0.2.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows-threading"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winit"
+version = "0.30.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732"
+dependencies = [
+ "ahash",
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.10.0",
+ "block2 0.5.1",
+ "bytemuck",
+ "calloop 0.13.0",
+ "cfg_aliases",
+ "concurrent-queue",
+ "core-foundation 0.9.4",
+ "core-graphics",
+ "cursor-icon",
+ "dpi",
+ "js-sys",
+ "libc",
+ "memmap2",
+ "ndk",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+ "objc2-ui-kit",
+ "orbclient",
+ "percent-encoding",
+ "pin-project",
+ "raw-window-handle",
+ "redox_syscall 0.4.1",
+ "rustix 0.38.44",
+ "sctk-adwaita",
+ "smithay-client-toolkit 0.19.2",
+ "smol_str",
+ "tracing",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.52.0",
+ "x11-dl",
+ "x11rb",
+ "xkbcommon-dl",
+]
+
+[[package]]
+name = "winnow"
+version = "0.7.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
+dependencies = [
+ "as-raw-xcb-connection",
+ "gethostname",
+ "libc",
+ "libloading",
+ "once_cell",
+ "rustix 1.1.3",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
+
+[[package]]
+name = "xcursor"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.10.0",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
+
+[[package]]
+name = "y4m"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
+
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
+[[package]]
+name = "yazi"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"
+
+[[package]]
+name = "zbus"
+version = "5.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-lite",
+ "hex",
+ "libc",
+ "ordered-stream",
+ "rustix 1.1.3",
+ "serde",
+ "serde_repr",
+ "tracing",
+ "uds_windows",
+ "uuid",
+ "windows-sys 0.61.2",
+ "winnow",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zbus_names",
+ "zvariant",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f"
+dependencies = [
+ "serde",
+ "winnow",
+ "zvariant",
+]
+
+[[package]]
+name = "zeno"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
+dependencies = [
+ "zune-core 0.4.12",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
+dependencies = [
+ "zune-core 0.5.1",
+]
+
+[[package]]
+name = "zvariant"
+version = "5.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "winnow",
+ "zvariant_derive",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn",
+ "winnow",
+]
diff --git a/tools/spectrum-installer/Cargo.lock.license b/tools/spectrum-installer/Cargo.lock.license
new file mode 100644
index 0000000..bfe02ea
--- /dev/null
+++ b/tools/spectrum-installer/Cargo.lock.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
diff --git a/tools/spectrum-installer/Cargo.toml b/tools/spectrum-installer/Cargo.toml
new file mode 100644
index 0000000..1e4500d
--- /dev/null
+++ b/tools/spectrum-installer/Cargo.toml
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[package]
+name = "spectrum-installer"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+iced = { version = "0.14", features = ["image", "qr_code"] }
+iced_aw = { version = "0.13", features = ["spinner"] }
+iced_fonts = { version = "0.3", features = ["bootstrap"] }
+iced_term = { version = "0.7" }
+blockdev = { version = "0.3" }
diff --git a/tools/spectrum-installer/default.nix b/tools/spectrum-installer/default.nix
new file mode 100644
index 0000000..08cbca1
--- /dev/null
+++ b/tools/spectrum-installer/default.nix
@@ -0,0 +1,55 @@
+# SPDX-FileCopyrightText: 2024 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2025-2026 Johannes Süllner <johannes.suellner@mailbox.org>
+# SPDX-License-Identifier: MIT
+
+import ../../lib/call-package.nix (
+args@{ callPackage, config, src, lib, rustPlatform, libxkbcommon, libGL, wayland }:
+
+rustPlatform.buildRustPackage rec {
+  name = "spectrum-installer";
+
+  src = lib.fileset.toSource {
+    root = ../..;
+    fileset = lib.fileset.intersection args.src ./.;
+  };
+  sourceRoot = "source/tools/spectrum-installer";
+
+  cargoLock.lockFile = ./Cargo.lock;
+
+  buildInputs = [
+    libGL
+    wayland
+  ];
+
+  postFixup = ''
+    patchelf $out/bin/spectrum-installer \
+      --add-rpath ${
+        lib.makeLibraryPath [
+          libxkbcommon
+          libGL
+          wayland
+        ]
+      }
+  '';
+
+  icons = callPackage (
+    { runCommand, inkscape }:
+    runCommand "spectrum_installer_icons" {
+      nativeBuildInputs = [ inkscape ];
+      logoSvg = ./res/installer_logo_mesh.svg;
+    } ''
+      mkdir $out
+      inkscape -w  22 -h  22 -o $out/22x22.png   "$logoSvg"
+      inkscape -w 400 -h 400 -o $out/400x400.png "$logoSvg"
+    ''
+  ) {};
+
+  postInstall = ''
+    mkdir -p "$out/share/spectrum-installer/repart.d"
+    for f in $(ls "./res/repart.d"); do
+      substitute "./res/repart.d/$f" "$out/share/spectrum-installer/repart.d/$f" --subst-var-by "version" "${config.version}"
+    done
+    install -D "${icons}/22x22.png" "$out/share/icons/hicolor/22x22/apps/${name}.png"
+    install -D "${icons}/400x400.png" "$out/share/icons/hicolor/400x400/apps/${name}.png"
+  '';
+}) (_: {})
diff --git a/tools/spectrum-installer/res/installer_logo_mesh.svg b/tools/spectrum-installer/res/installer_logo_mesh.svg
new file mode 100644
index 0000000..1e25829
--- /dev/null
+++ b/tools/spectrum-installer/res/installer_logo_mesh.svg
@@ -0,0 +1,85 @@
+<!-- SPDX-FileCopyrightText: 2020 hazelnot -->
+<!-- SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is> -->
+<!-- SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org> -->
+<!-- SPDX-License-Identifier: CC0-1.0 -->
+<!-- Spectrum logo design by hazelnot, implementation by Alyssa Ross, installer icon by Johannes Süllner -->
+
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
+  <defs>
+    <filter id="ring-shadow" x="-10" y="-10" width="120" height="120" color-interpolation-filters="sRGB">
+      <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
+      <feComposite in="SourceGraphic" in2="offset" operator="over" />
+    </filter>
+
+    <filter id="center-shadow" x="-10" y="-10" width="120" height="120" color-interpolation-filters="sRGB">
+      <feFlood flood-opacity="1" flood-color="#DDD" result="flood" />
+      <feComposite in="flood" in2="SourceGraphic" operator="in" result="composite1" />
+      <feGaussianBlur in="composite1" stdDeviation="6" result="blur" />
+      <feComposite in="SourceGraphic" in2="blur" operator="over" result="composite2" />
+    </filter>
+
+    <meshgradient id="ring-gradient" x="1.5" y="0.5">
+      <meshrow>
+        <meshpatch>
+          <stop path="L 1 -0.36603" stop-color="red" />
+          <stop path="L 0.5 0.5" stop-color="yellow" />
+          <stop path="L 0.5 0.5" stop-color="yellow" />
+          <stop path="L 1 0.5" stop-color="red" />
+        </meshpatch>
+        <meshpatch>
+          <stop path="L 0 -0.36603" />
+          <stop path="L 0.5 0.5" stop-color="lime" />
+          <stop path="L 0.5 0.5" stop-color="lime" />
+        </meshpatch>
+        <meshpatch>
+          <stop path="L -0.5 0.5" />
+          <stop path="L 0.5 0.5" stop-color="cyan" />
+          <stop path="L 0.5 0.5" stop-color="cyan" />
+        </meshpatch>
+        <meshpatch>
+          <stop path="L 0 1.36603" />
+          <stop path="L 0.5 0.5" stop-color="blue" />
+          <stop path="L 0.5 0.5" stop-color="blue" />
+        </meshpatch>
+        <meshpatch>
+          <stop path="L 1 1.36603" />
+          <stop path="L 0.5 0.5" stop-color="magenta" />
+          <stop path="L 0.5 0.5" stop-color="magenta" />
+        </meshpatch>
+        <meshpatch>
+          <stop path="L 1.5 0.5" />
+          <stop path="L 0.5 0.5" stop-color="red" />
+          <stop path="L 0.5 0.5" stop-color="red" />
+        </meshpatch>
+      </meshrow>
+    </meshgradient>
+
+    <marker
+       style="overflow:visible"
+       id="ArrowWideRounded"
+       refX="0"
+       refY="0"
+       orient="auto-start-reverse"
+       markerWidth="1"
+       markerHeight="1"
+       viewBox="0 0 1 1"
+       preserveAspectRatio="xMidYMid"
+       markerUnits="strokeWidth">
+      <path
+         style="fill:none;stroke:context-stroke;stroke-width:1;stroke-linecap:round"
+         d="M 3,-3 0,0 3,3"
+         transform="rotate(180,0.125,0)" />
+    </marker>
+  </defs>
+
+  <circle cx="50" cy="50" r="36" fill="none" stroke="url(#ring-gradient)" stroke-width="5" filter="url(#ring-shadow)" />
+
+  <circle cx="50" cy="50" r="27" fill="white" filter="url(#center-shadow)" />
+
+  <path
+     style="fill:none;stroke:#000000;stroke-width:4.5;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#ArrowWideRounded)"
+     d="M 50,29.5 V 54" />
+  <path
+     style="fill:none;stroke:#000000;stroke-width:4.5;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+     d="M 65.419353,63.617021 H 34.580646" />
+</svg>
diff --git a/tools/spectrum-installer/res/repart.d/10-esp.conf b/tools/spectrum-installer/res/repart.d/10-esp.conf
new file mode 100644
index 0000000..1f3c5fc
--- /dev/null
+++ b/tools/spectrum-installer/res/repart.d/10-esp.conf
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[Partition]
+Type=esp
+SizeMinBytes=500M
+SizeMaxBytes=500M
+CopyBlocks=/dev/disk/by-designator/esp
diff --git a/tools/spectrum-installer/res/repart.d/20-verity-A.conf b/tools/spectrum-installer/res/repart.d/20-verity-A.conf
new file mode 100644
index 0000000..6e688e3
--- /dev/null
+++ b/tools/spectrum-installer/res/repart.d/20-verity-A.conf
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[Partition]
+Type=root-verity
+Label=Spectrum_@version@.verity
+SizeMinBytes=162M
+SizeMaxBytes=162M
+CopyBlocks=auto
diff --git a/tools/spectrum-installer/res/repart.d/21-rootfs-A.conf b/tools/spectrum-installer/res/repart.d/21-rootfs-A.conf
new file mode 100644
index 0000000..9d5ea6b
--- /dev/null
+++ b/tools/spectrum-installer/res/repart.d/21-rootfs-A.conf
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[Partition]
+Type=root
+Label=Spectrum_@version@
+SizeMinBytes=20G
+SizeMaxBytes=20G
+CopyBlocks=auto
diff --git a/tools/spectrum-installer/res/repart.d/30-verity-B.conf b/tools/spectrum-installer/res/repart.d/30-verity-B.conf
new file mode 100644
index 0000000..3721ff0
--- /dev/null
+++ b/tools/spectrum-installer/res/repart.d/30-verity-B.conf
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[Partition]
+Type=root-verity
+Label=_empty
+SizeMinBytes=162M
+SizeMaxBytes=162M
diff --git a/tools/spectrum-installer/res/repart.d/31-rootfs-B.conf b/tools/spectrum-installer/res/repart.d/31-rootfs-B.conf
new file mode 100644
index 0000000..84e9f57
--- /dev/null
+++ b/tools/spectrum-installer/res/repart.d/31-rootfs-B.conf
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[Partition]
+Type=root
+Label=_empty
+SizeMinBytes=20G
+SizeMaxBytes=20G
diff --git a/tools/spectrum-installer/res/repart.d/40-home.conf b/tools/spectrum-installer/res/repart.d/40-home.conf
new file mode 100644
index 0000000..c487d34
--- /dev/null
+++ b/tools/spectrum-installer/res/repart.d/40-home.conf
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: CC0-1.0
+# SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+[Partition]
+Type=home
+SizeMinBytes=500M
+Format=btrfs
diff --git a/tools/spectrum-installer/shell.nix b/tools/spectrum-installer/shell.nix
new file mode 100644
index 0000000..87cbf16
--- /dev/null
+++ b/tools/spectrum-installer/shell.nix
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2025 iced contributors
+# SPDX-FileCopyrightText: 2026 Johannes Süllner <johannes.suellner@mailbox.org>
+
+# based on https://github.com/iced-rs/iced/blob/master/DEPENDENCIES.md
+# last updated 2026-01-11
+
+{ pkgs ? import <nixpkgs> { } }:
+
+let
+  dlopenLibraries = with pkgs; [
+    libxkbcommon
+
+    # GPU backend
+    vulkan-loader
+
+    # Window system
+    wayland
+  ];
+in pkgs.mkShell {
+  nativeBuildInputs = with pkgs; [
+    cargo
+    lldb
+    rustfmt
+  ];
+
+  env.RUSTFLAGS = "-C link-arg=-Wl,-rpath,${pkgs.lib.makeLibraryPath dlopenLibraries}";
+}
diff --git a/tools/spectrum-installer/src/destination_device.rs b/tools/spectrum-installer/src/destination_device.rs
new file mode 100644
index 0000000..15ef4d4
--- /dev/null
+++ b/tools/spectrum-installer/src/destination_device.rs
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2026 Johannes Süllner <johannes.suellner@mailbox.org>
+
+#[derive(Debug, Clone)]
+pub struct DestinationDevice {
+    pub name: String,
+    pub path: String,
+    pub size_bytes: u64,
+}
+
+impl DestinationDevice {
+    pub fn get_destination_devices() -> Result<Vec<DestinationDevice>, &'static str> {
+        let get_devices_result = blockdev::get_devices();
+        if let Ok(block_devices) = get_devices_result {
+            let non_system_devices = block_devices.non_system();
+            let possible_block_devices = non_system_devices
+                .iter()
+                .filter(|device| device.is_disk())
+                .map(|device| DestinationDevice {
+                    name: device.name.clone(),
+                    path: format!("/dev/{}", device.name.clone()),
+                    size_bytes: device.size,
+                });
+
+            Ok(possible_block_devices.collect())
+        } else {
+            Err("Fail")
+        }
+    }
+
+    pub fn size_as_human_readable_string(&self) -> String {
+        let mut size_factor: f64 = self.size_bytes as f64;
+        let mut exponent = 0; // base = 1024
+        while size_factor >= 1000.0 {
+            exponent += 1;
+            size_factor = size_factor / 1000.0;
+        }
+        let unit = match exponent {
+            0 => "",
+            1 => "K",
+            2 => "M",
+            3 => "G",
+            4 => "T",
+            5 => "P",
+            6 => "E",
+            7 => "Z",
+            8 => "Y",
+            9 => "R",
+            _ => "???",
+        };
+        format!("{:.2} {}B", size_factor, unit)
+    }
+}
diff --git a/tools/spectrum-installer/src/main.rs b/tools/spectrum-installer/src/main.rs
new file mode 100644
index 0000000..0ac5568
--- /dev/null
+++ b/tools/spectrum-installer/src/main.rs
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+mod destination_device;
+mod pages;
+
+use iced::Element;
+use iced::Size;
+use iced::Subscription;
+use iced::Task;
+use pages::Event;
+
+fn main() -> iced::Result {
+    iced::application(
+        SpectrumInstaller::new,
+        SpectrumInstaller::update,
+        SpectrumInstaller::view,
+    )
+    .subscription(SpectrumInstaller::subscription)
+    .font(iced_fonts::BOOTSTRAP_FONT_BYTES)
+    .title("Spectrum installer")
+    .window_size(Size::new(pages::WINDOW_WIDTH, pages::WINDOW_HEIGHT))
+    .resizable(false)
+    .run()
+}
+
+struct SpectrumInstaller {
+    current_page: Box<dyn pages::Page>,
+}
+
+impl SpectrumInstaller {
+    fn new() -> Self {
+        Self {
+            current_page: Box::new(pages::INITAL_PAGE),
+        }
+    }
+
+    fn update(&mut self, event: pages::Event) -> Task<Event> {
+        let (maybe_new_page, task) = self.current_page.update(event);
+        if let Some(new_page) = maybe_new_page {
+            self.current_page = new_page;
+        }
+        task
+    }
+
+    fn view(&self) -> Element<'_, pages::Event> {
+        self.current_page.view()
+    }
+
+    fn subscription(&self) -> Subscription<pages::Event> {
+        self.current_page.subscription()
+    }
+}
diff --git a/tools/spectrum-installer/src/pages/completion.rs b/tools/spectrum-installer/src/pages/completion.rs
new file mode 100644
index 0000000..80c989c
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/completion.rs
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use super::{BUTTON_TEXT_SIZE, Event, MARGIN_VERTICAL, Page, TITLE_TEXT_SIZE, UpdateResult};
+use crate::{center_horizontal_in_container, center_in_container};
+use iced::{
+    Element, Task,
+    widget::{column, qr_code, space, text},
+    window,
+};
+use std::process::Command;
+
+const S6_HPR_PATH: &str = "/bin/s6-linux-init-hpr";
+const QR_CODE_SIZE: u32 = 350;
+const MAX_QR_CODE_LOG_LENGTH: usize = 2000; // The QR code can't contain much more data.
+
+pub struct PageCompletion {
+    install_result: super::InstallResult,
+    qr_data_result: Option<Result<qr_code::Data, qr_code::Error>>,
+}
+
+impl PageCompletion {
+    pub fn new(install_result: super::InstallResult) -> Self {
+        let qr_data_result = match install_result.clone() {
+            Ok(_) => None,
+            Err((code, log)) => {
+                let log_len = log.len();
+                Some(qr_code::Data::new(
+                    format!(
+                        "Error code: {}\nLog:\n{}",
+                        code,
+                        if log_len > MAX_QR_CODE_LOG_LENGTH {
+                            &log[(log_len - MAX_QR_CODE_LOG_LENGTH)..]
+                        } else {
+                            &log
+                        }
+                    )
+                    .as_bytes(),
+                ))
+            }
+        };
+        Self {
+            install_result,
+            qr_data_result,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum PageCompletionEvent {
+    Quit,
+    Poweroff,
+    Reboot,
+}
+
+impl PageCompletion {
+    fn spawn_s6_hpr(parameter: &str) {
+        let _ = Command::new(S6_HPR_PATH).arg(parameter).spawn();
+    }
+}
+
+impl Page for PageCompletion {
+    fn update(&mut self, event: Event) -> UpdateResult {
+        if let Event::PageCompletion(page_completion_event) = event {
+            match page_completion_event {
+                PageCompletionEvent::Quit => {
+                    return (None, window::latest().and_then(window::close));
+                }
+                PageCompletionEvent::Poweroff => {
+                    Self::spawn_s6_hpr("-p");
+                }
+                PageCompletionEvent::Reboot => {
+                    Self::spawn_s6_hpr("-r");
+                }
+            }
+        };
+
+        (None, Task::none())
+    }
+
+    fn view(&self) -> Element<'_, Event> {
+        let main_element = match &self.install_result {
+            Ok(_) => {
+                center_in_container!(text("Spectrum installation succeeded!").size(TITLE_TEXT_SIZE))
+                    .into()
+            }
+            Err(_) => {
+                let mut widgets = column![
+                    space().height(MARGIN_VERTICAL),
+                    center_horizontal_in_container!(
+                        text("Spectrum installation failed :(").size(TITLE_TEXT_SIZE)
+                    ),
+                    space().height(MARGIN_VERTICAL),
+                    center_horizontal_in_container!(text(format!(
+                        "You can reach out to the team via Chat or the public mailing list\n\
+                        (discuss@spectrum-os.org).\n\
+                        See https://spectrum-os.org/doc/contributing/communication.html for details.\n\
+                        Scan the QR code to obtain the install log."
+                    ))),
+                ];
+
+                widgets = if let Some(Ok(qr_data)) = &self.qr_data_result {
+                    widgets.push(center_in_container!(
+                        qr_code(&qr_data).total_size(QR_CODE_SIZE)
+                    ))
+                } else {
+                    widgets.push(center_horizontal_in_container!(text(
+                        "Error encoding QR code."
+                    )))
+                };
+
+                widgets.into()
+            }
+        };
+
+        super::layout::bottom_buttons_layout(
+            [
+                vec![
+                    (
+                        text("Reboot").size(BUTTON_TEXT_SIZE).into(),
+                        Some(Event::PageCompletion(PageCompletionEvent::Reboot)),
+                    ),
+                    (
+                        text("Poweroff").size(BUTTON_TEXT_SIZE).into(),
+                        Some(Event::PageCompletion(PageCompletionEvent::Poweroff)),
+                    ),
+                ],
+                vec![(
+                    text("Quit installer, stay in live environment")
+                        .size(BUTTON_TEXT_SIZE)
+                        .into(),
+                    Some(Event::PageCompletion(PageCompletionEvent::Quit)),
+                )],
+            ],
+            main_element,
+        )
+    }
+}
diff --git a/tools/spectrum-installer/src/pages/confirmation.rs b/tools/spectrum-installer/src/pages/confirmation.rs
new file mode 100644
index 0000000..9d562e4
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/confirmation.rs
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use super::{
+    BUTTON_TEXT_SIZE, Event, MARGIN_LR, MARGIN_VERTICAL, Page, TITLE_TEXT_SIZE, UpdateResult,
+    WINDOW_WIDTH,
+};
+use crate::{center_horizontal_in_container, destination_device::DestinationDevice};
+use iced::{
+    Length, Task,
+    widget::{checkbox, column, container, space, text},
+};
+
+pub struct PageConfirmation {
+    device: DestinationDevice,
+    install_confirmed: bool,
+}
+
+impl PageConfirmation {
+    pub fn new(device: DestinationDevice) -> Self {
+        Self {
+            device,
+            install_confirmed: false,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum PageConfirmationEvent {
+    Back,
+    ConfirmCheckboxToggled(bool),
+    StartInstallation,
+}
+
+impl Page for PageConfirmation {
+    fn update(&mut self, event: Event) -> UpdateResult {
+        if let Event::PageConfirmation(page_event) = event {
+            match page_event {
+                PageConfirmationEvent::Back => {
+                    return (
+                        Some(Box::new(super::disk_selection::PageDiskSelection::new())),
+                        Task::none(),
+                    );
+                }
+                PageConfirmationEvent::ConfirmCheckboxToggled(bool) => {
+                    self.install_confirmed = bool;
+                }
+                PageConfirmationEvent::StartInstallation => {
+                    return (
+                        Some(Box::new(super::installation::PageInstallation::new(
+                            self.device.clone(),
+                        ))),
+                        Task::none(),
+                    );
+                }
+            }
+        }
+        (None, Task::none())
+    }
+
+    fn view(&self) -> iced::Element<'_, Event> {
+        let main_content = column![
+            space().height(Length::Fill),
+            center_horizontal_in_container!(text("Confirm Installation").size(TITLE_TEXT_SIZE)),
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(text(
+                "Erase all data on the following device and install Spectrum?"
+            )),
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(
+                container(super::layout::disk_element(&self.device))
+                    .width(WINDOW_WIDTH - 2.0 * MARGIN_LR)
+            ),
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(
+                checkbox(self.install_confirmed)
+                    .label(format!(
+                        "I understand all data on {} will be lost",
+                        self.device.name
+                    ))
+                    .on_toggle(|bool| Event::PageConfirmation(
+                        PageConfirmationEvent::ConfirmCheckboxToggled(bool)
+                    ))
+            ),
+            space().height(Length::Fill),
+        ];
+
+        super::layout::bottom_buttons_layout(
+            [[
+                (
+                    text("Back").size(BUTTON_TEXT_SIZE).into(),
+                    Some(Event::PageConfirmation(PageConfirmationEvent::Back)),
+                ),
+                (
+                    text("Start installation").size(BUTTON_TEXT_SIZE).into(),
+                    if self.install_confirmed {
+                        Some(Event::PageConfirmation(
+                            PageConfirmationEvent::StartInstallation,
+                        ))
+                    } else {
+                        None
+                    },
+                ),
+            ]],
+            main_content.into(),
+        )
+    }
+}
diff --git a/tools/spectrum-installer/src/pages/disk_selection.rs b/tools/spectrum-installer/src/pages/disk_selection.rs
new file mode 100644
index 0000000..4755acc
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/disk_selection.rs
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025-2026 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use super::{
+    BUTTON_TEXT_SIZE, Event, MARGIN_LR, MARGIN_VERTICAL, Page, TITLE_TEXT_SIZE, UpdateResult,
+    WINDOW_WIDTH,
+};
+use crate::{center_horizontal_in_container, destination_device::DestinationDevice};
+use iced::{
+    Length, Task,
+    widget::{
+        Scrollable, button, column,
+        scrollable::{Direction, Scrollbar},
+        space, text,
+    },
+};
+
+pub struct PageDiskSelection {
+    destination_device: Option<DestinationDevice>,
+}
+
+impl PageDiskSelection {
+    pub fn new() -> Self {
+        Self {
+            destination_device: None,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum PageDiskSelectionEvent {
+    Back,
+    SelectDevice(DestinationDevice),
+    ContinueWithDevice(DestinationDevice),
+}
+
+impl Page for PageDiskSelection {
+    fn update(&mut self, event: Event) -> UpdateResult {
+        if let Event::PageDiskSelection(page_disk_selection_event) = event {
+            match page_disk_selection_event {
+                PageDiskSelectionEvent::Back => {
+                    return (
+                        Some(Box::new(super::welcome::PageWelcome::new())),
+                        Task::none(),
+                    );
+                }
+                PageDiskSelectionEvent::SelectDevice(device) => {
+                    self.destination_device = Some(device);
+                }
+                PageDiskSelectionEvent::ContinueWithDevice(device) => {
+                    return (
+                        Some(Box::new(super::confirmation::PageConfirmation::new(device))),
+                        Task::none(),
+                    );
+                }
+            }
+        }
+        (None, Task::none())
+    }
+
+    fn view(&self) -> iced::Element<'_, Event> {
+        let destination_devices = DestinationDevice::get_destination_devices().unwrap_or(vec![]);
+
+        let destination_selection = if destination_devices.is_empty() {
+            column![text("No applicable devices to install Spectrum to found.")]
+        } else {
+            column(destination_devices.iter().map(|device| {
+                button(super::layout::disk_element(&device))
+                    .on_press(Event::PageDiskSelection(
+                        PageDiskSelectionEvent::SelectDevice(device.clone()),
+                    ))
+                    .width(WINDOW_WIDTH - 2.0 * MARGIN_LR)
+                    .style({
+                        if self
+                            .destination_device
+                            .as_ref()
+                            .is_some_and(|d| d.name == device.name)
+                        {
+                            button::success
+                        } else {
+                            button::primary
+                        }
+                    })
+                    .into()
+            }))
+            .spacing(8)
+            .into()
+        };
+
+        let main_content = column![
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(
+                text("Select the device to install Spectrum to").size(TITLE_TEXT_SIZE)
+            ),
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(
+                Scrollable::new(destination_selection)
+                    .height(Length::Fill)
+                    .direction(Direction::Vertical(Scrollbar::new()))
+            ),
+            space().height(MARGIN_VERTICAL),
+        ];
+
+        super::layout::bottom_buttons_layout(
+            [[
+                (
+                    text("Back").size(BUTTON_TEXT_SIZE).into(),
+                    Some(Event::PageDiskSelection(PageDiskSelectionEvent::Back)),
+                ),
+                (
+                    text("Next").size(BUTTON_TEXT_SIZE).into(),
+                    match &self.destination_device {
+                        None => None,
+                        Some(device) => Some(Event::PageDiskSelection(
+                            PageDiskSelectionEvent::ContinueWithDevice(device.clone()),
+                        )),
+                    },
+                ),
+            ]],
+            main_content.into(),
+        )
+    }
+}
diff --git a/tools/spectrum-installer/src/pages/installation.rs b/tools/spectrum-installer/src/pages/installation.rs
new file mode 100644
index 0000000..9295eea
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/installation.rs
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use super::{
+    BUTTON_TEXT_SIZE, Event, InstallResult, MARGIN_VERTICAL, Page, TITLE_TEXT_SIZE, UpdateResult,
+};
+use crate::{center_horizontal_in_container, destination_device::DestinationDevice};
+use iced::{
+    Length, Subscription, Task,
+    widget::{column, container, row, space, text},
+};
+use iced_aw::Spinner;
+use iced_term::Terminal;
+use std::fs;
+
+const RESULT_CODE_FILE: &str = "/tmp/spectrum-installer_result-code";
+const LOG_FILE: &str = "/tmp/spectrum-installer_log";
+
+pub struct PageInstallation {
+    terminal: Option<Terminal>,
+    installation_completed: bool,
+    installation_result: Option<InstallResult>,
+}
+
+impl PageInstallation {
+    pub fn new(device: DestinationDevice) -> Self {
+        let term = Terminal::new(
+            0,
+            iced_term::settings::Settings {
+                backend: iced_term::settings::BackendSettings {
+                    program: String::from("/usr/bin/sh"),
+                    args: Vec::from([
+                        String::from("-c"),
+                        // Install enforcing a new partition table.
+                        // HACK: Logging to /tmp as iced_term does not offer a way of obtaining
+                        // the command's result.
+                        String::from(
+                            [
+                                "set -o pipefail; \
+                                ( /usr/bin/systemd-repart \
+                                 --pretty=false \
+                                 --definitions=/usr/share/spectrum-installer/repart.d \
+                                 --empty=force \
+                                 --dry-run=no '",
+                                &device.path,
+                                "'; echo $? > '",
+                                RESULT_CODE_FILE,
+                                "') 2>&1 | tee '",
+                                LOG_FILE,
+                                "'",
+                            ]
+                            .concat(),
+                        ),
+                    ]),
+                    ..Default::default()
+                },
+                ..Default::default()
+            },
+        );
+
+        match term {
+            Ok(term) => Self {
+                terminal: Some(term),
+                installation_completed: false,
+                installation_result: None,
+            },
+            Err(e) => Self {
+                terminal: None,
+                installation_completed: true,
+                installation_result: Some(Err((0, e.to_string()))),
+            },
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum PageInstallationEvent {
+    Terminal(iced_term::Event),
+    Finish,
+}
+
+impl Page for PageInstallation {
+    fn update(&mut self, event: Event) -> UpdateResult {
+        if let Event::PageInstallation(page_event) = event {
+            match page_event {
+                PageInstallationEvent::Finish => {
+                    if let Some(installation_result) = self.installation_result.clone() {
+                        return (
+                            Some(Box::new(super::completion::PageCompletion::new(
+                                installation_result,
+                            ))),
+                            Task::none(),
+                        );
+                    }
+                }
+                PageInstallationEvent::Terminal(iced_term::Event::BackendCall(_, cmd)) => {
+                    if let Some(term) = &mut self.terminal {
+                        match term.handle(iced_term::Command::ProxyToBackend(cmd)) {
+                            iced_term::actions::Action::Shutdown => {
+                                let code = fs::read_to_string(RESULT_CODE_FILE);
+                                let log = fs::read_to_string(LOG_FILE);
+                                self.installation_result =
+                                    if let (Ok(code_as_str), Ok(log)) = (code, log) {
+                                        if let Ok(code) = code_as_str.trim().parse::<u32>() {
+                                            self.installation_completed = true;
+                                            if code == 0 {
+                                                Some(Ok(log))
+                                            } else {
+                                                Some(Err((code, log)))
+                                            }
+                                        } else {
+                                            None
+                                        }
+                                    } else {
+                                        None
+                                    };
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+            }
+        }
+        (None, Task::none())
+    }
+
+    fn subscription(&self) -> Subscription<Event> {
+        if let Some(term) = &self.terminal {
+            term.subscription()
+                .map(|t| Event::PageInstallation(PageInstallationEvent::Terminal(t)))
+        } else {
+            Subscription::none()
+        }
+    }
+
+    fn view(&self) -> iced::Element<'_, Event> {
+        let title_row: iced::Element<Event> = if !self.installation_completed {
+            row![
+                Spinner::new().height(25),
+                space().width(MARGIN_VERTICAL),
+                text("Installing ...").size(TITLE_TEXT_SIZE)
+            ]
+            .into()
+        } else {
+            text(
+                if self.installation_result.as_ref().is_some_and(|r| r.is_ok()) {
+                    "Installation completed."
+                } else {
+                    "Installation failed."
+                },
+            )
+            .size(TITLE_TEXT_SIZE)
+            .into()
+        };
+
+        let installation_status: iced::Element<Event> = if let Some(term) = &self.terminal {
+            iced_term::TerminalView::show(term)
+                .map::<_>(|t| Event::PageInstallation(PageInstallationEvent::Terminal(t)))
+                .into()
+        } else {
+            text("Could not start installation.").into()
+        };
+
+        let main_content = column![
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(title_row),
+            space().height(MARGIN_VERTICAL),
+            container(installation_status)
+                .width(Length::Fill)
+                .center_x(Length::Fill)
+                .height(Length::Fill),
+        ];
+
+        super::layout::bottom_buttons_layout(
+            [[(
+                text("Finish").size(BUTTON_TEXT_SIZE).into(),
+                if self.installation_completed {
+                    Some(Event::PageInstallation(PageInstallationEvent::Finish))
+                } else {
+                    None
+                },
+            )]],
+            main_content.into(),
+        )
+    }
+}
diff --git a/tools/spectrum-installer/src/pages/layout.rs b/tools/spectrum-installer/src/pages/layout.rs
new file mode 100644
index 0000000..d93c235
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/layout.rs
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025-2026 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use iced::{
+    Element, Length,
+    widget::{Button, Column, Row, button, column, container, row, text},
+};
+use iced_fonts::bootstrap as icons;
+
+use crate::destination_device::DestinationDevice;
+
+use super::Event;
+
+#[macro_export]
+macro_rules! center_horizontal_in_container {
+    ($e:expr) => {
+        iced::widget::container($e)
+            .width(iced::Length::Fill)
+            .center_x(iced::Length::Fill)
+    };
+}
+#[macro_export]
+macro_rules! center_in_container {
+    ($e:expr) => {
+        iced::widget::container($e)
+            .height(iced::Length::Fill)
+            .width(iced::Length::Fill)
+            .center_x(iced::Length::Fill)
+            .center_y(iced::Length::Fill)
+    };
+}
+
+const BOTTOM_BUTTON_SPACE: f32 = 2.0;
+const BOTTOM_BUTTON_ROW_HEIGHT: f32 = 50.0;
+const DISK_ICON_SIZE: u32 = 50;
+
+fn bottom_button<'a>(
+    button_content: Element<'a, Event>,
+    button_on_press: Option<Event>,
+) -> Button<'a, Event> {
+    button(
+        center_in_container!(button_content)
+            .height(Length::Fill)
+            .center_y(Length::Fill),
+    )
+    .on_press_maybe(button_on_press)
+    .width(Length::Fill)
+    .height(Length::Fill)
+}
+
+pub fn bottom_buttons_layout<'a>(
+    button_matrix: impl IntoIterator<
+        Item = impl IntoIterator<Item = (Element<'a, Event>, Option<Event>)>,
+    >,
+    main_element: Element<'a, Event>,
+) -> Element<'a, Event> {
+    let mut bottom_height = 0.0;
+    let mut bottom_element = Column::new().spacing(BOTTOM_BUTTON_SPACE);
+    for input_row in button_matrix.into_iter() {
+        let mut output_row = Row::new().spacing(BOTTOM_BUTTON_SPACE);
+        for (button_content, button_on_press) in input_row.into_iter() {
+            output_row = output_row.push(bottom_button(button_content, button_on_press));
+        }
+        bottom_element = bottom_element.push(output_row);
+        bottom_height += BOTTOM_BUTTON_ROW_HEIGHT;
+    }
+
+    column![
+        center_horizontal_in_container!(main_element).height(Length::Fill),
+        container(bottom_element).height(bottom_height)
+    ]
+    .into()
+}
+
+pub fn disk_element(device: &DestinationDevice) -> Element<'static, Event> {
+    let icon: Element<Event> = icons::hdd().size(DISK_ICON_SIZE).into();
+
+    row![
+        icon,
+        text(format!(
+            "{}\n{}\n{}",
+            device.name,
+            device.path,
+            device.size_as_human_readable_string(),
+        ))
+    ]
+    .spacing(8)
+    .into()
+}
diff --git a/tools/spectrum-installer/src/pages/mod.rs b/tools/spectrum-installer/src/pages/mod.rs
new file mode 100644
index 0000000..8fce75d
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/mod.rs
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use iced::{Subscription, Task};
+
+mod completion;
+mod confirmation;
+mod disk_selection;
+mod installation;
+mod layout;
+mod welcome;
+
+pub const WINDOW_WIDTH: f32 = 750.0;
+pub const WINDOW_HEIGHT: f32 = 650.0;
+
+pub const MARGIN_VERTICAL: f32 = 25.0;
+pub const MARGIN_LR: f32 = 15.0;
+pub const BUTTON_TEXT_SIZE: u32 = 20;
+pub const TITLE_TEXT_SIZE: u32 = 24;
+
+type UpdateResult = (Option<Box<dyn Page>>, Task<Event>);
+
+type InstallResult = Result<String, (u32, String)>;
+
+pub trait Page {
+    fn update(&mut self, event: Event) -> UpdateResult;
+    fn view(&self) -> iced::Element<'_, Event>;
+    fn subscription(&self) -> Subscription<Event> {
+        Subscription::none()
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum Event {
+    /* Pages are ordered as they appear during the installation. */
+    PageWelcome(welcome::PageWelcomeEvent),
+    PageDiskSelection(disk_selection::PageDiskSelectionEvent),
+    PageConfirmation(confirmation::PageConfirmationEvent),
+    PageInstallation(installation::PageInstallationEvent),
+    PageCompletion(completion::PageCompletionEvent),
+}
+
+pub const INITAL_PAGE: welcome::PageWelcome = welcome::PageWelcome {};
diff --git a/tools/spectrum-installer/src/pages/welcome.rs b/tools/spectrum-installer/src/pages/welcome.rs
new file mode 100644
index 0000000..1cc56c2
--- /dev/null
+++ b/tools/spectrum-installer/src/pages/welcome.rs
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: EUPL-1.2+
+// SPDX-FileCopyrightText: 2025 Johannes Süllner <johannes.suellner@mailbox.org>
+
+use super::{BUTTON_TEXT_SIZE, Event, MARGIN_VERTICAL, Page, TITLE_TEXT_SIZE, UpdateResult};
+use crate::{center_horizontal_in_container, center_in_container};
+use iced::{
+    Element, Length, Task,
+    widget::{column, image, space, text},
+};
+
+const LOGO_PATH_GLOBAL: &str = "/usr/share/icons/hicolor/400x400/apps/spectrum-installer.png";
+const LOGO_WIDTH: f32 = 400.0;
+
+pub struct PageWelcome {}
+
+impl PageWelcome {
+    pub fn new() -> Self {
+        Self {}
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum PageWelcomeEvent {
+    Continue,
+}
+
+impl Page for PageWelcome {
+    fn update(&mut self, event: Event) -> UpdateResult {
+        if let Event::PageWelcome(page_welcome_event) = event {
+            match page_welcome_event {
+                PageWelcomeEvent::Continue => {
+                    return (
+                        Some(Box::new(super::disk_selection::PageDiskSelection::new())),
+                        Task::none(),
+                    );
+                }
+            }
+        }
+        (None, Task::none())
+    }
+
+    fn view(&self) -> Element<'_, Event> {
+        let button_content = text("Next").size(BUTTON_TEXT_SIZE);
+
+        let main_content = column![
+            space().height(MARGIN_VERTICAL),
+            center_horizontal_in_container!(
+                text("Welcome to the installation of Spectrum!").size(TITLE_TEXT_SIZE)
+            ),
+            space().height(MARGIN_VERTICAL),
+            center_in_container!(image(LOGO_PATH_GLOBAL).width(Length::Fixed(LOGO_WIDTH)))
+        ];
+
+        super::layout::bottom_buttons_layout(
+            [[(
+                button_content.into(),
+                Some(Event::PageWelcome(PageWelcomeEvent::Continue)),
+            )]],
+            main_content.into(),
+        )
+    }
+}

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/5] host/rootfs: integrate spectrum-installer
  2026-02-04 17:55 [PATCH v2 0/5] Spectrum-installer Johannes Süllner
  2026-02-04 17:55 ` [PATCH v2 1/5] tools: add spectrum-installer Johannes Süllner
@ 2026-02-04 17:55 ` Johannes Süllner
  2026-02-05  4:17   ` Demi Marie Obenour
  2026-03-15 12:15   ` Alyssa Ross
  2026-02-04 17:55 ` [PATCH v2 3/5] release: drop combined and installer image Johannes Süllner
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 13+ messages in thread
From: Johannes Süllner @ 2026-02-04 17:55 UTC (permalink / raw)
  To: devel; +Cc: Johannes Süllner

The installer needs to run as root, so that `systemd-repart` can write
to disks. Since Weston is not running as root (since b26f59e), just as
with the root terminal, we add a s6-sudod service for the installer.

Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
---
 host/rootfs/default.nix                         |  4 ++--
 host/rootfs/file-list.mk                        |  3 +++
 .../spectrum-installer-as-root/notification-fd  |  1 +
 .../notification-fd.license                     |  2 ++
 .../service/spectrum-installer-as-root/run      | 17 +++++++++++++++++
 host/rootfs/image/etc/s6-rc/weston/run          |  1 +
 host/rootfs/image/etc/xdg/weston/weston.ini     |  5 +++++
 .../image/usr/bin/spectrum-installer-as-root    |  5 +++++
 pkgs/default.nix                                |  2 ++
 9 files changed, 38 insertions(+), 2 deletions(-)
 create mode 100644 host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd
 create mode 100644 host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd.license
 create mode 100755 host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/run
 create mode 100755 host/rootfs/image/usr/bin/spectrum-installer-as-root

diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix
index 66aa366..f53d3e3 100644
--- a/host/rootfs/default.nix
+++ b/host/rootfs/default.nix
@@ -4,7 +4,7 @@
 
 import ../../lib/call-package.nix (
 { callSpectrumPackage, config, spectrum-build-tools
-, src, pkgsMusl, inkscape, linux_latest, xorg
+, src, pkgsMusl, inkscape, linux_latest, spectrum-installer, xorg
 }:
 pkgsMusl.callPackage (
 
@@ -62,7 +62,7 @@ let
   # https://inbox.vuxu.org/musl/20251017-dlopen-use-rpath-of-caller-dso-v1-1-46c69eda1473@iscas.ac.cn/
   usrPackages = [
     appvm dejavu_fonts firmware kernel.modules kmod.lib lvm2 mesa
-    netvm systemd westonLite
+    netvm spectrum-installer systemd westonLite
   ];
 
   appvms = {
diff --git a/host/rootfs/file-list.mk b/host/rootfs/file-list.mk
index 3899d62..54d8e00 100644
--- a/host/rootfs/file-list.mk
+++ b/host/rootfs/file-list.mk
@@ -25,6 +25,8 @@ FILES = \
 	image/etc/s6-linux-init/run-image/service/serial-getty/notification-fd \
 	image/etc/s6-linux-init/run-image/service/serial-getty/run \
 	image/etc/s6-linux-init/run-image/service/serial-getty/template/run \
+	image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd \
+	image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/run \
 	image/etc/s6-linux-init/run-image/service/vm-services/notification-fd \
 	image/etc/s6-linux-init/run-image/service/vm-services/run \
 	image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/dbus/notification-fd \
@@ -62,6 +64,7 @@ FILES = \
 	image/usr/bin/run-appimage \
 	image/usr/bin/run-flatpak \
 	image/usr/bin/run-vmm \
+	image/usr/bin/spectrum-installer-as-root \
 	image/usr/bin/spectrum-update \
 	image/usr/bin/vm-console \
 	image/usr/bin/vm-import \
diff --git a/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd b/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd
@@ -0,0 +1 @@
+3
diff --git a/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd.license b/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd.license
new file mode 100644
index 0000000..0d3d47c
--- /dev/null
+++ b/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/notification-fd.license
@@ -0,0 +1,2 @@
+SPDX-License-Identifier: CC0-1.0
+SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
diff --git a/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/run b/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/run
new file mode 100755
index 0000000..a8ab652
--- /dev/null
+++ b/host/rootfs/image/etc/s6-linux-init/run-image/service/spectrum-installer-as-root/run
@@ -0,0 +1,17 @@
+#!/bin/execlineb -PW
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
+
+s6-ipcserver-socketbinder -a 0700 /run/spectrum-installer-as-root
+
+if { chown wayland /run/spectrum-installer-as-root }
+
+fdmove 1 3
+s6-ipcserverd -1P
+
+exec -c
+/bin/export PATH /usr/bin
+/bin/export WAYLAND_DISPLAY ""
+s6-sudod
+cd /
+spectrum-installer
diff --git a/host/rootfs/image/etc/s6-rc/weston/run b/host/rootfs/image/etc/s6-rc/weston/run
index fd59586..fa4772a 100644
--- a/host/rootfs/image/etc/s6-rc/weston/run
+++ b/host/rootfs/image/etc/s6-rc/weston/run
@@ -63,6 +63,7 @@ bwrap
   # For udev
   --ro-bind /run/udev /run/udev
   --bind /run/root-terminal /run/root-terminal
+  --bind /run/spectrum-installer-as-root /run/spectrum-installer-as-root
   --tmpfs /tmp
   --tmpfs /dev/shm
   # Filtered /proc (without nasty stuff)
diff --git a/host/rootfs/image/etc/xdg/weston/weston.ini b/host/rootfs/image/etc/xdg/weston/weston.ini
index a4763c6..f21041e 100644
--- a/host/rootfs/image/etc/xdg/weston/weston.ini
+++ b/host/rootfs/image/etc/xdg/weston/weston.ini
@@ -13,3 +13,8 @@ path=/bin/root-terminal
 icon=/usr/share/icons/hicolor/20x20/apps/com.system76.CosmicFiles.png
 displayname=Files
 path=/bin/cosmic-files
+
+[launcher]
+icon=/usr/share/icons/hicolor/22x22/apps/spectrum-installer.png
+displayname=Spectrum installer
+path=/bin/spectrum-installer-as-root
diff --git a/host/rootfs/image/usr/bin/spectrum-installer-as-root b/host/rootfs/image/usr/bin/spectrum-installer-as-root
new file mode 100755
index 0000000..c03d3ac
--- /dev/null
+++ b/host/rootfs/image/usr/bin/spectrum-installer-as-root
@@ -0,0 +1,5 @@
+#!/bin/execlineb -Ws0
+# SPDX-License-Identifier: EUPL-1.2+
+# SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
+
+s6-sudo -- /run/spectrum-installer-as-root $@
diff --git a/pkgs/default.nix b/pkgs/default.nix
index a80c5b3..5849e58 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -50,6 +50,8 @@ let
       appSupport = false;
       driverSupport = true;
     };
+    spectrum-installer =
+      self.callSpectrumPackage ../tools/spectrum-installer {};
     spectrum-router = self.callSpectrumPackage ../tools/router {};
     xdg-desktop-portal-spectrum-host =
       self.callSpectrumPackage ../tools/xdg-desktop-portal-spectrum-host {};

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 3/5] release: drop combined and installer image
  2026-02-04 17:55 [PATCH v2 0/5] Spectrum-installer Johannes Süllner
  2026-02-04 17:55 ` [PATCH v2 1/5] tools: add spectrum-installer Johannes Süllner
  2026-02-04 17:55 ` [PATCH v2 2/5] host/rootfs: integrate spectrum-installer Johannes Süllner
@ 2026-02-04 17:55 ` Johannes Süllner
  2026-03-15 12:20   ` Alyssa Ross
  2026-02-04 17:55 ` [PATCH v2 4/5] release/live: remove B slot from live image and reduce A slot size Johannes Süllner
  2026-02-04 17:55 ` [PATCH v2 5/5] Revert "scripts/make-gpt.sh: allow setting partition size" Johannes Süllner
  4 siblings, 1 reply; 13+ messages in thread
From: Johannes Süllner @ 2026-02-04 17:55 UTC (permalink / raw)
  To: devel; +Cc: Johannes Süllner

Installer is now part of the live system.

Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
---
 Documentation/development/uuid-reference.adoc |  22 ----
 release/checks/default.nix                    |   2 -
 release/checks/installer.nix                  |  16 ---
 release/combined/default.nix                  | 123 ------------------
 release/combined/eosimages.nix                |  21 ---
 release/combined/grub.cfg.in                  |  22 ----
 release/combined/run-vm.nix                   |  31 -----
 ...ble-gpt-partition-attribute-55-check.patch |  37 ------
 ...pt-disable-partition-table-CRC-check.patch |  36 -----
 .../0003-install-remove-Endless-OS-ad.patch   |  99 --------------
 ...4-finished-don-t-run-eos-diagnostics.patch |  44 -------
 ...omote-spectrum-not-the-Endless-forum.patch |  32 -----
 release/installer/app/default.nix             |  26 ----
 .../installer/app/vendor-customer-support.ini |   5 -
 release/installer/configuration.nix           |  69 ----------
 release/installer/default.nix                 |  22 ----
 release/installer/run-vm.nix                  |  47 -------
 release/installer/seat.rules                  |   9 --
 18 files changed, 663 deletions(-)
 delete mode 100644 release/checks/installer.nix
 delete mode 100644 release/combined/default.nix
 delete mode 100644 release/combined/eosimages.nix
 delete mode 100644 release/combined/grub.cfg.in
 delete mode 100644 release/combined/run-vm.nix
 delete mode 100644 release/installer/app/0001-gpt-disable-gpt-partition-attribute-55-check.patch
 delete mode 100644 release/installer/app/0002-gpt-disable-partition-table-CRC-check.patch
 delete mode 100644 release/installer/app/0003-install-remove-Endless-OS-ad.patch
 delete mode 100644 release/installer/app/0004-finished-don-t-run-eos-diagnostics.patch
 delete mode 100644 release/installer/app/0005-finished-promote-spectrum-not-the-Endless-forum.patch
 delete mode 100644 release/installer/app/default.nix
 delete mode 100644 release/installer/app/vendor-customer-support.ini
 delete mode 100644 release/installer/configuration.nix
 delete mode 100644 release/installer/default.nix
 delete mode 100644 release/installer/run-vm.nix
 delete mode 100644 release/installer/seat.rules

diff --git a/Documentation/development/uuid-reference.adoc b/Documentation/development/uuid-reference.adoc
index 16279c8..7e3641a 100644
--- a/Documentation/development/uuid-reference.adoc
+++ b/Documentation/development/uuid-reference.adoc
@@ -45,28 +45,6 @@ Specification].
 https://uapi-group.org/specifications/specs/discoverable_partitions_specification/[Discoverable Partitions
 Specification].
 
-=== `56a3bbc3-aefa-43d9-a64d-7b3fd59bbc4e`
-
-https://github.com/endlessm/eos-installer["eosimages"] partition on the
-Spectrum combined live system / installer image.
-
-== Combined Image Partition IDs
-
-These UUIDs are used as the unique partition identifiers for the
-Spectrum combined live system / installer image.
-
-=== `6e23b026-9f1e-479d-8a58-a0cda382e1ce`
-
-The Spectrum installer system.
-
-=== `18f2ccff-92f1-4bb1-a80e-24f76ecda90c`
-
-The not-yet-used B verity partition.
-
-=== `ec0c5ff3-f6b1-4adf-82b4-61336c4d135f`
-
-The not-yet-used B root filesystem partition.
-
 '''
 
 == Finding Undocumented UUIDs
diff --git a/release/checks/default.nix b/release/checks/default.nix
index 9d618dd..ac47cb3 100644
--- a/release/checks/default.nix
+++ b/release/checks/default.nix
@@ -14,8 +14,6 @@ import ../../lib/call-package.nix ({ callSpectrumPackage }:
 
   doc-anchors = callSpectrumPackage ./doc-anchors.nix {};
 
-  installer = callSpectrumPackage ./installer.nix {};
-
   integration = callSpectrumPackage ./integration {};
 
   pkg-tests = callSpectrumPackage ./pkg-tests.nix {};
diff --git a/release/checks/installer.nix b/release/checks/installer.nix
deleted file mode 100644
index d28a41b..0000000
--- a/release/checks/installer.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is>
-
-import ../../lib/call-package.nix ({ testers }: testers.nixosTest ({ ... }: {
-  name = "spectrum-test-installer";
-  enableOCR = true;
-
-  nodes = {
-    machine = ../installer/configuration.nix;
-  };
-
-  testScript = ''
-    start_all()
-    machine.wait_for_text("Spectrum")
-  '';
-})) (_: {})
diff --git a/release/combined/default.nix b/release/combined/default.nix
deleted file mode 100644
index 3be32d4..0000000
--- a/release/combined/default.nix
+++ /dev/null
@@ -1,123 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross <hi@alyssa.is>
-# SPDX-FileCopyrightText: 2021 Yureka <yuka@yuka.dev>
-# SPDX-FileCopyrightText: 2022 Unikie
-
-import ../../lib/call-package.nix (
-{ callSpectrumPackage
-, lib, runCommand, stdenv, replaceVars, writeClosure
-, dosfstools, grub2_efi, jq, libfaketime, mtools, squashfs-tools-ng
-, systemdMinimal, util-linux
-}:
-
-let
-  inherit (builtins) storeDir;
-  inherit (lib) removePrefix toUpper;
-
-  eosimages = callSpectrumPackage ./eosimages.nix {};
-
-  installerPartUuid = "6e23b026-9f1e-479d-8a58-a0cda382e1ce";
-  installer = callSpectrumPackage ../installer {
-    extraConfig = {
-      boot.initrd.availableKernelModules = [ "squashfs" ];
-
-      fileSystems.${storeDir} = {
-        device = "/dev/disk/by-partuuid/${installerPartUuid}";
-      };
-    };
-  };
-
-  rootfs = runCommand "installer.img" {
-    nativeBuildInputs = [ squashfs-tools-ng ];
-    __structuredAttrs = true;
-    unsafeDiscardReferences = { out = true; };
-    dontFixup = true;
-  } ''
-    sed 's,^${storeDir}/,,' ${writeClosure [ installer.store ]} |
-        tar -C ${storeDir} -c --verbatim-files-from -T - \
-            --owner 0 --group 0 | tar2sqfs $out
-  '';
-
-  efiArch = stdenv.hostPlatform.efiArch;
-
-  grub = grub2_efi;
-
-  grubCfg = replaceVars ./grub.cfg.in {
-    linux = removePrefix storeDir installer.kernel;
-    initrd = removePrefix storeDir installer.initramfs;
-    inherit (installer) kernelParams;
-  };
-
-  esp = runCommand "esp.img" {
-    nativeBuildInputs = [ grub libfaketime dosfstools mtools ];
-
-    env = {
-      grubTargetDir = "${grub}/lib/grub/${grub.grubTarget}";
-      # Definition copied from util/grub-install-common.c.
-      # Last checked: GRUB 2.06
-      pkglib_DATA = lib.escapeShellArgs [
-        "efiemu32.o" "efiemu64.o" "moddep.lst" "command.lst" "fs.lst"
-        "partmap.lst" "parttool.lst" "video.lst" "crypto.lst" "terminal.lst"
-        "modinfo.sh"
-      ];
-    };
-
-    __structuredAttrs = true;
-    unsafeDiscardReferences = { out = true; };
-    dontFixup = true;
-
-    passthru = { inherit grubCfg; };
-  } ''
-    truncate -s 15M $out
-    faketime "1970-01-01 00:00:00" mkfs.vfat -i 0x2178694e -n EFI $out
-    mmd -i $out ::/EFI ::/EFI/BOOT \
-        ::/grub ::/grub/${grub.grubTarget} ::/grub/fonts
-
-    mcopy -i $out ${grubCfg} ::/grub/grub.cfg
-    mcopy -i $out $grubTargetDir/*.mod ::/grub/${grub.grubTarget}
-    for file in $pkglib_DATA; do
-        path="$grubTargetDir/$file"
-        ! [ -e "$path" ] || mcopy -i $out "$path" ::/grub/${grub.grubTarget}
-    done
-    mcopy -i $out ${grub}/share/grub/unicode.pf2 ::/grub/fonts
-
-    grub-mkimage -o grub${efiArch}.efi -p "(hd0,gpt1)/grub" -O ${grub.grubTarget} part_gpt fat
-    mcopy -i $out grub${efiArch}.efi ::/EFI/BOOT/BOOT${toUpper efiArch}.EFI
-
-    fsck.vfat -n $out
-  '';
-in
-
-runCommand "spectrum-installer" {
-  nativeBuildInputs = [ grub jq util-linux systemdMinimal ];
-  __structuredAttrs = true;
-  unsafeDiscardReferences = { out = true; };
-  dontFixup = true;
-  passthru = { inherit eosimages esp installer rootfs; };
-} ''
-  blockSize() {
-      wc -c "$1" | awk '{printf "%d\n", ($1 + 511) / 512}'
-  }
-
-  fillPartition() {
-      read start size < <(sfdisk -J "$1" | jq -r --argjson index "$2" \
-          '.partitiontable.partitions[$index] | "\(.start) \(.size)"')
-      dd if="$3" of="$1" seek="$start" count="$size" conv=notrunc
-  }
-
-  espSize="$(blockSize ${esp})"
-  installerSize="$(blockSize ${rootfs})"
-  eosimagesSize="$(blockSize ${eosimages})"
-
-  truncate -s $(((3 * 2048 + $espSize + $installerSize + $eosimagesSize) * 512)) $out
-  sfdisk --no-reread --no-tell-kernel $out <<EOF
-  label: gpt
-  size=$espSize, type=U
-  size=$installerSize, type=L, uuid=${installerPartUuid}
-  size=$eosimagesSize, type=56a3bbc3-aefa-43d9-a64d-7b3fd59bbc4e
-  EOF
-
-  fillPartition $out 0 ${esp}
-  fillPartition $out 1 ${rootfs}
-  fillPartition $out 2 ${eosimages}
-'') (_: {})
diff --git a/release/combined/eosimages.nix b/release/combined/eosimages.nix
deleted file mode 100644
index 2f24a8f..0000000
--- a/release/combined/eosimages.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross <hi@alyssa.is>
-
-import ../../lib/call-package.nix (
-{ callSpectrumPackage, runCommand, e2fsprogs, tar2ext4 }:
-
-runCommand "eosimages.img" {
-  nativeBuildInputs = [ e2fsprogs tar2ext4 ];
-  imageName = "Spectrum-0.0-x86_64-generic.0.Live.img";
-  image = callSpectrumPackage ../live {};
-  __structuredAttrs = true;
-  unsafeDiscardReferences = { out = true; };
-  dontFixup = true;
-} ''
-  mkdir dir
-  cd dir
-  gzip -1 < $image > $imageName.gz
-  sha256sum -- $imageName.gz > $imageName.gz.sha256
-  tar -ch -- $imageName.gz $imageName.gz.sha256 | tar2ext4 -o $out
-  e2label $out eosimages
-'') (_: {})
diff --git a/release/combined/grub.cfg.in b/release/combined/grub.cfg.in
deleted file mode 100644
index a22f5fc..0000000
--- a/release/combined/grub.cfg.in
+++ /dev/null
@@ -1,22 +0,0 @@
-# SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2021-2022, 2024 Alyssa Ross <hi@alyssa.is>
-
-insmod efi_gop
-
-if [ "${grub_cpu}" == "x86_64" ]; then
-  insmod efi_uga
-fi
-
-insmod font
-loadfont /grub/fonts/unicode.pf2
-
-insmod gfxterm
-set gfxpayload=keep
-terminal_output gfxterm
-terminal_output console
-
-menuentry "Install Spectrum" {
-  set root=(hd0,gpt2)
-  linux @linux@ @kernelParams@
-  initrd @initrd@
-}
diff --git a/release/combined/run-vm.nix b/release/combined/run-vm.nix
deleted file mode 100644
index 16e00cd..0000000
--- a/release/combined/run-vm.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021, 2023-2024 Alyssa Ross <hi@alyssa.is>
-
-import ../../lib/call-package.nix (
-{ callSpectrumPackage, lib, writeShellScript, coreutils, qemu_kvm, stdenv }:
-
-let
-  image = callSpectrumPackage ./. {};
-in
-
-writeShellScript "run-spectrum-installer-vm.sh" ''
-  export PATH=${lib.makeBinPath [ coreutils qemu_kvm ]}
-  img="$(mktemp spectrum-installer-target.XXXXXXXXXX.img)"
-  truncate -s 20G "$img"
-  exec 3<>"$img"
-  rm -f "$img"
-  # usb-kbd is used here as edk2 does not support virtio-keyboard:
-  # https://github.com/tianocore/edk2/pull/6444
-  exec ${../../scripts/run-qemu.sh} -cpu max -m 4G \
-    -machine virtualization=on \
-    -device virtio-mouse \
-    -device virtio-gpu \
-    -parallel none \
-    -vga none \
-    -device qemu-xhci \
-    -device usb-kbd \
-    -device usb-storage,drive=drive1,removable=true \
-    -drive file=${qemu_kvm}/share/qemu/edk2-${stdenv.hostPlatform.qemuArch}-code.fd,format=raw,if=pflash,readonly=true \
-    -drive file=${image},id=drive1,format=raw,if=none,readonly=true \
-    -drive file=/proc/self/fd/3,format=raw,if=virtio
-'') (_: {})
diff --git a/release/installer/app/0001-gpt-disable-gpt-partition-attribute-55-check.patch b/release/installer/app/0001-gpt-disable-gpt-partition-attribute-55-check.patch
deleted file mode 100644
index 78bbc24..0000000
--- a/release/installer/app/0001-gpt-disable-gpt-partition-attribute-55-check.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 058050ee5ba6a8d8dae2ba7fa7334ee19f98dbf8 Mon Sep 17 00:00:00 2001
-SPDX-License-Identifier: GPL-2.0-or-later
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-From: Alyssa Ross <hi@alyssa.is>
-Date: Thu, 9 Dec 2021 21:10:11 +0000
-Subject: [PATCH 1/5] gpt: disable gpt partition attribute 55 check
-
-Endless OS uses flag 55 to mean a partition should be auto-grown, and
-to identify Endless OS partitions.  Both of these meanings of flag 55
-are specific to Endless OS though, so it doesn't make sense for
-installing other operating systems.
----
- gnome-image-installer/util/gpt.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/gnome-image-installer/util/gpt.c b/gnome-image-installer/util/gpt.c
-index 02926a5e..1261d9a0 100644
---- a/gnome-image-installer/util/gpt.c
-+++ b/gnome-image-installer/util/gpt.c
-@@ -253,12 +253,14 @@ int is_eos_gpt_valid(struct ptable *pt, uint64_t *size)
-           || memcmp(&pt->partitions[i].type_guid, GPT_GUID_LINUX_ROOTFS_AARCH64, 16)==0
-           || memcmp(&pt->partitions[i].type_guid, GPT_GUID_LINUX_ROOTFS_RISCV_32, 16)==0
-           || memcmp(&pt->partitions[i].type_guid, GPT_GUID_LINUX_ROOTFS_RISCV_64, 16)==0) {
-+#if 0
-         uint64_t flags = 0;
-         memcpy(&flags, pt->partitions[i].attributes, 8);
-         if(!is_nth_flag_set(flags, 55)) {
-           //  55th flag must be 1 for EOS images
-           continue ;
-         }
-+#endif
-         has_root=1;
-         break ;
-       }
--- 
-2.37.1
-
diff --git a/release/installer/app/0002-gpt-disable-partition-table-CRC-check.patch b/release/installer/app/0002-gpt-disable-partition-table-CRC-check.patch
deleted file mode 100644
index 1628355..0000000
--- a/release/installer/app/0002-gpt-disable-partition-table-CRC-check.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 56dff17022a2ca631ac2ea529a84cb424c913500 Mon Sep 17 00:00:00 2001
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-SPDX-License-Identifier: GPL-2.0-or-later
-From: Alyssa Ross <hi@alyssa.is>
-Date: Thu, 9 Dec 2021 21:09:33 +0000
-Subject: [PATCH 2/5] gpt: disable partition table CRC check
-
-This is faulty, I think because eos-installer only looks at the first
-three entries in the partition table.
----
- gnome-image-installer/util/gpt.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/gnome-image-installer/util/gpt.c b/gnome-image-installer/util/gpt.c
-index 1261d9a0..fb8bab17 100644
---- a/gnome-image-installer/util/gpt.c
-+++ b/gnome-image-installer/util/gpt.c
-@@ -223,6 +223,7 @@ int is_eos_gpt_valid(struct ptable *pt, uint64_t *size)
-         g_warning("invalid header crc");
-         return 0;
-     }
-+#if 0
-     //  crc32 of partition table
-     int n = pt->header.ptable_count * pt->header.ptable_partition_size;
-     uint8_t *buffer = (uint8_t*)malloc(n);
-@@ -236,6 +237,7 @@ int is_eos_gpt_valid(struct ptable *pt, uint64_t *size)
-         return 0;
-     }
-     free(buffer);
-+#endif
- 
-     // The first partition must be an EFI System Partition
-     if(memcmp(&pt->partitions[0].type_guid, GPT_GUID_EFI, 16)!=0) {
--- 
-2.37.1
-
diff --git a/release/installer/app/0003-install-remove-Endless-OS-ad.patch b/release/installer/app/0003-install-remove-Endless-OS-ad.patch
deleted file mode 100644
index 8881b00..0000000
--- a/release/installer/app/0003-install-remove-Endless-OS-ad.patch
+++ /dev/null
@@ -1,99 +0,0 @@
-From 1061a8c3cfb31dcc2519841360c6d14612898b2c Mon Sep 17 00:00:00 2001
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-SPDX-License-Identifier: GPL-2.0-or-later
-From: Alyssa Ross <hi@alyssa.is>
-Date: Thu, 9 Dec 2021 21:06:16 +0000
-Subject: [PATCH 3/5] install: remove Endless OS ad
-
----
- .../pages/install/gis-install-page.ui         | 74 -------------------
- 1 file changed, 74 deletions(-)
-
-diff --git a/gnome-image-installer/pages/install/gis-install-page.ui b/gnome-image-installer/pages/install/gis-install-page.ui
-index d3b7769c..2fcc31b9 100644
---- a/gnome-image-installer/pages/install/gis-install-page.ui
-+++ b/gnome-image-installer/pages/install/gis-install-page.ui
-@@ -45,80 +45,6 @@
-                 <property name="position">1</property>
-               </packing>
-             </child>
--            <child>
--              <object class="GtkOverlay" id="graphics_overlay">
--                <property name="visible">True</property>
--                <property name="can_focus">False</property>
--                <child>
--                  <object class="GtkImage" id="infographics">
--                    <property name="visible">True</property>
--                    <property name="can_focus">False</property>
--                    <property name="xpad">12</property>
--                    <property name="ypad">12</property>
--                    <property name="resource">/org/gnome/initial-setup/software_screens.png</property>
--                  </object>
--                  <packing>
--                    <property name="index">-1</property>
--                  </packing>
--                </child>
--                <child type="overlay">
--                  <object class="GtkBox" id="infolabels">
--                    <property name="visible">True</property>
--                    <property name="can_focus">False</property>
--                    <property name="halign">start</property>
--                    <property name="margin_left">48</property>
--                    <property name="margin_top">48</property>
--                    <property name="orientation">vertical</property>
--                    <child>
--                      <object class="GtkLabel" id="label1">
--                        <property name="visible">True</property>
--                        <property name="can_focus">False</property>
--                        <property name="halign">start</property>
--                        <property name="label" translatable="yes">Endless OS</property>
--                        <property name="xalign">0</property>
--                        <attributes>
--                          <attribute name="weight" value="bold"/>
--                          <attribute name="scale" value="2"/>
--                          <attribute name="foreground" value="#ffffffffffff"/>
--                        </attributes>
--                      </object>
--                      <packing>
--                        <property name="expand">False</property>
--                        <property name="fill">True</property>
--                        <property name="position">0</property>
--                      </packing>
--                    </child>
--                    <child>
--                      <object class="GtkLabel" id="label2">
--                        <property name="visible">True</property>
--                        <property name="can_focus">False</property>
--                        <property name="halign">start</property>
--                        <property name="margin_top">16</property>
--                        <property name="label" translatable="yes">Fast, easy to use and powerful – with or without internet.
--
--Endless OS is preloaded with over 100 apps, making it useful from the moment you turn it on.</property>
--                        <property name="wrap">True</property>
--                        <property name="max_width_chars">26</property>
--                        <property name="xalign">0</property>
--                        <attributes>
--                          <attribute name="foreground" value="#ffffffffffff"/>
--                        </attributes>
--                      </object>
--                      <packing>
--                        <property name="expand">False</property>
--                        <property name="fill">True</property>
--                        <property name="position">1</property>
--                      </packing>
--                    </child>
--                  </object>
--                </child>
--              </object>
--              <packing>
--                <property name="expand">False</property>
--                <property name="fill">True</property>
--                <property name="position">2</property>
--              </packing>
--            </child>
-           </object>
-           <packing>
-             <property name="expand">True</property>
--- 
-2.37.1
-
diff --git a/release/installer/app/0004-finished-don-t-run-eos-diagnostics.patch b/release/installer/app/0004-finished-don-t-run-eos-diagnostics.patch
deleted file mode 100644
index cf1b4ce..0000000
--- a/release/installer/app/0004-finished-don-t-run-eos-diagnostics.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From aefbf6a50097486616a7d0d3bc09e4efb0ed953b Mon Sep 17 00:00:00 2001
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-SPDX-License-Identifier: GPL-2.0-or-later
-From: Alyssa Ross <hi@alyssa.is>
-Date: Thu, 9 Dec 2021 21:06:36 +0000
-Subject: [PATCH 4/5] finished: don't run eos-diagnostics
-
-This has to be installed separately from eos-installer, and we don't
-currently do that for the Spectrum installer.  If there was an error,
-eos-installer would try to run it, and then display an additional
-error message about not being able to find it on the error screen.
----
- .../pages/finished/gis-finished-page.c            | 15 ---------------
- 1 file changed, 15 deletions(-)
-
-diff --git a/gnome-image-installer/pages/finished/gis-finished-page.c b/gnome-image-installer/pages/finished/gis-finished-page.c
-index 6cd26ff2..47b60adb 100644
---- a/gnome-image-installer/pages/finished/gis-finished-page.c
-+++ b/gnome-image-installer/pages/finished/gis-finished-page.c
-@@ -285,21 +285,6 @@ gis_finished_page_shown (GisPage *page)
-       gtk_widget_hide (priv->success_box);
-       gis_assistant_locale_changed (assistant);
- 
--      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
--        {
--          /* Running eos-diagnostics can be slow enough that we can't do it
--           * synchronously on the UI thread.
--           */
--          GFile *image_dir = G_FILE (gis_store_get_object (GIS_STORE_IMAGE_DIR));
--          /* See implementation of gis_write_diagnostics_async for rationale. */
--          const gchar *home_dir = priv->gedit != NULL ? g_get_home_dir () : NULL;
--
--          g_application_hold (G_APPLICATION (page->driver));
--          gis_write_diagnostics_async (NULL, image_dir, home_dir,
--                                       NULL, write_diagnostics_cb,
--                                       g_object_ref (self));
--        }
--
-       /* If running within a live session, hide the "Turn off" button on error,
-        * since we have a perfectly good [X] button on the titlebar and we want
-        * to encourage the user to notice the link to the diagnostics file.
--- 
-2.37.1
-
diff --git a/release/installer/app/0005-finished-promote-spectrum-not-the-Endless-forum.patch b/release/installer/app/0005-finished-promote-spectrum-not-the-Endless-forum.patch
deleted file mode 100644
index fc8c27d..0000000
--- a/release/installer/app/0005-finished-promote-spectrum-not-the-Endless-forum.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 786b48e3659db0a184600c5c7047d883b74c82cf Mon Sep 17 00:00:00 2001
-SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-SPDX-License-Identifier: GPL-2.0-or-later
-From: Alyssa Ross <hi@alyssa.is>
-Date: Thu, 9 Dec 2021 21:07:38 +0000
-Subject: [PATCH 5/5] finished: promote #spectrum, not the Endless forum
-
----
- gnome-image-installer/pages/finished/gis-finished-page.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
-diff --git a/gnome-image-installer/pages/finished/gis-finished-page.c b/gnome-image-installer/pages/finished/gis-finished-page.c
-index 47b60adb..2fc1ccf3 100644
---- a/gnome-image-installer/pages/finished/gis-finished-page.c
-+++ b/gnome-image-installer/pages/finished/gis-finished-page.c
-@@ -461,11 +461,9 @@ gis_finished_page_locale_changed (GisPage *page)
-     }
- 
-   support_email = get_customer_support_email ();
--  support_email_markup = g_strdup_printf ("<a href=\"mailto:%1$s\">%1$s</a>",
--                                          support_email);
-   /* Translators: the %s is the customer support email address */
--  support_markup = g_strdup_printf (_("Please contact %s or join the <a href=\"https://community.endlessos.com/\">Endless Community</a> to troubleshoot."),
--                                    support_email_markup);
-+  support_markup = g_strdup_printf (_("Please contact %s or join #spectrum on irc.libera.chat to troubleshoot."),
-+                                    support_email);
-   gtk_label_set_markup (priv->support_label, support_markup);
- }
- 
--- 
-2.37.1
-
diff --git a/release/installer/app/default.nix b/release/installer/app/default.nix
deleted file mode 100644
index f0d739e..0000000
--- a/release/installer/app/default.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
-{ eos-installer, fetchurl }:
-
-let
-  logo = fetchurl {
-    url = "https://spectrum-os.org/git/www/plain/logo/logo140.png?id=5ac2d787b12e05a9ea91e94ca9373ced55d7371a";
-    sha256 = "008dkzapyrkbva3ziyb2fa1annjwfk28q9kwj1bgblgrq6sxllxk";
-  };
-in
-
-eos-installer.overrideAttrs ({ patches ? [], postPatch ? "", ... }: {
-  patches = patches ++ [
-    ./0001-gpt-disable-gpt-partition-attribute-55-check.patch
-    ./0002-gpt-disable-partition-table-CRC-check.patch
-    ./0003-install-remove-Endless-OS-ad.patch
-    ./0004-finished-don-t-run-eos-diagnostics.patch
-    ./0005-finished-promote-spectrum-not-the-Endless-forum.patch
-  ];
-
-  postPatch = postPatch + ''
-    find . -type f -print0 | xargs -0 sed -i 's/Endless OS/Spectrum/g'
-    cp ${logo} gnome-image-installer/pages/finished/endless_logo.png
-  '';
-})
diff --git a/release/installer/app/vendor-customer-support.ini b/release/installer/app/vendor-customer-support.ini
deleted file mode 100644
index 4c4e650..0000000
--- a/release/installer/app/vendor-customer-support.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: CC0-1.0
-# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
-[Customer Support]
-Email = discuss@spectrum-os.org
diff --git a/release/installer/configuration.nix b/release/installer/configuration.nix
deleted file mode 100644
index 3f9ef24..0000000
--- a/release/installer/configuration.nix
+++ /dev/null
@@ -1,69 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021-2023, 2025 Alyssa Ross <hi@alyssa.is>
-
-{ lib, modulesPath, pkgs, ... }:
-
-let
-  inherit (builtins) readFile;
-in
-
-{
-  imports = [ (modulesPath + "/profiles/all-hardware.nix") ];
-
-  boot.consoleLogLevel = lib.mkDefault 2;
-  boot.kernelParams = [ "udev.log_priority=5" ];
-  boot.initrd.verbose = false;
-
-  boot.kernelPackages = pkgs.linuxPackages_latest;
-
-  boot.plymouth.enable = true;
-  boot.plymouth.logo = pkgs.callPackage (
-    { runCommand, fetchurl, inkscape }:
-    runCommand "spectrum-logo.png" {
-      nativeBuildInputs = [ inkscape ];
-      svg = fetchurl {
-        url = "https://spectrum-os.org/git/www/plain/logo/logo_mesh.svg?id=5ac2d787b12e05a9ea91e94ca9373ced55d7371a";
-        sha256 = "1k5025nc5mxdls7bw7wk1734inadvibqxvg8b8yg6arr3y9yy46k";
-      };
-    } ''
-      inkscape -w 48 -h 48 -o "$out" "$svg"
-    ''
-  ) {};
-
-  fileSystems."/" = { fsType = "tmpfs"; };
-
-  services.cage.enable = true;
-  services.cage.program = lib.getExe (pkgs.callPackage ./app {});
-
-  services.udev.extraRules = ''
-    KERNEL=="card0", TAG+="systemd"
-  '';
-
-  systemd.services.cage-tty1.after = [ "dev-dri-card0.device" ];
-  systemd.services.cage-tty1.wants = [ "dev-dri-card0.device" ];
-
-  # Force eos-installer to stop artificially constraining its size.
-  systemd.services.cage-tty1.environment.GIS_SMALL_SCREEN = "1";
-
-  users.users.demo = { group = "demo"; isNormalUser = true; };
-  users.groups.demo = {};
-  security.polkit.extraConfig = readFile ./seat.rules;
-
-  services.udisks2.enable = true;
-
-  documentation.enable = false;
-  networking.firewall.enable = false;
-  networking.resolvconf.enable = false;
-  nix.enable = false;
-  services.timesyncd.enable = false;
-
-  boot.loader.systemd-boot.enable = true;
-
-  environment.systemPackages = with pkgs; [ adwaita-icon-theme ];
-
-  systemd.tmpfiles.rules = [
-    "L+ /var/lib/eos-image-defaults/vendor-customer-support.ini - - - - ${app/vendor-customer-support.ini}"
-  ];
-
-  system.stateVersion = lib.trivial.release;
-}
diff --git a/release/installer/default.nix b/release/installer/default.nix
deleted file mode 100644
index 845eb24..0000000
--- a/release/installer/default.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021-2022 Alyssa Ross <hi@alyssa.is>
-
-import ../../lib/call-package.nix ({ extraConfig, nixos, writeClosure }:
-
-let
-  inherit (nixos {
-    imports = [ ./configuration.nix extraConfig ];
-  }) config;
-in
-
-{
-  kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
-
-  initramfs = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
-
-  kernelParams = toString ([
-    "init=${config.system.build.toplevel}/init"
-  ] ++ config.boot.kernelParams);
-
-  store = writeClosure [ config.system.build.toplevel ];
-}) (_: { extraConfig = {}; })
diff --git a/release/installer/run-vm.nix b/release/installer/run-vm.nix
deleted file mode 100644
index b9907f7..0000000
--- a/release/installer/run-vm.nix
+++ /dev/null
@@ -1,47 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2021-2025 Alyssa Ross <hi@alyssa.is>
-
-import ../../lib/call-package.nix (
-{ callSpectrumPackage, lib, coreutils, qemu_kvm, stdenv, writeShellScript }:
-
-let
-  inherit (builtins) storeDir;
-  inherit (lib) makeBinPath escapeShellArg;
-
-  eosimages = callSpectrumPackage ../combined/eosimages.nix {};
-
-  installer = callSpectrumPackage ./. {
-    extraConfig = {
-      boot.initrd.availableKernelModules = [ "9p" "9pnet_virtio" ];
-
-      fileSystems.${storeDir} = {
-        fsType = "9p";
-        device = "store";
-      };
-    };
-  };
-in
-
-writeShellScript "run-spectrum-installer-vm.sh" ''
-  export PATH=${makeBinPath [ coreutils qemu_kvm ]}
-  img="$(mktemp spectrum-installer-target.XXXXXXXXXX.img)"
-  truncate -s 40G "$img"
-  exec 3<>"$img"
-  rm -f "$img"
-  exec ${../../scripts/run-qemu.sh} -cpu max -m 4G \
-    -device virtio-keyboard \
-    -device virtio-mouse \
-    -device virtio-gpu \
-    -parallel none \
-    -vga none \
-    -virtfs local,mount_tag=store,path=/nix/store,security_model=none,readonly=true \
-    -drive file=${qemu_kvm}/share/qemu/edk2-${stdenv.hostPlatform.qemuArch}-code.fd,format=raw,if=pflash,readonly=true \
-    -drive file=${eosimages},format=raw,if=virtio,readonly=true \
-    -drive file=/proc/self/fd/3,format=raw,if=virtio \
-    -kernel ${installer.kernel} \
-    -initrd ${installer.initramfs} \
-    -append ${escapeShellArg (toString [
-      installer.kernelParams
-      "systemd.journald.forward_to_console"
-    ])}
-'') (_: {})
diff --git a/release/installer/seat.rules b/release/installer/seat.rules
deleted file mode 100644
index 79ff400..0000000
--- a/release/installer/seat.rules
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-License-Identifier: EUPL-1.2+
-// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
-
-polkit.addRule(function (action, subject) {
-    // The user of the graphical session should be able to do anything.
-    if (subject.seat) {
-        return polkit.Result.YES;
-    }
-});

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 4/5] release/live: remove B slot from live image and reduce A slot size
  2026-02-04 17:55 [PATCH v2 0/5] Spectrum-installer Johannes Süllner
                   ` (2 preceding siblings ...)
  2026-02-04 17:55 ` [PATCH v2 3/5] release: drop combined and installer image Johannes Süllner
@ 2026-02-04 17:55 ` Johannes Süllner
  2026-03-15 12:21   ` Alyssa Ross
  2026-02-04 17:55 ` [PATCH v2 5/5] Revert "scripts/make-gpt.sh: allow setting partition size" Johannes Süllner
  4 siblings, 1 reply; 13+ messages in thread
From: Johannes Süllner @ 2026-02-04 17:55 UTC (permalink / raw)
  To: devel; +Cc: Johannes Süllner

In contrast to the old installer, with the new installer the live image
is not copied as a whole to the destination disk any more. Instead,
`systemd-repart` creates partitions with the desired size, and then
copies the individual partitions. So the partitions are effectively
enlarged during the installation, making it possible to shrink them to
the minimum size here.

Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
---
 release/live/Makefile | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/release/live/Makefile b/release/live/Makefile
index f5fe558..2d693f4 100644
--- a/release/live/Makefile
+++ b/release/live/Makefile
@@ -10,10 +10,8 @@ dest = build/live.img
 $(dest): ../../scripts/format-uuid.sh ../../scripts/make-gpt.sh ../../scripts/sfdisk-field.awk build/boot.fat $(ROOT_FS_IMAGES)
 	../../scripts/make-gpt.sh $@.tmp \
 	    build/boot.fat:c12a7328-f81f-11d2-ba4b-00a0c93ec93b \
-	    $(ROOT_FS_VERITY):verity:$$(../../scripts/format-uuid.sh "$$(dd if=$(ROOT_FS_VERITY_ROOTHASH) bs=32 skip=1 count=1 status=none)"):Spectrum_'$(VERSION).verity:162' \
-	    $(ROOT_FS_IMAGE):root:$$(../../scripts/format-uuid.sh "$$(head -c 32 $(ROOT_FS_VERITY_ROOTHASH))"):Spectrum_'$(VERSION):20000' \
-	    /dev/null:verity:18f2ccff-92f1-4bb1-a80e-24f76ecda90c:_empty:162 \
-	    /dev/null:root:ec0c5ff3-f6b1-4adf-82b4-61336c4d135f:_empty:20000
+	    $(ROOT_FS_VERITY):verity:$$(../../scripts/format-uuid.sh "$$(dd if=$(ROOT_FS_VERITY_ROOTHASH) bs=32 skip=1 count=1 status=none)"):Spectrum_'$(VERSION).verity' \
+	    $(ROOT_FS_IMAGE):root:$$(../../scripts/format-uuid.sh "$$(head -c 32 $(ROOT_FS_VERITY_ROOTHASH))"):Spectrum_'$(VERSION)'
 	mv $@.tmp $@
 
 build/boot.fat: $(SYSTEMD_BOOT_EFI) $(SPECTRUM_EFI)

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 5/5] Revert "scripts/make-gpt.sh: allow setting partition size"
  2026-02-04 17:55 [PATCH v2 0/5] Spectrum-installer Johannes Süllner
                   ` (3 preceding siblings ...)
  2026-02-04 17:55 ` [PATCH v2 4/5] release/live: remove B slot from live image and reduce A slot size Johannes Süllner
@ 2026-02-04 17:55 ` Johannes Süllner
  2026-03-15 12:23   ` Alyssa Ross
  4 siblings, 1 reply; 13+ messages in thread
From: Johannes Süllner @ 2026-02-04 17:55 UTC (permalink / raw)
  To: devel; +Cc: Johannes Süllner

This reverts commit d4832d18f7c938782efd88185b31180d7475b575.

The only place where partition sizes were set was for the live image,
and this was removed in the parent commit.

Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
---
 scripts/make-gpt.sh      | 31 ++++++++++++-------------------
 scripts/sfdisk-field.awk | 22 +++++-----------------
 2 files changed, 17 insertions(+), 36 deletions(-)

diff --git a/scripts/make-gpt.sh b/scripts/make-gpt.sh
index 998a54d..e39828b 100755
--- a/scripts/make-gpt.sh
+++ b/scripts/make-gpt.sh
@@ -1,10 +1,10 @@
 #!/bin/sh --
 #
-# SPDX-FileCopyrightText: 2021-2023, 2025 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2021-2023 Alyssa Ross <hi@alyssa.is>
 # SPDX-FileCopyrightText: 2022 Unikie
 # SPDX-License-Identifier: EUPL-1.2+
 #
-# usage: make-gpt.sh GPT_PATH PATH:PARTTYPE[:PARTUUID[:PARTLABEL[:PARTMiB]]]...
+# usage: make-gpt.sh GPT_PATH PATH:PARTTYPE[:PARTUUID[:PARTLABEL]]...
 
 set -euo pipefail
 
@@ -35,28 +35,21 @@ scriptsDir="$(dirname "$0")"
 out="$1"
 shift
 
-table=$(for partition; do
-	size="$(sizeMiB "${partition%%:*}")"
-	awk -f "$scriptsDir/sfdisk-field.awk" \
-		-v partition="$partition" \
-		-v size="$size"
-done)
+nl='
+'
+table="label: gpt"
 
 # Keep 1MiB free at the start, and 1MiB free at the end.
-gptMiB=2
-while read -r partition; do
-	# Here we rely on sfdisk-field.awk always putting size last.
-	: $((gptMiB += ${partition##*=}))
-done <<EOF
-$table
-EOF
+gptBytes=$((ONE_MiB * 2))
+for partition; do
+	sizeMiB="$(sizeMiB "${partition%%:*}")"
+	table="$table${nl}size=${sizeMiB}MiB,$(awk -f "$scriptsDir/sfdisk-field.awk" -v partition="$partition")"
+	gptBytes="$((gptBytes + sizeMiB * ONE_MiB))"
+done
 
 rm -f "$out"
-truncate -s "${gptMiB}M" "$out"
-
+truncate -s "$gptBytes" "$out"
 sfdisk --no-reread --no-tell-kernel "$out" <<EOF
-label: gpt
-sector-size: $ONE_MiB
 $table
 EOF
 
diff --git a/scripts/sfdisk-field.awk b/scripts/sfdisk-field.awk
index 78b438e..e13c86d 100644
--- a/scripts/sfdisk-field.awk
+++ b/scripts/sfdisk-field.awk
@@ -1,7 +1,7 @@
 #!/usr/bin/awk -f
 #
 # SPDX-License-Identifier: EUPL-1.2+
-# SPDX-FileCopyrightText: 2022, 2024-2025 Alyssa Ross <hi@alyssa.is>
+# SPDX-FileCopyrightText: 2022, 2024 Alyssa Ross <hi@alyssa.is>
 
 BEGIN {
 	types["root.aarch64"] = "b921b045-1df0-41c3-af44-4c6f280d3fae"
@@ -9,11 +9,12 @@ BEGIN {
 	types["verity.aarch64"] = "df3300ce-d69f-4c92-978c-9bfb0f38d820"
 	types["verity.x86_64"] = "2c7357ed-ebd2-46d9-aec1-23d437ec2bf5"
 
-	# Field #1 is the partition path, which is read by make-gpt.sh
-	# but not relevant for running sfdisk, so skip it.
+	# Field #1 is the partition path, which make-gpt.sh will turn into
+	# the size field.  Since it's handled elsewhere, we skip that
+	# first field.
 	skip=1
 
-	split("type uuid name size", keys)
+	split("type uuid name", keys)
 	split(partition, fields, ":")
 
 	arch = ENVIRON["ARCH"]
@@ -30,21 +31,8 @@ BEGIN {
 		if (keys[n - skip] == "type") {
 			if (uuid = types[fields[n] "." arch])
 				fields[n] = uuid
-		} else if (keys[n - skip] == "size") {
-			if (fields[n] < size) {
-				printf "%s MiB partition content is too big for %s MiB partition\n",
-					size, fields[n] > "/dev/stderr"
-				exit 1
-			}
-
-			size = fields[n]
-			continue # Handled at the end.
 		}
 
 		printf "%s=%s,", keys[n - skip], fields[n]
 	}
-
-	# Always output a size field, either supplied in input or
-	# default value of the size variable.
-	printf "size=%s\n", size
 }

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/5] host/rootfs: integrate spectrum-installer
  2026-02-04 17:55 ` [PATCH v2 2/5] host/rootfs: integrate spectrum-installer Johannes Süllner
@ 2026-02-05  4:17   ` Demi Marie Obenour
  2026-02-05  8:56     ` Johannes Süllner
  2026-03-15 11:56     ` Alyssa Ross
  2026-03-15 12:15   ` Alyssa Ross
  1 sibling, 2 replies; 13+ messages in thread
From: Demi Marie Obenour @ 2026-02-05  4:17 UTC (permalink / raw)
  To: Johannes Süllner, devel


[-- Attachment #1.1.1: Type: text/plain, Size: 395 bytes --]

On 2/4/26 12:55, Johannes Süllner wrote:
> The installer needs to run as root, so that `systemd-repart` can write
> to disks. Since Weston is not running as root (since b26f59e), just as
> with the root terminal, we add a s6-sudod service for the installer.

Could this be conditional to the live image?  Installed images don't
need it.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)

[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/5] host/rootfs: integrate spectrum-installer
  2026-02-05  4:17   ` Demi Marie Obenour
@ 2026-02-05  8:56     ` Johannes Süllner
  2026-03-15 11:56     ` Alyssa Ross
  1 sibling, 0 replies; 13+ messages in thread
From: Johannes Süllner @ 2026-02-05  8:56 UTC (permalink / raw)
  To: Demi Marie Obenour, Johannes Süllner, devel

On Thu Feb 5, 2026 at 5:17 AM CET, Demi Marie Obenour wrote:
> On 2/4/26 12:55, Johannes Süllner wrote:
>> The installer needs to run as root, so that `systemd-repart` can write
>> to disks. Since Weston is not running as root (since b26f59e), just as
>> with the root terminal, we add a s6-sudod service for the installer.
>
> Could this be conditional to the live image?  Installed images don't
> need it.

I think currently, the only difference between a live system and an
installed system is the presence of the B slot partitions. So pretty
much the only solution I can think of is adding an overlay filesystem
to the live image and moving the installer into that.
I welcome other ideas of course.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/5] host/rootfs: integrate spectrum-installer
  2026-02-05  4:17   ` Demi Marie Obenour
  2026-02-05  8:56     ` Johannes Süllner
@ 2026-03-15 11:56     ` Alyssa Ross
  1 sibling, 0 replies; 13+ messages in thread
From: Alyssa Ross @ 2026-03-15 11:56 UTC (permalink / raw)
  To: Demi Marie Obenour, Johannes Süllner, devel

[-- Attachment #1: Type: text/plain, Size: 672 bytes --]

Demi Marie Obenour <demiobenour@gmail.com> writes:

> On 2/4/26 12:55, Johannes Süllner wrote:
>> The installer needs to run as root, so that `systemd-repart` can write
>> to disks. Since Weston is not running as root (since b26f59e), just as
>> with the root terminal, we add a s6-sudod service for the installer.
>
> Could this be conditional to the live image?  Installed images don't
> need it.

My hope would be to not have a distinction in the end between live and
installed images.  If you're worried about attack surface we could have
some switch to disable it before boot, as far out as possible like in
the kernel command line or a UEFI variable.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 2/5] host/rootfs: integrate spectrum-installer
  2026-02-04 17:55 ` [PATCH v2 2/5] host/rootfs: integrate spectrum-installer Johannes Süllner
  2026-02-05  4:17   ` Demi Marie Obenour
@ 2026-03-15 12:15   ` Alyssa Ross
  1 sibling, 0 replies; 13+ messages in thread
From: Alyssa Ross @ 2026-03-15 12:15 UTC (permalink / raw)
  To: Johannes Süllner; +Cc: devel

[-- Attachment #1: Type: text/plain, Size: 413 bytes --]

Johannes Süllner <johannes.suellner@mailbox.org> writes:

> The installer needs to run as root, so that `systemd-repart` can write
> to disks. Since Weston is not running as root (since b26f59e), just as
> with the root terminal, we add a s6-sudod service for the installer.
>
> Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>

Is the "disk" group not enough?  We have to be full root?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 3/5] release: drop combined and installer image
  2026-02-04 17:55 ` [PATCH v2 3/5] release: drop combined and installer image Johannes Süllner
@ 2026-03-15 12:20   ` Alyssa Ross
  0 siblings, 0 replies; 13+ messages in thread
From: Alyssa Ross @ 2026-03-15 12:20 UTC (permalink / raw)
  To: Johannes Süllner; +Cc: devel

[-- Attachment #1: Type: text/plain, Size: 2540 bytes --]

Johannes Süllner <johannes.suellner@mailbox.org> writes:

> Installer is now part of the live system.
>
> Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
> ---
>  Documentation/development/uuid-reference.adoc |  22 ----
>  release/checks/default.nix                    |   2 -
>  release/checks/installer.nix                  |  16 ---
>  release/combined/default.nix                  | 123 ------------------
>  release/combined/eosimages.nix                |  21 ---
>  release/combined/grub.cfg.in                  |  22 ----
>  release/combined/run-vm.nix                   |  31 -----
>  ...ble-gpt-partition-attribute-55-check.patch |  37 ------
>  ...pt-disable-partition-table-CRC-check.patch |  36 -----
>  .../0003-install-remove-Endless-OS-ad.patch   |  99 --------------
>  ...4-finished-don-t-run-eos-diagnostics.patch |  44 -------
>  ...omote-spectrum-not-the-Endless-forum.patch |  32 -----
>  release/installer/app/default.nix             |  26 ----
>  .../installer/app/vendor-customer-support.ini |   5 -
>  release/installer/configuration.nix           |  69 ----------
>  release/installer/default.nix                 |  22 ----
>  release/installer/run-vm.nix                  |  47 -------
>  release/installer/seat.rules                  |   9 --
>  18 files changed, 663 deletions(-)
>  delete mode 100644 release/checks/installer.nix
>  delete mode 100644 release/combined/default.nix
>  delete mode 100644 release/combined/eosimages.nix
>  delete mode 100644 release/combined/grub.cfg.in
>  delete mode 100644 release/combined/run-vm.nix
>  delete mode 100644 release/installer/app/0001-gpt-disable-gpt-partition-attribute-55-check.patch
>  delete mode 100644 release/installer/app/0002-gpt-disable-partition-table-CRC-check.patch
>  delete mode 100644 release/installer/app/0003-install-remove-Endless-OS-ad.patch
>  delete mode 100644 release/installer/app/0004-finished-don-t-run-eos-diagnostics.patch
>  delete mode 100644 release/installer/app/0005-finished-promote-spectrum-not-the-Endless-forum.patch
>  delete mode 100644 release/installer/app/default.nix
>  delete mode 100644 release/installer/app/vendor-customer-support.ini
>  delete mode 100644 release/installer/configuration.nix
>  delete mode 100644 release/installer/default.nix
>  delete mode 100644 release/installer/run-vm.nix
>  delete mode 100644 release/installer/seat.rules

release/combined needs to be dropped from release.nix.  release/live
would be a sensible replacement.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 4/5] release/live: remove B slot from live image and reduce A slot size
  2026-02-04 17:55 ` [PATCH v2 4/5] release/live: remove B slot from live image and reduce A slot size Johannes Süllner
@ 2026-03-15 12:21   ` Alyssa Ross
  0 siblings, 0 replies; 13+ messages in thread
From: Alyssa Ross @ 2026-03-15 12:21 UTC (permalink / raw)
  To: Johannes Süllner; +Cc: devel

[-- Attachment #1: Type: text/plain, Size: 563 bytes --]

Johannes Süllner <johannes.suellner@mailbox.org> writes:

> In contrast to the old installer, with the new installer the live image
> is not copied as a whole to the destination disk any more. Instead,
> `systemd-repart` creates partitions with the desired size, and then
> copies the individual partitions. So the partitions are effectively
> enlarged during the installation, making it possible to shrink them to
> the minimum size here.
>
> Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>

Reviewed-by: Alyssa Ross <hi@alyssa.is>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 5/5] Revert "scripts/make-gpt.sh: allow setting partition size"
  2026-02-04 17:55 ` [PATCH v2 5/5] Revert "scripts/make-gpt.sh: allow setting partition size" Johannes Süllner
@ 2026-03-15 12:23   ` Alyssa Ross
  0 siblings, 0 replies; 13+ messages in thread
From: Alyssa Ross @ 2026-03-15 12:23 UTC (permalink / raw)
  To: Johannes Süllner; +Cc: devel

[-- Attachment #1: Type: text/plain, Size: 547 bytes --]

Johannes Süllner <johannes.suellner@mailbox.org> writes:

> This reverts commit d4832d18f7c938782efd88185b31180d7475b575.
>
> The only place where partition sizes were set was for the live image,
> and this was removed in the parent commit.
>
> Signed-off-by: Johannes Süllner <johannes.suellner@mailbox.org>
> ---
>  scripts/make-gpt.sh      | 31 ++++++++++++-------------------
>  scripts/sfdisk-field.awk | 22 +++++-----------------
>  2 files changed, 17 insertions(+), 36 deletions(-)

Reviewed-by: Alyssa Ross <hi@alyssa.is>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-03-15 12:23 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-04 17:55 [PATCH v2 0/5] Spectrum-installer Johannes Süllner
2026-02-04 17:55 ` [PATCH v2 1/5] tools: add spectrum-installer Johannes Süllner
2026-02-04 17:55 ` [PATCH v2 2/5] host/rootfs: integrate spectrum-installer Johannes Süllner
2026-02-05  4:17   ` Demi Marie Obenour
2026-02-05  8:56     ` Johannes Süllner
2026-03-15 11:56     ` Alyssa Ross
2026-03-15 12:15   ` Alyssa Ross
2026-02-04 17:55 ` [PATCH v2 3/5] release: drop combined and installer image Johannes Süllner
2026-03-15 12:20   ` Alyssa Ross
2026-02-04 17:55 ` [PATCH v2 4/5] release/live: remove B slot from live image and reduce A slot size Johannes Süllner
2026-03-15 12:21   ` Alyssa Ross
2026-02-04 17:55 ` [PATCH v2 5/5] Revert "scripts/make-gpt.sh: allow setting partition size" Johannes Süllner
2026-03-15 12:23   ` Alyssa Ross

Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/crosvm
	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/nixpkgs
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock
	https://spectrum-os.org/git/www

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).