Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
1e8c1355ad chore(deps): bump actions/github-script from 8 to 9
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 15:59:35 +00:00
19 changed files with 1102 additions and 1935 deletions

View File

@@ -12,7 +12,7 @@ jobs:
steps: steps:
- name: Check and close feature issues - name: Check and close feature issues
uses: actions/github-script@v8 uses: actions/github-script@v9
with: with:
script: | script: |
const issue = context.payload.issue; const issue = context.payload.issue;

986
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@
"anstyle-query 1.1.5 registry+https://github.com/rust-lang/crates.io-index", "anstyle-query 1.1.5 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-wincon 3.0.11 registry+https://github.com/rust-lang/crates.io-index", "anstyle-wincon 3.0.11 registry+https://github.com/rust-lang/crates.io-index",
"anyhow 1.0.102 registry+https://github.com/rust-lang/crates.io-index", "anyhow 1.0.102 registry+https://github.com/rust-lang/crates.io-index",
"approx 0.5.1 registry+https://github.com/rust-lang/crates.io-index", "approx 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
"arboard 3.6.1 registry+https://github.com/rust-lang/crates.io-index", "arboard 3.6.1 registry+https://github.com/rust-lang/crates.io-index",
"arrayvec 0.7.6 registry+https://github.com/rust-lang/crates.io-index", "arrayvec 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
"as-slice 0.2.1 registry+https://github.com/rust-lang/crates.io-index", "as-slice 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
@@ -41,27 +41,25 @@
"beef 0.5.2 registry+https://github.com/rust-lang/crates.io-index", "beef 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
"bit_field 0.10.3 registry+https://github.com/rust-lang/crates.io-index", "bit_field 0.10.3 registry+https://github.com/rust-lang/crates.io-index",
"bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index", "bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index",
"bitflags 2.11.1 registry+https://github.com/rust-lang/crates.io-index", "bitflags 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
"bitstream-io 4.10.0 registry+https://github.com/rust-lang/crates.io-index", "bitstream-io 4.9.0 registry+https://github.com/rust-lang/crates.io-index",
"block-buffer 0.10.4 registry+https://github.com/rust-lang/crates.io-index", "block-buffer 0.10.4 registry+https://github.com/rust-lang/crates.io-index",
"by_address 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.25.0 registry+https://github.com/rust-lang/crates.io-index", "bytemuck 1.25.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index", "bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index",
"cc 1.2.61 registry+https://github.com/rust-lang/crates.io-index", "cc 1.2.57 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index", "cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 1.0.4 registry+https://github.com/rust-lang/crates.io-index", "cfg-if 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
"chacha20 0.10.0 registry+https://github.com/rust-lang/crates.io-index",
"chrono 0.4.44 registry+https://github.com/rust-lang/crates.io-index", "chrono 0.4.44 registry+https://github.com/rust-lang/crates.io-index",
"chrono-tz 0.10.4 registry+https://github.com/rust-lang/crates.io-index", "chrono-tz 0.10.4 registry+https://github.com/rust-lang/crates.io-index",
"clap 4.6.1 registry+https://github.com/rust-lang/crates.io-index", "clap 4.6.0 registry+https://github.com/rust-lang/crates.io-index",
"clap_builder 4.6.0 registry+https://github.com/rust-lang/crates.io-index", "clap_builder 4.6.0 registry+https://github.com/rust-lang/crates.io-index",
"clap_derive 4.6.1 registry+https://github.com/rust-lang/crates.io-index", "clap_derive 4.6.0 registry+https://github.com/rust-lang/crates.io-index",
"clap_lex 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "clap_lex 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"color-eyre 0.6.5 registry+https://github.com/rust-lang/crates.io-index", "color-eyre 0.6.5 registry+https://github.com/rust-lang/crates.io-index",
"color-spantrace 0.3.0 registry+https://github.com/rust-lang/crates.io-index", "color-spantrace 0.3.0 registry+https://github.com/rust-lang/crates.io-index",
"colorchoice 1.0.5 registry+https://github.com/rust-lang/crates.io-index", "colorchoice 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
"core2 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
"cpufeatures 0.2.17 registry+https://github.com/rust-lang/crates.io-index", "cpufeatures 0.2.17 registry+https://github.com/rust-lang/crates.io-index",
"cpufeatures 0.3.0 registry+https://github.com/rust-lang/crates.io-index",
"crc32fast 1.5.0 registry+https://github.com/rust-lang/crates.io-index", "crc32fast 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"crossbeam-channel 0.5.15 registry+https://github.com/rust-lang/crates.io-index", "crossbeam-channel 0.5.15 registry+https://github.com/rust-lang/crates.io-index",
"crossbeam-deque 0.8.6 registry+https://github.com/rust-lang/crates.io-index", "crossbeam-deque 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
@@ -101,8 +99,7 @@
"epaint_default_fonts 0.33.3 registry+https://github.com/rust-lang/crates.io-index", "epaint_default_fonts 0.33.3 registry+https://github.com/rust-lang/crates.io-index",
"equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index", "equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index", "eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index",
"fast-srgb8 1.0.0 registry+https://github.com/rust-lang/crates.io-index", "fastrand 2.3.0 registry+https://github.com/rust-lang/crates.io-index",
"fastrand 2.4.1 registry+https://github.com/rust-lang/crates.io-index",
"fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index", "fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
"filetime 0.2.27 registry+https://github.com/rust-lang/crates.io-index", "filetime 0.2.27 registry+https://github.com/rust-lang/crates.io-index",
"find-msvc-tools 0.1.9 registry+https://github.com/rust-lang/crates.io-index", "find-msvc-tools 0.1.9 registry+https://github.com/rust-lang/crates.io-index",
@@ -118,11 +115,12 @@
"futures-sink 0.3.32 registry+https://github.com/rust-lang/crates.io-index", "futures-sink 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"futures-task 0.3.32 registry+https://github.com/rust-lang/crates.io-index", "futures-task 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"futures-util 0.3.32 registry+https://github.com/rust-lang/crates.io-index", "futures-util 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.1.16 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.2.17 registry+https://github.com/rust-lang/crates.io-index", "getrandom 0.2.17 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.3.4 registry+https://github.com/rust-lang/crates.io-index", "getrandom 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.4.2 registry+https://github.com/rust-lang/crates.io-index", "getrandom 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index", "gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index",
"gif 0.14.2 registry+https://github.com/rust-lang/crates.io-index", "gif 0.14.1 registry+https://github.com/rust-lang/crates.io-index",
"git2 0.20.4 registry+https://github.com/rust-lang/crates.io-index", "git2 0.20.4 registry+https://github.com/rust-lang/crates.io-index",
"gl_generator 0.14.0 registry+https://github.com/rust-lang/crates.io-index", "gl_generator 0.14.0 registry+https://github.com/rust-lang/crates.io-index",
"glob 0.3.3 registry+https://github.com/rust-lang/crates.io-index", "glob 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
@@ -134,7 +132,7 @@
"hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.15.5 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.15.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.17.0 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.16.1 registry+https://github.com/rust-lang/crates.io-index",
"heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index", "heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index", "hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index", "hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -145,15 +143,15 @@
"iana-time-zone 0.1.65 registry+https://github.com/rust-lang/crates.io-index", "iana-time-zone 0.1.65 registry+https://github.com/rust-lang/crates.io-index",
"ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index", "ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
"idna 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "idna 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"idna_adapter 1.2.2 registry+https://github.com/rust-lang/crates.io-index", "idna_adapter 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"image 0.25.10 registry+https://github.com/rust-lang/crates.io-index", "image 0.25.10 registry+https://github.com/rust-lang/crates.io-index",
"image-webp 0.2.4 registry+https://github.com/rust-lang/crates.io-index", "image-webp 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"imgref 1.12.1 registry+https://github.com/rust-lang/crates.io-index", "imgref 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"indenter 0.3.4 registry+https://github.com/rust-lang/crates.io-index", "indenter 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index", "indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 2.14.0 registry+https://github.com/rust-lang/crates.io-index", "indexmap 2.13.0 registry+https://github.com/rust-lang/crates.io-index",
"ipnet 2.12.0 registry+https://github.com/rust-lang/crates.io-index", "ipnet 2.12.0 registry+https://github.com/rust-lang/crates.io-index",
"iri-string 0.7.12 registry+https://github.com/rust-lang/crates.io-index", "iri-string 0.7.10 registry+https://github.com/rust-lang/crates.io-index",
"is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"is_terminal_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index", "is_terminal_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index",
"itertools 0.14.0 registry+https://github.com/rust-lang/crates.io-index", "itertools 0.14.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -162,9 +160,9 @@
"jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index", "jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
"khronos_api 3.1.0 registry+https://github.com/rust-lang/crates.io-index", "khronos_api 3.1.0 registry+https://github.com/rust-lang/crates.io-index",
"lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index", "lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"libc 0.2.186 registry+https://github.com/rust-lang/crates.io-index", "libc 0.2.183 registry+https://github.com/rust-lang/crates.io-index",
"libgit2-sys 0.18.3+1.9.2 registry+https://github.com/rust-lang/crates.io-index", "libgit2-sys 0.18.3+1.9.2 registry+https://github.com/rust-lang/crates.io-index",
"libz-sys 1.1.28 registry+https://github.com/rust-lang/crates.io-index", "libz-sys 1.1.25 registry+https://github.com/rust-lang/crates.io-index",
"linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index", "linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index",
"litrs 1.0.0 registry+https://github.com/rust-lang/crates.io-index", "litrs 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
"lock_api 0.4.14 registry+https://github.com/rust-lang/crates.io-index", "lock_api 0.4.14 registry+https://github.com/rust-lang/crates.io-index",
@@ -182,13 +180,12 @@
"moxcms 0.8.1 registry+https://github.com/rust-lang/crates.io-index", "moxcms 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"native-tls 0.2.18 registry+https://github.com/rust-lang/crates.io-index", "native-tls 0.2.18 registry+https://github.com/rust-lang/crates.io-index",
"net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index", "net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index",
"no_std_io2 0.9.3 registry+https://github.com/rust-lang/crates.io-index",
"nohash-hasher 0.2.0 registry+https://github.com/rust-lang/crates.io-index", "nohash-hasher 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"ntapi 0.4.3 registry+https://github.com/rust-lang/crates.io-index", "ntapi 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"num 0.4.3 registry+https://github.com/rust-lang/crates.io-index", "num 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"num-bigint 0.4.6 registry+https://github.com/rust-lang/crates.io-index", "num-bigint 0.4.6 registry+https://github.com/rust-lang/crates.io-index",
"num-complex 0.4.6 registry+https://github.com/rust-lang/crates.io-index", "num-complex 0.4.6 registry+https://github.com/rust-lang/crates.io-index",
"num-conv 0.2.1 registry+https://github.com/rust-lang/crates.io-index", "num-conv 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"num-derive 0.4.2 registry+https://github.com/rust-lang/crates.io-index", "num-derive 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"num-integer 0.1.46 registry+https://github.com/rust-lang/crates.io-index", "num-integer 0.1.46 registry+https://github.com/rust-lang/crates.io-index",
"num-iter 0.1.45 registry+https://github.com/rust-lang/crates.io-index", "num-iter 0.1.45 registry+https://github.com/rust-lang/crates.io-index",
@@ -199,15 +196,16 @@
"once_cell 1.21.4 registry+https://github.com/rust-lang/crates.io-index", "once_cell 1.21.4 registry+https://github.com/rust-lang/crates.io-index",
"once_cell_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index", "once_cell_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index",
"owned_ttf_parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index", "owned_ttf_parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
"palette 0.7.6 registry+https://github.com/rust-lang/crates.io-index", "palette 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"palette_derive 0.7.6 registry+https://github.com/rust-lang/crates.io-index", "palette_derive 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot 0.12.5 registry+https://github.com/rust-lang/crates.io-index", "parking_lot 0.12.5 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot_core 0.9.12 registry+https://github.com/rust-lang/crates.io-index", "parking_lot_core 0.9.12 registry+https://github.com/rust-lang/crates.io-index",
"paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index", "paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
"pastey 0.1.1 registry+https://github.com/rust-lang/crates.io-index", "pastey 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"percent-encoding 2.3.2 registry+https://github.com/rust-lang/crates.io-index", "percent-encoding 2.3.2 registry+https://github.com/rust-lang/crates.io-index",
"pin-project-lite 0.2.17 registry+https://github.com/rust-lang/crates.io-index", "pin-project-lite 0.2.17 registry+https://github.com/rust-lang/crates.io-index",
"pkg-config 0.3.33 registry+https://github.com/rust-lang/crates.io-index", "pin-utils 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"pkg-config 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"png 0.16.8 registry+https://github.com/rust-lang/crates.io-index", "png 0.16.8 registry+https://github.com/rust-lang/crates.io-index",
"png 0.18.1 registry+https://github.com/rust-lang/crates.io-index", "png 0.18.1 registry+https://github.com/rust-lang/crates.io-index",
"powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index", "powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -215,21 +213,23 @@
"proc-macro2 1.0.106 registry+https://github.com/rust-lang/crates.io-index", "proc-macro2 1.0.106 registry+https://github.com/rust-lang/crates.io-index",
"profiling 1.0.17 registry+https://github.com/rust-lang/crates.io-index", "profiling 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"profiling-procmacros 1.0.17 registry+https://github.com/rust-lang/crates.io-index", "profiling-procmacros 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"psm 0.1.31 registry+https://github.com/rust-lang/crates.io-index", "psm 0.1.30 registry+https://github.com/rust-lang/crates.io-index",
"pxfm 0.1.29 registry+https://github.com/rust-lang/crates.io-index", "pxfm 0.1.28 registry+https://github.com/rust-lang/crates.io-index",
"qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index", "qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index", "quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"quote 1.0.45 registry+https://github.com/rust-lang/crates.io-index", "quote 1.0.45 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.10.1 registry+https://github.com/rust-lang/crates.io-index", "rand 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.8.6 registry+https://github.com/rust-lang/crates.io-index", "rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.9.4 registry+https://github.com/rust-lang/crates.io-index", "rand 0.9.2 registry+https://github.com/rust-lang/crates.io-index",
"rand_chacha 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"rand_chacha 0.3.1 registry+https://github.com/rust-lang/crates.io-index", "rand_chacha 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"rand_chacha 0.9.0 registry+https://github.com/rust-lang/crates.io-index", "rand_chacha 0.9.0 registry+https://github.com/rust-lang/crates.io-index",
"rand_core 0.10.1 registry+https://github.com/rust-lang/crates.io-index", "rand_core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"rand_core 0.6.4 registry+https://github.com/rust-lang/crates.io-index", "rand_core 0.6.4 registry+https://github.com/rust-lang/crates.io-index",
"rand_core 0.9.5 registry+https://github.com/rust-lang/crates.io-index", "rand_core 0.9.5 registry+https://github.com/rust-lang/crates.io-index",
"rand_pcg 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index", "raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
"rayon 1.12.0 registry+https://github.com/rust-lang/crates.io-index", "rayon 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
"rayon-core 1.13.0 registry+https://github.com/rust-lang/crates.io-index", "rayon-core 1.13.0 registry+https://github.com/rust-lang/crates.io-index",
"ref-cast 1.0.25 registry+https://github.com/rust-lang/crates.io-index", "ref-cast 1.0.25 registry+https://github.com/rust-lang/crates.io-index",
"ref-cast-impl 1.0.25 registry+https://github.com/rust-lang/crates.io-index", "ref-cast-impl 1.0.25 registry+https://github.com/rust-lang/crates.io-index",
@@ -240,10 +240,10 @@
"roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index", "roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
"rustc-demangle 0.1.27 registry+https://github.com/rust-lang/crates.io-index", "rustc-demangle 0.1.27 registry+https://github.com/rust-lang/crates.io-index",
"rustc_version 0.4.1 registry+https://github.com/rust-lang/crates.io-index", "rustc_version 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"rustls-pki-types 1.14.1 registry+https://github.com/rust-lang/crates.io-index", "rustls-pki-types 1.14.0 registry+https://github.com/rust-lang/crates.io-index",
"ryu 1.0.23 registry+https://github.com/rust-lang/crates.io-index", "ryu 1.0.23 registry+https://github.com/rust-lang/crates.io-index",
"scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index", "scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"semver 1.0.28 registry+https://github.com/rust-lang/crates.io-index", "semver 1.0.27 registry+https://github.com/rust-lang/crates.io-index",
"serde 1.0.228 registry+https://github.com/rust-lang/crates.io-index", "serde 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_core 1.0.228 registry+https://github.com/rust-lang/crates.io-index", "serde_core 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive 1.0.228 registry+https://github.com/rust-lang/crates.io-index", "serde_derive 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
@@ -262,22 +262,22 @@
"shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index", "shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
"shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index", "shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
"signature 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "signature 2.2.0 registry+https://github.com/rust-lang/crates.io-index",
"siphasher 0.3.11 registry+https://github.com/rust-lang/crates.io-index",
"siphasher 1.0.2 registry+https://github.com/rust-lang/crates.io-index", "siphasher 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"smallvec 1.15.1 registry+https://github.com/rust-lang/crates.io-index", "smallvec 1.15.1 registry+https://github.com/rust-lang/crates.io-index",
"smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"socket2 0.6.3 registry+https://github.com/rust-lang/crates.io-index", "socket2 0.6.3 registry+https://github.com/rust-lang/crates.io-index",
"stable_deref_trait 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "stable_deref_trait 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"stacker 0.1.24 registry+https://github.com/rust-lang/crates.io-index", "stacker 0.1.23 registry+https://github.com/rust-lang/crates.io-index",
"static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"supports-color 3.0.2 registry+https://github.com/rust-lang/crates.io-index", "supports-color 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
"supports-hyperlinks 3.2.0 registry+https://github.com/rust-lang/crates.io-index", "supports-hyperlinks 3.2.0 registry+https://github.com/rust-lang/crates.io-index",
"supports-unicode 3.0.0 registry+https://github.com/rust-lang/crates.io-index", "supports-unicode 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
"symlink 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index", "syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index",
"syn 2.0.117 registry+https://github.com/rust-lang/crates.io-index", "syn 2.0.117 registry+https://github.com/rust-lang/crates.io-index",
"sync_wrapper 1.0.2 registry+https://github.com/rust-lang/crates.io-index", "sync_wrapper 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"tempfile 3.27.0 registry+https://github.com/rust-lang/crates.io-index", "tempfile 3.27.0 registry+https://github.com/rust-lang/crates.io-index",
"terminal_size 0.4.4 registry+https://github.com/rust-lang/crates.io-index", "terminal_size 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 2.0.18 registry+https://github.com/rust-lang/crates.io-index", "thiserror 2.0.18 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 2.0.18 registry+https://github.com/rust-lang/crates.io-index", "thiserror-impl 2.0.18 registry+https://github.com/rust-lang/crates.io-index",
"thread_local 1.1.9 registry+https://github.com/rust-lang/crates.io-index", "thread_local 1.1.9 registry+https://github.com/rust-lang/crates.io-index",
@@ -285,14 +285,14 @@
"time-core 0.1.8 registry+https://github.com/rust-lang/crates.io-index", "time-core 0.1.8 registry+https://github.com/rust-lang/crates.io-index",
"toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index", "toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index",
"ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index", "ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
"typenum 1.20.0 registry+https://github.com/rust-lang/crates.io-index", "typenum 1.19.0 registry+https://github.com/rust-lang/crates.io-index",
"tz-rs 0.7.3 registry+https://github.com/rust-lang/crates.io-index", "tz-rs 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"tzdb 0.7.3 registry+https://github.com/rust-lang/crates.io-index", "tzdb 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"tzdb_data 0.2.4 registry+https://github.com/rust-lang/crates.io-index", "tzdb_data 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"unicase 2.9.0 registry+https://github.com/rust-lang/crates.io-index", "unicase 2.9.0 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.24 registry+https://github.com/rust-lang/crates.io-index", "unicode-ident 1.0.24 registry+https://github.com/rust-lang/crates.io-index",
"unicode-linebreak 0.1.5 registry+https://github.com/rust-lang/crates.io-index", "unicode-linebreak 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
"unicode-segmentation 1.13.2 registry+https://github.com/rust-lang/crates.io-index", "unicode-segmentation 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.1.14 registry+https://github.com/rust-lang/crates.io-index", "unicode-width 0.1.14 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "unicode-width 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"unicode-xid 0.2.6 registry+https://github.com/rust-lang/crates.io-index", "unicode-xid 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
@@ -300,11 +300,11 @@
"url 2.5.8 registry+https://github.com/rust-lang/crates.io-index", "url 2.5.8 registry+https://github.com/rust-lang/crates.io-index",
"utf8_iter 1.0.4 registry+https://github.com/rust-lang/crates.io-index", "utf8_iter 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
"utf8parse 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "utf8parse 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"uuid 1.23.1 registry+https://github.com/rust-lang/crates.io-index", "uuid 1.22.0 registry+https://github.com/rust-lang/crates.io-index",
"vcpkg 0.2.15 registry+https://github.com/rust-lang/crates.io-index", "vcpkg 0.2.15 registry+https://github.com/rust-lang/crates.io-index",
"version_check 0.9.5 registry+https://github.com/rust-lang/crates.io-index", "version_check 0.9.5 registry+https://github.com/rust-lang/crates.io-index",
"web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"webbrowser 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "webbrowser 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"weezl 0.1.12 registry+https://github.com/rust-lang/crates.io-index", "weezl 0.1.12 registry+https://github.com/rust-lang/crates.io-index",
"win-msgbox 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "win-msgbox 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"winapi 0.3.9 registry+https://github.com/rust-lang/crates.io-index", "winapi 0.3.9 registry+https://github.com/rust-lang/crates.io-index",
@@ -347,6 +347,7 @@
"windows-strings 0.5.1 registry+https://github.com/rust-lang/crates.io-index", "windows-strings 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.60.2 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.60.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.61.2 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.61.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index", "windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
@@ -366,12 +367,12 @@
"winit 0.30.13 registry+https://github.com/rust-lang/crates.io-index", "winit 0.30.13 registry+https://github.com/rust-lang/crates.io-index",
"wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index", "wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
"yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index", "yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy 0.8.48 registry+https://github.com/rust-lang/crates.io-index", "zerocopy 0.8.47 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy-derive 0.8.48 registry+https://github.com/rust-lang/crates.io-index", "zerocopy-derive 0.8.47 registry+https://github.com/rust-lang/crates.io-index",
"zeroize 1.8.2 registry+https://github.com/rust-lang/crates.io-index", "zeroize 1.8.2 registry+https://github.com/rust-lang/crates.io-index",
"zune-core 0.5.1 registry+https://github.com/rust-lang/crates.io-index", "zune-core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index", "zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
"zune-jpeg 0.5.15 registry+https://github.com/rust-lang/crates.io-index" "zune-jpeg 0.5.14 registry+https://github.com/rust-lang/crates.io-index"
] ]
], ],
[ [
@@ -393,8 +394,8 @@
"av1-grain 0.2.5 registry+https://github.com/rust-lang/crates.io-index", "av1-grain 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
"rav1e 0.8.1 registry+https://github.com/rust-lang/crates.io-index", "rav1e 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"v_frame 0.3.9 registry+https://github.com/rust-lang/crates.io-index", "v_frame 0.3.9 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy 0.8.48 registry+https://github.com/rust-lang/crates.io-index", "zerocopy 0.8.47 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy-derive 0.8.48 registry+https://github.com/rust-lang/crates.io-index" "zerocopy-derive 0.8.47 registry+https://github.com/rust-lang/crates.io-index"
] ]
], ],
[ [
@@ -411,7 +412,7 @@
"exr 1.74.0 registry+https://github.com/rust-lang/crates.io-index", "exr 1.74.0 registry+https://github.com/rust-lang/crates.io-index",
"lebe 0.5.3 registry+https://github.com/rust-lang/crates.io-index", "lebe 0.5.3 registry+https://github.com/rust-lang/crates.io-index",
"moxcms 0.8.1 registry+https://github.com/rust-lang/crates.io-index", "moxcms 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"pxfm 0.1.29 registry+https://github.com/rust-lang/crates.io-index", "pxfm 0.1.28 registry+https://github.com/rust-lang/crates.io-index",
"ravif 0.13.0 registry+https://github.com/rust-lang/crates.io-index", "ravif 0.13.0 registry+https://github.com/rust-lang/crates.io-index",
"subtle 2.6.1 registry+https://github.com/rust-lang/crates.io-index" "subtle 2.6.1 registry+https://github.com/rust-lang/crates.io-index"
] ]
@@ -428,9 +429,8 @@
"CC0-1.0", "CC0-1.0",
[ [
"dunce 1.0.5 registry+https://github.com/rust-lang/crates.io-index", "dunce 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
"fast-srgb8 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
"file-id 0.1.0 registry+https://github.com/rust-lang/crates.io-index", "file-id 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"imgref 1.12.1 registry+https://github.com/rust-lang/crates.io-index", "imgref 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"notify 6.1.1 registry+https://github.com/rust-lang/crates.io-index", "notify 6.1.1 registry+https://github.com/rust-lang/crates.io-index",
"notify-debouncer-full 0.1.0 registry+https://github.com/rust-lang/crates.io-index" "notify-debouncer-full 0.1.0 registry+https://github.com/rust-lang/crates.io-index"
] ]
@@ -476,13 +476,12 @@
"beef 0.5.2 registry+https://github.com/rust-lang/crates.io-index", "beef 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
"bit_field 0.10.3 registry+https://github.com/rust-lang/crates.io-index", "bit_field 0.10.3 registry+https://github.com/rust-lang/crates.io-index",
"bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index", "bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index",
"bitflags 2.11.1 registry+https://github.com/rust-lang/crates.io-index", "bitflags 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
"bitstream-io 4.10.0 registry+https://github.com/rust-lang/crates.io-index", "bitstream-io 4.9.0 registry+https://github.com/rust-lang/crates.io-index",
"block-buffer 0.10.4 registry+https://github.com/rust-lang/crates.io-index", "block-buffer 0.10.4 registry+https://github.com/rust-lang/crates.io-index",
"brotli 8.0.2 registry+https://github.com/rust-lang/crates.io-index", "brotli 8.0.2 registry+https://github.com/rust-lang/crates.io-index",
"brotli-decompressor 5.0.0 registry+https://github.com/rust-lang/crates.io-index", "brotli-decompressor 5.0.0 registry+https://github.com/rust-lang/crates.io-index",
"built 0.8.0 registry+https://github.com/rust-lang/crates.io-index", "built 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"by_address 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.25.0 registry+https://github.com/rust-lang/crates.io-index", "bytemuck 1.25.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index", "bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index",
"byteorder 1.5.0 registry+https://github.com/rust-lang/crates.io-index", "byteorder 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -491,25 +490,24 @@
"calm_io 0.1.1 registry+https://github.com/rust-lang/crates.io-index", "calm_io 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"calmio_filters 0.1.0 registry+https://github.com/rust-lang/crates.io-index", "calmio_filters 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"catppuccin-egui 5.6.0 git+https://github.com/LGUG2Z/catppuccin-egui?rev=b2f95cbf441d1dd99f3c955ef10dcb84ce23c20a", "catppuccin-egui 5.6.0 git+https://github.com/LGUG2Z/catppuccin-egui?rev=b2f95cbf441d1dd99f3c955ef10dcb84ce23c20a",
"cc 1.2.61 registry+https://github.com/rust-lang/crates.io-index", "cc 1.2.57 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index", "cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 1.0.4 registry+https://github.com/rust-lang/crates.io-index", "cfg-if 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
"cfg_aliases 0.2.1 registry+https://github.com/rust-lang/crates.io-index", "cfg_aliases 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"chacha20 0.10.0 registry+https://github.com/rust-lang/crates.io-index",
"chrono 0.4.44 registry+https://github.com/rust-lang/crates.io-index", "chrono 0.4.44 registry+https://github.com/rust-lang/crates.io-index",
"chrono-tz 0.10.4 registry+https://github.com/rust-lang/crates.io-index", "chrono-tz 0.10.4 registry+https://github.com/rust-lang/crates.io-index",
"chumsky 0.9.3 registry+https://github.com/rust-lang/crates.io-index", "chumsky 0.9.3 registry+https://github.com/rust-lang/crates.io-index",
"clap 4.6.1 registry+https://github.com/rust-lang/crates.io-index", "clap 4.6.0 registry+https://github.com/rust-lang/crates.io-index",
"clap_builder 4.6.0 registry+https://github.com/rust-lang/crates.io-index", "clap_builder 4.6.0 registry+https://github.com/rust-lang/crates.io-index",
"clap_derive 4.6.1 registry+https://github.com/rust-lang/crates.io-index", "clap_derive 4.6.0 registry+https://github.com/rust-lang/crates.io-index",
"clap_lex 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "clap_lex 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"color-eyre 0.6.5 registry+https://github.com/rust-lang/crates.io-index", "color-eyre 0.6.5 registry+https://github.com/rust-lang/crates.io-index",
"color-spantrace 0.3.0 registry+https://github.com/rust-lang/crates.io-index", "color-spantrace 0.3.0 registry+https://github.com/rust-lang/crates.io-index",
"color-thief 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "color-thief 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"color_quant 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "color_quant 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"colorchoice 1.0.5 registry+https://github.com/rust-lang/crates.io-index", "colorchoice 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
"core2 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
"cpufeatures 0.2.17 registry+https://github.com/rust-lang/crates.io-index", "cpufeatures 0.2.17 registry+https://github.com/rust-lang/crates.io-index",
"cpufeatures 0.3.0 registry+https://github.com/rust-lang/crates.io-index",
"crc32fast 1.5.0 registry+https://github.com/rust-lang/crates.io-index", "crc32fast 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"crossbeam-channel 0.5.15 registry+https://github.com/rust-lang/crates.io-index", "crossbeam-channel 0.5.15 registry+https://github.com/rust-lang/crates.io-index",
"crossbeam-deque 0.8.6 registry+https://github.com/rust-lang/crates.io-index", "crossbeam-deque 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
@@ -553,14 +551,14 @@
"equator-macro 0.4.2 registry+https://github.com/rust-lang/crates.io-index", "equator-macro 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index", "equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index", "eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index",
"fast-srgb8 1.0.0 registry+https://github.com/rust-lang/crates.io-index", "fastrand 2.3.0 registry+https://github.com/rust-lang/crates.io-index",
"fastrand 2.4.1 registry+https://github.com/rust-lang/crates.io-index", "fax 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
"fax 0.2.7 registry+https://github.com/rust-lang/crates.io-index", "fax_derive 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index", "fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
"filetime 0.2.27 registry+https://github.com/rust-lang/crates.io-index", "filetime 0.2.27 registry+https://github.com/rust-lang/crates.io-index",
"find-msvc-tools 0.1.9 registry+https://github.com/rust-lang/crates.io-index", "find-msvc-tools 0.1.9 registry+https://github.com/rust-lang/crates.io-index",
"flate2 1.1.9 registry+https://github.com/rust-lang/crates.io-index", "flate2 1.1.9 registry+https://github.com/rust-lang/crates.io-index",
"flavours 0.7.2 git+https://github.com/LGUG2Z/flavours?rev=24518c129918fe3260aa559eded7657e50752cb1", "flavours 0.7.2 git+https://github.com/LGUG2Z/flavours",
"fnv 1.0.7 registry+https://github.com/rust-lang/crates.io-index", "fnv 1.0.7 registry+https://github.com/rust-lang/crates.io-index",
"font-loader 0.11.0 registry+https://github.com/rust-lang/crates.io-index", "font-loader 0.11.0 registry+https://github.com/rust-lang/crates.io-index",
"form_urlencoded 1.2.2 registry+https://github.com/rust-lang/crates.io-index", "form_urlencoded 1.2.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -575,11 +573,12 @@
"futures-task 0.3.32 registry+https://github.com/rust-lang/crates.io-index", "futures-task 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"futures-util 0.3.32 registry+https://github.com/rust-lang/crates.io-index", "futures-util 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"generic-array 0.14.7 registry+https://github.com/rust-lang/crates.io-index", "generic-array 0.14.7 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.1.16 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.2.17 registry+https://github.com/rust-lang/crates.io-index", "getrandom 0.2.17 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.3.4 registry+https://github.com/rust-lang/crates.io-index", "getrandom 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.4.2 registry+https://github.com/rust-lang/crates.io-index", "getrandom 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index", "gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index",
"gif 0.14.2 registry+https://github.com/rust-lang/crates.io-index", "gif 0.14.1 registry+https://github.com/rust-lang/crates.io-index",
"git2 0.20.4 registry+https://github.com/rust-lang/crates.io-index", "git2 0.20.4 registry+https://github.com/rust-lang/crates.io-index",
"glob 0.3.3 registry+https://github.com/rust-lang/crates.io-index", "glob 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
"glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index", "glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -589,7 +588,7 @@
"hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.15.5 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.15.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.17.0 registry+https://github.com/rust-lang/crates.io-index", "hashbrown 0.16.1 registry+https://github.com/rust-lang/crates.io-index",
"heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index", "heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index", "hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index", "hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -598,21 +597,21 @@
"http-body 1.0.1 registry+https://github.com/rust-lang/crates.io-index", "http-body 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
"http-body-util 0.1.3 registry+https://github.com/rust-lang/crates.io-index", "http-body-util 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"httparse 1.10.1 registry+https://github.com/rust-lang/crates.io-index", "httparse 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"hyper 1.9.0 registry+https://github.com/rust-lang/crates.io-index", "hyper 1.8.1 registry+https://github.com/rust-lang/crates.io-index",
"hyper-tls 0.6.0 registry+https://github.com/rust-lang/crates.io-index", "hyper-tls 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"hyper-util 0.1.20 registry+https://github.com/rust-lang/crates.io-index", "hyper-util 0.1.20 registry+https://github.com/rust-lang/crates.io-index",
"iana-time-zone 0.1.65 registry+https://github.com/rust-lang/crates.io-index", "iana-time-zone 0.1.65 registry+https://github.com/rust-lang/crates.io-index",
"ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index", "ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
"idna 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "idna 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"idna_adapter 1.2.2 registry+https://github.com/rust-lang/crates.io-index", "idna_adapter 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"image 0.23.14 registry+https://github.com/rust-lang/crates.io-index", "image 0.23.14 registry+https://github.com/rust-lang/crates.io-index",
"image 0.25.10 registry+https://github.com/rust-lang/crates.io-index", "image 0.25.10 registry+https://github.com/rust-lang/crates.io-index",
"image-webp 0.2.4 registry+https://github.com/rust-lang/crates.io-index", "image-webp 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"indenter 0.3.4 registry+https://github.com/rust-lang/crates.io-index", "indenter 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index", "indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 2.14.0 registry+https://github.com/rust-lang/crates.io-index", "indexmap 2.13.0 registry+https://github.com/rust-lang/crates.io-index",
"ipnet 2.12.0 registry+https://github.com/rust-lang/crates.io-index", "ipnet 2.12.0 registry+https://github.com/rust-lang/crates.io-index",
"iri-string 0.7.12 registry+https://github.com/rust-lang/crates.io-index", "iri-string 0.7.10 registry+https://github.com/rust-lang/crates.io-index",
"is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"is_terminal_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index", "is_terminal_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index",
"itertools 0.14.0 registry+https://github.com/rust-lang/crates.io-index", "itertools 0.14.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -620,9 +619,9 @@
"jobserver 0.1.34 registry+https://github.com/rust-lang/crates.io-index", "jobserver 0.1.34 registry+https://github.com/rust-lang/crates.io-index",
"jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index", "jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
"lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index", "lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"libc 0.2.186 registry+https://github.com/rust-lang/crates.io-index", "libc 0.2.183 registry+https://github.com/rust-lang/crates.io-index",
"libgit2-sys 0.18.3+1.9.2 registry+https://github.com/rust-lang/crates.io-index", "libgit2-sys 0.18.3+1.9.2 registry+https://github.com/rust-lang/crates.io-index",
"libz-sys 1.1.28 registry+https://github.com/rust-lang/crates.io-index", "libz-sys 1.1.25 registry+https://github.com/rust-lang/crates.io-index",
"linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index", "linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index",
"litrs 1.0.0 registry+https://github.com/rust-lang/crates.io-index", "litrs 1.0.0 registry+https://github.com/rust-lang/crates.io-index",
"lock_api 0.4.14 registry+https://github.com/rust-lang/crates.io-index", "lock_api 0.4.14 registry+https://github.com/rust-lang/crates.io-index",
@@ -642,14 +641,13 @@
"miniz_oxide 0.3.7 registry+https://github.com/rust-lang/crates.io-index", "miniz_oxide 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index", "miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.8.9 registry+https://github.com/rust-lang/crates.io-index", "miniz_oxide 0.8.9 registry+https://github.com/rust-lang/crates.io-index",
"mio 1.2.0 registry+https://github.com/rust-lang/crates.io-index", "mio 1.1.1 registry+https://github.com/rust-lang/crates.io-index",
"miow 0.6.1 registry+https://github.com/rust-lang/crates.io-index", "miow 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
"nanoid 0.4.0 registry+https://github.com/rust-lang/crates.io-index", "nanoid 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
"native-tls 0.2.18 registry+https://github.com/rust-lang/crates.io-index", "native-tls 0.2.18 registry+https://github.com/rust-lang/crates.io-index",
"net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index", "net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index",
"netdev 0.41.0 registry+https://github.com/rust-lang/crates.io-index", "netdev 0.40.1 registry+https://github.com/rust-lang/crates.io-index",
"new_debug_unreachable 1.0.6 registry+https://github.com/rust-lang/crates.io-index", "new_debug_unreachable 1.0.6 registry+https://github.com/rust-lang/crates.io-index",
"no_std_io2 0.9.3 registry+https://github.com/rust-lang/crates.io-index",
"nohash-hasher 0.2.0 registry+https://github.com/rust-lang/crates.io-index", "nohash-hasher 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"nom 7.1.3 registry+https://github.com/rust-lang/crates.io-index", "nom 7.1.3 registry+https://github.com/rust-lang/crates.io-index",
"nom 8.0.0 registry+https://github.com/rust-lang/crates.io-index", "nom 8.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -659,7 +657,7 @@
"num 0.4.3 registry+https://github.com/rust-lang/crates.io-index", "num 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"num-bigint 0.4.6 registry+https://github.com/rust-lang/crates.io-index", "num-bigint 0.4.6 registry+https://github.com/rust-lang/crates.io-index",
"num-complex 0.4.6 registry+https://github.com/rust-lang/crates.io-index", "num-complex 0.4.6 registry+https://github.com/rust-lang/crates.io-index",
"num-conv 0.2.1 registry+https://github.com/rust-lang/crates.io-index", "num-conv 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"num-derive 0.4.2 registry+https://github.com/rust-lang/crates.io-index", "num-derive 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"num-integer 0.1.46 registry+https://github.com/rust-lang/crates.io-index", "num-integer 0.1.46 registry+https://github.com/rust-lang/crates.io-index",
"num-iter 0.1.45 registry+https://github.com/rust-lang/crates.io-index", "num-iter 0.1.45 registry+https://github.com/rust-lang/crates.io-index",
@@ -669,11 +667,11 @@
"object 0.37.3 registry+https://github.com/rust-lang/crates.io-index", "object 0.37.3 registry+https://github.com/rust-lang/crates.io-index",
"once_cell 1.21.4 registry+https://github.com/rust-lang/crates.io-index", "once_cell 1.21.4 registry+https://github.com/rust-lang/crates.io-index",
"once_cell_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index", "once_cell_polyfill 1.70.2 registry+https://github.com/rust-lang/crates.io-index",
"open 5.3.4 registry+https://github.com/rust-lang/crates.io-index", "open 5.3.3 registry+https://github.com/rust-lang/crates.io-index",
"os_info 3.14.0 registry+https://github.com/rust-lang/crates.io-index", "os_info 3.14.0 registry+https://github.com/rust-lang/crates.io-index",
"owo-colors 4.3.0 registry+https://github.com/rust-lang/crates.io-index", "owo-colors 4.3.0 registry+https://github.com/rust-lang/crates.io-index",
"palette 0.7.6 registry+https://github.com/rust-lang/crates.io-index", "palette 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"palette_derive 0.7.6 registry+https://github.com/rust-lang/crates.io-index", "palette_derive 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot 0.12.5 registry+https://github.com/rust-lang/crates.io-index", "parking_lot 0.12.5 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot_core 0.9.12 registry+https://github.com/rust-lang/crates.io-index", "parking_lot_core 0.9.12 registry+https://github.com/rust-lang/crates.io-index",
"paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index", "paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
@@ -681,12 +679,15 @@
"percent-encoding 2.3.2 registry+https://github.com/rust-lang/crates.io-index", "percent-encoding 2.3.2 registry+https://github.com/rust-lang/crates.io-index",
"phf 0.11.3 registry+https://github.com/rust-lang/crates.io-index", "phf 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
"phf 0.12.1 registry+https://github.com/rust-lang/crates.io-index", "phf 0.12.1 registry+https://github.com/rust-lang/crates.io-index",
"phf_generator 0.11.3 registry+https://github.com/rust-lang/crates.io-index", "phf 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"phf_macros 0.11.3 registry+https://github.com/rust-lang/crates.io-index", "phf_codegen 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"phf_generator 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"phf_shared 0.11.3 registry+https://github.com/rust-lang/crates.io-index", "phf_shared 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
"phf_shared 0.12.1 registry+https://github.com/rust-lang/crates.io-index", "phf_shared 0.12.1 registry+https://github.com/rust-lang/crates.io-index",
"phf_shared 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"pin-project-lite 0.2.17 registry+https://github.com/rust-lang/crates.io-index", "pin-project-lite 0.2.17 registry+https://github.com/rust-lang/crates.io-index",
"pkg-config 0.3.33 registry+https://github.com/rust-lang/crates.io-index", "pin-utils 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"pkg-config 0.3.32 registry+https://github.com/rust-lang/crates.io-index",
"png 0.16.8 registry+https://github.com/rust-lang/crates.io-index", "png 0.16.8 registry+https://github.com/rust-lang/crates.io-index",
"png 0.18.1 registry+https://github.com/rust-lang/crates.io-index", "png 0.18.1 registry+https://github.com/rust-lang/crates.io-index",
"powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index", "powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -695,21 +696,23 @@
"proc-macro2 1.0.106 registry+https://github.com/rust-lang/crates.io-index", "proc-macro2 1.0.106 registry+https://github.com/rust-lang/crates.io-index",
"profiling 1.0.17 registry+https://github.com/rust-lang/crates.io-index", "profiling 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"profiling-procmacros 1.0.17 registry+https://github.com/rust-lang/crates.io-index", "profiling-procmacros 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"psm 0.1.31 registry+https://github.com/rust-lang/crates.io-index", "psm 0.1.30 registry+https://github.com/rust-lang/crates.io-index",
"qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index", "qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index", "quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"quote 1.0.45 registry+https://github.com/rust-lang/crates.io-index", "quote 1.0.45 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.10.1 registry+https://github.com/rust-lang/crates.io-index", "rand 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.8.6 registry+https://github.com/rust-lang/crates.io-index", "rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.9.4 registry+https://github.com/rust-lang/crates.io-index", "rand 0.9.2 registry+https://github.com/rust-lang/crates.io-index",
"rand_chacha 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"rand_chacha 0.3.1 registry+https://github.com/rust-lang/crates.io-index", "rand_chacha 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"rand_chacha 0.9.0 registry+https://github.com/rust-lang/crates.io-index", "rand_chacha 0.9.0 registry+https://github.com/rust-lang/crates.io-index",
"rand_core 0.10.1 registry+https://github.com/rust-lang/crates.io-index", "rand_core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"rand_core 0.6.4 registry+https://github.com/rust-lang/crates.io-index", "rand_core 0.6.4 registry+https://github.com/rust-lang/crates.io-index",
"rand_core 0.9.5 registry+https://github.com/rust-lang/crates.io-index", "rand_core 0.9.5 registry+https://github.com/rust-lang/crates.io-index",
"rand_pcg 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"random_word 0.5.2 registry+https://github.com/rust-lang/crates.io-index", "random_word 0.5.2 registry+https://github.com/rust-lang/crates.io-index",
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index", "raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
"rayon 1.12.0 registry+https://github.com/rust-lang/crates.io-index", "rayon 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
"rayon-core 1.13.0 registry+https://github.com/rust-lang/crates.io-index", "rayon-core 1.13.0 registry+https://github.com/rust-lang/crates.io-index",
"ref-cast 1.0.25 registry+https://github.com/rust-lang/crates.io-index", "ref-cast 1.0.25 registry+https://github.com/rust-lang/crates.io-index",
"ref-cast-impl 1.0.25 registry+https://github.com/rust-lang/crates.io-index", "ref-cast-impl 1.0.25 registry+https://github.com/rust-lang/crates.io-index",
@@ -721,14 +724,14 @@
"roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index", "roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
"rustc-demangle 0.1.27 registry+https://github.com/rust-lang/crates.io-index", "rustc-demangle 0.1.27 registry+https://github.com/rust-lang/crates.io-index",
"rustc_version 0.4.1 registry+https://github.com/rust-lang/crates.io-index", "rustc_version 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"rustls-pki-types 1.14.1 registry+https://github.com/rust-lang/crates.io-index", "rustls-pki-types 1.14.0 registry+https://github.com/rust-lang/crates.io-index",
"same-file 1.0.6 registry+https://github.com/rust-lang/crates.io-index", "same-file 1.0.6 registry+https://github.com/rust-lang/crates.io-index",
"schannel 0.1.29 registry+https://github.com/rust-lang/crates.io-index", "schannel 0.1.29 registry+https://github.com/rust-lang/crates.io-index",
"schemars 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "schemars 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"schemars_derive 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "schemars_derive 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"scoped_threadpool 0.1.9 registry+https://github.com/rust-lang/crates.io-index", "scoped_threadpool 0.1.9 registry+https://github.com/rust-lang/crates.io-index",
"scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index", "scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"semver 1.0.28 registry+https://github.com/rust-lang/crates.io-index", "semver 1.0.27 registry+https://github.com/rust-lang/crates.io-index",
"serde 1.0.228 registry+https://github.com/rust-lang/crates.io-index", "serde 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_core 1.0.228 registry+https://github.com/rust-lang/crates.io-index", "serde_core 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive 1.0.228 registry+https://github.com/rust-lang/crates.io-index", "serde_derive 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
@@ -748,20 +751,20 @@
"shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index", "shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
"shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index", "shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
"signature 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "signature 2.2.0 registry+https://github.com/rust-lang/crates.io-index",
"simd-adler32 0.3.9 registry+https://github.com/rust-lang/crates.io-index", "simd-adler32 0.3.8 registry+https://github.com/rust-lang/crates.io-index",
"simd_helpers 0.1.0 registry+https://github.com/rust-lang/crates.io-index", "simd_helpers 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"siphasher 0.3.11 registry+https://github.com/rust-lang/crates.io-index",
"siphasher 1.0.2 registry+https://github.com/rust-lang/crates.io-index", "siphasher 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"slab 0.4.12 registry+https://github.com/rust-lang/crates.io-index", "slab 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
"smallvec 1.15.1 registry+https://github.com/rust-lang/crates.io-index", "smallvec 1.15.1 registry+https://github.com/rust-lang/crates.io-index",
"smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"socket2 0.6.3 registry+https://github.com/rust-lang/crates.io-index", "socket2 0.6.3 registry+https://github.com/rust-lang/crates.io-index",
"stable_deref_trait 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "stable_deref_trait 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"stacker 0.1.24 registry+https://github.com/rust-lang/crates.io-index", "stacker 0.1.23 registry+https://github.com/rust-lang/crates.io-index",
"static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"strsim 0.11.1 registry+https://github.com/rust-lang/crates.io-index", "strsim 0.11.1 registry+https://github.com/rust-lang/crates.io-index",
"strum 0.28.0 registry+https://github.com/rust-lang/crates.io-index", "strum 0.27.2 registry+https://github.com/rust-lang/crates.io-index",
"strum_macros 0.28.0 registry+https://github.com/rust-lang/crates.io-index", "strum_macros 0.27.2 registry+https://github.com/rust-lang/crates.io-index",
"symlink 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index", "syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index",
"syn 2.0.117 registry+https://github.com/rust-lang/crates.io-index", "syn 2.0.117 registry+https://github.com/rust-lang/crates.io-index",
"synstructure 0.13.2 registry+https://github.com/rust-lang/crates.io-index", "synstructure 0.13.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -769,7 +772,7 @@
"sysinfo 0.38.4 registry+https://github.com/rust-lang/crates.io-index", "sysinfo 0.38.4 registry+https://github.com/rust-lang/crates.io-index",
"systray-util 0.2.0 registry+https://github.com/rust-lang/crates.io-index", "systray-util 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"tempfile 3.27.0 registry+https://github.com/rust-lang/crates.io-index", "tempfile 3.27.0 registry+https://github.com/rust-lang/crates.io-index",
"terminal_size 0.4.4 registry+https://github.com/rust-lang/crates.io-index", "terminal_size 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"textwrap 0.16.2 registry+https://github.com/rust-lang/crates.io-index", "textwrap 0.16.2 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 2.0.18 registry+https://github.com/rust-lang/crates.io-index", "thiserror 2.0.18 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 2.0.18 registry+https://github.com/rust-lang/crates.io-index", "thiserror-impl 2.0.18 registry+https://github.com/rust-lang/crates.io-index",
@@ -778,8 +781,8 @@
"tiff 0.6.1 registry+https://github.com/rust-lang/crates.io-index", "tiff 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
"time 0.3.47 registry+https://github.com/rust-lang/crates.io-index", "time 0.3.47 registry+https://github.com/rust-lang/crates.io-index",
"time-core 0.1.8 registry+https://github.com/rust-lang/crates.io-index", "time-core 0.1.8 registry+https://github.com/rust-lang/crates.io-index",
"tokio 1.52.1 registry+https://github.com/rust-lang/crates.io-index", "tokio 1.50.0 registry+https://github.com/rust-lang/crates.io-index",
"tokio-macros 2.7.0 registry+https://github.com/rust-lang/crates.io-index", "tokio-macros 2.6.1 registry+https://github.com/rust-lang/crates.io-index",
"tokio-native-tls 0.3.1 registry+https://github.com/rust-lang/crates.io-index", "tokio-native-tls 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"tokio-util 0.7.18 registry+https://github.com/rust-lang/crates.io-index", "tokio-util 0.7.18 registry+https://github.com/rust-lang/crates.io-index",
"toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index", "toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index",
@@ -788,7 +791,7 @@
"tower-layer 0.3.3 registry+https://github.com/rust-lang/crates.io-index", "tower-layer 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
"tower-service 0.3.3 registry+https://github.com/rust-lang/crates.io-index", "tower-service 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
"tracing 0.1.44 registry+https://github.com/rust-lang/crates.io-index", "tracing 0.1.44 registry+https://github.com/rust-lang/crates.io-index",
"tracing-appender 0.2.5 registry+https://github.com/rust-lang/crates.io-index", "tracing-appender 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"tracing-attributes 0.1.31 registry+https://github.com/rust-lang/crates.io-index", "tracing-attributes 0.1.31 registry+https://github.com/rust-lang/crates.io-index",
"tracing-core 0.1.36 registry+https://github.com/rust-lang/crates.io-index", "tracing-core 0.1.36 registry+https://github.com/rust-lang/crates.io-index",
"tracing-error 0.2.1 registry+https://github.com/rust-lang/crates.io-index", "tracing-error 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
@@ -796,13 +799,13 @@
"tracing-subscriber 0.3.23 registry+https://github.com/rust-lang/crates.io-index", "tracing-subscriber 0.3.23 registry+https://github.com/rust-lang/crates.io-index",
"try-lock 0.2.5 registry+https://github.com/rust-lang/crates.io-index", "try-lock 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
"ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index", "ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
"typenum 1.20.0 registry+https://github.com/rust-lang/crates.io-index", "typenum 1.19.0 registry+https://github.com/rust-lang/crates.io-index",
"tz-rs 0.7.3 registry+https://github.com/rust-lang/crates.io-index", "tz-rs 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"tzdb_data 0.2.4 registry+https://github.com/rust-lang/crates.io-index", "tzdb_data 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"uds_windows 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "uds_windows 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"unicase 2.9.0 registry+https://github.com/rust-lang/crates.io-index", "unicase 2.9.0 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.24 registry+https://github.com/rust-lang/crates.io-index", "unicode-ident 1.0.24 registry+https://github.com/rust-lang/crates.io-index",
"unicode-segmentation 1.13.2 registry+https://github.com/rust-lang/crates.io-index", "unicode-segmentation 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.1.14 registry+https://github.com/rust-lang/crates.io-index", "unicode-width 0.1.14 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "unicode-width 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"unicode-xid 0.2.6 registry+https://github.com/rust-lang/crates.io-index", "unicode-xid 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
@@ -811,13 +814,13 @@
"url 2.5.8 registry+https://github.com/rust-lang/crates.io-index", "url 2.5.8 registry+https://github.com/rust-lang/crates.io-index",
"utf8_iter 1.0.4 registry+https://github.com/rust-lang/crates.io-index", "utf8_iter 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
"utf8parse 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "utf8parse 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"uuid 1.23.1 registry+https://github.com/rust-lang/crates.io-index", "uuid 1.22.0 registry+https://github.com/rust-lang/crates.io-index",
"vcpkg 0.2.15 registry+https://github.com/rust-lang/crates.io-index", "vcpkg 0.2.15 registry+https://github.com/rust-lang/crates.io-index",
"version_check 0.9.5 registry+https://github.com/rust-lang/crates.io-index", "version_check 0.9.5 registry+https://github.com/rust-lang/crates.io-index",
"walkdir 2.5.0 registry+https://github.com/rust-lang/crates.io-index", "walkdir 2.5.0 registry+https://github.com/rust-lang/crates.io-index",
"want 0.3.1 registry+https://github.com/rust-lang/crates.io-index", "want 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index", "web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"webbrowser 1.2.1 registry+https://github.com/rust-lang/crates.io-index", "webbrowser 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"weezl 0.1.12 registry+https://github.com/rust-lang/crates.io-index", "weezl 0.1.12 registry+https://github.com/rust-lang/crates.io-index",
"which 8.0.2 registry+https://github.com/rust-lang/crates.io-index", "which 8.0.2 registry+https://github.com/rust-lang/crates.io-index",
"win-msgbox 0.2.2 registry+https://github.com/rust-lang/crates.io-index", "win-msgbox 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -864,6 +867,7 @@
"windows-strings 0.5.1 registry+https://github.com/rust-lang/crates.io-index", "windows-strings 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.60.2 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.60.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.61.2 registry+https://github.com/rust-lang/crates.io-index", "windows-sys 0.61.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index", "windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
@@ -881,18 +885,18 @@
"windows_x86_64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index", "windows_x86_64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index", "windows_x86_64_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"winput 0.2.5 registry+https://github.com/rust-lang/crates.io-index", "winput 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
"winreg 0.56.0 registry+https://github.com/rust-lang/crates.io-index", "winreg 0.55.0 registry+https://github.com/rust-lang/crates.io-index",
"wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index", "wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
"xml-rs 0.8.28 registry+https://github.com/rust-lang/crates.io-index", "xml-rs 0.8.28 registry+https://github.com/rust-lang/crates.io-index",
"y4m 0.8.0 registry+https://github.com/rust-lang/crates.io-index", "y4m 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index", "yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy 0.8.48 registry+https://github.com/rust-lang/crates.io-index", "zerocopy 0.8.47 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy-derive 0.8.48 registry+https://github.com/rust-lang/crates.io-index", "zerocopy-derive 0.8.47 registry+https://github.com/rust-lang/crates.io-index",
"zeroize 1.8.2 registry+https://github.com/rust-lang/crates.io-index", "zeroize 1.8.2 registry+https://github.com/rust-lang/crates.io-index",
"zmij 1.0.21 registry+https://github.com/rust-lang/crates.io-index", "zmij 1.0.21 registry+https://github.com/rust-lang/crates.io-index",
"zune-core 0.5.1 registry+https://github.com/rust-lang/crates.io-index", "zune-core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index", "zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
"zune-jpeg 0.5.15 registry+https://github.com/rust-lang/crates.io-index" "zune-jpeg 0.5.14 registry+https://github.com/rust-lang/crates.io-index"
] ]
], ],
[ [
@@ -924,25 +928,25 @@
[ [
"Unicode-3.0", "Unicode-3.0",
[ [
"icu_collections 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_collections 2.1.1 registry+https://github.com/rust-lang/crates.io-index",
"icu_locale_core 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_locale_core 2.1.1 registry+https://github.com/rust-lang/crates.io-index",
"icu_normalizer 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_normalizer 2.1.1 registry+https://github.com/rust-lang/crates.io-index",
"icu_normalizer_data 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_normalizer_data 2.1.1 registry+https://github.com/rust-lang/crates.io-index",
"icu_properties 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_properties 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
"icu_properties_data 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_properties_data 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
"icu_provider 2.2.0 registry+https://github.com/rust-lang/crates.io-index", "icu_provider 2.1.1 registry+https://github.com/rust-lang/crates.io-index",
"litemap 0.8.2 registry+https://github.com/rust-lang/crates.io-index", "litemap 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"potential_utf 0.1.5 registry+https://github.com/rust-lang/crates.io-index", "potential_utf 0.1.4 registry+https://github.com/rust-lang/crates.io-index",
"tinystr 0.8.3 registry+https://github.com/rust-lang/crates.io-index", "tinystr 0.8.2 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.24 registry+https://github.com/rust-lang/crates.io-index", "unicode-ident 1.0.24 registry+https://github.com/rust-lang/crates.io-index",
"writeable 0.6.3 registry+https://github.com/rust-lang/crates.io-index", "writeable 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
"yoke 0.8.2 registry+https://github.com/rust-lang/crates.io-index", "yoke 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"yoke-derive 0.8.2 registry+https://github.com/rust-lang/crates.io-index", "yoke-derive 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"zerofrom 0.1.7 registry+https://github.com/rust-lang/crates.io-index", "zerofrom 0.1.6 registry+https://github.com/rust-lang/crates.io-index",
"zerofrom-derive 0.1.7 registry+https://github.com/rust-lang/crates.io-index", "zerofrom-derive 0.1.6 registry+https://github.com/rust-lang/crates.io-index",
"zerotrie 0.2.4 registry+https://github.com/rust-lang/crates.io-index", "zerotrie 0.2.3 registry+https://github.com/rust-lang/crates.io-index",
"zerovec 0.11.6 registry+https://github.com/rust-lang/crates.io-index", "zerovec 0.11.5 registry+https://github.com/rust-lang/crates.io-index",
"zerovec-derive 0.11.3 registry+https://github.com/rust-lang/crates.io-index" "zerovec-derive 0.11.2 registry+https://github.com/rust-lang/crates.io-index"
] ]
], ],
[ [
@@ -963,19 +967,17 @@
"adler32 1.2.0 registry+https://github.com/rust-lang/crates.io-index", "adler32 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.25.0 registry+https://github.com/rust-lang/crates.io-index", "bytemuck 1.25.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index", "bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index",
"const_format 0.2.36 registry+https://github.com/rust-lang/crates.io-index", "const_format 0.2.35 registry+https://github.com/rust-lang/crates.io-index",
"const_format_proc_macros 0.2.34 registry+https://github.com/rust-lang/crates.io-index", "const_format_proc_macros 0.2.34 registry+https://github.com/rust-lang/crates.io-index",
"cursor-icon 1.2.0 registry+https://github.com/rust-lang/crates.io-index", "cursor-icon 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"foldhash 0.1.5 registry+https://github.com/rust-lang/crates.io-index", "foldhash 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
"glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index", "glow 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
"konst 0.2.20 registry+https://github.com/rust-lang/crates.io-index",
"konst_macro_rules 0.2.19 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index", "miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.8.9 registry+https://github.com/rust-lang/crates.io-index", "miniz_oxide 0.8.9 registry+https://github.com/rust-lang/crates.io-index",
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index", "raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
"zune-core 0.5.1 registry+https://github.com/rust-lang/crates.io-index", "zune-core 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index", "zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
"zune-jpeg 0.5.15 registry+https://github.com/rust-lang/crates.io-index" "zune-jpeg 0.5.14 registry+https://github.com/rust-lang/crates.io-index"
] ]
] ]
] ]

View File

@@ -172,143 +172,6 @@ consistently to all splits of that type throughout the layout. Additional values
- Unspecified ratios default to sharing the remaining space equally - Unspecified ratios default to sharing the remaining space equally
- You only need to specify the ratios you want to customize; trailing values can be omitted - You only need to specify the ratios you want to customize; trailing values can be omitted
## Layout Options Rules
You can dynamically change `layout_options` based on the number of containers on a workspace
using `layout_options_rules`. This uses the same threshold-based logic as `layout_rules`:
when the container count is greater than or equal to a threshold, the highest matching
threshold's options are used.
Rules **fully replace** the base `layout_options` when they match. If no rule matches, the
base `layout_options` is used.
### Configuration
```json
{
"monitors": [
{
"workspaces": [
{
"name": "main",
"layout": "VerticalStack",
"layout_options": {
"column_ratios": [0.6],
"row_ratios": [0.4]
},
"layout_options_rules": {
"3": { "column_ratios": [0.55] },
"5": { "column_ratios": [0.3, 0.3, 0.3], "row_ratios": [0.5] }
}
}
]
}
]
}
```
In the example above:
| Container Count | Effective `layout_options` |
|-----------------|---------------------------|
| 1-2 | Base: `column_ratios: [0.6]`, `row_ratios: [0.4]` |
| 3-4 | Rule "3": `column_ratios: [0.55]` (no row_ratios, no scrolling, no grid) |
| 5+ | Rule "5": `column_ratios: [0.3, 0.3, 0.3]`, `row_ratios: [0.5]` |
Rules can include any field that `layout_options` supports: `column_ratios`, `row_ratios`,
`scrolling`, and `grid`. When a rule matches, it completely replaces the base options. Fields
not specified in the matching rule default to their standard defaults (not the base
`layout_options` values).
### Example: Scrolling Layout with Dynamic Columns
```json
{
"layout": "Scrolling",
"layout_options": {
"scrolling": { "columns": 2 }
},
"layout_options_rules": {
"4": { "scrolling": { "columns": 3 } },
"7": { "scrolling": { "columns": 4 } }
}
}
```
This increases the visible scrolling columns as more windows are added.
## Layout Defaults
You can define global per-layout default `layout_options` and `layout_options_rules` using
the top-level `layout_defaults` setting. This avoids repeating the same configuration across
every workspace that uses the same layout.
### Configuration
```json
{
"layout_defaults": {
"VerticalStack": {
"layout_options": { "column_ratios": [0.7] },
"layout_options_rules": {
"2": { "column_ratios": [0.7] },
"3": { "column_ratios": [0.55] },
"5": { "column_ratios": [0.4] }
}
},
"Columns": {
"layout_options": { "column_ratios": [0.3, 0.4] },
"layout_options_rules": {
"4": { "column_ratios": [0.2, 0.3, 0.3] }
}
},
"HorizontalStack": {
"layout_options": { "row_ratios": [0.6] }
}
},
"monitors": [
{
"workspaces": [
{
"name": "main",
"layout": "VerticalStack"
}
]
}
]
}
```
In this example, every workspace using `VerticalStack`, `Columns`, or `HorizontalStack`
automatically gets the global `layout_options` and `layout_options_rules` without needing
to specify them per-workspace. Note that `VerticalStack` only has 2 columns (main + stack),
so only a single `column_ratios` value is meaningful, while `Columns` distributes windows
across multiple columns where additional ratios control each column's width.
### Resolution Cascade
Global defaults act as a fallback. If a workspace defines **either** `layout_options` or
`layout_options_rules`, it **completely replaces** all global `layout_defaults` for that
layout. Global defaults are only used when the workspace has **neither** setting.
Within the effective source (workspace or global):
1. Try threshold match from the rules (highest matching threshold wins)
2. If a rule matches → use it (full replacement of base options)
3. Otherwise → use the base `layout_options`
### Override Examples
| Workspace Config | Global Config | Effective Behavior |
|------------------|---------------|--------------------|
| No `layout_options`, no rules | `layout_defaults` has both | Uses global base + global rules |
| Has `layout_options` only | `layout_defaults` has both | Workspace base only (all globals ignored) |
| Has `layout_options_rules` only | `layout_defaults` has both | Workspace rules only (all globals ignored) |
| Has both | `layout_defaults` has both | All workspace (all globals ignored) |
This "complete replacement" semantic means you never get a mix of workspace and global
settings for the same layout. If you override anything at the workspace level, you take
full control of that layout's options for that workspace.
## Progressive Ratio Behavior ## Progressive Ratio Behavior
Ratios are applied progressively as windows are added. For example, with `row_ratios: [0.3, 0.5]` in a VerticalStack: Ratios are applied progressively as windows are added. For example, with `row_ratios: [0.3, 0.5]` in a VerticalStack:

View File

@@ -723,7 +723,7 @@ impl Systray {
.filter(|icon| icon.is_visible) .filter(|icon| icon.is_visible)
.cloned() .cloned()
.collect(); .collect();
icons.sort_by_key(|a| a.stable_id.to_string()); icons.sort_by(|a, b| a.stable_id.to_string().cmp(&b.stable_id.to_string()));
icons icons
} }
@@ -731,7 +731,7 @@ impl Systray {
fn get_all_icons() -> Vec<CachedIcon> { fn get_all_icons() -> Vec<CachedIcon> {
let state = SYSTRAY_STATE.lock(); let state = SYSTRAY_STATE.lock();
let mut icons: Vec<_> = state.icons.values().cloned().collect(); let mut icons: Vec<_> = state.icons.values().cloned().collect();
icons.sort_by_key(|a| a.stable_id.to_string()); icons.sort_by(|a, b| a.stable_id.to_string().cmp(&b.stable_id.to_string()));
icons icons
} }

View File

@@ -1,5 +1,3 @@
use std::collections::HashMap;
use clap::ValueEnum; use clap::ValueEnum;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -56,7 +54,7 @@ pub fn validate_ratios(ratios: &[f32]) -> [Option<f32>; MAX_RATIOS] {
} }
#[derive( #[derive(
Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Display, EnumString, ValueEnum, Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, Display, EnumString, ValueEnum,
)] )]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
/// A predefined komorebi layout /// A predefined komorebi layout
@@ -252,21 +250,6 @@ pub struct GridLayoutOptions {
pub rows: usize, pub rows: usize,
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
/// Per-layout default options entry for the `layout_defaults` global setting.
/// Contains both base layout options and threshold-based layout options rules.
pub struct LayoutDefaultEntry {
/// Default layout options for this layout
#[serde(skip_serializing_if = "Option::is_none")]
pub layout_options: Option<LayoutOptions>,
/// Threshold-based layout options rules in the format of threshold => options.
/// When container count >= threshold, the highest matching threshold's options
/// fully replace the base `layout_options`.
#[serde(skip_serializing_if = "Option::is_none")]
pub layout_options_rules: Option<HashMap<usize, LayoutOptions>>,
}
impl DefaultLayout { impl DefaultLayout {
pub fn leftmost_index(&self, len: usize) -> usize { pub fn leftmost_index(&self, len: usize) -> usize {
match self { match self {
@@ -437,5 +420,370 @@ impl DefaultLayout {
} }
#[cfg(test)] #[cfg(test)]
#[path = "default_layout_tests.rs"] mod tests {
mod tests; use super::*;
// Helper to create LayoutOptions with column ratios
fn layout_options_with_column_ratios(ratios: &[f32]) -> LayoutOptions {
let mut arr = [None; MAX_RATIOS];
for (i, &r) in ratios.iter().take(MAX_RATIOS).enumerate() {
arr[i] = Some(r);
}
LayoutOptions {
scrolling: None,
grid: None,
column_ratios: Some(arr),
row_ratios: None,
}
}
// Helper to create LayoutOptions with row ratios
fn layout_options_with_row_ratios(ratios: &[f32]) -> LayoutOptions {
let mut arr = [None; MAX_RATIOS];
for (i, &r) in ratios.iter().take(MAX_RATIOS).enumerate() {
arr[i] = Some(r);
}
LayoutOptions {
scrolling: None,
grid: None,
column_ratios: None,
row_ratios: Some(arr),
}
}
// Helper to create LayoutOptions with both column and row ratios
fn layout_options_with_ratios(column_ratios: &[f32], row_ratios: &[f32]) -> LayoutOptions {
let mut col_arr = [None; MAX_RATIOS];
for (i, &r) in column_ratios.iter().take(MAX_RATIOS).enumerate() {
col_arr[i] = Some(r);
}
let mut row_arr = [None; MAX_RATIOS];
for (i, &r) in row_ratios.iter().take(MAX_RATIOS).enumerate() {
row_arr[i] = Some(r);
}
LayoutOptions {
scrolling: None,
grid: None,
column_ratios: Some(col_arr),
row_ratios: Some(row_arr),
}
}
mod deserialize_ratios_tests {
use super::*;
#[test]
fn test_deserialize_valid_ratios() {
let json = r#"{"column_ratios": [0.3, 0.4, 0.2]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
assert_eq!(ratios[0], Some(0.3));
assert_eq!(ratios[1], Some(0.4));
assert_eq!(ratios[2], Some(0.2));
assert_eq!(ratios[3], None);
assert_eq!(ratios[4], None);
}
#[test]
fn test_deserialize_clamps_values_to_min() {
// Values below MIN_RATIO should be clamped
let json = r#"{"column_ratios": [0.05]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
assert_eq!(ratios[0], Some(MIN_RATIO)); // Clamped to 0.1
}
#[test]
fn test_deserialize_clamps_values_to_max() {
// Values above MAX_RATIO should be clamped
let json = r#"{"column_ratios": [0.95]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
// 0.9 is the max, so it should be clamped
assert!(ratios[0].unwrap() <= MAX_RATIO);
}
#[test]
fn test_deserialize_truncates_when_sum_exceeds_one() {
// Sum of ratios should not reach 1.0
// [0.5, 0.4] = 0.9, then 0.3 would make it 1.2, so it should be truncated
let json = r#"{"column_ratios": [0.5, 0.4, 0.3]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
assert_eq!(ratios[0], Some(0.5));
assert_eq!(ratios[1], Some(0.4));
// Third ratio should be truncated because 0.5 + 0.4 + 0.3 >= 1.0
assert_eq!(ratios[2], None);
}
#[test]
fn test_deserialize_truncates_at_max_ratios() {
// More than MAX_RATIOS values should be truncated
let json = r#"{"column_ratios": [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
// Only MAX_RATIOS (5) values should be stored
for item in ratios.iter().take(MAX_RATIOS) {
assert_eq!(*item, Some(0.1));
}
}
#[test]
fn test_deserialize_empty_array() {
let json = r#"{"column_ratios": []}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
for item in ratios.iter().take(MAX_RATIOS) {
assert_eq!(*item, None);
}
}
#[test]
fn test_deserialize_null() {
let json = r#"{"column_ratios": null}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.column_ratios.is_none());
}
#[test]
fn test_deserialize_row_ratios() {
let json = r#"{"row_ratios": [0.3, 0.5]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.row_ratios.unwrap();
assert_eq!(ratios[0], Some(0.3));
assert_eq!(ratios[1], Some(0.5));
assert_eq!(ratios[2], None);
}
}
mod serialize_ratios_tests {
use super::*;
#[test]
fn test_serialize_ratios_compact() {
let opts = layout_options_with_column_ratios(&[0.3, 0.4]);
let json = serde_json::to_string(&opts).unwrap();
// Should serialize ratios as compact array without trailing nulls in the ratios array
assert!(json.contains("0.3") && json.contains("0.4"));
}
#[test]
fn test_serialize_none_ratios() {
let opts = LayoutOptions {
scrolling: None,
grid: None,
column_ratios: None,
row_ratios: None,
};
let json = serde_json::to_string(&opts).unwrap();
// None values should serialize as null or be omitted
assert!(!json.contains("["));
}
#[test]
fn test_roundtrip_serialization() {
let original = layout_options_with_column_ratios(&[0.3, 0.4, 0.2]);
let json = serde_json::to_string(&original).unwrap();
let deserialized: LayoutOptions = serde_json::from_str(&json).unwrap();
assert_eq!(original.column_ratios, deserialized.column_ratios);
}
#[test]
fn test_serialize_row_ratios() {
let opts = layout_options_with_row_ratios(&[0.3, 0.5]);
let json = serde_json::to_string(&opts).unwrap();
assert!(json.contains("row_ratios"));
assert!(json.contains("0.3") && json.contains("0.5"));
}
#[test]
fn test_roundtrip_row_ratios() {
let original = layout_options_with_row_ratios(&[0.4, 0.3]);
let json = serde_json::to_string(&original).unwrap();
let deserialized: LayoutOptions = serde_json::from_str(&json).unwrap();
assert_eq!(original.row_ratios, deserialized.row_ratios);
assert!(original.column_ratios.is_none());
}
#[test]
fn test_roundtrip_both_ratios() {
let original = layout_options_with_ratios(&[0.3, 0.4], &[0.5, 0.3]);
let json = serde_json::to_string(&original).unwrap();
let deserialized: LayoutOptions = serde_json::from_str(&json).unwrap();
assert_eq!(original.column_ratios, deserialized.column_ratios);
assert_eq!(original.row_ratios, deserialized.row_ratios);
}
}
mod ratio_constants_tests {
use super::*;
#[test]
fn test_constants_valid_ranges() {
const {
assert!(MIN_RATIO > 0.0);
assert!(MIN_RATIO < MAX_RATIO);
assert!(MAX_RATIO < 1.0);
assert!(DEFAULT_RATIO >= MIN_RATIO && DEFAULT_RATIO <= MAX_RATIO);
assert!(
DEFAULT_SECONDARY_RATIO >= MIN_RATIO && DEFAULT_SECONDARY_RATIO <= MAX_RATIO
);
assert!(MAX_RATIOS >= 1);
}
}
#[test]
fn test_default_ratio_is_half() {
assert_eq!(DEFAULT_RATIO, 0.5);
}
#[test]
fn test_max_ratios_is_five() {
assert_eq!(MAX_RATIOS, 5);
}
}
mod layout_options_tests {
use super::*;
#[test]
fn test_layout_options_default_values() {
let json = r#"{}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.scrolling.is_none());
assert!(opts.grid.is_none());
assert!(opts.column_ratios.is_none());
assert!(opts.row_ratios.is_none());
}
#[test]
fn test_layout_options_with_all_fields() {
let json = r#"{
"scrolling": {"columns": 3},
"grid": {"rows": 2},
"column_ratios": [0.3, 0.4],
"row_ratios": [0.5]
}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.scrolling.is_some());
assert_eq!(opts.scrolling.unwrap().columns, 3);
assert!(opts.grid.is_some());
assert_eq!(opts.grid.unwrap().rows, 2);
assert!(opts.column_ratios.is_some());
assert!(opts.row_ratios.is_some());
}
}
mod default_layout_tests {
use super::*;
#[test]
fn test_cycle_next_covers_all_layouts() {
let start = DefaultLayout::BSP;
let mut current = start;
let mut visited = vec![current];
loop {
current = current.cycle_next();
if current == start {
break;
}
assert!(
!visited.contains(&current),
"Cycle contains duplicate: {:?}",
current
);
visited.push(current);
}
// Should have visited all layouts
assert_eq!(visited.len(), 9); // 9 layouts total
}
#[test]
fn test_cycle_previous_is_inverse_of_next() {
// Note: cycle_previous has some inconsistencies in the current implementation
// This test documents the expected behavior for most layouts
let layouts_with_correct_inverse = [
DefaultLayout::Columns,
DefaultLayout::Rows,
DefaultLayout::VerticalStack,
DefaultLayout::HorizontalStack,
DefaultLayout::UltrawideVerticalStack,
DefaultLayout::Grid,
DefaultLayout::RightMainVerticalStack,
];
for layout in layouts_with_correct_inverse {
let next = layout.cycle_next();
assert_eq!(
next.cycle_previous(),
layout,
"cycle_previous should be inverse of cycle_next for {:?}",
layout
);
}
}
#[test]
fn test_leftmost_index_standard_layouts() {
assert_eq!(DefaultLayout::BSP.leftmost_index(5), 0);
assert_eq!(DefaultLayout::Columns.leftmost_index(5), 0);
assert_eq!(DefaultLayout::Rows.leftmost_index(5), 0);
assert_eq!(DefaultLayout::VerticalStack.leftmost_index(5), 0);
assert_eq!(DefaultLayout::HorizontalStack.leftmost_index(5), 0);
assert_eq!(DefaultLayout::Grid.leftmost_index(5), 0);
}
#[test]
fn test_leftmost_index_ultrawide() {
assert_eq!(DefaultLayout::UltrawideVerticalStack.leftmost_index(1), 0);
assert_eq!(DefaultLayout::UltrawideVerticalStack.leftmost_index(2), 1);
assert_eq!(DefaultLayout::UltrawideVerticalStack.leftmost_index(5), 1);
}
#[test]
fn test_leftmost_index_right_main() {
assert_eq!(DefaultLayout::RightMainVerticalStack.leftmost_index(1), 0);
assert_eq!(DefaultLayout::RightMainVerticalStack.leftmost_index(2), 1);
assert_eq!(DefaultLayout::RightMainVerticalStack.leftmost_index(5), 1);
}
#[test]
fn test_rightmost_index_standard_layouts() {
assert_eq!(DefaultLayout::BSP.rightmost_index(5), 4);
assert_eq!(DefaultLayout::Columns.rightmost_index(5), 4);
assert_eq!(DefaultLayout::Rows.rightmost_index(5), 4);
assert_eq!(DefaultLayout::VerticalStack.rightmost_index(5), 4);
}
#[test]
fn test_rightmost_index_right_main() {
assert_eq!(DefaultLayout::RightMainVerticalStack.rightmost_index(1), 0);
assert_eq!(DefaultLayout::RightMainVerticalStack.rightmost_index(5), 0);
}
#[test]
fn test_rightmost_index_ultrawide() {
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(1), 0);
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(2), 0);
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(3), 2);
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(5), 4);
}
}
}

View File

@@ -1,954 +0,0 @@
use super::*;
// Helper to create LayoutOptions with column ratios
fn layout_options_with_column_ratios(ratios: &[f32]) -> LayoutOptions {
let mut arr = [None; MAX_RATIOS];
for (i, &r) in ratios.iter().take(MAX_RATIOS).enumerate() {
arr[i] = Some(r);
}
LayoutOptions {
scrolling: None,
grid: None,
column_ratios: Some(arr),
row_ratios: None,
}
}
// Helper to create LayoutOptions with row ratios
fn layout_options_with_row_ratios(ratios: &[f32]) -> LayoutOptions {
let mut arr = [None; MAX_RATIOS];
for (i, &r) in ratios.iter().take(MAX_RATIOS).enumerate() {
arr[i] = Some(r);
}
LayoutOptions {
scrolling: None,
grid: None,
column_ratios: None,
row_ratios: Some(arr),
}
}
// Helper to create LayoutOptions with both column and row ratios
fn layout_options_with_ratios(column_ratios: &[f32], row_ratios: &[f32]) -> LayoutOptions {
let mut col_arr = [None; MAX_RATIOS];
for (i, &r) in column_ratios.iter().take(MAX_RATIOS).enumerate() {
col_arr[i] = Some(r);
}
let mut row_arr = [None; MAX_RATIOS];
for (i, &r) in row_ratios.iter().take(MAX_RATIOS).enumerate() {
row_arr[i] = Some(r);
}
LayoutOptions {
scrolling: None,
grid: None,
column_ratios: Some(col_arr),
row_ratios: Some(row_arr),
}
}
mod deserialize_ratios_tests {
use super::*;
#[test]
fn test_deserialize_valid_ratios() {
let json = r#"{"column_ratios": [0.3, 0.4, 0.2]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
assert_eq!(ratios[0], Some(0.3));
assert_eq!(ratios[1], Some(0.4));
assert_eq!(ratios[2], Some(0.2));
assert_eq!(ratios[3], None);
assert_eq!(ratios[4], None);
}
#[test]
fn test_deserialize_clamps_values_to_min() {
// Values below MIN_RATIO should be clamped
let json = r#"{"column_ratios": [0.05]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
assert_eq!(ratios[0], Some(MIN_RATIO)); // Clamped to 0.1
}
#[test]
fn test_deserialize_clamps_values_to_max() {
// Values above MAX_RATIO should be clamped
let json = r#"{"column_ratios": [0.95]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
// 0.9 is the max, so it should be clamped
assert!(ratios[0].unwrap() <= MAX_RATIO);
}
#[test]
fn test_deserialize_truncates_when_sum_exceeds_one() {
// Sum of ratios should not reach 1.0
// [0.5, 0.4] = 0.9, then 0.3 would make it 1.2, so it should be truncated
let json = r#"{"column_ratios": [0.5, 0.4, 0.3]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
assert_eq!(ratios[0], Some(0.5));
assert_eq!(ratios[1], Some(0.4));
// Third ratio should be truncated because 0.5 + 0.4 + 0.3 >= 1.0
assert_eq!(ratios[2], None);
}
#[test]
fn test_deserialize_truncates_at_max_ratios() {
// More than MAX_RATIOS values should be truncated
let json = r#"{"column_ratios": [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
// Only MAX_RATIOS (5) values should be stored
for item in ratios.iter().take(MAX_RATIOS) {
assert_eq!(*item, Some(0.1));
}
}
#[test]
fn test_deserialize_empty_array() {
let json = r#"{"column_ratios": []}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.column_ratios.unwrap();
for item in ratios.iter().take(MAX_RATIOS) {
assert_eq!(*item, None);
}
}
#[test]
fn test_deserialize_null() {
let json = r#"{"column_ratios": null}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.column_ratios.is_none());
}
#[test]
fn test_deserialize_row_ratios() {
let json = r#"{"row_ratios": [0.3, 0.5]}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let ratios = opts.row_ratios.unwrap();
assert_eq!(ratios[0], Some(0.3));
assert_eq!(ratios[1], Some(0.5));
assert_eq!(ratios[2], None);
}
}
mod serialize_ratios_tests {
use super::*;
#[test]
fn test_serialize_ratios_compact() {
let opts = layout_options_with_column_ratios(&[0.3, 0.4]);
let json = serde_json::to_string(&opts).unwrap();
// Should serialize ratios as compact array without trailing nulls in the ratios array
assert!(json.contains("0.3") && json.contains("0.4"));
}
#[test]
fn test_serialize_none_ratios() {
let opts = LayoutOptions {
scrolling: None,
grid: None,
column_ratios: None,
row_ratios: None,
};
let json = serde_json::to_string(&opts).unwrap();
// None values should serialize as null or be omitted
assert!(!json.contains("["));
}
#[test]
fn test_roundtrip_serialization() {
let original = layout_options_with_column_ratios(&[0.3, 0.4, 0.2]);
let json = serde_json::to_string(&original).unwrap();
let deserialized: LayoutOptions = serde_json::from_str(&json).unwrap();
assert_eq!(original.column_ratios, deserialized.column_ratios);
}
#[test]
fn test_serialize_row_ratios() {
let opts = layout_options_with_row_ratios(&[0.3, 0.5]);
let json = serde_json::to_string(&opts).unwrap();
assert!(json.contains("row_ratios"));
assert!(json.contains("0.3") && json.contains("0.5"));
}
#[test]
fn test_roundtrip_row_ratios() {
let original = layout_options_with_row_ratios(&[0.4, 0.3]);
let json = serde_json::to_string(&original).unwrap();
let deserialized: LayoutOptions = serde_json::from_str(&json).unwrap();
assert_eq!(original.row_ratios, deserialized.row_ratios);
assert!(original.column_ratios.is_none());
}
#[test]
fn test_roundtrip_both_ratios() {
let original = layout_options_with_ratios(&[0.3, 0.4], &[0.5, 0.3]);
let json = serde_json::to_string(&original).unwrap();
let deserialized: LayoutOptions = serde_json::from_str(&json).unwrap();
assert_eq!(original.column_ratios, deserialized.column_ratios);
assert_eq!(original.row_ratios, deserialized.row_ratios);
}
}
mod ratio_constants_tests {
use super::*;
#[test]
fn test_constants_valid_ranges() {
const {
assert!(MIN_RATIO > 0.0);
assert!(MIN_RATIO < MAX_RATIO);
assert!(MAX_RATIO < 1.0);
assert!(DEFAULT_RATIO >= MIN_RATIO && DEFAULT_RATIO <= MAX_RATIO);
assert!(DEFAULT_SECONDARY_RATIO >= MIN_RATIO && DEFAULT_SECONDARY_RATIO <= MAX_RATIO);
assert!(MAX_RATIOS >= 1);
}
}
#[test]
fn test_default_ratio_is_half() {
assert_eq!(DEFAULT_RATIO, 0.5);
}
#[test]
fn test_max_ratios_is_five() {
assert_eq!(MAX_RATIOS, 5);
}
}
mod layout_options_tests {
use super::*;
#[test]
fn test_layout_options_default_values() {
let json = r#"{}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.scrolling.is_none());
assert!(opts.grid.is_none());
assert!(opts.column_ratios.is_none());
assert!(opts.row_ratios.is_none());
}
#[test]
fn test_layout_options_with_all_fields() {
let json = r#"{
"scrolling": {"columns": 3},
"grid": {"rows": 2},
"column_ratios": [0.3, 0.4],
"row_ratios": [0.5]
}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.scrolling.is_some());
assert_eq!(opts.scrolling.unwrap().columns, 3);
assert!(opts.grid.is_some());
assert_eq!(opts.grid.unwrap().rows, 2);
assert!(opts.column_ratios.is_some());
assert!(opts.row_ratios.is_some());
}
}
mod default_layout_tests {
use super::*;
#[test]
fn test_cycle_next_covers_all_layouts() {
let start = DefaultLayout::BSP;
let mut current = start;
let mut visited = vec![current];
loop {
current = current.cycle_next();
if current == start {
break;
}
assert!(
!visited.contains(&current),
"Cycle contains duplicate: {:?}",
current
);
visited.push(current);
}
// Should have visited all layouts
assert_eq!(visited.len(), 9); // 9 layouts total
}
#[test]
fn test_cycle_previous_is_inverse_of_next() {
// Note: cycle_previous has some inconsistencies in the current implementation
// This test documents the expected behavior for most layouts
let layouts_with_correct_inverse = [
DefaultLayout::Columns,
DefaultLayout::Rows,
DefaultLayout::VerticalStack,
DefaultLayout::HorizontalStack,
DefaultLayout::UltrawideVerticalStack,
DefaultLayout::Grid,
DefaultLayout::RightMainVerticalStack,
];
for layout in layouts_with_correct_inverse {
let next = layout.cycle_next();
assert_eq!(
next.cycle_previous(),
layout,
"cycle_previous should be inverse of cycle_next for {:?}",
layout
);
}
}
#[test]
fn test_leftmost_index_standard_layouts() {
assert_eq!(DefaultLayout::BSP.leftmost_index(5), 0);
assert_eq!(DefaultLayout::Columns.leftmost_index(5), 0);
assert_eq!(DefaultLayout::Rows.leftmost_index(5), 0);
assert_eq!(DefaultLayout::VerticalStack.leftmost_index(5), 0);
assert_eq!(DefaultLayout::HorizontalStack.leftmost_index(5), 0);
assert_eq!(DefaultLayout::Grid.leftmost_index(5), 0);
}
#[test]
fn test_leftmost_index_ultrawide() {
assert_eq!(DefaultLayout::UltrawideVerticalStack.leftmost_index(1), 0);
assert_eq!(DefaultLayout::UltrawideVerticalStack.leftmost_index(2), 1);
assert_eq!(DefaultLayout::UltrawideVerticalStack.leftmost_index(5), 1);
}
#[test]
fn test_leftmost_index_right_main() {
assert_eq!(DefaultLayout::RightMainVerticalStack.leftmost_index(1), 0);
assert_eq!(DefaultLayout::RightMainVerticalStack.leftmost_index(2), 1);
assert_eq!(DefaultLayout::RightMainVerticalStack.leftmost_index(5), 1);
}
#[test]
fn test_rightmost_index_standard_layouts() {
assert_eq!(DefaultLayout::BSP.rightmost_index(5), 4);
assert_eq!(DefaultLayout::Columns.rightmost_index(5), 4);
assert_eq!(DefaultLayout::Rows.rightmost_index(5), 4);
assert_eq!(DefaultLayout::VerticalStack.rightmost_index(5), 4);
}
#[test]
fn test_rightmost_index_right_main() {
assert_eq!(DefaultLayout::RightMainVerticalStack.rightmost_index(1), 0);
assert_eq!(DefaultLayout::RightMainVerticalStack.rightmost_index(5), 0);
}
#[test]
fn test_rightmost_index_ultrawide() {
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(1), 0);
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(2), 0);
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(3), 2);
assert_eq!(DefaultLayout::UltrawideVerticalStack.rightmost_index(5), 4);
}
}
mod layout_options_rules_tests {
use super::*;
#[test]
fn test_hashmap_deserialization_ratios_only() {
// layout_options_rules entries with only ratios
// Note: ratios must sum to < 1.0 to avoid truncation by validate_ratios
let json = r#"{
"2": {"column_ratios": [0.7]},
"3": {"column_ratios": [0.55]},
"5": {"column_ratios": [0.3, 0.3, 0.3]}
}"#;
let rules: std::collections::HashMap<usize, LayoutOptions> =
serde_json::from_str(json).unwrap();
assert_eq!(rules.len(), 3);
assert_eq!(rules[&2].column_ratios.unwrap()[0], Some(0.7));
assert_eq!(rules[&3].column_ratios.unwrap()[0], Some(0.55));
let r5 = rules[&5].column_ratios.unwrap();
assert_eq!(r5[0], Some(0.3));
assert_eq!(r5[1], Some(0.3));
assert_eq!(r5[2], Some(0.3));
// No scrolling/grid in these entries
assert!(rules[&2].scrolling.is_none());
assert!(rules[&2].grid.is_none());
}
#[test]
fn test_hashmap_deserialization_full_options() {
// layout_options_rules entries with full options including scrolling/grid
let json = r#"{
"2": {"column_ratios": [0.7], "scrolling": {"columns": 3}},
"5": {"column_ratios": [0.3, 0.3, 0.3], "grid": {"rows": 2}}
}"#;
let rules: std::collections::HashMap<usize, LayoutOptions> =
serde_json::from_str(json).unwrap();
assert_eq!(rules.len(), 2);
assert_eq!(rules[&2].scrolling.unwrap().columns, 3);
assert!(rules[&2].grid.is_none());
assert!(rules[&5].scrolling.is_none());
assert_eq!(rules[&5].grid.unwrap().rows, 2);
}
#[test]
fn test_rule_entry_with_all_fields() {
let json = r#"{
"column_ratios": [0.6, 0.3],
"scrolling": {"columns": 4, "center_focused_column": true},
"grid": {"rows": 2},
"row_ratios": [0.5]
}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
let col = opts.column_ratios.unwrap();
assert_eq!(col[0], Some(0.6));
assert_eq!(col[1], Some(0.3));
let row = opts.row_ratios.unwrap();
assert_eq!(row[0], Some(0.5));
assert_eq!(opts.scrolling.unwrap().columns, 4);
assert_eq!(opts.scrolling.unwrap().center_focused_column, Some(true));
assert_eq!(opts.grid.unwrap().rows, 2);
}
#[test]
fn test_rule_entry_empty_object_gives_defaults() {
let json = r#"{}"#;
let opts: LayoutOptions = serde_json::from_str(json).unwrap();
assert!(opts.column_ratios.is_none());
assert!(opts.row_ratios.is_none());
assert!(opts.scrolling.is_none());
assert!(opts.grid.is_none());
}
}
mod layout_default_entry_tests {
use super::*;
use std::collections::HashMap;
#[test]
fn test_default_layout_as_hashmap_key() {
let mut map: HashMap<DefaultLayout, &str> = HashMap::new();
map.insert(DefaultLayout::BSP, "bsp");
map.insert(DefaultLayout::VerticalStack, "vstack");
map.insert(DefaultLayout::Columns, "cols");
assert_eq!(map.len(), 3);
assert_eq!(map[&DefaultLayout::BSP], "bsp");
assert_eq!(map[&DefaultLayout::VerticalStack], "vstack");
assert_eq!(map[&DefaultLayout::Columns], "cols");
}
#[test]
fn test_default_layout_hash_consistency() {
// Same variant inserted twice should overwrite
let mut map: HashMap<DefaultLayout, i32> = HashMap::new();
map.insert(DefaultLayout::Grid, 1);
map.insert(DefaultLayout::Grid, 2);
assert_eq!(map.len(), 1);
assert_eq!(map[&DefaultLayout::Grid], 2);
}
#[test]
fn test_layout_default_entry_deserialize_full() {
let json = r#"{
"layout_options": {"column_ratios": [0.7]},
"layout_options_rules": {
"2": {"column_ratios": [0.7]},
"3": {"column_ratios": [0.55]},
"5": {"column_ratios": [0.3, 0.3, 0.3]}
}
}"#;
let entry: LayoutDefaultEntry = serde_json::from_str(json).unwrap();
let base = entry.layout_options.unwrap();
assert_eq!(base.column_ratios.unwrap()[0], Some(0.7));
let rules = entry.layout_options_rules.unwrap();
assert_eq!(rules.len(), 3);
assert_eq!(rules[&2].column_ratios.unwrap()[0], Some(0.7));
assert_eq!(rules[&3].column_ratios.unwrap()[0], Some(0.55));
let r5 = rules[&5].column_ratios.unwrap();
assert_eq!(r5[0], Some(0.3));
assert_eq!(r5[1], Some(0.3));
assert_eq!(r5[2], Some(0.3));
}
#[test]
fn test_layout_default_entry_deserialize_only_base() {
let json = r#"{
"layout_options": {"column_ratios": [0.6]}
}"#;
let entry: LayoutDefaultEntry = serde_json::from_str(json).unwrap();
assert!(entry.layout_options.is_some());
assert_eq!(
entry.layout_options.unwrap().column_ratios.unwrap()[0],
Some(0.6)
);
assert!(entry.layout_options_rules.is_none());
}
#[test]
fn test_layout_default_entry_deserialize_only_rules() {
let json = r#"{
"layout_options_rules": {
"3": {"column_ratios": [0.4]}
}
}"#;
let entry: LayoutDefaultEntry = serde_json::from_str(json).unwrap();
assert!(entry.layout_options.is_none());
let rules = entry.layout_options_rules.unwrap();
assert_eq!(rules.len(), 1);
assert_eq!(rules[&3].column_ratios.unwrap()[0], Some(0.4));
}
#[test]
fn test_layout_default_entry_deserialize_empty() {
let json = r#"{}"#;
let entry: LayoutDefaultEntry = serde_json::from_str(json).unwrap();
assert!(entry.layout_options.is_none());
assert!(entry.layout_options_rules.is_none());
}
#[test]
fn test_layout_default_entry_roundtrip() {
let json = r#"{
"layout_options": {"column_ratios": [0.7]},
"layout_options_rules": {
"2": {"column_ratios": [0.6]},
"5": {"column_ratios": [0.3, 0.3, 0.3]}
}
}"#;
let original: LayoutDefaultEntry = serde_json::from_str(json).unwrap();
let serialized = serde_json::to_string(&original).unwrap();
let deserialized: LayoutDefaultEntry = serde_json::from_str(&serialized).unwrap();
assert_eq!(
original.layout_options.unwrap().column_ratios,
deserialized.layout_options.unwrap().column_ratios
);
let orig_rules = original.layout_options_rules.unwrap();
let deser_rules = deserialized.layout_options_rules.unwrap();
assert_eq!(orig_rules.len(), deser_rules.len());
for (key, orig_opts) in &orig_rules {
let deser_opts = &deser_rules[key];
assert_eq!(orig_opts.column_ratios, deser_opts.column_ratios);
}
}
#[test]
fn test_layout_defaults_full_config_deserialize() {
// Simulate the top-level layout_defaults field
let json = r#"{
"VerticalStack": {
"layout_options": {"column_ratios": [0.7]},
"layout_options_rules": {
"2": {"column_ratios": [0.7]},
"3": {"column_ratios": [0.55]}
}
},
"HorizontalStack": {
"layout_options": {"column_ratios": [0.6]}
},
"Columns": {
"layout_options_rules": {
"4": {"column_ratios": [0.3, 0.3, 0.3]}
}
}
}"#;
let defaults: HashMap<DefaultLayout, LayoutDefaultEntry> =
serde_json::from_str(json).unwrap();
assert_eq!(defaults.len(), 3);
// VerticalStack: has both base and rules
let vs = &defaults[&DefaultLayout::VerticalStack];
assert!(vs.layout_options.is_some());
assert_eq!(vs.layout_options_rules.as_ref().unwrap().len(), 2);
// HorizontalStack: has only base
let hs = &defaults[&DefaultLayout::HorizontalStack];
assert!(hs.layout_options.is_some());
assert!(hs.layout_options_rules.is_none());
// Columns: has only rules
let cols = &defaults[&DefaultLayout::Columns];
assert!(cols.layout_options.is_none());
assert_eq!(cols.layout_options_rules.as_ref().unwrap().len(), 1);
}
#[test]
fn test_layout_default_entry_with_scrolling_and_grid() {
let json = r#"{
"layout_options": {
"column_ratios": [0.5],
"scrolling": {"columns": 3},
"grid": {"rows": 2}
},
"layout_options_rules": {
"4": {
"scrolling": {"columns": 5, "center_focused_column": true}
}
}
}"#;
let entry: LayoutDefaultEntry = serde_json::from_str(json).unwrap();
let base = entry.layout_options.unwrap();
assert_eq!(base.scrolling.unwrap().columns, 3);
assert_eq!(base.grid.unwrap().rows, 2);
let rules = entry.layout_options_rules.unwrap();
let r4 = &rules[&4];
assert_eq!(r4.scrolling.unwrap().columns, 5);
assert_eq!(r4.scrolling.unwrap().center_focused_column, Some(true));
// Rule doesn't inherit base fields - full replacement
assert!(r4.column_ratios.is_none());
assert!(r4.grid.is_none());
}
#[test]
fn test_layout_default_entry_skip_serializing_none() {
// When both fields are None, they should not appear in output
let entry = LayoutDefaultEntry {
layout_options: None,
layout_options_rules: None,
};
let json = serde_json::to_string(&entry).unwrap();
assert!(!json.contains("layout_options"));
assert!(!json.contains("layout_options_rules"));
assert_eq!(json, "{}");
}
}
/// Tests for the complete-replacement cascade logic.
///
/// This mirrors the resolution algorithm in workspace.rs::update():
/// - If the workspace defines EITHER layout_options OR layout_options_rules,
/// it completely replaces the global layout_defaults for this layout.
/// - Global defaults are only used when the workspace has NEITHER setting.
/// - Within the effective source (workspace or global):
/// 1. Try threshold match from rules (highest matching threshold wins)
/// 2. If a rule matches -> use it (full replacement of base)
/// 3. Else -> use the base layout_options
///
/// Since the actual cascade is in workspace.rs (which has heavy WM dependencies),
/// we test the pure algorithm here using the same data structures.
mod cascade_resolution_tests {
use super::*;
/// Simulates the cascade resolution logic from workspace.rs::update().
/// This is a pure function equivalent of the inline code in update().
fn resolve_effective_options(
container_count: usize,
workspace_base: Option<LayoutOptions>,
workspace_rules: &[(usize, LayoutOptions)], // sorted by threshold ascending
global_base: Option<LayoutOptions>,
global_rules: &[(usize, LayoutOptions)], // sorted by threshold ascending
) -> Option<LayoutOptions> {
let has_workspace_overrides = workspace_base.is_some() || !workspace_rules.is_empty();
let (effective_base, effective_rules): (Option<LayoutOptions>, &[(usize, LayoutOptions)]) =
if has_workspace_overrides {
(workspace_base, workspace_rules)
} else {
(global_base, global_rules)
};
// Try threshold match from effective rules
let mut matched = None;
for (threshold, opts) in effective_rules {
if container_count >= *threshold {
matched = Some(*opts);
}
}
// If a rule matched, use it (full replacement); otherwise use effective base
if matched.is_some() {
matched
} else {
effective_base
}
}
fn opts_with_ratio(ratio: f32) -> LayoutOptions {
layout_options_with_column_ratios(&[ratio])
}
// --- No overrides ---
#[test]
fn test_no_workspace_no_global_returns_none() {
let result = resolve_effective_options(3, None, &[], None, &[]);
assert!(result.is_none());
}
// --- Base-only scenarios ---
#[test]
fn test_workspace_base_only() {
let ws_base = opts_with_ratio(0.7);
let result = resolve_effective_options(3, Some(ws_base), &[], None, &[]);
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
#[test]
fn test_global_base_only() {
let global_base = opts_with_ratio(0.6);
let result = resolve_effective_options(3, None, &[], Some(global_base), &[]);
assert_eq!(result.unwrap().column_ratios, global_base.column_ratios);
}
#[test]
fn test_workspace_base_overrides_all_globals() {
// Workspace has base → globals (both base and rules) are ignored entirely
let ws_base = opts_with_ratio(0.7);
let global_base = opts_with_ratio(0.6);
let global_rules = vec![(2, opts_with_ratio(0.5))];
let result =
resolve_effective_options(3, Some(ws_base), &[], Some(global_base), &global_rules);
// Workspace base wins; global rules are NOT used even though they would match
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
// --- Rules-only scenarios ---
#[test]
fn test_global_rules_match() {
let global_rules = vec![(2, opts_with_ratio(0.6)), (4, opts_with_ratio(0.5))];
// 3 containers: matches threshold 2, not 4
let result = resolve_effective_options(3, None, &[], None, &global_rules);
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.6));
}
#[test]
fn test_global_rules_highest_matching_threshold_wins() {
let global_rules = vec![(2, opts_with_ratio(0.6)), (4, opts_with_ratio(0.5))];
// 5 containers: matches both thresholds 2 and 4; highest (4) wins
let result = resolve_effective_options(5, None, &[], None, &global_rules);
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.5));
}
#[test]
fn test_global_rules_no_match_falls_through_to_none() {
let global_rules = vec![(5, opts_with_ratio(0.5))];
// 3 containers: doesn't match threshold 5
let result = resolve_effective_options(3, None, &[], None, &global_rules);
assert!(result.is_none());
}
#[test]
fn test_global_rules_no_match_falls_through_to_global_base() {
let global_base = opts_with_ratio(0.6);
let global_rules = vec![(5, opts_with_ratio(0.5))];
// 3 containers: doesn't match threshold 5, falls back to global base
let result = resolve_effective_options(3, None, &[], Some(global_base), &global_rules);
assert_eq!(result.unwrap().column_ratios, global_base.column_ratios);
}
#[test]
fn test_workspace_rules_override_global_rules() {
let ws_rules = vec![(2, opts_with_ratio(0.8))];
let global_rules = vec![(2, opts_with_ratio(0.6))];
// Workspace has rules → global rules are ignored entirely
let result = resolve_effective_options(3, None, &ws_rules, None, &global_rules);
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.8));
}
// --- Complete replacement: workspace having EITHER setting disables ALL globals ---
#[test]
fn test_workspace_rules_disable_global_base() {
// Workspace has rules but no base. Global has base.
// Since workspace has a setting, globals are completely replaced.
let ws_rules = vec![(2, opts_with_ratio(0.8))];
let global_base = opts_with_ratio(0.6);
// Rule matches → use it. Global base is NOT available as fallback.
let result = resolve_effective_options(3, None, &ws_rules, Some(global_base), &[]);
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.8));
}
#[test]
fn test_workspace_rules_no_match_does_not_fall_to_global_base() {
// Workspace has rules (but they don't match). Global has base.
// Since workspace has a setting, globals are completely replaced → returns None.
let ws_rules = vec![(5, opts_with_ratio(0.8))];
let global_base = opts_with_ratio(0.6);
let result = resolve_effective_options(3, None, &ws_rules, Some(global_base), &[]);
// No workspace base, no rule match, globals ignored → None
assert!(result.is_none());
}
#[test]
fn test_workspace_base_disables_global_rules() {
// Workspace has base but no rules. Global has rules.
// Since workspace has a setting, globals are completely replaced.
let ws_base = opts_with_ratio(0.7);
let global_rules = vec![(2, opts_with_ratio(0.5))];
// No workspace rules → no rule match → use workspace base. Global rules ignored.
let result = resolve_effective_options(3, Some(ws_base), &[], None, &global_rules);
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
#[test]
fn test_workspace_base_disables_global_rules_and_base() {
// Workspace has base. Global has both rules and base.
// Since workspace has a setting, all globals are completely replaced.
let ws_base = opts_with_ratio(0.7);
let global_base = opts_with_ratio(0.6);
let global_rules = vec![(2, opts_with_ratio(0.5))];
let result =
resolve_effective_options(3, Some(ws_base), &[], Some(global_base), &global_rules);
// Only workspace base is used; global rules and base are both ignored
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
#[test]
fn test_workspace_rules_disable_global_rules_and_base() {
// Workspace has rules. Global has both rules and base.
// Since workspace has a setting, all globals are completely replaced.
let ws_rules = vec![(2, opts_with_ratio(0.8))];
let global_base = opts_with_ratio(0.6);
let global_rules = vec![(2, opts_with_ratio(0.5))];
let result =
resolve_effective_options(3, None, &ws_rules, Some(global_base), &global_rules);
// Workspace rule matches → 0.8. Global base and rules both ignored.
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.8));
}
// --- Full replacement semantics (rule match replaces base) ---
#[test]
fn test_rule_match_is_full_replacement_not_merge() {
// When a rule matches, its options FULLY REPLACE the base.
// Fields not specified in the rule default to their standard defaults.
let ws_base = layout_options_with_ratios(&[0.7], &[0.4]);
let rule_opts = layout_options_with_column_ratios(&[0.5]);
// rule_opts has column_ratios but no row_ratios
let ws_rules = vec![(2, rule_opts)];
let result = resolve_effective_options(3, Some(ws_base), &ws_rules, None, &[]);
let effective = result.unwrap();
// Column ratios come from the rule
assert_eq!(effective.column_ratios.unwrap()[0], Some(0.5));
// Row ratios are NOT inherited from ws_base - they're None (full replacement)
assert!(effective.row_ratios.is_none());
}
// --- Edge cases ---
#[test]
fn test_exact_threshold_match() {
let rules = vec![(3, opts_with_ratio(0.6))];
let result = resolve_effective_options(3, None, &rules, None, &[]);
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.6));
}
#[test]
fn test_container_count_one_below_threshold() {
let rules = vec![(3, opts_with_ratio(0.6))];
let result = resolve_effective_options(2, None, &rules, None, &[]);
assert!(result.is_none());
}
#[test]
fn test_zero_containers() {
let ws_base = opts_with_ratio(0.7);
let rules = vec![(1, opts_with_ratio(0.5))];
let result = resolve_effective_options(0, Some(ws_base), &rules, None, &[]);
// 0 containers doesn't match threshold 1 → falls back to workspace base
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
#[test]
fn test_many_thresholds_correct_match() {
let rules = vec![
(1, opts_with_ratio(0.8)),
(3, opts_with_ratio(0.6)),
(5, opts_with_ratio(0.4)),
(8, opts_with_ratio(0.3)),
];
// 6 containers: matches 1, 3, 5 but not 8. Highest match is 5.
let result = resolve_effective_options(6, None, &rules, None, &[]);
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.4));
}
#[test]
fn test_workspace_rules_disable_global_rules_even_if_ws_rules_dont_match() {
// Key behavior: if workspace has ANY setting, globals are entirely ignored.
// Even if workspace rules don't match, we don't fall back to global rules.
let ws_rules = vec![(10, opts_with_ratio(0.8))]; // threshold too high
let global_rules = vec![(2, opts_with_ratio(0.5))]; // would match
let result = resolve_effective_options(3, None, &ws_rules, None, &global_rules);
// Workspace has rules → all globals ignored. WS rules don't match → None.
assert!(result.is_none());
}
#[test]
fn test_all_four_sources_present_rules_match() {
// All four sources present: workspace base, workspace rules, global base, global rules
let ws_base = opts_with_ratio(0.7);
let ws_rules = vec![(2, opts_with_ratio(0.8))];
let global_base = opts_with_ratio(0.6);
let global_rules = vec![(2, opts_with_ratio(0.5))];
let result = resolve_effective_options(
3,
Some(ws_base),
&ws_rules,
Some(global_base),
&global_rules,
);
// Workspace has settings → uses workspace only. Rule matches → 0.8
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.8));
}
#[test]
fn test_all_four_sources_present_rules_no_match() {
// All four sources present, but workspace rules don't match
let ws_base = opts_with_ratio(0.7);
let ws_rules = vec![(10, opts_with_ratio(0.8))]; // threshold too high
let global_base = opts_with_ratio(0.6);
let global_rules = vec![(10, opts_with_ratio(0.5))]; // also too high
let result = resolve_effective_options(
3,
Some(ws_base),
&ws_rules,
Some(global_base),
&global_rules,
);
// Workspace has settings → uses workspace only. No rule match → workspace base 0.7
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
// --- Workspace with both base and rules ---
#[test]
fn test_workspace_both_rule_matches() {
let ws_base = opts_with_ratio(0.7);
let ws_rules = vec![(2, opts_with_ratio(0.5))];
let result = resolve_effective_options(3, Some(ws_base), &ws_rules, None, &[]);
// Rule matches → use rule (full replacement), not ws_base
assert_eq!(result.unwrap().column_ratios.unwrap()[0], Some(0.5));
}
#[test]
fn test_workspace_both_rule_no_match() {
let ws_base = opts_with_ratio(0.7);
let ws_rules = vec![(10, opts_with_ratio(0.5))];
let result = resolve_effective_options(3, Some(ws_base), &ws_rules, None, &[]);
// Rule doesn't match → fall back to ws_base
assert_eq!(result.unwrap().column_ratios, ws_base.column_ratios);
}
}

View File

@@ -15,7 +15,7 @@ serde = { workspace = true }
serde_variant = "0.1" serde_variant = "0.1"
strum = { workspace = true } strum = { workspace = true }
hex_color = { version = "3", features = ["serde"] } hex_color = { version = "3", features = ["serde"] }
flavours = { git = "https://github.com/LGUG2Z/flavours", rev = "24518c129918fe3260aa559eded7657e50752cb1" } flavours = { git = "https://github.com/LGUG2Z/flavours", version = "0.7.2" }
[features] [features]
default = ["schemars"] default = ["schemars"]

View File

@@ -32,7 +32,6 @@ pub use komorebi_layouts::DefaultLayout;
pub use komorebi_layouts::Direction; pub use komorebi_layouts::Direction;
pub use komorebi_layouts::GridLayoutOptions; pub use komorebi_layouts::GridLayoutOptions;
pub use komorebi_layouts::Layout; pub use komorebi_layouts::Layout;
pub use komorebi_layouts::LayoutDefaultEntry;
pub use komorebi_layouts::LayoutOptions; pub use komorebi_layouts::LayoutOptions;
pub use komorebi_layouts::MAX_RATIO; pub use komorebi_layouts::MAX_RATIO;
pub use komorebi_layouts::MAX_RATIOS; pub use komorebi_layouts::MAX_RATIOS;

View File

@@ -238,9 +238,6 @@ lazy_static! {
static ref FLOATING_WINDOW_TOGGLE_ASPECT_RATIO: Arc<Mutex<AspectRatio>> = Arc::new(Mutex::new(AspectRatio::Predefined(PredefinedAspectRatio::Widescreen))); static ref FLOATING_WINDOW_TOGGLE_ASPECT_RATIO: Arc<Mutex<AspectRatio>> = Arc::new(Mutex::new(AspectRatio::Predefined(PredefinedAspectRatio::Widescreen)));
static ref CURRENT_VIRTUAL_DESKTOP: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None)); static ref CURRENT_VIRTUAL_DESKTOP: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None));
pub static ref LAYOUT_DEFAULTS: Arc<Mutex<HashMap<DefaultLayout, LayoutDefaultEntry>>> =
Arc::new(Mutex::new(HashMap::new()));
} }
pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10); pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10);

View File

@@ -307,10 +307,12 @@ impl Monitor {
DefaultLayout::RightMainVerticalStack => { DefaultLayout::RightMainVerticalStack => {
workspace.add_container_to_front(container); workspace.add_container_to_front(container);
} }
DefaultLayout::UltrawideVerticalStack DefaultLayout::UltrawideVerticalStack => {
if workspace.containers().len() == 1 => if workspace.containers().len() == 1 {
{ workspace.insert_container_at_idx(0, container);
workspace.insert_container_at_idx(0, container); } else {
workspace.add_container_to_back(container);
}
} }
_ => { _ => {
workspace.add_container_to_back(container); workspace.add_container_to_back(container);
@@ -330,10 +332,12 @@ impl Monitor {
match layout { match layout {
DefaultLayout::RightMainVerticalStack DefaultLayout::RightMainVerticalStack
| DefaultLayout::UltrawideVerticalStack | DefaultLayout::UltrawideVerticalStack => {
if workspace.containers().len() == 1 => if workspace.containers().len() == 1 {
{ workspace.add_container_to_back(container);
workspace.add_container_to_back(container); } else {
workspace.insert_container_at_idx(target_index, container);
}
} }
_ => { _ => {
workspace.insert_container_at_idx(target_index, container); workspace.insert_container_at_idx(target_index, container);

View File

@@ -28,10 +28,12 @@ pub fn listen_for_movements(wm: Arc<Mutex<WindowManager>>) {
Action::Press => ignore_movement = true, Action::Press => ignore_movement = true,
Action::Release => ignore_movement = false, Action::Release => ignore_movement = false,
}, },
Event::MouseMoveRelative { .. } if !ignore_movement => { Event::MouseMoveRelative { .. } => {
match wm.lock().raise_window_at_cursor_pos() { if !ignore_movement {
Ok(()) => {} match wm.lock().raise_window_at_cursor_pos() {
Err(error) => tracing::error!("{}", error), Ok(()) => {}
Err(error) => tracing::error!("{}", error),
}
} }
} }
_ => {} _ => {}

View File

@@ -253,8 +253,6 @@ impl From<&WindowManager> for State {
layout: workspace.layout.clone(), layout: workspace.layout.clone(),
layout_options: workspace.layout_options, layout_options: workspace.layout_options,
layout_rules: workspace.layout_rules.clone(), layout_rules: workspace.layout_rules.clone(),
layout_options_rules: workspace.layout_options_rules.clone(),
layout_defaults_cache: workspace.layout_defaults_cache.clone(),
work_area_offset_rules: workspace.work_area_offset_rules.clone(), work_area_offset_rules: workspace.work_area_offset_rules.clone(),
layout_flip: workspace.layout_flip, layout_flip: workspace.layout_flip,
workspace_padding: workspace.workspace_padding, workspace_padding: workspace.workspace_padding,

View File

@@ -13,7 +13,6 @@ use crate::FloatingLayerBehaviour;
use crate::HIDING_BEHAVIOUR; use crate::HIDING_BEHAVIOUR;
use crate::IGNORE_IDENTIFIERS; use crate::IGNORE_IDENTIFIERS;
use crate::LAYERED_WHITELIST; use crate::LAYERED_WHITELIST;
use crate::LAYOUT_DEFAULTS;
use crate::MANAGE_IDENTIFIERS; use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES; use crate::MONITOR_INDEX_PREFERENCES;
use crate::NO_TITLEBAR; use crate::NO_TITLEBAR;
@@ -54,7 +53,6 @@ use crate::core::DefaultLayout;
use crate::core::FocusFollowsMouseImplementation; use crate::core::FocusFollowsMouseImplementation;
use crate::core::HidingBehaviour; use crate::core::HidingBehaviour;
use crate::core::Layout; use crate::core::Layout;
use crate::core::LayoutDefaultEntry;
use crate::core::LayoutOptions; use crate::core::LayoutOptions;
use crate::core::MoveBehaviour; use crate::core::MoveBehaviour;
use crate::core::OperationBehaviour; use crate::core::OperationBehaviour;
@@ -217,12 +215,6 @@ pub struct WorkspaceConfig {
/// Layout-specific options /// Layout-specific options
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub layout_options: Option<LayoutOptions>, pub layout_options: Option<LayoutOptions>,
/// Threshold-based layout options rules in the format of threshold => options.
/// When container count >= threshold, the highest matching threshold's options
/// fully replace the base `layout_options`.
/// This follows the same threshold logic as `layout_rules`.
#[serde(skip_serializing_if = "Option::is_none")]
pub layout_options_rules: Option<HashMap<usize, LayoutOptions>>,
/// END OF LIFE FEATURE: Custom Layout /// END OF LIFE FEATURE: Custom Layout
#[deprecated(note = "End of life feature")] #[deprecated(note = "End of life feature")]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@@ -350,11 +342,6 @@ impl From<&Workspace> for WorkspaceConfig {
); );
value.layout_options value.layout_options
}, },
layout_options_rules: if value.layout_options_rules.is_empty() {
None
} else {
Some(value.layout_options_rules.iter().copied().collect())
},
#[allow(deprecated)] #[allow(deprecated)]
custom_layout: value custom_layout: value
.workspace_config .workspace_config
@@ -595,11 +582,6 @@ pub struct StaticConfig {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "schemars", schemars(extend("default" = DEFAULT_CONTAINER_PADDING)))] #[cfg_attr(feature = "schemars", schemars(extend("default" = DEFAULT_CONTAINER_PADDING)))]
pub default_container_padding: Option<i32>, pub default_container_padding: Option<i32>,
/// Per-layout default options and rules, keyed by layout name.
/// Applied as fallback when a workspace does not define its own layout_options or layout_options_rules.
/// If a workspace defines either setting, all global defaults for that layout are completely replaced.
#[serde(skip_serializing_if = "Option::is_none")]
pub layout_defaults: Option<HashMap<DefaultLayout, LayoutDefaultEntry>>,
/// Monitor and workspace configurations /// Monitor and workspace configurations
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub monitors: Option<Vec<MonitorConfig>>, pub monitors: Option<Vec<MonitorConfig>>,
@@ -909,14 +891,6 @@ impl From<&WindowManager> for StaticConfig {
default_container_padding: Option::from( default_container_padding: Option::from(
DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst), DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst),
), ),
layout_defaults: {
let guard = LAYOUT_DEFAULTS.lock();
if guard.is_empty() {
None
} else {
Some(guard.clone())
}
},
monitors: Option::from(monitors), monitors: Option::from(monitors),
window_hiding_behaviour: Option::from(*HIDING_BEHAVIOUR.lock()), window_hiding_behaviour: Option::from(*HIDING_BEHAVIOUR.lock()),
global_work_area_offset: value.work_area_offset, global_work_area_offset: value.work_area_offset,
@@ -1032,12 +1006,6 @@ impl StaticConfig {
DEFAULT_WORKSPACE_PADDING.store(workspace, Ordering::SeqCst); DEFAULT_WORKSPACE_PADDING.store(workspace, Ordering::SeqCst);
} }
if let Some(defaults) = &self.layout_defaults {
*LAYOUT_DEFAULTS.lock() = defaults.clone();
} else {
LAYOUT_DEFAULTS.lock().clear();
}
if let Some(border_width) = self.border_width { if let Some(border_width) = self.border_width {
border_manager::BORDER_WIDTH.store(border_width, Ordering::SeqCst); border_manager::BORDER_WIDTH.store(border_width, Ordering::SeqCst);
} }
@@ -1446,7 +1414,7 @@ impl StaticConfig {
workspace_config.layout = Some(DefaultLayout::Columns); workspace_config.layout = Some(DefaultLayout::Columns);
} }
ws.load_static_config(workspace_config, value.layout_defaults.as_ref())?; ws.load_static_config(workspace_config)?;
} }
} }
@@ -1529,10 +1497,7 @@ impl StaticConfig {
for (j, ws) in m.workspaces_mut().iter_mut().enumerate() { for (j, ws) in m.workspaces_mut().iter_mut().enumerate() {
if let Some(workspace_config) = monitor_config.workspaces.get(j) { if let Some(workspace_config) = monitor_config.workspaces.get(j) {
ws.load_static_config( ws.load_static_config(workspace_config)?;
workspace_config,
value.layout_defaults.as_ref(),
)?;
} }
} }
@@ -1614,7 +1579,7 @@ impl StaticConfig {
for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() { for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() {
if let Some(workspace_config) = monitor_config.workspaces.get(j) { if let Some(workspace_config) = monitor_config.workspaces.get(j) {
ws.load_static_config(workspace_config, value.layout_defaults.as_ref())?; ws.load_static_config(workspace_config)?;
} }
} }
@@ -1697,10 +1662,7 @@ impl StaticConfig {
for (j, ws) in m.workspaces_mut().iter_mut().enumerate() { for (j, ws) in m.workspaces_mut().iter_mut().enumerate() {
if let Some(workspace_config) = monitor_config.workspaces.get(j) { if let Some(workspace_config) = monitor_config.workspaces.get(j) {
ws.load_static_config( ws.load_static_config(workspace_config)?;
workspace_config,
value.layout_defaults.as_ref(),
)?;
} }
} }

View File

@@ -3376,7 +3376,7 @@ impl WindowManager {
let rules: &mut Vec<(usize, Layout)> = &mut workspace.layout_rules; let rules: &mut Vec<(usize, Layout)> = &mut workspace.layout_rules;
rules.retain(|pair| pair.0 != at_container_count); rules.retain(|pair| pair.0 != at_container_count);
rules.push((at_container_count, Layout::Default(layout))); rules.push((at_container_count, Layout::Default(layout)));
rules.sort_by_key(|a| a.0); rules.sort_by(|a, b| a.0.cmp(&b.0));
// If this is the focused workspace on a non-focused screen, let's update it // If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
@@ -3419,7 +3419,7 @@ impl WindowManager {
let rules: &mut Vec<(usize, Layout)> = &mut workspace.layout_rules; let rules: &mut Vec<(usize, Layout)> = &mut workspace.layout_rules;
rules.retain(|pair| pair.0 != at_container_count); rules.retain(|pair| pair.0 != at_container_count);
rules.push((at_container_count, Layout::Custom(layout))); rules.push((at_container_count, Layout::Custom(layout)));
rules.sort_by_key(|a| a.0); rules.sort_by(|a, b| a.0.cmp(&b.0));
// If this is the focused workspace on a non-focused screen, let's update it // If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {

View File

@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::Display; use std::fmt::Display;
@@ -26,7 +25,6 @@ use crate::core::CustomLayout;
use crate::core::CycleDirection; use crate::core::CycleDirection;
use crate::core::DefaultLayout; use crate::core::DefaultLayout;
use crate::core::Layout; use crate::core::Layout;
use crate::core::LayoutDefaultEntry;
use crate::core::LayoutOptions; use crate::core::LayoutOptions;
use crate::core::OperationDirection; use crate::core::OperationDirection;
use crate::core::Rect; use crate::core::Rect;
@@ -63,14 +61,6 @@ pub struct Workspace {
pub layout: Layout, pub layout: Layout,
pub layout_options: Option<LayoutOptions>, pub layout_options: Option<LayoutOptions>,
pub layout_rules: Vec<(usize, Layout)>, pub layout_rules: Vec<(usize, Layout)>,
/// Threshold-based layout options rules (container_count >= threshold -> use these options).
/// Sorted by threshold ascending at load time.
#[serde(default)]
pub layout_options_rules: Vec<(usize, LayoutOptions)>,
/// Cached per-layout defaults from the global `layout_defaults` config setting.
/// Pre-sorted at config load time; used as fallback when workspace has no overrides.
#[serde(skip)]
pub(crate) layout_defaults_cache: HashMap<DefaultLayout, CachedLayoutDefault>,
pub work_area_offset_rules: Vec<(usize, Rect)>, pub work_area_offset_rules: Vec<(usize, Rect)>,
pub layout_flip: Option<Axis>, pub layout_flip: Option<Axis>,
pub workspace_padding: Option<i32>, pub workspace_padding: Option<i32>,
@@ -129,8 +119,6 @@ impl Default for Workspace {
layout: Layout::Default(DefaultLayout::BSP), layout: Layout::Default(DefaultLayout::BSP),
layout_options: None, layout_options: None,
layout_rules: vec![], layout_rules: vec![],
layout_options_rules: vec![],
layout_defaults_cache: HashMap::new(),
work_area_offset_rules: vec![], work_area_offset_rules: vec![],
layout_flip: None, layout_flip: None,
workspace_padding: Option::from(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)), workspace_padding: Option::from(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)),
@@ -177,49 +165,8 @@ pub struct WorkspaceGlobals {
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>, pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
/// Cached per-layout default options (pre-sorted rules) derived from the global `layout_defaults`.
pub(crate) struct CachedLayoutDefault {
pub layout_options: Option<LayoutOptions>,
/// Threshold-based rules, sorted by threshold ascending at load time
pub layout_options_rules: Vec<(usize, LayoutOptions)>,
}
/// Convert an optional HashMap of threshold-based layout options rules into a Vec sorted by
/// threshold ascending.
fn sorted_layout_options_rules(
rules: Option<&HashMap<usize, LayoutOptions>>,
) -> Vec<(usize, LayoutOptions)> {
match rules {
Some(rules) => {
let mut sorted: Vec<(usize, LayoutOptions)> =
rules.iter().map(|(t, o)| (*t, *o)).collect();
sorted.sort_by_key(|(t, _)| *t);
sorted
}
None => vec![],
}
}
/// Find the highest matching threshold rule for the given container count.
/// Rules must be sorted by threshold ascending.
fn resolve_threshold_match(
rules: &[(usize, LayoutOptions)],
container_count: usize,
) -> Option<LayoutOptions> {
rules
.iter()
.rev()
.find(|(threshold, _)| container_count >= *threshold)
.map(|(_, opts)| *opts)
}
impl Workspace { impl Workspace {
pub fn load_static_config( pub fn load_static_config(&mut self, config: &WorkspaceConfig) -> eyre::Result<()> {
&mut self,
config: &WorkspaceConfig,
layout_defaults: Option<&HashMap<DefaultLayout, LayoutDefaultEntry>>,
) -> eyre::Result<()> {
self.name = Option::from(config.name.clone()); self.name = Option::from(config.name.clone());
self.container_padding = config.container_padding; self.container_padding = config.container_padding;
@@ -304,78 +251,19 @@ impl Workspace {
self.layout_flip = config.layout_flip; self.layout_flip = config.layout_flip;
self.floating_layer_behaviour = config.floating_layer_behaviour; self.floating_layer_behaviour = config.floating_layer_behaviour;
self.wallpaper = config.wallpaper.clone(); self.wallpaper = config.wallpaper.clone();
// Load layout options directly (LayoutOptions is used in both config and runtime)
self.layout_options = config.layout_options; self.layout_options = config.layout_options;
// Load threshold-based layout options rules, sorted by threshold ascending
self.layout_options_rules =
sorted_layout_options_rules(config.layout_options_rules.as_ref());
tracing::debug!( tracing::debug!(
"Workspace '{}' loaded layout_options: {:?}, layout_options_rules: {} entries", "Workspace '{}' loaded layout_options: {:?}",
self.name.as_deref().unwrap_or("unnamed"), self.name.as_deref().unwrap_or("unnamed"),
self.layout_options, self.layout_options
self.layout_options_rules.len(),
); );
// Cache per-layout defaults from global layout_defaults, pre-sorting rules
self.layout_defaults_cache = if let Some(defaults) = layout_defaults {
defaults
.iter()
.map(|(layout, entry)| {
(
*layout,
CachedLayoutDefault {
layout_options: entry.layout_options,
layout_options_rules: sorted_layout_options_rules(
entry.layout_options_rules.as_ref(),
),
},
)
})
.collect()
} else {
HashMap::new()
};
self.workspace_config = Some(config.clone()); self.workspace_config = Some(config.clone());
Ok(()) Ok(())
} }
/// Compute effective layout options using the complete-replacement cascade:
///
/// If the workspace defines EITHER `layout_options` OR `layout_options_rules`,
/// it completely replaces the global `layout_defaults` for this layout.
/// Global defaults are only used when the workspace has NEITHER setting.
///
/// Within the effective source (workspace or global):
/// 1. Try threshold match from rules (highest matching threshold wins)
/// 2. If a rule matches -> use it (full replacement of base)
/// 3. Else -> use the base `layout_options`
fn effective_layout_options(&self) -> Option<LayoutOptions> {
let container_count = self.containers().len();
let has_workspace_overrides =
self.layout_options.is_some() || !self.layout_options_rules.is_empty();
let (effective_base, effective_rules): (Option<LayoutOptions>, &[(usize, LayoutOptions)]) =
if has_workspace_overrides {
(self.layout_options, &self.layout_options_rules)
} else {
match &self.layout {
Layout::Default(dl) => match self.layout_defaults_cache.get(dl) {
Some(entry) => (entry.layout_options, &entry.layout_options_rules),
None => (None, &[]),
},
Layout::Custom(_) => (None, &[]),
}
};
resolve_threshold_match(effective_rules, container_count).or(effective_base)
}
pub fn hide(&mut self, omit: Option<isize>) { pub fn hide(&mut self, omit: Option<isize>) {
for window in self.floating_windows_mut().iter_mut().rev() { for window in self.floating_windows_mut().iter_mut().rev() {
let mut should_hide = omit.is_none(); let mut should_hide = omit.is_none();
@@ -696,14 +584,10 @@ impl Workspace {
} else if let Some(window) = &mut self.maximized_window { } else if let Some(window) = &mut self.maximized_window {
window.maximize(); window.maximize();
} else if !self.containers().is_empty() { } else if !self.containers().is_empty() {
let effective_layout_options = self.effective_layout_options();
tracing::debug!( tracing::debug!(
"Workspace '{}' update() - effective_layout_options: {:?} (base: {:?}, rules: {})", "Workspace '{}' update() - self.layout_options before calculate: {:?}",
self.name.as_deref().unwrap_or("unnamed"), self.name.as_deref().unwrap_or("unnamed"),
effective_layout_options, self.layout_options
self.layout_options,
self.layout_options_rules.len(),
); );
let mut layouts = self.layout.as_boxed_arrangement().calculate( let mut layouts = self.layout.as_boxed_arrangement().calculate(
&adjusted_work_area, &adjusted_work_area,
@@ -714,7 +598,7 @@ impl Workspace {
self.layout_flip, self.layout_flip,
&self.resize_dimensions, &self.resize_dimensions,
self.focused_container_idx(), self.focused_container_idx(),
effective_layout_options, self.layout_options,
&self.latest_layout, &self.latest_layout,
); );

View File

@@ -1924,11 +1924,13 @@ fn main() -> eyre::Result<()> {
"Application specific configuration file path has not been set. Try running 'komorebic fetch-asc'\n" "Application specific configuration file path has not been set. Try running 'komorebic fetch-asc'\n"
); );
} }
Some(AppSpecificConfigurationPath::Single(path)) if !path.exists() => { Some(AppSpecificConfigurationPath::Single(path)) => {
println!( if !path.exists() {
"Application specific configuration file path '{}' does not exist. Try running 'komorebic fetch-asc'\n", println!(
path.display() "Application specific configuration file path '{}' does not exist. Try running 'komorebic fetch-asc'\n",
); path.display()
);
}
} }
_ => {} _ => {}
} }

View File

@@ -9683,26 +9683,6 @@
} }
] ]
}, },
"layout_options_rules": {
"description": "Threshold-based layout options rules (container_count >= threshold -> use these options).\nSorted by threshold ascending at load time.",
"type": "array",
"default": [],
"items": {
"type": "array",
"maxItems": 2,
"minItems": 2,
"prefixItems": [
{
"type": "integer",
"format": "uint",
"minimum": 0
},
{
"$ref": "#/$defs/LayoutOptions"
}
]
}
},
"layout_rules": { "layout_rules": {
"type": "array", "type": "array",
"items": { "items": {
@@ -10005,19 +9985,6 @@
} }
] ]
}, },
"layout_options_rules": {
"description": "Threshold-based layout options rules in the format of threshold => options.\nWhen container count >= threshold, the highest matching threshold's options\nfully replace the base `layout_options`.\nThis follows the same threshold logic as `layout_rules`.",
"type": [
"object",
"null"
],
"additionalProperties": false,
"patternProperties": {
"^\\d+$": {
"$ref": "#/$defs/LayoutOptions"
}
}
},
"layout_rules": { "layout_rules": {
"description": "Layout rules in the format of threshold => layout", "description": "Layout rules in the format of threshold => layout",
"type": [ "type": [

View File

@@ -304,16 +304,6 @@
"$ref": "#/$defs/MatchingRule" "$ref": "#/$defs/MatchingRule"
} }
}, },
"layout_defaults": {
"description": "Per-layout default options and rules, keyed by layout name.\nApplied as fallback when a workspace does not define its own layout_options or layout_options_rules.\nIf a workspace defines either setting, all global defaults for that layout are completely replaced.",
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/$defs/LayoutDefaultEntry"
}
},
"manage_rules": { "manage_rules": {
"description": "Individual window force-manage rules", "description": "Individual window force-manage rules",
"type": [ "type": [
@@ -3300,36 +3290,6 @@
"colours" "colours"
] ]
}, },
"LayoutDefaultEntry": {
"description": "Per-layout default options entry for the `layout_defaults` global setting.\nContains both base layout options and threshold-based layout options rules.",
"type": "object",
"properties": {
"layout_options": {
"description": "Default layout options for this layout",
"anyOf": [
{
"$ref": "#/$defs/LayoutOptions"
},
{
"type": "null"
}
]
},
"layout_options_rules": {
"description": "Threshold-based layout options rules in the format of threshold => options.\nWhen container count >= threshold, the highest matching threshold's options\nfully replace the base `layout_options`.",
"type": [
"object",
"null"
],
"additionalProperties": false,
"patternProperties": {
"^\\d+$": {
"$ref": "#/$defs/LayoutOptions"
}
}
}
}
},
"LayoutOptions": { "LayoutOptions": {
"description": "Options for specific layouts", "description": "Options for specific layouts",
"type": "object", "type": "object",
@@ -4254,19 +4214,6 @@
} }
] ]
}, },
"layout_options_rules": {
"description": "Threshold-based layout options rules in the format of threshold => options.\nWhen container count >= threshold, the highest matching threshold's options\nfully replace the base `layout_options`.\nThis follows the same threshold logic as `layout_rules`.",
"type": [
"object",
"null"
],
"additionalProperties": false,
"patternProperties": {
"^\\d+$": {
"$ref": "#/$defs/LayoutOptions"
}
}
},
"layout_rules": { "layout_rules": {
"description": "Layout rules in the format of threshold => layout", "description": "Layout rules in the format of threshold => layout",
"type": [ "type": [