max / audiofiles
- Co-Authored-By
- Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 files changed,
+350 insertions,
-344 deletions
| @@ -3,21 +3,14 @@ | |||
| 3 | 3 | version = 4 | |
| 4 | 4 | ||
| 5 | 5 | [[package]] | |
| 6 | - | name = "ab_glyph" | |
| 7 | - | version = "0.2.32" | |
| 6 | + | name = "accesskit" | |
| 7 | + | version = "0.24.0" | |
| 8 | 8 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 9 | - | checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" | |
| 9 | + | checksum = "5351dcebb14b579ccab05f288596b2ae097005be7ee50a7c3d4ca9d0d5a66f6a" | |
| 10 | 10 | dependencies = [ | |
| 11 | - | "ab_glyph_rasterizer", | |
| 12 | - | "owned_ttf_parser", | |
| 11 | + | "uuid", | |
| 13 | 12 | ] | |
| 14 | 13 | ||
| 15 | - | [[package]] | |
| 16 | - | name = "ab_glyph_rasterizer" | |
| 17 | - | version = "0.1.10" | |
| 18 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 19 | - | checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" | |
| 20 | - | ||
| 21 | 14 | [[package]] | |
| 22 | 15 | name = "adler2" | |
| 23 | 16 | version = "2.0.1" | |
| @@ -59,21 +52,21 @@ | |||
| 59 | 52 | ||
| 60 | 53 | [[package]] | |
| 61 | 54 | name = "alsa" | |
| 62 | - | version = "0.9.1" | |
| 55 | + | version = "0.11.0" | |
| 63 | 56 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 64 | - | checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" | |
| 57 | + | checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" | |
| 65 | 58 | dependencies = [ | |
| 66 | 59 | "alsa-sys", | |
| 67 | - | "bitflags 2.11.0", | |
| 60 | + | "bitflags 2.12.1", | |
| 68 | 61 | "cfg-if", | |
| 69 | 62 | "libc", | |
| 70 | 63 | ] | |
| 71 | 64 | ||
| 72 | 65 | [[package]] | |
| 73 | 66 | name = "alsa-sys" | |
| 74 | - | version = "0.3.1" | |
| 67 | + | version = "0.4.0" | |
| 75 | 68 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 76 | - | checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" | |
| 69 | + | checksum = "ad7569085a265dd3f607ebecce7458eaab2132a84393534c95b18dcbc3f31e04" | |
| 77 | 70 | dependencies = [ | |
| 78 | 71 | "libc", | |
| 79 | 72 | "pkg-config", | |
| @@ -81,23 +74,30 @@ | |||
| 81 | 74 | ||
| 82 | 75 | [[package]] | |
| 83 | 76 | name = "android-activity" | |
| 84 | - | version = "0.6.0" | |
| 77 | + | version = "0.6.1" | |
| 85 | 78 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 86 | - | checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" | |
| 79 | + | checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" | |
| 87 | 80 | dependencies = [ | |
| 88 | 81 | "android-properties", | |
| 89 | - | "bitflags 2.11.0", | |
| 82 | + | "bitflags 2.12.1", | |
| 90 | 83 | "cc", | |
| 91 | - | "cesu8", | |
| 92 | - | "jni", | |
| 93 | - | "jni-sys", | |
| 84 | + | "jni 0.22.4", | |
| 94 | 85 | "libc", | |
| 95 | 86 | "log", | |
| 96 | - | "ndk 0.9.0", | |
| 87 | + | "ndk", | |
| 97 | 88 | "ndk-context", | |
| 98 | - | "ndk-sys 0.6.0+11769913", | |
| 89 | + | "ndk-sys", | |
| 99 | 90 | "num_enum", | |
| 100 | - | "thiserror 1.0.69", | |
| 91 | + | "thiserror 2.0.18", | |
| 92 | + | ] | |
| 93 | + | ||
| 94 | + | [[package]] | |
| 95 | + | name = "android-build" | |
| 96 | + | version = "0.1.4" | |
| 97 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 98 | + | checksum = "f9fc9904ad2ad097c3c1cfe2eacaaf0fc24710936fa9ed941cb310b7c6ed2ab7" | |
| 99 | + | dependencies = [ | |
| 100 | + | "windows-sys 0.52.0", | |
| 101 | 101 | ] | |
| 102 | 102 | ||
| 103 | 103 | [[package]] | |
| @@ -117,9 +117,9 @@ | |||
| 117 | 117 | ||
| 118 | 118 | [[package]] | |
| 119 | 119 | name = "anyhow" | |
| 120 | - | version = "1.0.101" | |
| 120 | + | version = "1.0.102" | |
| 121 | 121 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 122 | - | checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" | |
| 122 | + | checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" | |
| 123 | 123 | ||
| 124 | 124 | [[package]] | |
| 125 | 125 | name = "arboard" | |
| @@ -130,7 +130,7 @@ | |||
| 130 | 130 | "clipboard-win", | |
| 131 | 131 | "image", | |
| 132 | 132 | "log", | |
| 133 | - | "objc2 0.6.3", | |
| 133 | + | "objc2 0.6.4", | |
| 134 | 134 | "objc2-app-kit 0.3.2", | |
| 135 | 135 | "objc2-core-foundation", | |
| 136 | 136 | "objc2-core-graphics", | |
| @@ -165,15 +165,6 @@ | |||
| 165 | 165 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 166 | 166 | checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" | |
| 167 | 167 | ||
| 168 | - | [[package]] | |
| 169 | - | name = "ash" | |
| 170 | - | version = "0.38.0+1.3.281" | |
| 171 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 172 | - | checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" | |
| 173 | - | dependencies = [ | |
| 174 | - | "libloading 0.8.9", | |
| 175 | - | ] | |
| 176 | - | ||
| 177 | 168 | [[package]] | |
| 178 | 169 | name = "ashpd" | |
| 179 | 170 | version = "0.11.1" | |
| @@ -258,7 +249,7 @@ | |||
| 258 | 249 | "futures-lite", | |
| 259 | 250 | "parking", | |
| 260 | 251 | "polling", | |
| 261 | - | "rustix 1.1.3", | |
| 252 | + | "rustix 1.1.4", | |
| 262 | 253 | "slab", | |
| 263 | 254 | "windows-sys 0.61.2", | |
| 264 | 255 | ] | |
| @@ -300,7 +291,7 @@ | |||
| 300 | 291 | "cfg-if", | |
| 301 | 292 | "event-listener", | |
| 302 | 293 | "futures-lite", | |
| 303 | - | "rustix 1.1.3", | |
| 294 | + | "rustix 1.1.4", | |
| 304 | 295 | ] | |
| 305 | 296 | ||
| 306 | 297 | [[package]] | |
| @@ -311,14 +302,14 @@ | |||
| 311 | 302 | dependencies = [ | |
| 312 | 303 | "proc-macro2", | |
| 313 | 304 | "quote", | |
| 314 | - | "syn 2.0.116", | |
| 305 | + | "syn 2.0.117", | |
| 315 | 306 | ] | |
| 316 | 307 | ||
| 317 | 308 | [[package]] | |
| 318 | 309 | name = "async-signal" | |
| 319 | - | version = "0.2.13" | |
| 310 | + | version = "0.2.14" | |
| 320 | 311 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 321 | - | checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" | |
| 312 | + | checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" | |
| 322 | 313 | dependencies = [ | |
| 323 | 314 | "async-io", | |
| 324 | 315 | "async-lock", | |
| @@ -326,7 +317,7 @@ | |||
| 326 | 317 | "cfg-if", | |
| 327 | 318 | "futures-core", | |
| 328 | 319 | "futures-io", | |
| 329 | - | "rustix 1.1.3", | |
| 320 | + | "rustix 1.1.4", | |
| 330 | 321 | "signal-hook-registry", | |
| 331 | 322 | "slab", | |
| 332 | 323 | "windows-sys 0.61.2", | |
| @@ -346,7 +337,7 @@ | |||
| 346 | 337 | dependencies = [ | |
| 347 | 338 | "proc-macro2", | |
| 348 | 339 | "quote", | |
| 349 | - | "syn 2.0.116", | |
| 340 | + | "syn 2.0.117", | |
| 350 | 341 | ] | |
| 351 | 342 | ||
| 352 | 343 | [[package]] | |
| @@ -401,6 +392,7 @@ | |||
| 401 | 392 | "tempfile", | |
| 402 | 393 | "thiserror 2.0.18", | |
| 403 | 394 | "tokio", | |
| 395 | + | "toml", | |
| 404 | 396 | "tracing", | |
| 405 | 397 | "tracing-subscriber", | |
| 406 | 398 | "tray-icon", | |
| @@ -430,7 +422,7 @@ | |||
| 430 | 422 | "egui_extras", | |
| 431 | 423 | "hound", | |
| 432 | 424 | "libc", | |
| 433 | - | "objc2 0.6.3", | |
| 425 | + | "objc2 0.6.4", | |
| 434 | 426 | "objc2-app-kit 0.3.2", | |
| 435 | 427 | "objc2-foundation 0.3.2", | |
| 436 | 428 | "parking_lot", | |
| @@ -444,8 +436,8 @@ | |||
| 444 | 436 | "thiserror 2.0.18", | |
| 445 | 437 | "toml", | |
| 446 | 438 | "tracing", | |
| 447 | - | "windows 0.62.2", | |
| 448 | - | "windows-core 0.62.2", | |
| 439 | + | "windows", | |
| 440 | + | "windows-core", | |
| 449 | 441 | ] | |
| 450 | 442 | ||
| 451 | 443 | [[package]] | |
| @@ -519,9 +511,9 @@ | |||
| 519 | 511 | ||
| 520 | 512 | [[package]] | |
| 521 | 513 | name = "autocfg" | |
| 522 | - | version = "1.5.0" | |
| 514 | + | version = "1.5.1" | |
| 523 | 515 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 524 | - | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" | |
| 516 | + | checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" | |
| 525 | 517 | ||
| 526 | 518 | [[package]] | |
| 527 | 519 | name = "base64" | |
| @@ -535,38 +527,20 @@ | |||
| 535 | 527 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 536 | 528 | checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" | |
| 537 | 529 | ||
| 538 | - | [[package]] | |
| 539 | - | name = "bindgen" | |
| 540 | - | version = "0.72.1" | |
| 541 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 542 | - | checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" | |
| 543 | - | dependencies = [ | |
| 544 | - | "bitflags 2.11.0", | |
| 545 | - | "cexpr", | |
| 546 | - | "clang-sys", | |
| 547 | - | "itertools", | |
| 548 | - | "proc-macro2", | |
| 549 | - | "quote", | |
| 550 | - | "regex", | |
| 551 | - | "rustc-hash 2.1.1", | |
| 552 | - | "shlex", | |
| 553 | - | "syn 2.0.116", | |
| 554 | - | ] | |
| 555 | - | ||
| 556 | 530 | [[package]] | |
| 557 | 531 | name = "bit-set" | |
| 558 | - | version = "0.8.0" | |
| 532 | + | version = "0.9.1" | |
| 559 | 533 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 560 | - | checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" | |
| 534 | + | checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" | |
| 561 | 535 | dependencies = [ | |
| 562 | 536 | "bit-vec", | |
| 563 | 537 | ] | |
| 564 | 538 | ||
| 565 | 539 | [[package]] | |
| 566 | 540 | name = "bit-vec" | |
| 567 | - | version = "0.8.0" | |
| 541 | + | version = "0.9.1" | |
| 568 | 542 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 569 | - | checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" | |
| 543 | + | checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" | |
| 570 | 544 | ||
| 571 | 545 | [[package]] | |
| 572 | 546 | name = "bitflags" | |
| @@ -576,9 +550,9 @@ | |||
| 576 | 550 | ||
| 577 | 551 | [[package]] | |
| 578 | 552 | name = "bitflags" | |
| 579 | - | version = "2.11.0" | |
| 553 | + | version = "2.12.1" | |
| 580 | 554 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 581 | - | checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" | |
| 555 | + | checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" | |
| 582 | 556 | dependencies = [ | |
| 583 | 557 | "serde_core", | |
| 584 | 558 | ] | |
| @@ -622,7 +596,7 @@ | |||
| 622 | 596 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 623 | 597 | checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" | |
| 624 | 598 | dependencies = [ | |
| 625 | - | "objc2 0.6.3", | |
| 599 | + | "objc2 0.6.4", | |
| 626 | 600 | ] | |
| 627 | 601 | ||
| 628 | 602 | [[package]] | |
| @@ -646,9 +620,9 @@ | |||
| 646 | 620 | ||
| 647 | 621 | [[package]] | |
| 648 | 622 | name = "bumpalo" | |
| 649 | - | version = "3.19.1" | |
| 623 | + | version = "3.20.3" | |
| 650 | 624 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 651 | - | checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" | |
| 625 | + | checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" | |
| 652 | 626 | ||
| 653 | 627 | [[package]] | |
| 654 | 628 | name = "bytemuck" | |
| @@ -667,7 +641,7 @@ | |||
| 667 | 641 | dependencies = [ | |
| 668 | 642 | "proc-macro2", | |
| 669 | 643 | "quote", | |
| 670 | - | "syn 2.0.116", | |
| 644 | + | "syn 2.0.117", | |
| 671 | 645 | ] | |
| 672 | 646 | ||
| 673 | 647 | [[package]] | |
| @@ -694,7 +668,7 @@ | |||
| 694 | 668 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 695 | 669 | checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" | |
| 696 | 670 | dependencies = [ | |
| 697 | - | "bitflags 2.11.0", | |
| 671 | + | "bitflags 2.12.1", | |
| 698 | 672 | "cairo-sys-rs", | |
| 699 | 673 | "glib", | |
| 700 | 674 | "libc", | |
| @@ -719,7 +693,7 @@ | |||
| 719 | 693 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 720 | 694 | checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" | |
| 721 | 695 | dependencies = [ | |
| 722 | - | "bitflags 2.11.0", | |
| 696 | + | "bitflags 2.12.1", | |
| 723 | 697 | "log", | |
| 724 | 698 | "polling", | |
| 725 | 699 | "rustix 0.38.44", | |
| @@ -733,9 +707,9 @@ | |||
| 733 | 707 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 734 | 708 | checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" | |
| 735 | 709 | dependencies = [ | |
| 736 | - | "bitflags 2.11.0", | |
| 710 | + | "bitflags 2.12.1", | |
| 737 | 711 | "polling", | |
| 738 | - | "rustix 1.1.3", | |
| 712 | + | "rustix 1.1.4", | |
| 739 | 713 | "slab", | |
| 740 | 714 | "tracing", | |
| 741 | 715 | ] | |
| @@ -759,16 +733,16 @@ | |||
| 759 | 733 | checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" | |
| 760 | 734 | dependencies = [ | |
| 761 | 735 | "calloop 0.14.4", | |
| 762 | - | "rustix 1.1.3", | |
| 736 | + | "rustix 1.1.4", | |
| 763 | 737 | "wayland-backend", | |
| 764 | 738 | "wayland-client", | |
| 765 | 739 | ] | |
| 766 | 740 | ||
| 767 | 741 | [[package]] | |
| 768 | 742 | name = "cc" | |
| 769 | - | version = "1.2.56" | |
| 743 | + | version = "1.2.63" | |
| 770 | 744 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 771 | - | checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" | |
| 745 | + | checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" | |
| 772 | 746 | dependencies = [ | |
| 773 | 747 | "find-msvc-tools", | |
| 774 | 748 | "jobserver", | |
| @@ -782,15 +756,6 @@ | |||
| 782 | 756 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 783 | 757 | checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" | |
| 784 | 758 | ||
| 785 | - | [[package]] | |
| 786 | - | name = "cexpr" | |
| 787 | - | version = "0.6.0" | |
| 788 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 789 | - | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" | |
| 790 | - | dependencies = [ | |
| 791 | - | "nom", | |
| 792 | - | ] | |
| 793 | - | ||
| 794 | 759 | [[package]] | |
| 795 | 760 | name = "cfg-expr" | |
| 796 | 761 | version = "0.15.8" | |
| @@ -871,17 +836,6 @@ | |||
| 871 | 836 | "zeroize", | |
| 872 | 837 | ] | |
| 873 | 838 | ||
| 874 | - | [[package]] | |
| 875 | - | name = "clang-sys" | |
| 876 | - | version = "1.8.1" | |
| 877 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 878 | - | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" | |
| 879 | - | dependencies = [ | |
| 880 | - | "glob", | |
| 881 | - | "libc", | |
| 882 | - | "libloading 0.8.9", | |
| 883 | - | ] | |
| 884 | - | ||
| 885 | 839 | [[package]] | |
| 886 | 840 | name = "clipboard-win" | |
| 887 | 841 | version = "5.4.1" | |
| @@ -893,14 +847,22 @@ | |||
| 893 | 847 | ||
| 894 | 848 | [[package]] | |
| 895 | 849 | name = "codespan-reporting" | |
| 896 | - | version = "0.11.1" | |
| 850 | + | version = "0.13.1" | |
| 897 | 851 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 898 | - | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" | |
| 852 | + | checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" | |
| 899 | 853 | dependencies = [ | |
| 900 | - | "termcolor", | |
| 901 | 854 | "unicode-width", | |
| 902 | 855 | ] | |
| 903 | 856 | ||
| 857 | + | [[package]] | |
| 858 | + | name = "color" | |
| 859 | + | version = "0.3.3" | |
| 860 | + | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 861 | + | checksum = "2ec7c5eb7a16992b1904d76c517d170ab353b0e0b3d5a0c81a8a0cd1037893cf" | |
| 862 | + | dependencies = [ | |
| 863 | + | "bytemuck", | |
| 864 | + | ] | |
| 865 | + | ||
| 904 | 866 | [[package]] | |
| 905 | 867 | name = "combine" | |
| 906 | 868 | version = "4.6.7" | |
| @@ -992,32 +954,26 @@ | |||
| 992 | 954 | ||
| 993 | 955 | [[package]] | |
| 994 | 956 | name = "coreaudio-rs" | |
| 995 | - | version = "0.11.3" | |
| 957 | + | version = "0.14.2" | |
| 996 | 958 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 997 | - | checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" | |
| 959 | + | checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58" | |
| 998 | 960 | dependencies = [ | |
| 999 | - | "bitflags 1.3.2", | |
| 1000 | - | "core-foundation-sys", | |
| 1001 | - | "coreaudio-sys", | |
| 1002 | - | ] | |
| 1003 | - | ||
| 1004 | - | [[package]] | |
| 1005 | - | name = "coreaudio-sys" | |
| 1006 | - | version = "0.2.17" | |
| 1007 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1008 | - | checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6" | |
| 1009 | - | dependencies = [ | |
| 1010 | - | "bindgen", | |
| 961 | + | "bitflags 2.12.1", | |
| 962 | + | "libc", | |
| 963 | + | "objc2-audio-toolbox", | |
| 964 | + | "objc2-core-audio", | |
| 965 | + | "objc2-core-audio-types", | |
| 966 | + | "objc2-core-foundation", | |
| 1011 | 967 | ] | |
| 1012 | 968 | ||
| 1013 | 969 | [[package]] | |
| 1014 | 970 | name = "coremidi" | |
| 1015 | - | version = "0.8.0" | |
| 971 | + | version = "0.9.0" | |
| 1016 | 972 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1017 | - | checksum = "964eb3e10ea8b0d29c797086aab3ca730f75e06dced0cb980642fd274a5cca30" | |
| 973 | + | checksum = "f32f5d3e1b800aa7ea10e9e83c87cbc1daef1397ee86cd599458b3c3b136428a" | |
| 1018 | 974 | dependencies = [ | |
| 1019 | 975 | "block", | |
| 1020 | - | "core-foundation 0.9.4", | |
| 976 | + | "core-foundation 0.10.1", | |
| 1021 | 977 | "core-foundation-sys", | |
| 1022 | 978 | "coremidi-sys", | |
| 1023 | 979 | ] | |
| @@ -1033,25 +989,32 @@ | |||
| 1033 | 989 | ||
| 1034 | 990 | [[package]] | |
| 1035 | 991 | name = "cpal" | |
| 1036 | - | version = "0.15.3" | |
| 992 | + | version = "0.17.3" | |
| 1037 | 993 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1038 | - | checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" | |
| 994 | + | checksum = "d8942da362c0f0d895d7cac616263f2f9424edc5687364dfd1d25ef7eba506d7" | |
| 1039 | 995 | dependencies = [ | |
| 1040 | 996 | "alsa", | |
| 1041 | - | "core-foundation-sys", | |
| 1042 | 997 | "coreaudio-rs", | |
| 1043 | 998 | "dasp_sample", | |
| 1044 | - | "jni", | |
| 999 | + | "jni 0.21.1", | |
| 1045 | 1000 | "js-sys", | |
| 1046 | 1001 | "libc", | |
| 1047 | 1002 | "mach2", | |
| 1048 | - | "ndk 0.8.0", | |
| 1003 | + | "ndk", | |
| 1049 | 1004 | "ndk-context", | |
| 1050 | - | "oboe", | |
| 1005 | + | "num-derive", | |
| 1006 | + | "num-traits", | |
| 1007 | + | "objc2 0.6.4", | |
| 1008 | + | "objc2-audio-toolbox", | |
| 1009 | + | "objc2-avf-audio", | |
| 1010 | + | "objc2-core-audio", | |
| 1011 | + | "objc2-core-audio-types", | |
| 1012 | + | "objc2-core-foundation", | |
| 1013 | + | "objc2-foundation 0.3.2", | |
| 1051 | 1014 | "wasm-bindgen", | |
| 1052 | 1015 | "wasm-bindgen-futures", | |
| 1053 | 1016 | "web-sys", | |
| 1054 | - | "windows 0.54.0", | |
| 1017 | + | "windows", | |
| 1055 | 1018 | ] | |
| 1056 | 1019 | ||
| 1057 | 1020 | [[package]] | |
| @@ -1175,32 +1138,32 @@ | |||
| 1175 | 1138 | ||
| 1176 | 1139 | [[package]] | |
| 1177 | 1140 | name = "dispatch2" | |
| 1178 | - | version = "0.3.0" | |
| 1141 | + | version = "0.3.1" | |
| 1179 | 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1180 | - | checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" | |
| 1143 | + | checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" | |
| 1181 | 1144 | dependencies = [ | |
| 1182 | - | "bitflags 2.11.0", | |
| 1145 | + | "bitflags 2.12.1", | |
| 1183 | 1146 | "block2 0.6.2", | |
| 1184 | 1147 | "libc", | |
| 1185 | - | "objc2 0.6.3", | |
| 1148 | + | "objc2 0.6.4", | |
| 1186 | 1149 | ] | |
| 1187 | 1150 | ||
| 1188 | 1151 | [[package]] | |
| 1189 | 1152 | name = "displaydoc" | |
| 1190 | - | version = "0.2.5" | |
| 1153 | + | version = "0.2.6" | |
| 1191 | 1154 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1192 | - | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" | |
| 1155 | + | checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" | |
| 1193 | 1156 | dependencies = [ | |
| 1194 | 1157 | "proc-macro2", |
Lines truncated
| @@ -12,14 +12,14 @@ | |||
| 12 | 12 | audiofiles-browser = { path = "crates/audiofiles-browser" } | |
| 13 | 13 | audiofiles-sync = { path = "crates/audiofiles-sync" } | |
| 14 | 14 | audiofiles-rhai = { path = "crates/audiofiles-rhai" } | |
| 15 | - | egui = { version = "0.31.1", default-features = false, features = ["default_fonts"] } | |
| 16 | - | egui_extras = { version = "0.31.1", default-features = false } | |
| 17 | - | eframe = { version = "0.31.1", default-features = false, features = ["default_fonts", "glow"] } | |
| 18 | - | cpal = "=0.15.3" | |
| 15 | + | egui = { version = "0.34", default-features = false, features = ["default_fonts"] } | |
| 16 | + | egui_extras = { version = "0.34", default-features = false } | |
| 17 | + | eframe = { version = "0.34", default-features = false, features = ["default_fonts", "glow"] } | |
| 18 | + | cpal = "0.17" | |
| 19 | 19 | rusqlite = { version = "0.31.0", features = ["bundled"] } | |
| 20 | 20 | thiserror = "2.0.18" | |
| 21 | 21 | sha2 = "0.10.9" | |
| 22 | - | symphonia = { version = "0.5.5", default-features = false, features = ["wav", "aiff", "mp3", "flac", "ogg", "vorbis", "pcm"] } | |
| 22 | + | symphonia = { version = "0.5.5", default-features = false, features = ["wav", "aiff", "mp3", "flac", "ogg", "vorbis", "pcm", "aac", "alac", "isomp4", "caf"] } | |
| 23 | 23 | parking_lot = "0.12.5" | |
| 24 | 24 | dirs = "6.0.0" | |
| 25 | 25 | stratum-dsp = "=1.0.0" | |
| @@ -34,7 +34,7 @@ | |||
| 34 | 34 | tracing = "0.1.44" | |
| 35 | 35 | tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } | |
| 36 | 36 | rhai = { version = "1.21", features = ["sync"] } | |
| 37 | - | tray-icon = "0.21" | |
| 37 | + | tray-icon = "0.22" | |
| 38 | 38 | tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "sync"] } | |
| 39 | 39 | uuid = { version = "1", features = ["v4"] } | |
| 40 | 40 | base64 = "0.22" | |
| @@ -45,7 +45,7 @@ | |||
| 45 | 45 | open = "5" | |
| 46 | 46 | rayon = "1.10" | |
| 47 | 47 | libc = "0.2" | |
| 48 | - | midir = "0.10" | |
| 48 | + | midir = "0.11" | |
| 49 | 49 | docengine = { path = "../../MNW/shared/docengine" } | |
| 50 | 50 | tagtree = { path = "../../MNW/shared/tagtree" } | |
| 51 | 51 | theme-common = { path = "../../MNW/shared/theme-common" } |
| @@ -1,40 +1,37 @@ | |||
| 1 | 1 | # audiofiles — Todo | |
| 2 | 2 | ||
| 3 | - | **Last updated:** 2026-05-31 late evening (post-launch-eve fuzz pass). | |
| 3 | + | **Last updated:** 2026-06-02 (rust-quality + egui 0.34 sweep). | |
| 4 | 4 | ||
| 5 | 5 | ## Status | |
| 6 | 6 | ||
| 7 | - | 5 launch-blocker fixes landed in commit `c18d7e1` (unpushed). Compile clean; preferences (6/6) + export (56/56) targeted tests green. See commit message for the fix list. | |
| 7 | + | Launch shipped 2026-06-01 (see `/Users/max/Code/launchplan_final.md`). Post-launch rust-quality session 2026-06-02 landed all 5 rust-fuzz items: typed errors, `synckit.toml` parser, dep bumps (eframe/egui 0.31→0.34, cpal 0.15→0.17, midir 0.10→0.11, tray-icon 0.21→0.22), full egui 0.34 deprecation sweep (App trait shape change, ~100 deprecations cleared, 26 panel sites restructured). 269/269 tests green, 0 deprecation warnings. Unpushed; uncommitted. | |
| 8 | 8 | ||
| 9 | - | ## Open before launch (Monday 2026-06-01) | |
| 9 | + | ## Manual verification needed (before commit) | |
| 10 | 10 | ||
| 11 | - | - [ ] Push `main` to all remotes (commit `c18d7e1`) | |
| 12 | - | - [ ] Build signed binaries per platform (macOS arm64 primary; iOS per launch-platforms policy; Win/Linux unsupported per policy) | |
| 13 | - | - [ ] Notarize macOS DMG; verify with `spctl -a -vvv -t install` | |
| 14 | - | - [ ] Cargo.toml version bump for launch release; CHANGELOG entry | |
| 15 | - | - [ ] First-launch smoke test on a clean macOS account: activation → vault setup → drop a folder → see no emoji prefixes → open About (Cmd+I) → toggle update check off → confirm preferences.json written | |
| 11 | + | - [ ] Launch the app and eyeball the egui 0.34 restructure: confirm toolbar / sidebar / detail / file-list / footer panels render in the right places across all screens (activation, vault setup, browser, import wizard, export wizard, db-error). Structural change from `Panel::show(ctx)` to `show_inside(ui)` is mechanically correct but unverified visually. | |
| 16 | 12 | ||
| 17 | 13 | ## Run #9 deferrals (Phase 4) | |
| 18 | 14 | ||
| 19 | 15 | ### Trust / data integrity (creator-fuzz) | |
| 20 | - | - [ ] **Export silently strips BWF / iXML / smpl / cue / ID3 chunks on conversion.** `encode.rs` / `encode_aiff.rs` write only fmt+data / COMM+SSND. At minimum, warn on the Configure step when format != Original. Long-term: round-trip BWF `bext`, `smpl` loop points, `cue ` markers in the WAV encoder. | |
| 21 | - | - [ ] **Format support gaps.** `AUDIO_EXTENSIONS` (`crates/audiofiles-core/src/util.rs:6`) excludes `.m4a`/`.alac`, `.opus`, `.w64`, `.caf`, `.bwf`. Either expand or surface skip count on import with extension breakdown. | |
| 22 | - | - [ ] **Export not atomic.** `runner.rs` writes directly to dest; partial files survive a mid-write kill. Switch to `dest.tmp` + `fs::rename` on success; cleanup tmp on error. | |
| 23 | - | - [ ] **Edit-result import then `remove_file` not atomic across processes.** `state/import_workflow.rs:1288-1297` deletes temp before VFS link is confirmed. | |
| 24 | - | - [ ] **`SampleStore::remove` deletes DB row first, then file — orphan blob on file-delete failure.** `store.rs:191`. | |
| 16 | + | - [x] **Export silently strips BWF / iXML / smpl / cue / ID3 chunks on conversion.** Added a one-line warning under the Format radio group in `ui/export_screens.rs` when format != Original. Long-term (still open): round-trip BWF `bext`, `smpl` loop points, `cue ` markers in the WAV encoder. | |
| 17 | + | - [x] **Format support gaps in `AUDIO_EXTENSIONS`.** Added `m4a`, `alac`, `caf`, `bwf` to `util.rs`; enabled symphonia `aac`, `alac`, `isomp4`, `caf` features in the workspace `Cargo.toml`. Skipped: `opus` (no symphonia codec) and `w64` (no symphonia format) — documented in the extension list rustdoc. | |
| 18 | + | - [x] **Export not atomic.** Added `write_atomic` helper in `export/runner.rs`: each format branch (Original copy / Wav encode / Aiff encode) now writes to `dest.audiofiles_tmp` and `fs::rename`s into place. On any error the tmp is removed, so a killed export leaves no partial files in the user's export dir. | |
| 19 | + | - [x] **Edit-result temp leaked on import failure** — `state/import_workflow.rs:1288-1297` only ran `remove_file` on the success branch, leaking the temp on every `import_file` error. Restructured to capture `import_result`, unconditionally remove the temp, then match the result. (The "VFS link not confirmed" concern in the original note is a non-issue: once `import_file` copies the bytes into the content-addressed store, the temp is redundant; the worst failure downstream of that is an orphan blob in the store, recoverable via `remove_orphaned_samples`.) | |
| 20 | + | - [x] **`SampleStore::remove` deletes DB row first, then file** — inverted to file-first. ENOENT on the file is now tolerated (idempotent). Failure mode flipped from silent orphan-blob accumulation to a visible dangling row the user can retry. Comment + test name + assertions all updated; added `remove_tolerates_missing_file` test. | |
| 25 | 21 | ||
| 26 | 22 | ### UX polish (use-fuzz) | |
| 27 | 23 | - [ ] **Preserve form fields on signup-style error swaps.** (Not applicable to audiofiles per se, but the pattern — re-render the wizard step with user input — would apply if any wizard step ever fails inline.) | |
| 28 | - | - [ ] **Sort-arrow glyphs U+25B2 / U+25BC** in `file_list.rs::draw_sort_header` are documented exceptions to the no-emoji rule. Get explicit user sign-off or replace with text ("asc" / "desc"). | |
| 29 | - | - [ ] **First-launch welcome cannot be dismissed inline** — only disappears after successful import or "Show welcome" toggle. Add a small "Dismiss" link near the bottom of `file_list.rs:44-96`. | |
| 30 | - | - [ ] **Toolbar buttons have no visual hierarchy** — Import, Export, Sync, Settings, Help all render identically. When library is empty, paint Import with `widgets::primary_button` styling. | |
| 31 | - | - [ ] **Error toast / status copy nits**: `activation.rs:119` "Activating..." should use U+2026 (`…`); `library.rs:100` "Locate failed: {e}" is terse — make it "Could not locate sample on disk — {e}". | |
| 24 | + | - [x] **Sort-arrow glyphs U+25B2 / U+25BC** — kept as documented exception per user sign-off 2026-06-02. | |
| 25 | + | - [x] **First-launch welcome cannot be dismissed inline** — added a small "Dismiss" link at the bottom of the welcome (`ui/file_list.rs`); reuses the existing `state.dismiss_first_launch_hint()` which already persists the pref. | |
| 26 | + | - [x] **Toolbar Import emphasis when library is empty** — `ui/toolbar.rs` now bolds the Import label when contents+search+filters are all empty. Single conditional, no extra widget. | |
| 27 | + | - [x] **Error toast / status copy nits** — `activation.rs:119` `Activating...` → `Activating…` (U+2026); `library.rs:100` `Locate failed: {e}` → `Could not locate sample on disk — {e}` (U+2014). | |
| 32 | 28 | ||
| 33 | 29 | ### Rust quality (rust-fuzz) | |
| 34 | - | - [ ] **`Result<_, String>` leaks past the typed-error wall** in `crates/audiofiles-browser/src/state/playback.rs:9,20`, `crates/audiofiles-app/src/midi.rs:35`, `crates/audiofiles-app/src/license.rs:209`. Fold into `PreviewError` / `MidiError` / `LicenseError`. | |
| 35 | - | - [ ] **`unwrap()` after `is_none()` check** at `crates/audiofiles-browser/src/backend/direct.rs:571, 595` — idiomatic refactor to `idx.get_or_insert_with(...)`. | |
| 36 | - | - [ ] **Hand-rolled `synckit.toml` parser** at `crates/audiofiles-app/src/main.rs:134-149` — `toml` is already a workspace dep; replace with `toml::from_str::<HashMap<String, String>>()`. | |
| 37 | - | - [ ] **Dependency duplication** — `cargo tree -d` shows four `windows-core` versions (0.54/0.56/0.58/0.62) and two `objc2-foundation` (0.2.2/0.3.2). Bloats Win/macOS binaries; bump `tray-icon` / `objc2` callers in sync. | |
| 30 | + | - [x] **`Result<_, String>` leaks past the typed-error wall** — folded into `PreviewError` (new `InvalidHash`/`FileNotFound` variants), new `MidiError` (with `#[from] InitError`/`ConnectError`), new `DeactivationError` for `license::deactivate_key`. | |
| 31 | + | - [x] **`unwrap()` after `is_none()` check** at `direct.rs:571, 595` — extracted to `let built = idx.as_ref().expect("set on the line above");` in both `find_similar` and `find_near_duplicates`. The original todo proposed `get_or_insert_with` but that can't propagate the `?` from `load_data`; the match-style refactor preserves error propagation and makes the lazy-init invariant explicit. | |
| 32 | + | - [x] **Hand-rolled `synckit.toml` parser** — replaced with `toml::from_str::<HashMap<String, String>>()`; added `toml` dep to `audiofiles-app`. | |
| 33 | + | - [x] **Dependency duplication** — bumped eframe 0.31→0.34, egui 0.31→0.34, cpal 0.15→0.17, midir 0.10→0.11, tray-icon 0.21→0.22. `windows-core` dups fully eliminated (4→0 on win target). `objc2-foundation` still 2× (0.2.2 via winit 0.30, 0.3.2 via arboard/glutin/muda) — blocked on winit bumping objc2; floor reached for our codebase. | |
| 34 | + | - [x] **egui 0.34 deprecation cleanup** — full sweep: `App::update` → `App::ui` (new trait shape, `ui: &mut Ui` instead of `ctx`); `SidePanel`/`TopBottomPanel` → `Panel::left`/`Panel::right`/`Panel::top`/`Panel::bottom`; `Panel::show(ctx,...)` → `show_inside(ui,...)` across 26 sites; `Memory::toggle_popup`/`close_popup` → `Popup::toggle_id`/`close_id`; `popup_below_widget` → `Popup::from_response`; `show_tooltip_at_pointer` → `Tooltip::always_open`; `Context::style`/`set_style` → `global_style`/`set_global_style`; `Context::screen_rect` → `content_rect`; `Ui::close_menu` → `Ui::close`; `cpal DeviceTrait::name` → `description().name()`. Required signature changes to `draw_browser` and all leaf `draw_*` screen fns (configure/progress/tagging/export/activation/vault_setup/db_error) from `ctx: &Context` → `ui: &mut Ui`. 0 deprecation warnings remaining; 269 tests pass. | |
| 38 | 35 | ||
| 39 | 36 | ### Repo hygiene (launchplan §2.3) | |
| 40 | 37 | - [ ] Remove `crates/audiofiles-app/tests/harness/mod.rs.bak` if it ever reappears (deleted this session as part of the fix commit). | |
| @@ -50,4 +47,10 @@ | |||
| 50 | 47 | - [ ] Move About modal trigger into a Help menu (toolbar Help button is already there but routes only to keyboard-shortcuts overlay; add a sub-action for About). | |
| 51 | 48 | - [ ] `Cmd+,` for preferences as macOS users will expect (currently only Cmd/Ctrl+I → About → toggle). | |
| 52 | 49 | - [ ] SyncKit upload contract audit — what exactly gets uploaded vs encrypted; verify E2E boundary; audit `crates/audiofiles-sync/src/service/upload.rs`. | |
| 53 | - | - [ ] Database migration safety review — 12 inline migrations in `db.rs` not audited this pass. | |
| 50 | + | - [x] **Database migration safety review** (2026-06-02). Subagent-audited all 17 migrations; per-migration risk grade in session transcript. Landed: | |
| 51 | + | - Made M004, M005, M006, M007, M012 fully idempotent (`CREATE TABLE/INDEX/TRIGGER IF NOT EXISTS`; M007 seed `INSERT OR IGNORE`). M008–M011, M016, M017 were already replay-safe via `DROP IF EXISTS + CREATE`. M014 already idempotent. | |
| 52 | + | - Added `migration_replay_from_file_no_op` and `migration_replay_from_version_two_against_full_schema` tests. The replay test rolls `PRAGMA user_version=2` against a populated schema and re-runs every migration from M003 onward; future non-idempotent CREATEs will fail this test loudly. | |
| 53 | + | - Documented why M001 (initial schema) and M002 (table-rebuild dance with `DROP TABLE tags; ALTER tags_v2 RENAME TO tags`) are inherently one-shot and not replay-safe. | |
| 54 | + | - **Deferred, not blocking:** recovery branch in `migrate()` lines 784-831 still bumps `user_version` after `tracing::warn!` if a non-ALTER statement in the recovery batch fails for a reason other than "already exists". Idempotent CREATEs eliminate the most likely failure modes, but the strict-validator rewrite is still a worthwhile follow-up. | |
| 55 | + | - **Deferred, design discussion:** `vfs_nodes.sample_hash ON DELETE CASCADE` silently wipes every VFS placement of a sample on delete. Orphan-to-tombstone may be safer for multi-device sync. | |
| 56 | + | - **Deferred, historical:** M015 backfill INSERT into `collections` fires `sync_collections_insert` from M007 → emits spurious `sync_changelog` rows on devices that ran M015. Has already shipped. Future migrations should wrap data backfills with `UPDATE sync_state SET value='1' WHERE key='applying_remote'` and reset on commit. |
| @@ -25,10 +25,11 @@ | |||
| 25 | 25 | chrono = { workspace = true } | |
| 26 | 26 | midir = { workspace = true } | |
| 27 | 27 | rfd = { workspace = true } | |
| 28 | + | toml = { workspace = true } | |
| 28 | 29 | ||
| 29 | 30 | [dev-dependencies] | |
| 30 | 31 | tempfile = "3" | |
| 31 | 32 | ||
| 32 | 33 | [target.'cfg(target_os = "linux")'.dependencies] | |
| 33 | - | eframe = { version = "0.31.1", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] } | |
| 34 | + | eframe = { version = "0.34", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] } | |
| 34 | 35 | gtk = "0.18" |
| @@ -7,7 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | 8 | impl AudioFilesApp { | |
| 9 | 9 | /// Draw the license activation screen. | |
| 10 | - | pub(crate) fn draw_activation_screen(&mut self, ctx: &egui::Context) { | |
| 10 | + | pub(crate) fn draw_activation_screen(&mut self, ui: &mut egui::Ui) { | |
| 11 | 11 | // Poll async activation result (take from lock, then drop guard before mutating self) | |
| 12 | 12 | let activation = self.activation_result.lock().take(); | |
| 13 | 13 | if let Some(result) = activation { | |
| @@ -40,7 +40,7 @@ | |||
| 40 | 40 | } | |
| 41 | 41 | } | |
| 42 | 42 | ||
| 43 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 43 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 44 | 44 | let available = ui.available_size(); | |
| 45 | 45 | ||
| 46 | 46 | ui.add_space((available.y * 0.35).max(40.0)); | |
| @@ -116,7 +116,7 @@ | |||
| 116 | 116 | ||
| 117 | 117 | let can_activate = !self.activating && !self.license_key_input.trim().is_empty(); | |
| 118 | 118 | ui.horizontal(|ui| { | |
| 119 | - | let button_text = if self.activating { "Activating..." } else { "Activate" }; | |
| 119 | + | let button_text = if self.activating { "Activating\u{2026}" } else { "Activate" }; | |
| 120 | 120 | if ui.add_enabled(can_activate, egui::Button::new(button_text)).clicked() { | |
| 121 | 121 | self.start_activation(); | |
| 122 | 122 | } |
| @@ -37,11 +37,14 @@ | |||
| 37 | 37 | .default_output_device() | |
| 38 | 38 | .ok_or(AudioError::NoDevice)?; | |
| 39 | 39 | ||
| 40 | - | let device_name = device.name().unwrap_or_else(|_| "default".to_string()); | |
| 40 | + | let device_name = device | |
| 41 | + | .description() | |
| 42 | + | .map(|d| d.name().to_string()) | |
| 43 | + | .unwrap_or_else(|_| "default".to_string()); | |
| 41 | 44 | let config = device.default_output_config()?; | |
| 42 | 45 | ||
| 43 | 46 | let channels = config.channels() as usize; | |
| 44 | - | let device_sample_rate = config.sample_rate().0; | |
| 47 | + | let device_sample_rate = config.sample_rate(); | |
| 45 | 48 | ||
| 46 | 49 | let stream = match config.sample_format() { | |
| 47 | 50 | cpal::SampleFormat::F32 => build_stream::<f32>( |
| @@ -142,21 +142,9 @@ | |||
| 142 | 142 | const SYNCKIT_TOML: &str = include_str!("../../../synckit.toml"); | |
| 143 | 143 | ||
| 144 | 144 | /// Extract the api_key value from the bundled synckit.toml. | |
| 145 | - | fn parse_synckit_toml_key() -> Option<&'static str> { | |
| 146 | - | for line in SYNCKIT_TOML.lines() { | |
| 147 | - | let line = line.trim(); | |
| 148 | - | if let Some(rest) = line.strip_prefix("api_key") { | |
| 149 | - | let rest = rest.trim_start(); | |
| 150 | - | if let Some(rest) = rest.strip_prefix('=') { | |
| 151 | - | let rest = rest.trim(); | |
| 152 | - | let rest = rest.trim_matches('"'); | |
| 153 | - | if !rest.is_empty() { | |
| 154 | - | return Some(rest); | |
| 155 | - | } | |
| 156 | - | } | |
| 157 | - | } | |
| 158 | - | } | |
| 159 | - | None | |
| 145 | + | fn parse_synckit_toml_key() -> Option<String> { | |
| 146 | + | let table: std::collections::HashMap<String, String> = toml::from_str(SYNCKIT_TOML).ok()?; | |
| 147 | + | table.get("api_key").filter(|s| !s.is_empty()).cloned() | |
| 160 | 148 | } | |
| 161 | 149 | ||
| 162 | 150 | /// Load a saved API key from the data directory, falling back to env vars and bundled toml. | |
| @@ -447,7 +435,9 @@ | |||
| 447 | 435 | ||
| 448 | 436 | impl eframe::App for AudioFilesApp { | |
| 449 | 437 | #[allow(unused_variables)] | |
| 450 | - | fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { | |
| 438 | + | fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) { | |
| 439 | + | let ctx = ui.ctx().clone(); | |
| 440 | + | let ctx = &ctx; | |
| 451 | 441 | // Pump GTK events so libappindicator (tray) stays responsive on Linux. | |
| 452 | 442 | #[cfg(target_os = "linux")] | |
| 453 | 443 | if self.gtk_ok { | |
| @@ -469,13 +459,13 @@ | |||
| 469 | 459 | ||
| 470 | 460 | match self.screen { | |
| 471 | 461 | AppScreen::Activation => { | |
| 472 | - | self.draw_activation_screen(ctx); | |
| 462 | + | self.draw_activation_screen(ui); | |
| 473 | 463 | } | |
| 474 | 464 | AppScreen::VaultSetup => { | |
| 475 | - | self.draw_vault_setup_screen(ctx); | |
| 465 | + | self.draw_vault_setup_screen(ui); | |
| 476 | 466 | } | |
| 477 | 467 | AppScreen::Browser => { | |
| 478 | - | self.update_browser(ctx); | |
| 468 | + | self.update_browser(ui); | |
| 479 | 469 | } | |
| 480 | 470 | } | |
| 481 | 471 | ||
| @@ -519,7 +509,9 @@ | |||
| 519 | 509 | ||
| 520 | 510 | impl AudioFilesApp { | |
| 521 | 511 | /// All browser-mode update logic (tray, sync, drops, draw). | |
| 522 | - | fn update_browser(&mut self, ctx: &egui::Context) { | |
| 512 | + | fn update_browser(&mut self, ui: &mut egui::Ui) { | |
| 513 | + | let ctx = ui.ctx().clone(); | |
| 514 | + | let ctx = &ctx; | |
| 523 | 515 | // Poll tray menu events | |
| 524 | 516 | if let Some(ref tray) = self.tray { | |
| 525 | 517 | if let Some(action) = tray.poll() { | |
| @@ -716,7 +708,7 @@ | |||
| 716 | 708 | } | |
| 717 | 709 | } | |
| 718 | 710 | } | |
| 719 | - | audiofiles_browser::editor::draw_browser(ctx, browser, self.sync_manager.as_ref()); | |
| 711 | + | audiofiles_browser::editor::draw_browser(ui, browser, self.sync_manager.as_ref()); | |
| 720 | 712 | ||
| 721 | 713 | // Drop target indicator: while files are hovering, paint a clear | |
| 722 | 714 | // border on top of the whole window plus a centered label. This is | |
| @@ -724,7 +716,7 @@ | |||
| 724 | 716 | // us on Linux/Windows. Rendered as a foreground layer so it sits | |
| 725 | 717 | // above panel chrome but doesn't intercept clicks. | |
| 726 | 718 | if hovered_count > 0 { | |
| 727 | - | let screen = ctx.screen_rect(); | |
| 719 | + | let screen = ctx.content_rect(); | |
| 728 | 720 | let rect = screen.shrink(theme::space::MD); | |
| 729 | 721 | let painter = ctx.layer_painter(egui::LayerId::new( | |
| 730 | 722 | egui::Order::Foreground, | |
| @@ -754,7 +746,7 @@ | |||
| 754 | 746 | ); | |
| 755 | 747 | } | |
| 756 | 748 | } else { | |
| 757 | - | self.draw_db_error_screen(ctx); | |
| 749 | + | self.draw_db_error_screen(ui); | |
| 758 | 750 | } | |
| 759 | 751 | } | |
| 760 | 752 | } | |
| @@ -763,8 +755,8 @@ | |||
| 763 | 755 | /// Render the "vault failed to open" recovery surface. Replaces the prior | |
| 764 | 756 | /// dead-end label with explicit recovery actions: Retry the same path, | |
| 765 | 757 | /// Choose a different vault, or open the data folder for manual triage. | |
| 766 | - | fn draw_db_error_screen(&mut self, ctx: &egui::Context) { | |
| 767 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 758 | + | fn draw_db_error_screen(&mut self, ui: &mut egui::Ui) { | |
| 759 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 768 | 760 | ui.add_space(48.0); | |
| 769 | 761 | ui.vertical_centered(|ui| { | |
| 770 | 762 | ui.heading("audiofiles"); |
| @@ -12,7 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | 13 | impl AudioFilesApp { | |
| 14 | 14 | /// Draw the vault setup screen (first-open flow, after activation). | |
| 15 | - | pub(crate) fn draw_vault_setup_screen(&mut self, ctx: &egui::Context) { | |
| 15 | + | pub(crate) fn draw_vault_setup_screen(&mut self, ui: &mut egui::Ui) { | |
| 16 | 16 | let default_path = vault::default_vault_path(); | |
| 17 | 17 | let existing_db = default_path.join("audiofiles.db").exists(); | |
| 18 | 18 | ||
| @@ -21,7 +21,7 @@ | |||
| 21 | 21 | self.vault_setup_name = DEFAULT_VAULT_NAME.to_string(); | |
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 24 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 25 | 25 | let available = ui.available_size(); | |
| 26 | 26 | ui.add_space((available.y * 0.20).max(40.0)); | |
| 27 | 27 |
| @@ -10,10 +10,12 @@ | |||
| 10 | 10 | /// | |
| 11 | 11 | /// Pass `sync_manager: None` when sync is not configured (e.g. from the CLAP plugin). | |
| 12 | 12 | pub fn draw_browser( | |
| 13 | - | ctx: &egui::Context, | |
| 13 | + | ui: &mut egui::Ui, | |
| 14 | 14 | state: &mut BrowserState, | |
| 15 | 15 | sync_manager: Option<&audiofiles_sync::SyncManager>, | |
| 16 | 16 | ) { | |
| 17 | + | let ctx = ui.ctx().clone(); | |
| 18 | + | let ctx = &ctx; | |
| 17 | 19 | theme::apply_theme(ctx); | |
| 18 | 20 | ||
| 19 | 21 | match &state.import_mode { | |
| @@ -23,10 +25,10 @@ | |||
| 23 | 25 | ctx.request_repaint(); | |
| 24 | 26 | } | |
| 25 | 27 | handle_keyboard(ctx, state); | |
| 26 | - | draw_normal_browser(ctx, state, sync_manager); | |
| 28 | + | draw_normal_browser(ui, state, sync_manager); | |
| 27 | 29 | } | |
| 28 | 30 | ImportMode::ConfigureImport { .. } => { | |
| 29 | - | import_screens::draw_configure_import(ctx, state); | |
| 31 | + | import_screens::draw_configure_import(ui, state); | |
| 30 | 32 | } | |
| 31 | 33 | ImportMode::Importing { .. } | |
| 32 | 34 | | ImportMode::Analyzing { .. } | |
| @@ -37,56 +39,56 @@ | |||
| 37 | 39 | } | |
| 38 | 40 | match &state.import_mode { | |
| 39 | 41 | ImportMode::Importing { .. } => { | |
| 40 | - | import_screens::draw_import_progress(ctx, state); | |
| 42 | + | import_screens::draw_import_progress(ui, state); | |
| 41 | 43 | } | |
| 42 | 44 | ImportMode::Analyzing { .. } => { | |
| 43 | - | import_screens::draw_analysis_progress(ctx, state); | |
| 45 | + | import_screens::draw_analysis_progress(ui, state); | |
| 44 | 46 | } | |
| 45 | 47 | ImportMode::Exporting { .. } => { | |
| 46 | - | export_screens::draw_export_progress(ctx, state); | |
| 48 | + | export_screens::draw_export_progress(ui, state); | |
| 47 | 49 | } | |
| 48 | 50 | ImportMode::Cleaning { .. } => { | |
| 49 | - | import_screens::draw_cleanup_progress(ctx, state); | |
| 51 | + | import_screens::draw_cleanup_progress(ui, state); | |
| 50 | 52 | } | |
| 51 | 53 | // poll_workers may have transitioned the mode | |
| 52 | 54 | ImportMode::TagFolders { .. } => { | |
| 53 | - | import_screens::draw_tag_folders(ctx, state); | |
| 55 | + | import_screens::draw_tag_folders(ui, state); | |
| 54 | 56 | } | |
| 55 | 57 | ImportMode::ConfigureAnalysis { .. } => { | |
| 56 | - | import_screens::draw_configure_analysis(ctx, state); | |
| 58 | + | import_screens::draw_configure_analysis(ui, state); | |
| 57 | 59 | } | |
| 58 | 60 | ImportMode::ReviewSuggestions { .. } => { | |
| 59 | - | import_screens::draw_review_suggestions(ctx, state); | |
| 61 | + | import_screens::draw_review_suggestions(ui, state); | |
| 60 | 62 | } | |
| 61 | 63 | ImportMode::ExportComplete { .. } => { | |
| 62 | - | export_screens::draw_export_complete(ctx, state); | |
| 64 | + | export_screens::draw_export_complete(ui, state); | |
| 63 | 65 | } | |
| 64 | 66 | ImportMode::ReviewErrors => { | |
| 65 | - | import_screens::draw_review_errors(ctx, state); | |
| 67 | + | import_screens::draw_review_errors(ui, state); | |
| 66 | 68 | } | |
| 67 | 69 | _ => {} | |
| 68 | 70 | } | |
| 69 | 71 | } | |
| 70 | 72 | ImportMode::TagFolders { .. } => { | |
| 71 | - | import_screens::draw_tag_folders(ctx, state); | |
| 73 | + | import_screens::draw_tag_folders(ui, state); | |
| 72 | 74 | } | |
| 73 | 75 | ImportMode::ConfigureAnalysis { .. } => { | |
| 74 | - | import_screens::draw_configure_analysis(ctx, state); | |
| 76 | + | import_screens::draw_configure_analysis(ui, state); | |
| 75 | 77 | } | |
| 76 | 78 | ImportMode::ReviewSuggestions { .. } => { | |
| 77 | - | import_screens::draw_review_suggestions(ctx, state); | |
| 79 | + | import_screens::draw_review_suggestions(ui, state); | |
| 78 | 80 | } | |
| 79 | 81 | ImportMode::ConfigureExport { .. } => { | |
| 80 | - | export_screens::draw_configure_export(ctx, state); | |
| 82 | + | export_screens::draw_configure_export(ui, state); | |
| 81 | 83 | } | |
| 82 | 84 | ImportMode::ExportComplete { .. } => { | |
| 83 | - | export_screens::draw_export_complete(ctx, state); | |
| 85 | + | export_screens::draw_export_complete(ui, state); | |
| 84 | 86 | } | |
| 85 | 87 | ImportMode::ReviewErrors => { | |
| 86 | - | import_screens::draw_review_errors(ctx, state); | |
| 88 | + | import_screens::draw_review_errors(ui, state); | |
| 87 | 89 | } | |
| 88 | 90 | ImportMode::OperationCancelled { .. } => { | |
| 89 | - | import_screens::draw_operation_cancelled(ctx, state); | |
| 91 | + | import_screens::draw_operation_cancelled(ui, state); | |
| 90 | 92 | } | |
| 91 | 93 | } | |
| 92 | 94 | ||
| @@ -141,42 +143,45 @@ | |||
| 141 | 143 | ||
| 142 | 144 | /// Draw the main browser layout: toolbar, footer, sidebar, detail panel, and file list. | |
| 143 | 145 | fn draw_normal_browser( | |
| 144 | - | ctx: &egui::Context, | |
| 146 | + | ui: &mut egui::Ui, | |
| 145 | 147 | state: &mut BrowserState, | |
| 146 | 148 | sync_manager: Option<&audiofiles_sync::SyncManager>, | |
| 147 | 149 | ) { | |
| 150 | + | let ctx = ui.ctx().clone(); | |
| 151 | + | ||
| 148 | 152 | // Top toolbar (breadcrumb + search) | |
| 149 | - | egui::TopBottomPanel::top("toolbar") | |
| 150 | - | .exact_height(56.0) | |
| 151 | - | .show(ctx, |ui| { | |
| 153 | + | egui::Panel::top("toolbar") | |
| 154 | + | .exact_size(56.0) | |
| 155 | + | .show_inside(ui, |ui| { | |
| 152 | 156 | toolbar::draw_toolbar(ui, state, sync_manager); | |
| 153 | 157 | }); | |
| 154 | 158 | ||
| 155 | 159 | // Bottom footer | |
| 156 | - | egui::TopBottomPanel::bottom("footer").show(ctx, |ui| { | |
| 157 | - | footer::draw_footer(ui, ctx, state); | |
| 160 | + | egui::Panel::bottom("footer").show_inside(ui, |ui| { | |
| 161 | + | let ctx = ui.ctx().clone(); | |
| 162 | + | footer::draw_footer(ui, &ctx, state); | |
| 158 | 163 | }); | |
| 159 | 164 | ||
| 160 | 165 | // Floating MIDI/instrument window | |
| 161 | 166 | if state.show_midi_window { | |
| 162 | - | instrument_panel::draw_midi_window(ctx, state); | |
| 167 | + | instrument_panel::draw_midi_window(&ctx, state); | |
| 163 | 168 | } | |
| 164 | 169 | ||
| 165 | 170 | // Left sidebar (or filter panel) | |
| 166 | 171 | if state.filter_panel_open { | |
| 167 | - | egui::SidePanel::left("filter_panel") | |
| 168 | - | .default_width(200.0) | |
| 169 | - | .width_range(160.0..=300.0) | |
| 170 | - | .show(ctx, |ui| { | |
| 172 | + | egui::Panel::left("filter_panel") | |
| 173 | + | .default_size(200.0) | |
| 174 | + | .size_range(160.0..=300.0) | |
| 175 | + | .show_inside(ui, |ui| { | |
| 171 | 176 | egui::ScrollArea::vertical().show(ui, |ui| { | |
| 172 | 177 | filter_panel::draw_filter_panel(ui, state); | |
| 173 | 178 | }); | |
| 174 | 179 | }); | |
| 175 | 180 | } else if state.sidebar_visible { | |
| 176 | - | egui::SidePanel::left("sidebar") | |
| 177 | - | .default_width(180.0) | |
| 178 | - | .width_range(120.0..=280.0) | |
| 179 | - | .show(ctx, |ui| { | |
| 181 | + | egui::Panel::left("sidebar") | |
| 182 | + | .default_size(180.0) | |
| 183 | + | .size_range(120.0..=280.0) | |
| 184 | + | .show_inside(ui, |ui| { | |
| 180 | 185 | sidebar::draw_sidebar(ui, state); | |
| 181 | 186 | }); | |
| 182 | 187 | } | |
| @@ -184,22 +189,19 @@ | |||
| 184 | 189 | // Right detail panel (auto-hide below 700px). | |
| 185 | 190 | // 700.0 is the minimum window width at which the detail panel is shown. | |
| 186 | 191 | // Below this, the file list alone needs the full width to remain usable. | |
| 187 | - | if state.detail_visible { | |
| 188 | - | let available = ctx.screen_rect().width(); | |
| 189 | - | if available >= 700.0 { | |
| 190 | - | egui::SidePanel::right("detail") | |
| 191 | - | .default_width(250.0) | |
| 192 | - | .width_range(200.0..=400.0) | |
| 193 | - | .show(ctx, |ui| { | |
| 194 | - | egui::ScrollArea::vertical().show(ui, |ui| { | |
| 195 | - | detail::draw_detail(ui, state); | |
| 196 | - | }); | |
| 192 | + | if state.detail_visible && ctx.content_rect().width() >= 700.0 { | |
| 193 | + | egui::Panel::right("detail") | |
| 194 | + | .default_size(250.0) | |
| 195 | + | .size_range(200.0..=400.0) | |
| 196 | + | .show_inside(ui, |ui| { | |
| 197 | + | egui::ScrollArea::vertical().show(ui, |ui| { | |
| 198 | + | detail::draw_detail(ui, state); | |
| 197 | 199 | }); | |
| 198 | - | } | |
| 200 | + | }); | |
| 199 | 201 | } | |
| 200 | 202 | ||
| 201 | 203 | // Central file list | |
| 202 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 204 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 203 | 205 | file_list::draw_file_list(ui, state, sync_manager); | |
| 204 | 206 | }); | |
| 205 | 207 | } |
| @@ -590,7 +590,7 @@ | |||
| 590 | 590 | .filter_map(|n| n.node.sample_hash.as_ref().map(|h| h.to_string())) | |
| 591 | 591 | .collect(); | |
| 592 | 592 | pending_apply = Some((tag.clone(), targets)); | |
| 593 | - | ui.close_menu(); | |
| 593 | + | ui.close(); | |
| 594 | 594 | } | |
| 595 | 595 | } | |
| 596 | 596 | let remove_label = if full { | |
| @@ -605,7 +605,7 @@ | |||
| 605 | 605 | .filter_map(|n| n.node.sample_hash.as_ref().map(|h| h.to_string())) | |
| 606 | 606 | .collect(); | |
| 607 | 607 | pending_remove = Some((tag.clone(), targets)); | |
| 608 | - | ui.close_menu(); | |
| 608 | + | ui.close(); | |
| 609 | 609 | } | |
| 610 | 610 | }); | |
| 611 | 611 | } |
| @@ -75,7 +75,7 @@ | |||
| 75 | 75 | } | |
| 76 | 76 | ||
| 77 | 77 | /// Draw the export configuration screen. | |
| 78 | - | pub fn draw_configure_export(ctx: &egui::Context, state: &mut BrowserState) { | |
| 78 | + | pub fn draw_configure_export(ui: &mut egui::Ui, state: &mut BrowserState) { | |
| 79 | 79 | let (item_count, profile_count) = match &state.import_mode { | |
| 80 | 80 | ImportMode::ConfigureExport { | |
| 81 | 81 | items, | |
| @@ -85,7 +85,7 @@ | |||
| 85 | 85 | _ => return, | |
| 86 | 86 | }; | |
| 87 | 87 | ||
| 88 | - | egui::TopBottomPanel::bottom("export_footer").show(ctx, |ui| { | |
| 88 | + | egui::Panel::bottom("export_footer").show_inside(ui, |ui| { | |
| 89 | 89 | ui.add_space(theme::space::SM); | |
| 90 | 90 | ||
| 91 | 91 | // Warnings | |
| @@ -194,7 +194,7 @@ | |||
| 194 | 194 | ui.add_space(theme::space::XS); | |
| 195 | 195 | }); | |
| 196 | 196 | ||
| 197 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 197 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 198 | 198 | egui::ScrollArea::vertical().show(ui, |ui| { | |
| 199 | 199 | ui.heading("Export Samples"); | |
| 200 | 200 | ui.add_space(theme::space::SM); | |
| @@ -319,6 +319,23 @@ | |||
| 319 | 319 | if ui.radio(is_aiff, "AIFF (decode and re-encode)").clicked() && !is_aiff { | |
| 320 | 320 | config.format = ExportFormat::Aiff; | |
| 321 | 321 | } | |
| 322 | + | ||
| 323 | + | // Heads-up: WAV/AIFF re-encode writes only fmt+data / | |
| 324 | + | // COMM+SSND. Embedded BWF (bext), iXML, smpl loop points, | |
| 325 | + | // cue markers, and ID3 tags are not preserved. Choose | |
| 326 | + | // Original to keep them intact. | |
| 327 | + | if config.format != ExportFormat::Original { | |
| 328 | + | ui.add_space(theme::space::XS); | |
| 329 | + | ui.label( | |
| 330 | + | egui::RichText::new( | |
| 331 | + | "Re-encoding strips embedded metadata chunks \ | |
| 332 | + | (BWF, iXML, loop points, cue markers, ID3). \ | |
| 333 | + | Choose Original to preserve them.", | |
| 334 | + | ) | |
| 335 | + | .small() | |
| 336 | + | .color(theme::accent_yellow()), | |
| 337 | + | ); | |
| 338 | + | } | |
| 322 | 339 | } | |
| 323 | 340 | ui.add_space(theme::space::MD); | |
| 324 | 341 | ||
| @@ -516,7 +533,7 @@ | |||
| 516 | 533 | } | |
| 517 | 534 | ||
| 518 | 535 | /// Draw the export progress screen. | |
| 519 | - | pub fn draw_export_progress(ctx: &egui::Context, state: &mut BrowserState) { | |
| 536 | + | pub fn draw_export_progress(ui: &mut egui::Ui, state: &mut BrowserState) { | |
| 520 | 537 | let (completed, total, current_name) = match &state.import_mode { | |
| 521 | 538 | ImportMode::Exporting { | |
| 522 | 539 | completed, | |
| @@ -526,7 +543,7 @@ | |||
| 526 | 543 | _ => return, | |
| 527 | 544 | }; | |
| 528 | 545 | ||
| 529 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 546 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 530 | 547 | ui.heading("Exporting..."); | |
| 531 | 548 | ui.add_space(theme::space::SECTION); | |
| 532 | 549 | ||
| @@ -561,13 +578,13 @@ | |||
| 561 | 578 | } | |
| 562 | 579 | ||
| 563 | 580 | /// Draw the export complete screen with summary and error list. | |
| 564 | - | pub fn draw_export_complete(ctx: &egui::Context, state: &mut BrowserState) { | |
| 581 | + | pub fn draw_export_complete(ui: &mut egui::Ui, state: &mut BrowserState) { | |
| 565 | 582 | let (total, error_count) = match &state.import_mode { | |
| 566 | 583 | ImportMode::ExportComplete { total, errors } => (*total, errors.len()), | |
| 567 | 584 | _ => return, | |
| 568 | 585 | }; | |
| 569 | 586 | ||
| 570 | - | egui::CentralPanel::default().show(ctx, |ui| { | |
| 587 | + | egui::CentralPanel::default().show_inside(ui, |ui| { | |
| 571 | 588 | ui.heading("Export Complete"); | |
| 572 | 589 | ui.add_space(theme::space::LG); | |
| 573 | 590 |
| @@ -49,7 +49,7 @@ | |||
| 49 | 49 | state.status = | |
| 50 | 50 | "Sync not ready — open the Sync panel first".to_string(); | |
| 51 | 51 | } | |
| 52 | - | ui.close_menu(); | |
| 52 | + | ui.close(); | |
| 53 | 53 | } | |
| 54 | 54 | } | |
| 55 | 55 | } | |
| @@ -60,14 +60,14 @@ | |||
| 60 | 60 | let hash = hash.clone(); | |
| 61 | 61 | state.trigger_preview(&hash); | |
| 62 | 62 | } | |
| 63 | - | ui.close_menu(); | |
| 63 | + | ui.close(); | |
| 64 | 64 | } | |
| 65 | 65 | if ui.button("Copy Path").clicked() { | |
| 66 | 66 | if let Some(path) = state.selected_sample_path() { | |
| 67 | 67 | state.status = format!("Copied: {path}"); | |
| 68 | 68 | ui.ctx().copy_text(path); | |
| 69 | 69 | } | |
| 70 | - | ui.close_menu(); | |
| 70 | + | ui.close(); | |
| 71 | 71 | } | |
| 72 | 72 | // M-6: one-click jump to the file in the system file manager. | |
| 73 | 73 | // macOS / Windows highlight the file itself; Linux falls back to | |
| @@ -95,21 +95,21 @@ | |||
| 95 | 95 | let _ = std::process::Command::new("xdg-open").arg(&parent).spawn(); | |
| 96 | 96 | } | |
| 97 | 97 | } | |
| 98 | - | ui.close_menu(); | |
| 98 | + | ui.close(); | |
| 99 | 99 | } | |
| 100 | 100 | if ui.button("Find Similar (Shift+F)").clicked() { | |
| 101 | 101 | if let Some(hash) = &node.node.sample_hash { | |
| 102 | 102 | let hash = hash.clone(); | |
| 103 | 103 | state.find_similar(&hash); | |
| 104 | 104 | } | |
| 105 | - | ui.close_menu(); | |
| 105 | + | ui.close(); | |
| 106 | 106 | } | |
| 107 | 107 | if ui.button("Find Duplicates (Shift+D)").clicked() { | |
| 108 | 108 | if let Some(hash) = &node.node.sample_hash { | |
| 109 | 109 | let hash = hash.clone(); | |
| 110 | 110 | state.find_near_duplicates(&hash); | |
| 111 | 111 | } | |
| 112 | - | ui.close_menu(); | |
| 112 | + | ui.close(); | |
| 113 | 113 | } | |
| 114 | 114 | // Add to Collection submenu | |
| 115 | 115 | if let Some(hash) = &node.node.sample_hash { | |
| @@ -123,7 +123,7 @@ | |||
| 123 | 123 | let _ = state.backend.add_to_collection(coll.id, &hash_clone); | |
| 124 | 124 | state.refresh_collections(); | |
| 125 | 125 | state.status = format!("Added to {}", coll.name); | |
| 126 | - | ui.close_menu(); | |
| 126 | + | ui.close(); | |
| 127 | 127 | } | |
| 128 | 128 | } | |
| 129 | 129 | }); | |
| @@ -134,7 +134,7 @@ | |||
| 134 | 134 | let _ = state.backend.remove_from_collection(active_id, &hash_clone); | |
| 135 | 135 | state.refresh_collections(); | |
| 136 | 136 | state.activate_collection(active_id); | |
| 137 | - | ui.close_menu(); | |
| 137 | + | ui.close(); | |
| 138 | 138 | } | |
| 139 | 139 | } | |
| 140 | 140 | } | |
| @@ -144,7 +144,7 @@ | |||
| 144 | 144 | let hash_clone = hash.clone(); | |
| 145 | 145 | if ui.button("Edit... (E)").clicked() { | |
| 146 | 146 | state.open_edit_window(&hash_clone); | |
| 147 | - | ui.close_menu(); | |
| 147 | + | ui.close(); | |
| 148 | 148 | } | |
| 149 | 149 | } | |
| 150 | 150 | if ui.button("Play as Instrument").clicked() { | |
| @@ -156,12 +156,12 @@ | |||
| 156 | 156 | state.show_midi_window = true; | |
| 157 | 157 | state.status = format!("Instrument: {name}"); | |
| 158 | 158 | } | |
| 159 | - | ui.close_menu(); | |
| 159 | + | ui.close(); | |
| 160 | 160 | } | |
| 161 | 161 | if ui.button("Export...").clicked() { | |
| 162 | 162 | state.selection.set_single(row_idx); | |
| 163 | 163 | state.start_export_flow(Some(vec![node.node.id])); | |
| 164 | - | ui.close_menu(); | |
| 164 | + | ui.close(); | |
| 165 | 165 | } | |
| 166 | 166 | // M-7: single-row Re-analyze parity with the multi-row menu. | |
| 167 | 167 | // Reuses ReanalyzeOverwrite with a one-element vec so the | |
| @@ -188,41 +188,41 @@ | |||
| 188 | 188 | } | |
| 189 | 189 | } | |
| 190 | 190 | } | |
| 191 | - | ui.close_menu(); | |
| 191 | + | ui.close(); | |
| 192 | 192 | } | |
| 193 | 193 | } | |
| 194 | 194 | ui.separator(); | |
| 195 | 195 | if widgets::danger_button(ui, "Delete").clicked() { | |
| 196 | 196 | state.selection.set_single(row_idx); | |
| 197 | 197 | state.confirm_delete_selected(); | |
| 198 | - | ui.close_menu(); | |
| 198 | + | ui.close(); | |
| 199 | 199 | } | |
| 200 | 200 | } | |
| 201 | 201 | NodeType::Directory => { | |
| 202 | 202 | if ui.button("Open").clicked() { | |
| 203 | 203 | state.selection.set_single(row_idx); | |
| 204 | 204 | state.enter_directory(); | |
| 205 | - | ui.close_menu(); | |
| 205 | + | ui.close(); | |
| 206 | 206 | } | |
| 207 | 207 | if ui.button("New Folder").clicked() { | |
| 208 | 208 | state.show_dir_create = true; | |
| 209 | 209 | state.dir_create_input.clear(); | |
| 210 | - | ui.close_menu(); | |
| 210 | + | ui.close(); | |
| 211 | 211 | } | |
| 212 | 212 | if ui.button("Rename").clicked() { | |
| 213 | 213 | state.dir_rename_target = Some((node.node.id, node.node.name.clone())); | |
| 214 | - | ui.close_menu(); | |
| 214 | + | ui.close(); | |
| 215 | 215 | } | |
| 216 | 216 | if ui.button("Export...").clicked() { | |
| 217 | 217 | state.selection.set_single(row_idx); | |
| 218 | 218 | state.start_export_flow(Some(vec![node.node.id])); | |
| 219 | - | ui.close_menu(); | |
| 219 | + | ui.close(); | |
| 220 | 220 | } | |
| 221 | 221 | ui.separator(); | |
| 222 | 222 | if widgets::danger_button(ui, "Delete").clicked() { | |
| 223 | 223 | state.selection.set_single(row_idx); | |
| 224 | 224 | state.confirm_delete_selected(); | |
| 225 | - | ui.close_menu(); | |
| 225 | + | ui.close(); | |
| 226 | 226 | } | |
| 227 | 227 | } | |
| 228 | 228 | } | |
| @@ -236,14 +236,14 @@ | |||
| 236 | 236 | ||
| 237 | 237 | if ui.button("Invert Selection (Cmd+Shift+I)").clicked() { | |
| 238 | 238 | state.invert_selection(); | |
| 239 | - | ui.close_menu(); | |
| 239 | + | ui.close(); | |
| 240 | 240 | } | |
| 241 | 241 | ||
| 242 | 242 | ui.separator(); | |
| 243 | 243 | ||
| 244 | 244 | if ui.button("Tag... (Cmd+T)").clicked() { | |
| 245 | 245 | state.open_bulk_tag_modal(); | |
| 246 | - | ui.close_menu(); | |
| 246 | + | ui.close(); | |
| 247 | 247 | } | |
| 248 | 248 | // m-16: Cmd+M conflicts with the macOS minimize-window shortcut. Label | |
| 249 | 249 | // advertises Cmd+Shift+M; the actual key binding lives in | |
| @@ -251,16 +251,16 @@ | |||
| 251 | 251 | // there to match. | |
| 252 | 252 | if ui.button("Move to... (Cmd+Shift+M)").clicked() { | |
| 253 | 253 | state.open_bulk_move_modal(); | |
| 254 | - | ui.close_menu(); | |
| 254 | + | ui.close(); | |
| 255 | 255 | } | |
| 256 | 256 | if ui.button("Rename... (F2)").clicked() { | |
| 257 | 257 | state.open_bulk_rename_modal(); | |
| 258 | - | ui.close_menu(); | |
| 258 | + | ui.close(); | |
| 259 | 259 | } | |
| 260 | 260 | if ui.button("Export...").clicked() { | |
| 261 | 261 | let node_ids = state.selected_node_ids(); | |
| 262 | 262 | state.start_export_flow(Some(node_ids)); | |
| 263 | - | ui.close_menu(); | |
| 263 | + | ui.close(); | |
| 264 | 264 | } | |
| 265 | 265 | ||
| 266 | 266 | // Add to Collection submenu (bulk) | |
| @@ -277,7 +277,7 @@ | |||
| 277 | 277 | } | |
| 278 | 278 | state.refresh_collections(); | |
| 279 | 279 | state.status = format!("Added {} items to {}", nodes.len(), coll.name); | |
| 280 | - | ui.close_menu(); | |
| 280 | + | ui.close(); | |
| 281 | 281 | } | |
| 282 | 282 | } | |
| 283 | 283 | }); | |
| @@ -294,7 +294,7 @@ | |||
| 294 | 294 | } | |
| 295 | 295 | state.refresh_collections(); | |
| 296 | 296 | state.activate_collection(active_id); | |
| 297 | - | ui.close_menu(); | |
| 297 | + | ui.close(); | |
| 298 | 298 | } | |
| 299 | 299 | } | |
| 300 | 300 | ||
| @@ -325,7 +325,7 @@ | |||
| 325 | 325 | } else { | |
| 326 | 326 | state.start_analysis_flow(hashes); | |
| 327 | 327 | } | |
| 328 | - | ui.close_menu(); | |
| 328 | + | ui.close(); | |
| 329 | 329 | } | |
| 330 | 330 | ||
| 331 | 331 | // Copy tags from focused sample to all selected | |
| @@ -351,7 +351,7 @@ | |||
| 351 | 351 | state.status = format!("Copied {} tags to {} samples", src_tags.len(), applied); | |
| 352 | 352 | state.refresh_selected_tags(); | |
| 353 | 353 | } | |
| 354 | - | ui.close_menu(); | |
| 354 | + | ui.close(); | |
| 355 | 355 | } | |
| 356 | 356 | } | |
| 357 | 357 | } | |
| @@ -383,12 +383,12 @@ | |||
| 383 | 383 | }; | |
| 384 | 384 | ui.ctx().copy_text(paths.join("\n")); | |
| 385 | 385 | } | |
| 386 | - | ui.close_menu(); | |
| 386 | + | ui.close(); | |
| 387 | 387 | } | |
| 388 | 388 | ||
| 389 | 389 | if widgets::danger_button(ui, "Delete").clicked() { | |
| 390 | 390 | state.confirm_delete_selected(); | |
| 391 | - | ui.close_menu(); | |
| 391 | + | ui.close(); | |
| 392 | 392 | } | |
| 393 | 393 | } | |
| 394 | 394 | ||
| @@ -397,7 +397,7 @@ | |||
| 397 | 397 | if ui.button("New Folder").clicked() { | |
| 398 | 398 | state.show_dir_create = true; | |
| 399 | 399 | state.dir_create_input.clear(); | |
| 400 | - | ui.close_menu(); | |
| 400 | + | ui.close(); | |
| 401 | 401 | } | |
| 402 | 402 | if ui.button("Import files...").clicked() { | |
| 403 | 403 | if let Some(paths) = rfd::FileDialog::new() | |
| @@ -409,7 +409,7 @@ | |||
| 409 | 409 | state.import_path(&path); | |
| 410 | 410 | } | |
| 411 | 411 | } | |
| 412 | - | ui.close_menu(); | |
| 412 | + | ui.close(); | |
| 413 | 413 | } | |
| 414 | 414 | // C-2: matches the toolbar's "Import folder..." (wizard path). The quick | |
| 415 | 415 | // import shortcut is only offered from the toolbar to keep this menu | |
| @@ -418,7 +418,7 @@ | |||
| 418 | 418 | if let Some(path) = rfd::FileDialog::new().pick_folder() { | |
| 419 | 419 | state.show_import_options(path); | |
| 420 | 420 | } | |
| 421 | - | ui.close_menu(); | |
| 421 | + | ui.close(); | |
| 422 | 422 | } | |
| 423 | 423 | if state.selection.count() > 0 { | |
| 424 | 424 | ui.separator(); | |
| @@ -427,11 +427,11 @@ | |||
| 427 | 427 | state.selection.clear(); | |
| 428 | 428 | state.refresh_selected_tags(); | |
| 429 | 429 | state.refresh_selected_detail(); | |
| 430 | - | ui.close_menu(); | |
| 430 | + | ui.close(); | |
| 431 | 431 | } | |
| 432 | 432 | if ui.button("Invert Selection (Cmd+Shift+I)").clicked() { | |
| 433 | 433 | state.invert_selection(); | |
| 434 | - | ui.close_menu(); | |
| 434 | + | ui.close(); | |
| 435 | 435 | } | |
| 436 | 436 | } | |
| 437 | 437 | } |
| @@ -35,7 +35,7 @@ | |||
| 35 | 35 | pub fn draw_footer(ui: &mut egui::Ui, ctx: &egui::Context, state: &mut BrowserState) { | |
| 36 | 36 | ui.add_space(theme::space::SM); | |
| 37 | 37 | ||
| 38 | - | let narrow = ctx.screen_rect().width() < 1000.0; | |
| 38 | + | let narrow = ctx.content_rect().width() < 1000.0; | |
| 39 | 39 | ||
| 40 | 40 | // Transport row | |
| 41 | 41 | ui.horizontal(|ui| { |