Add file based debug logging (#347)
* add basic logging to handy * 100kb of logs * add open button to the ui for logs * use log everywhere * some minor cleanup * finish debug logging
This commit is contained in:
parent
315c32eab4
commit
18536aa81e
26 changed files with 701 additions and 222 deletions
387
src-tauri/Cargo.lock
generated
387
src-tauri/Cargo.lock
generated
|
|
@ -8,6 +8,17 @@ version = "2.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
|
|
@ -72,6 +83,23 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d"
|
||||
|
||||
[[package]]
|
||||
name = "android_logger"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"env_filter",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
|
|
@ -81,56 +109,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
|
|
@ -492,6 +470,18 @@ dependencies = [
|
|||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
version = "0.1.6"
|
||||
|
|
@ -538,6 +528,29 @@ dependencies = [
|
|||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"cfg_aliases",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate 3.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.108",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "8.0.2"
|
||||
|
|
@ -565,6 +578,39 @@ version = "3.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byte-unit"
|
||||
version = "5.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174"
|
||||
dependencies = [
|
||||
"rust_decimal",
|
||||
"serde",
|
||||
"utf8-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
|
||||
dependencies = [
|
||||
"bytecheck_derive",
|
||||
"ptr_meta",
|
||||
"simdutf8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytecheck_derive"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.24.0"
|
||||
|
|
@ -790,12 +836,6 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
|
|
@ -1506,19 +1546,6 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"jiff",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "epoll"
|
||||
version = "4.4.0"
|
||||
|
|
@ -1668,6 +1695,15 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "field-offset"
|
||||
version = "0.3.6"
|
||||
|
|
@ -1792,6 +1828,12 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futf"
|
||||
version = "0.1.5"
|
||||
|
|
@ -2292,7 +2334,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"cpal",
|
||||
"enigo",
|
||||
"env_logger 0.11.8",
|
||||
"env_filter",
|
||||
"flate2",
|
||||
"futures-util",
|
||||
"hound",
|
||||
|
|
@ -2315,6 +2357,7 @@ dependencies = [
|
|||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-global-shortcut",
|
||||
"tauri-plugin-log",
|
||||
"tauri-plugin-macos-permissions",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-os",
|
||||
|
|
@ -2334,6 +2377,9 @@ name = "hashbrown"
|
|||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash 0.7.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
|
|
@ -2341,7 +2387,7 @@ version = "0.14.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.8.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2856,12 +2902,6 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
|
@ -2900,30 +2940,6 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.108",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
|
|
@ -3126,6 +3142,9 @@ name = "log"
|
|||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
dependencies = [
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
|
|
@ -3565,6 +3584,15 @@ dependencies = [
|
|||
"syn 2.0.108",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
|
|
@ -3893,12 +3921,6 @@ version = "1.21.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.3.2"
|
||||
|
|
@ -4486,6 +4508,26 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
|
||||
dependencies = [
|
||||
"ptr_meta_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta_derive"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.25"
|
||||
|
|
@ -4589,6 +4631,12 @@ version = "5.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
|
|
@ -4824,6 +4872,15 @@ version = "0.8.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.24"
|
||||
|
|
@ -4909,6 +4966,35 @@ version = "0.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53"
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bytecheck",
|
||||
"bytes",
|
||||
"hashbrown 0.12.3",
|
||||
"ptr_meta",
|
||||
"rend",
|
||||
"rkyv_derive",
|
||||
"seahash",
|
||||
"tinyvec",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv_derive"
|
||||
version = "0.7.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rodio"
|
||||
version = "0.20.1"
|
||||
|
|
@ -4976,6 +5062,22 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"borsh",
|
||||
"bytes",
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
"rkyv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
|
@ -5180,6 +5282,12 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "secrecy"
|
||||
version = "0.10.3"
|
||||
|
|
@ -5477,6 +5585,12 @@ version = "0.3.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
|
|
@ -6169,6 +6283,12 @@ dependencies = [
|
|||
"syn 2.0.108",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.44"
|
||||
|
|
@ -6385,6 +6505,28 @@ dependencies = [
|
|||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-log"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5709c792b8630290b5d9811a1f8fe983dd925fc87c7fc7f4923616458cd00b6"
|
||||
dependencies = [
|
||||
"android_logger",
|
||||
"byte-unit",
|
||||
"fern",
|
||||
"log",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"swift-rs",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-macos-permissions"
|
||||
version = "2.3.0"
|
||||
|
|
@ -6727,7 +6869,9 @@ checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
|
|||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
|
|
@ -7027,7 +7171,7 @@ dependencies = [
|
|||
"async-openai 0.29.6",
|
||||
"async-trait",
|
||||
"derive_builder",
|
||||
"env_logger 0.10.2",
|
||||
"env_logger",
|
||||
"hound",
|
||||
"log",
|
||||
"ndarray",
|
||||
|
|
@ -7259,18 +7403,18 @@ version = "0.7.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-width"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.18.1"
|
||||
|
|
@ -7294,6 +7438,12 @@ dependencies = [
|
|||
"ringbuffer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
|
@ -8359,6 +8509,15 @@ dependencies = [
|
|||
"x11-dl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11"
|
||||
version = "2.21.0"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ tauri = { version = "2.9.1", features = [
|
|||
"tray-icon",
|
||||
'image-png',
|
||||
] }
|
||||
tauri-plugin-log = "2.7.1"
|
||||
tauri-plugin-opener = "2.5.2"
|
||||
tauri-plugin-store = "2.4.1"
|
||||
tauri-plugin-os = "2.3.2"
|
||||
|
|
@ -49,8 +50,8 @@ cpal = "0.16.0"
|
|||
anyhow = "1.0.95"
|
||||
rubato = "0.16.2"
|
||||
hound = "3.5.1"
|
||||
env_logger = "0.11.6"
|
||||
log = "0.4.25"
|
||||
env_filter = "0.1.0"
|
||||
tokio = "1.43.0"
|
||||
vad-rs = { git = "https://github.com/cjpais/vad-rs", default-features = false }
|
||||
enigo = "0.6.1"
|
||||
|
|
|
|||
|
|
@ -318,14 +318,14 @@ impl ShortcutAction for TranscribeAction {
|
|||
"Text pasted successfully in {:?}",
|
||||
paste_time.elapsed()
|
||||
),
|
||||
Err(e) => eprintln!("Failed to paste transcription: {}", e),
|
||||
Err(e) => error!("Failed to paste transcription: {}", e),
|
||||
}
|
||||
// Hide the overlay after transcription is complete
|
||||
utils::hide_recording_overlay(&ah_clone);
|
||||
change_tray_icon(&ah_clone, TrayIconState::Idle);
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Failed to run paste on main thread: {:?}", e);
|
||||
error!("Failed to run paste on main thread: {:?}", e);
|
||||
utils::hide_recording_overlay(&ah);
|
||||
change_tray_icon(&ah, TrayIconState::Idle);
|
||||
});
|
||||
|
|
@ -359,7 +359,7 @@ struct TestAction;
|
|||
|
||||
impl ShortcutAction for TestAction {
|
||||
fn start(&self, app: &AppHandle, binding_id: &str, shortcut_str: &str) {
|
||||
println!(
|
||||
log::info!(
|
||||
"Shortcut ID '{}': Started - {} (App: {})", // Changed "Pressed" to "Started" for consistency
|
||||
binding_id,
|
||||
shortcut_str,
|
||||
|
|
@ -368,7 +368,7 @@ impl ShortcutAction for TestAction {
|
|||
}
|
||||
|
||||
fn stop(&self, app: &AppHandle, binding_id: &str, shortcut_str: &str) {
|
||||
println!(
|
||||
log::info!(
|
||||
"Shortcut ID '{}': Stopped - {} (App: {})", // Changed "Released" to "Stopped" for consistency
|
||||
binding_id,
|
||||
shortcut_str,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::settings;
|
||||
use cpal::traits::{DeviceTrait, HostTrait};
|
||||
use log::{debug, error, warn};
|
||||
use rodio::OutputStreamBuilder;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
|
@ -21,7 +22,7 @@ fn play_sound(app: &AppHandle, resource_path: &str, base_dir: tauri::path::BaseD
|
|||
let audio_path = match app_handle.path().resolve(&resource_path, base_dir) {
|
||||
Ok(path) => path.to_path_buf(),
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
error!(
|
||||
"Failed to resolve audio file path '{}': {}",
|
||||
resource_path, e
|
||||
);
|
||||
|
|
@ -33,7 +34,7 @@ fn play_sound(app: &AppHandle, resource_path: &str, base_dir: tauri::path::BaseD
|
|||
let selected_device = settings.selected_output_device.clone();
|
||||
|
||||
if let Err(e) = play_audio_file(&audio_path, selected_device, volume) {
|
||||
eprintln!("Failed to play sound '{}': {}", resource_path, e);
|
||||
error!("Failed to play sound '{}': {}", resource_path, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -87,7 +88,7 @@ fn play_audio_file(
|
|||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let stream_builder = if let Some(device_name) = selected_device {
|
||||
if device_name == "Default" {
|
||||
println!("Using default device");
|
||||
debug!("Using default device");
|
||||
OutputStreamBuilder::from_default_device()?
|
||||
} else {
|
||||
let host = crate::audio_toolkit::get_cpal_host();
|
||||
|
|
@ -104,13 +105,13 @@ fn play_audio_file(
|
|||
match found_device {
|
||||
Some(device) => OutputStreamBuilder::from_device(device)?,
|
||||
None => {
|
||||
eprintln!("Device '{}' not found, using default device", device_name);
|
||||
warn!("Device '{}' not found, using default device", device_name);
|
||||
OutputStreamBuilder::from_default_device()?
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Using default device");
|
||||
debug!("Using default device");
|
||||
OutputStreamBuilder::from_default_device()?
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl AudioRecorder {
|
|||
let sample_rate = config.sample_rate().0;
|
||||
let channels = config.channels() as usize;
|
||||
|
||||
println!(
|
||||
log::info!(
|
||||
"Using device: {:?}\nSample rate: {}\nChannels: {}\nFormat: {:?}",
|
||||
thread_device.name(),
|
||||
sample_rate,
|
||||
|
|
@ -188,14 +188,14 @@ impl AudioRecorder {
|
|||
}
|
||||
|
||||
if sample_tx.send(output_buffer.clone()).is_err() {
|
||||
eprintln!("Failed to send samples");
|
||||
log::error!("Failed to send samples");
|
||||
}
|
||||
};
|
||||
|
||||
device.build_input_stream(
|
||||
&config.clone().into(),
|
||||
stream_cb,
|
||||
|err| eprintln!("Stream error: {}", err),
|
||||
|err| log::error!("Stream error: {}", err),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ impl FrameResampler {
|
|||
.process(&[&self.in_buf[..]], None)
|
||||
{
|
||||
// let duration = start.elapsed();
|
||||
// println!("Resampler took: {:?}", duration);
|
||||
// log::debug!("Resampler took: {:?}", duration);
|
||||
self.emit_frames(&out[0], &mut emit);
|
||||
}
|
||||
self.in_buf.clear();
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ impl VoiceActivityDetector for SileroVad {
|
|||
.compute(frame)
|
||||
.map_err(|e| anyhow::anyhow!("Silero VAD error: {e}"))?;
|
||||
|
||||
// println!("Silero VAD result: prob = {}", result.prob);
|
||||
|
||||
if result.prob > self.threshold {
|
||||
Ok(VadFrame::Speech(frame))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ impl VoiceActivityDetector for SmoothedVad {
|
|||
|
||||
// 2. Delegate to the wrapped boolean VAD
|
||||
let is_voice = self.inner_vad.is_voice(frame)?;
|
||||
// println!("Is Voice: {}", is_voice);
|
||||
|
||||
match (self.in_speech, is_voice) {
|
||||
// Potential start of speech - need to accumulate onset frames
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use enigo::Enigo;
|
|||
use enigo::Key;
|
||||
use enigo::Keyboard;
|
||||
use enigo::Settings;
|
||||
use log::info;
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
|
||||
|
|
@ -138,7 +139,7 @@ pub fn paste(text: String, app_handle: AppHandle) -> Result<(), String> {
|
|||
let settings = get_settings(&app_handle);
|
||||
let paste_method = settings.paste_method;
|
||||
|
||||
println!("Using paste method: {:?}", paste_method);
|
||||
info!("Using paste method: {:?}", paste_method);
|
||||
|
||||
// Perform the paste operation
|
||||
match paste_method {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::audio_feedback;
|
|||
use crate::audio_toolkit::audio::{list_input_devices, list_output_devices};
|
||||
use crate::managers::audio::{AudioRecordingManager, MicrophoneMode};
|
||||
use crate::settings::{get_settings, write_settings};
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
|
@ -153,7 +154,7 @@ pub fn play_test_sound(app: AppHandle, sound_type: String) {
|
|||
"start" => audio_feedback::SoundType::Start,
|
||||
"stop" => audio_feedback::SoundType::Stop,
|
||||
_ => {
|
||||
eprintln!("Unknown sound type: {}", sound_type);
|
||||
warn!("Unknown sound type: {}", sound_type);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ pub mod history;
|
|||
pub mod models;
|
||||
pub mod transcription;
|
||||
|
||||
use crate::utils::cancel_current_operation;
|
||||
use crate::{settings, utils::cancel_current_operation};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri_plugin_log::LogLevel;
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
|
||||
#[tauri::command]
|
||||
|
|
@ -22,6 +23,32 @@ pub fn get_app_dir_path(app: AppHandle) -> Result<String, String> {
|
|||
Ok(app_data_dir.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_log_dir_path(app: AppHandle) -> Result<String, String> {
|
||||
let log_dir = app
|
||||
.path()
|
||||
.app_log_dir()
|
||||
.map_err(|e| format!("Failed to get log directory: {}", e))?;
|
||||
|
||||
Ok(log_dir.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn set_log_level(app: AppHandle, level: LogLevel) -> Result<(), String> {
|
||||
let log_level: log::Level = level.clone().into();
|
||||
// Update the file log level atomic so the filter picks up the new level
|
||||
crate::FILE_LOG_LEVEL.store(
|
||||
log_level.to_level_filter() as u8,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
|
||||
let mut settings = settings::get_settings(&app);
|
||||
settings.log_level = level;
|
||||
settings::write_settings(&app, settings);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn open_recordings_folder(app: AppHandle) -> Result<(), String> {
|
||||
let app_data_dir = app
|
||||
|
|
@ -38,3 +65,33 @@ pub fn open_recordings_folder(app: AppHandle) -> Result<(), String> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn open_log_dir(app: AppHandle) -> Result<(), String> {
|
||||
let log_dir = app
|
||||
.path()
|
||||
.app_log_dir()
|
||||
.map_err(|e| format!("Failed to get log directory: {}", e))?;
|
||||
|
||||
let path = log_dir.to_string_lossy().as_ref().to_string();
|
||||
app.opener()
|
||||
.open_path(path, None::<String>)
|
||||
.map_err(|e| format!("Failed to open log directory: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn open_app_data_dir(app: AppHandle) -> Result<(), String> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.map_err(|e| format!("Failed to get app data directory: {}", e))?;
|
||||
|
||||
let path = app_data_dir.to_string_lossy().as_ref().to_string();
|
||||
app.opener()
|
||||
.open_path(path, None::<String>)
|
||||
.map_err(|e| format!("Failed to open app data directory: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ mod tests {
|
|||
// This will run on macOS and should not panic
|
||||
let result = is_clamshell();
|
||||
assert!(result.is_ok());
|
||||
println!("Clamshell state: {:?}", result.unwrap());
|
||||
let _ = result.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -87,8 +87,6 @@ mod tests {
|
|||
fn test_has_builtin_display() {
|
||||
let result = has_builtin_display();
|
||||
assert!(result.is_ok());
|
||||
if let Ok(has_builtin) = result {
|
||||
println!("Has built-in display (is laptop): {}", has_builtin);
|
||||
}
|
||||
let _ = result.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@ mod shortcut;
|
|||
mod tray;
|
||||
mod utils;
|
||||
|
||||
use env_filter::Builder as EnvFilterBuilder;
|
||||
use managers::audio::AudioRecordingManager;
|
||||
use managers::history::HistoryManager;
|
||||
use managers::model::ModelManager;
|
||||
use managers::transcription::TranscriptionManager;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::image::Image;
|
||||
|
||||
|
|
@ -24,6 +26,45 @@ use tauri::tray::TrayIconBuilder;
|
|||
use tauri::Emitter;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri_plugin_autostart::{MacosLauncher, ManagerExt};
|
||||
use tauri_plugin_log::{Builder as LogBuilder, RotationStrategy, Target, TargetKind};
|
||||
|
||||
// Global atomic to store the file log level filter
|
||||
// We use u8 to store the log::LevelFilter as a number
|
||||
pub static FILE_LOG_LEVEL: AtomicU8 = AtomicU8::new(log::LevelFilter::Debug as u8);
|
||||
|
||||
fn level_filter_from_u8(value: u8) -> log::LevelFilter {
|
||||
match value {
|
||||
0 => log::LevelFilter::Off,
|
||||
1 => log::LevelFilter::Error,
|
||||
2 => log::LevelFilter::Warn,
|
||||
3 => log::LevelFilter::Info,
|
||||
4 => log::LevelFilter::Debug,
|
||||
5 => log::LevelFilter::Trace,
|
||||
_ => log::LevelFilter::Trace,
|
||||
}
|
||||
}
|
||||
|
||||
fn build_console_filter() -> env_filter::Filter {
|
||||
let mut builder = EnvFilterBuilder::new();
|
||||
|
||||
match std::env::var("RUST_LOG") {
|
||||
Ok(spec) if !spec.trim().is_empty() => {
|
||||
if let Err(err) = builder.try_parse(&spec) {
|
||||
log::warn!(
|
||||
"Ignoring invalid RUST_LOG value '{}': {}. Falling back to info-level console logging",
|
||||
spec,
|
||||
err
|
||||
);
|
||||
builder.filter_level(log::LevelFilter::Info);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
builder.filter_level(log::LevelFilter::Info);
|
||||
}
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ShortcutToggleStates {
|
||||
|
|
@ -37,21 +78,21 @@ fn show_main_window(app: &AppHandle) {
|
|||
if let Some(main_window) = app.get_webview_window("main") {
|
||||
// First, ensure the window is visible
|
||||
if let Err(e) = main_window.show() {
|
||||
eprintln!("Failed to show window: {}", e);
|
||||
log::error!("Failed to show window: {}", e);
|
||||
}
|
||||
// Then, bring it to the front and give it focus
|
||||
if let Err(e) = main_window.set_focus() {
|
||||
eprintln!("Failed to focus window: {}", e);
|
||||
log::error!("Failed to focus window: {}", e);
|
||||
}
|
||||
// Optional: On macOS, ensure the app becomes active if it was an accessory
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Err(e) = app.set_activation_policy(tauri::ActivationPolicy::Regular) {
|
||||
eprintln!("Failed to set activation policy to Regular: {}", e);
|
||||
log::error!("Failed to set activation policy to Regular: {}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Main window not found.");
|
||||
log::error!("Main window not found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,9 +196,34 @@ fn trigger_update_check(app: AppHandle) -> Result<(), String> {
|
|||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
env_logger::init();
|
||||
// Parse console logging directives from RUST_LOG, falling back to info-level logging
|
||||
// when the variable is unset
|
||||
let console_filter = build_console_filter();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(
|
||||
LogBuilder::new()
|
||||
.level(log::LevelFilter::Trace) // Set to most verbose level globally
|
||||
.max_file_size(500_000)
|
||||
.rotation_strategy(RotationStrategy::KeepOne)
|
||||
.clear_targets()
|
||||
.targets([
|
||||
// Console output respects RUST_LOG environment variable
|
||||
Target::new(TargetKind::Stdout).filter({
|
||||
let console_filter = console_filter.clone();
|
||||
move |metadata| console_filter.enabled(metadata)
|
||||
}),
|
||||
// File logs respect the user's settings (stored in FILE_LOG_LEVEL atomic)
|
||||
Target::new(TargetKind::LogDir {
|
||||
file_name: Some("handy".into()),
|
||||
})
|
||||
.filter(|metadata| {
|
||||
let file_level = FILE_LOG_LEVEL.load(Ordering::Relaxed);
|
||||
metadata.level() <= level_filter_from_u8(file_level)
|
||||
}),
|
||||
])
|
||||
.build(),
|
||||
)
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||
show_main_window(app);
|
||||
}))
|
||||
|
|
@ -185,6 +251,9 @@ pub fn run() {
|
|||
.manage(Mutex::new(ShortcutToggleStates::default()))
|
||||
.setup(move |app| {
|
||||
let settings = settings::get_settings(&app.handle());
|
||||
let file_log_level: log::Level = settings.log_level.clone().into();
|
||||
// Store the file log level in the atomic for the filter to use
|
||||
FILE_LOG_LEVEL.store(file_log_level.to_level_filter() as u8, Ordering::Relaxed);
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
initialize_core_logic(&app_handle);
|
||||
|
|
@ -209,12 +278,12 @@ pub fn run() {
|
|||
.app_handle()
|
||||
.set_activation_policy(tauri::ActivationPolicy::Accessory);
|
||||
if let Err(e) = res {
|
||||
println!("Failed to set activation policy: {}", e);
|
||||
log::error!("Failed to set activation policy: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
tauri::WindowEvent::ThemeChanged(theme) => {
|
||||
println!("Theme changed to: {:?}", theme);
|
||||
log::info!("Theme changed to: {:?}", theme);
|
||||
// Update tray icon to match new theme, maintaining idle state
|
||||
utils::change_tray_icon(&window.app_handle(), utils::TrayIconState::Idle);
|
||||
}
|
||||
|
|
@ -253,7 +322,11 @@ pub fn run() {
|
|||
trigger_update_check,
|
||||
commands::cancel_operation,
|
||||
commands::get_app_dir_path,
|
||||
commands::get_log_dir_path,
|
||||
commands::set_log_level,
|
||||
commands::open_recordings_folder,
|
||||
commands::open_log_dir,
|
||||
commands::open_app_data_dir,
|
||||
commands::models::get_available_models,
|
||||
commands::models::get_model_info,
|
||||
commands::models::download_model,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::audio_toolkit::{list_input_devices, vad::SmoothedVad, AudioRecorder,
|
|||
use crate::helpers::clamshell;
|
||||
use crate::settings::{get_settings, AppSettings};
|
||||
use crate::utils;
|
||||
use log::{debug, info};
|
||||
use log::{debug, error, info};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use tauri::Manager;
|
||||
|
|
@ -339,7 +339,7 @@ impl AudioRecordingManager {
|
|||
// Ensure microphone is open in on-demand mode
|
||||
if matches!(*self.mode.lock().unwrap(), MicrophoneMode::OnDemand) {
|
||||
if let Err(e) = self.start_microphone_stream() {
|
||||
eprintln!("Failed to open microphone stream: {e}");
|
||||
error!("Failed to open microphone stream: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -354,7 +354,7 @@ impl AudioRecordingManager {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
eprintln!("Recorder not available");
|
||||
error!("Recorder not available");
|
||||
false
|
||||
} else {
|
||||
false
|
||||
|
|
@ -384,12 +384,12 @@ impl AudioRecordingManager {
|
|||
match rec.stop() {
|
||||
Ok(buf) => buf,
|
||||
Err(e) => {
|
||||
eprintln!("stop() failed: {e}");
|
||||
error!("stop() failed: {e}");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Recorder not available");
|
||||
error!("Recorder not available");
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
|
|
@ -402,7 +402,7 @@ impl AudioRecordingManager {
|
|||
|
||||
// Pad if very short
|
||||
let s_len = samples.len();
|
||||
// println!("Got {} samples", { s_len });
|
||||
// debug!("Got {} samples", s_len);
|
||||
if s_len < WHISPER_SAMPLE_RATE && s_len > 0 {
|
||||
let mut padded = samples;
|
||||
padded.resize(WHISPER_SAMPLE_RATE * 5 / 4, 0.0);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::settings::{get_settings, write_settings};
|
|||
use anyhow::Result;
|
||||
use flate2::read::GzDecoder;
|
||||
use futures_util::StreamExt;
|
||||
use log::{debug, info, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
|
@ -225,9 +226,9 @@ impl ModelManager {
|
|||
|
||||
// Only copy if user doesn't already have the model
|
||||
if !user_path.exists() {
|
||||
println!("Migrating bundled model {} to user directory", filename);
|
||||
info!("Migrating bundled model {} to user directory", filename);
|
||||
fs::copy(&bundled_path, &user_path)?;
|
||||
println!("Successfully migrated {}", filename);
|
||||
info!("Successfully migrated {}", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,7 +251,7 @@ impl ModelManager {
|
|||
|
||||
// Clean up any leftover .extracting directories from interrupted extractions
|
||||
if extracting_path.exists() {
|
||||
println!("Cleaning up interrupted extraction for model: {}", model.id);
|
||||
warn!("Cleaning up interrupted extraction for model: {}", model.id);
|
||||
let _ = fs::remove_dir_all(&extracting_path);
|
||||
}
|
||||
|
||||
|
|
@ -292,7 +293,7 @@ impl ModelManager {
|
|||
// Find the first available (downloaded) model
|
||||
let models = self.available_models.lock().unwrap();
|
||||
if let Some(available_model) = models.values().find(|model| model.is_downloaded) {
|
||||
println!(
|
||||
info!(
|
||||
"Auto-selecting model: {} ({})",
|
||||
available_model.id, available_model.name
|
||||
);
|
||||
|
|
@ -302,7 +303,7 @@ impl ModelManager {
|
|||
updated_settings.selected_model = available_model.id.clone();
|
||||
write_settings(&self.app_handle, updated_settings);
|
||||
|
||||
println!("Successfully auto-selected model: {}", available_model.id);
|
||||
info!("Successfully auto-selected model: {}", available_model.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,10 +340,10 @@ impl ModelManager {
|
|||
// Check if we have a partial download to resume
|
||||
let resume_from = if partial_path.exists() {
|
||||
let size = partial_path.metadata()?.len();
|
||||
println!("Resuming download of model {} from byte {}", model_id, size);
|
||||
info!("Resuming download of model {} from byte {}", model_id, size);
|
||||
size
|
||||
} else {
|
||||
println!("Starting fresh download of model {} from {}", model_id, url);
|
||||
info!("Starting fresh download of model {} from {}", model_id, url);
|
||||
0
|
||||
};
|
||||
|
||||
|
|
@ -456,7 +457,7 @@ impl ModelManager {
|
|||
if model_info.is_directory {
|
||||
// Emit extraction started event
|
||||
let _ = self.app_handle.emit("model-extraction-started", model_id);
|
||||
println!("Extracting archive for directory-based model: {}", model_id);
|
||||
info!("Extracting archive for directory-based model: {}", model_id);
|
||||
|
||||
// Use a temporary extraction directory to ensure atomic operations
|
||||
let temp_extract_dir = self
|
||||
|
|
@ -515,7 +516,7 @@ impl ModelManager {
|
|||
fs::rename(&temp_extract_dir, &final_model_dir)?;
|
||||
}
|
||||
|
||||
println!("Successfully extracted archive for model: {}", model_id);
|
||||
info!("Successfully extracted archive for model: {}", model_id);
|
||||
// Emit extraction completed event
|
||||
let _ = self.app_handle.emit("model-extraction-completed", model_id);
|
||||
|
||||
|
|
@ -539,7 +540,7 @@ impl ModelManager {
|
|||
// Emit completion event
|
||||
let _ = self.app_handle.emit("model-download-complete", model_id);
|
||||
|
||||
println!(
|
||||
info!(
|
||||
"Successfully downloaded model {} to {:?}",
|
||||
model_id, model_path
|
||||
);
|
||||
|
|
@ -548,7 +549,7 @@ impl ModelManager {
|
|||
}
|
||||
|
||||
pub fn delete_model(&self, model_id: &str) -> Result<()> {
|
||||
println!("ModelManager: delete_model called for: {}", model_id);
|
||||
debug!("ModelManager: delete_model called for: {}", model_id);
|
||||
|
||||
let model_info = {
|
||||
let models = self.available_models.lock().unwrap();
|
||||
|
|
@ -558,43 +559,40 @@ impl ModelManager {
|
|||
let model_info =
|
||||
model_info.ok_or_else(|| anyhow::anyhow!("Model not found: {}", model_id))?;
|
||||
|
||||
println!("ModelManager: Found model info: {:?}", model_info);
|
||||
debug!("ModelManager: Found model info: {:?}", model_info);
|
||||
|
||||
let model_path = self.models_dir.join(&model_info.filename);
|
||||
let partial_path = self
|
||||
.models_dir
|
||||
.join(format!("{}.partial", &model_info.filename));
|
||||
println!("ModelManager: Model path: {:?}", model_path);
|
||||
println!("ModelManager: Partial path: {:?}", partial_path);
|
||||
debug!("ModelManager: Model path: {:?}", model_path);
|
||||
debug!("ModelManager: Partial path: {:?}", partial_path);
|
||||
|
||||
let mut deleted_something = false;
|
||||
|
||||
if model_info.is_directory {
|
||||
// Delete complete model directory if it exists
|
||||
if model_path.exists() && model_path.is_dir() {
|
||||
println!(
|
||||
"ModelManager: Deleting model directory at: {:?}",
|
||||
model_path
|
||||
);
|
||||
info!("Deleting model directory at: {:?}", model_path);
|
||||
fs::remove_dir_all(&model_path)?;
|
||||
println!("ModelManager: Model directory deleted successfully");
|
||||
info!("Model directory deleted successfully");
|
||||
deleted_something = true;
|
||||
}
|
||||
} else {
|
||||
// Delete complete model file if it exists
|
||||
if model_path.exists() {
|
||||
println!("ModelManager: Deleting model file at: {:?}", model_path);
|
||||
info!("Deleting model file at: {:?}", model_path);
|
||||
fs::remove_file(&model_path)?;
|
||||
println!("ModelManager: Model file deleted successfully");
|
||||
info!("Model file deleted successfully");
|
||||
deleted_something = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete partial file if it exists (same for both types)
|
||||
if partial_path.exists() {
|
||||
println!("ModelManager: Deleting partial file at: {:?}", partial_path);
|
||||
info!("Deleting partial file at: {:?}", partial_path);
|
||||
fs::remove_file(&partial_path)?;
|
||||
println!("ModelManager: Partial file deleted successfully");
|
||||
info!("Partial file deleted successfully");
|
||||
deleted_something = true;
|
||||
}
|
||||
|
||||
|
|
@ -604,7 +602,7 @@ impl ModelManager {
|
|||
|
||||
// Update download status
|
||||
self.update_download_status()?;
|
||||
println!("ModelManager: Download status updated");
|
||||
debug!("ModelManager: download status updated");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -655,7 +653,7 @@ impl ModelManager {
|
|||
}
|
||||
|
||||
pub fn cancel_download(&self, model_id: &str) -> Result<()> {
|
||||
println!("ModelManager: cancel_download called for: {}", model_id);
|
||||
debug!("ModelManager: cancel_download called for: {}", model_id);
|
||||
|
||||
let _model_info = {
|
||||
let models = self.available_models.lock().unwrap();
|
||||
|
|
@ -680,7 +678,7 @@ impl ModelManager {
|
|||
// Update download status to reflect current state
|
||||
self.update_download_status()?;
|
||||
|
||||
println!("ModelManager: Download cancelled for: {}", model_id);
|
||||
info!("Download cancelled for: {}", model_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::audio_toolkit::apply_custom_words;
|
|||
use crate::managers::model::{EngineType, ModelManager};
|
||||
use crate::settings::{get_settings, ModelUnloadTimeout};
|
||||
use anyhow::Result;
|
||||
use log::debug;
|
||||
use log::{debug, error, info, warn};
|
||||
use serde::Serialize;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
|
@ -289,7 +289,7 @@ impl TranscriptionManager {
|
|||
thread::spawn(move || {
|
||||
let settings = get_settings(&self_clone.app_handle);
|
||||
if let Err(e) = self_clone.load_model(&settings.selected_model) {
|
||||
eprintln!("Failed to load model: {}", e);
|
||||
error!("Failed to load model: {}", e);
|
||||
}
|
||||
let mut is_loading = self_clone.is_loading.lock().unwrap();
|
||||
*is_loading = false;
|
||||
|
|
@ -314,10 +314,10 @@ impl TranscriptionManager {
|
|||
|
||||
let st = std::time::Instant::now();
|
||||
|
||||
println!("Audio vector length: {}", audio.len());
|
||||
debug!("Audio vector length: {}", audio.len());
|
||||
|
||||
if audio.len() == 0 {
|
||||
println!("Empty audio vector");
|
||||
debug!("Empty audio vector");
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
||||
|
|
@ -393,17 +393,29 @@ impl TranscriptionManager {
|
|||
} else {
|
||||
""
|
||||
};
|
||||
println!("\ntook {}ms{}", (et - st).as_millis(), translation_note);
|
||||
info!(
|
||||
"Transcription completed in {}ms{}",
|
||||
(et - st).as_millis(),
|
||||
translation_note
|
||||
);
|
||||
|
||||
let final_result = corrected_result.trim().to_string();
|
||||
|
||||
if final_result.is_empty() {
|
||||
info!("Transcription result is empty");
|
||||
} else {
|
||||
info!("Transcription result: {}", final_result);
|
||||
}
|
||||
|
||||
// Check if we should immediately unload the model after transcription
|
||||
if settings.model_unload_timeout == ModelUnloadTimeout::Immediately {
|
||||
println!("⚡ Immediately unloading model after transcription");
|
||||
info!("Immediately unloading model after transcription");
|
||||
if let Err(e) = self.unload_model() {
|
||||
eprintln!("Failed to immediately unload model: {}", e);
|
||||
error!("Failed to immediately unload model: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(corrected_result.trim().to_string())
|
||||
Ok(final_result)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +429,7 @@ impl Drop for TranscriptionManager {
|
|||
// Wait for the thread to finish gracefully
|
||||
if let Some(handle) = self.watcher_handle.lock().unwrap().take() {
|
||||
if let Err(e) = handle.join() {
|
||||
eprintln!("Failed to join idle watcher thread: {:?}", e);
|
||||
warn!("Failed to join idle watcher thread: {:?}", e);
|
||||
} else {
|
||||
debug!("Idle watcher thread joined successfully");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use log::{debug, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_log::LogLevel;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
|
|
@ -181,6 +183,8 @@ pub struct AppSettings {
|
|||
pub overlay_position: OverlayPosition,
|
||||
#[serde(default = "default_debug_mode")]
|
||||
pub debug_mode: bool,
|
||||
#[serde(default = "default_log_level")]
|
||||
pub log_level: LogLevel,
|
||||
#[serde(default)]
|
||||
pub custom_words: Vec<String>,
|
||||
#[serde(default)]
|
||||
|
|
@ -248,6 +252,10 @@ fn default_debug_mode() -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn default_log_level() -> LogLevel {
|
||||
LogLevel::Debug
|
||||
}
|
||||
|
||||
fn default_word_correction_threshold() -> f64 {
|
||||
0.18
|
||||
}
|
||||
|
|
@ -374,6 +382,7 @@ pub fn get_default_settings() -> AppSettings {
|
|||
selected_language: "auto".to_string(),
|
||||
overlay_position: OverlayPosition::Bottom,
|
||||
debug_mode: false,
|
||||
log_level: default_log_level(),
|
||||
custom_words: Vec::new(),
|
||||
model_unload_timeout: ModelUnloadTimeout::Never,
|
||||
word_correction_threshold: default_word_correction_threshold(),
|
||||
|
|
@ -425,11 +434,11 @@ pub fn load_or_create_app_settings(app: &AppHandle) -> AppSettings {
|
|||
// Parse the entire settings object
|
||||
match serde_json::from_value::<AppSettings>(settings_value) {
|
||||
Ok(settings) => {
|
||||
println!("Found existing settings: {:?}", settings);
|
||||
debug!("Found existing settings: {:?}", settings);
|
||||
settings
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Failed to parse settings: {}", e);
|
||||
warn!("Failed to parse settings: {}", e);
|
||||
// Fall back to default settings if parsing fails
|
||||
let default_settings = get_default_settings();
|
||||
store.set("settings", serde_json::to_value(&default_settings).unwrap());
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use log::{error, warn};
|
||||
use serde::Serialize;
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
use tauri_plugin_autostart::ManagerExt;
|
||||
|
|
@ -16,7 +17,7 @@ pub fn init_shortcuts(app: &AppHandle) {
|
|||
// Register shortcuts with the bindings from settings
|
||||
for (_id, binding) in settings.bindings {
|
||||
if let Err(e) = _register_shortcut(app, binding) {
|
||||
eprintln!("Failed to register shortcut {} during init: {}", _id, e);
|
||||
error!("Failed to register shortcut {} during init: {}", _id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +42,7 @@ pub fn change_binding(
|
|||
Some(binding) => binding.clone(),
|
||||
None => {
|
||||
let error_msg = format!("Binding with id '{}' not found", id);
|
||||
eprintln!("change_binding error: {}", error_msg);
|
||||
warn!("change_binding error: {}", error_msg);
|
||||
return Ok(BindingResponse {
|
||||
success: false,
|
||||
binding: None,
|
||||
|
|
@ -53,12 +54,12 @@ pub fn change_binding(
|
|||
// Unregister the existing binding
|
||||
if let Err(e) = _unregister_shortcut(&app, binding_to_modify.clone()) {
|
||||
let error_msg = format!("Failed to unregister shortcut: {}", e);
|
||||
eprintln!("change_binding error: {}", error_msg);
|
||||
error!("change_binding error: {}", error_msg);
|
||||
}
|
||||
|
||||
// Validate the new shortcut before we touch the current registration
|
||||
if let Err(e) = validate_shortcut_string(&binding) {
|
||||
eprintln!("change_binding validation error: {}", e);
|
||||
warn!("change_binding validation error: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ pub fn change_binding(
|
|||
// Register the new binding
|
||||
if let Err(e) = _register_shortcut(&app, updated_binding.clone()) {
|
||||
let error_msg = format!("Failed to register shortcut: {}", e);
|
||||
eprintln!("change_binding error: {}", error_msg);
|
||||
error!("change_binding error: {}", error_msg);
|
||||
return Ok(BindingResponse {
|
||||
success: false,
|
||||
binding: None,
|
||||
|
|
@ -135,7 +136,7 @@ pub fn change_sound_theme_setting(app: AppHandle, theme: String) -> Result<(), S
|
|||
"pop" => SoundTheme::Pop,
|
||||
"custom" => SoundTheme::Custom,
|
||||
other => {
|
||||
eprintln!("Invalid sound theme '{}', defaulting to marimba", other);
|
||||
warn!("Invalid sound theme '{}', defaulting to marimba", other);
|
||||
SoundTheme::Marimba
|
||||
}
|
||||
};
|
||||
|
|
@ -168,7 +169,7 @@ pub fn change_overlay_position_setting(app: AppHandle, position: String) -> Resu
|
|||
"top" => OverlayPosition::Top,
|
||||
"bottom" => OverlayPosition::Bottom,
|
||||
other => {
|
||||
eprintln!("Invalid overlay position '{}', defaulting to bottom", other);
|
||||
warn!("Invalid overlay position '{}', defaulting to bottom", other);
|
||||
OverlayPosition::Bottom
|
||||
}
|
||||
};
|
||||
|
|
@ -271,7 +272,7 @@ pub fn change_paste_method_setting(app: AppHandle, method: String) -> Result<(),
|
|||
#[cfg(not(target_os = "macos"))]
|
||||
"shift_insert" => PasteMethod::ShiftInsert,
|
||||
other => {
|
||||
eprintln!("Invalid paste method '{}', defaulting to ctrl_v", other);
|
||||
warn!("Invalid paste method '{}', defaulting to ctrl_v", other);
|
||||
PasteMethod::CtrlV
|
||||
}
|
||||
};
|
||||
|
|
@ -287,7 +288,7 @@ pub fn change_clipboard_handling_setting(app: AppHandle, handling: String) -> Re
|
|||
"dont_modify" => ClipboardHandling::DontModify,
|
||||
"copy_to_clipboard" => ClipboardHandling::CopyToClipboard,
|
||||
other => {
|
||||
eprintln!(
|
||||
warn!(
|
||||
"Invalid clipboard handling '{}', defaulting to dont_modify",
|
||||
other
|
||||
);
|
||||
|
|
@ -644,7 +645,7 @@ fn validate_shortcut_string(raw: &str) -> Result<(), String> {
|
|||
pub fn suspend_binding(app: AppHandle, id: String) -> Result<(), String> {
|
||||
if let Some(b) = settings::get_bindings(&app).get(&id).cloned() {
|
||||
if let Err(e) = _unregister_shortcut(&app, b) {
|
||||
eprintln!("suspend_binding error for id '{}': {}", id, e);
|
||||
error!("suspend_binding error for id '{}': {}", id, e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -656,7 +657,7 @@ pub fn suspend_binding(app: AppHandle, id: String) -> Result<(), String> {
|
|||
pub fn resume_binding(app: AppHandle, id: String) -> Result<(), String> {
|
||||
if let Some(b) = settings::get_bindings(&app).get(&id).cloned() {
|
||||
if let Err(e) = _register_shortcut(&app, b) {
|
||||
eprintln!("resume_binding error for id '{}': {}", id, e);
|
||||
error!("resume_binding error for id '{}': {}", id, e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -666,7 +667,7 @@ pub fn resume_binding(app: AppHandle, id: String) -> Result<(), String> {
|
|||
fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), String> {
|
||||
// Validate human-level rules first
|
||||
if let Err(e) = validate_shortcut_string(&binding.current_binding) {
|
||||
eprintln!(
|
||||
warn!(
|
||||
"_register_shortcut validation error for binding '{}': {}",
|
||||
binding.current_binding, e
|
||||
);
|
||||
|
|
@ -681,7 +682,7 @@ fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), S
|
|||
"Failed to parse shortcut '{}': {}",
|
||||
binding.current_binding, e
|
||||
);
|
||||
eprintln!("_register_shortcut parse error: {}", error_msg);
|
||||
error!("_register_shortcut parse error: {}", error_msg);
|
||||
return Err(error_msg);
|
||||
}
|
||||
};
|
||||
|
|
@ -689,7 +690,7 @@ fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), S
|
|||
// Prevent duplicate registrations that would silently shadow one another
|
||||
if app.global_shortcut().is_registered(shortcut) {
|
||||
let error_msg = format!("Shortcut '{}' is already in use", binding.current_binding);
|
||||
eprintln!("_register_shortcut duplicate error: {}", error_msg);
|
||||
warn!("_register_shortcut duplicate error: {}", error_msg);
|
||||
return Err(error_msg);
|
||||
}
|
||||
|
||||
|
|
@ -733,8 +734,8 @@ fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), S
|
|||
}
|
||||
}
|
||||
} else {
|
||||
println!(
|
||||
"Warning: No action defined in ACTION_MAP for shortcut ID '{}'. Shortcut: '{}', State: {:?}",
|
||||
warn!(
|
||||
"No action defined in ACTION_MAP for shortcut ID '{}'. Shortcut: '{}', State: {:?}",
|
||||
binding_id_for_closure, shortcut_string, event.state
|
||||
);
|
||||
}
|
||||
|
|
@ -742,7 +743,7 @@ fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(), S
|
|||
})
|
||||
.map_err(|e| {
|
||||
let error_msg = format!("Couldn't register shortcut '{}': {}", binding.current_binding, e);
|
||||
eprintln!("_register_shortcut registration error: {}", error_msg);
|
||||
error!("_register_shortcut registration error: {}", error_msg);
|
||||
error_msg
|
||||
})?;
|
||||
|
||||
|
|
@ -757,7 +758,7 @@ fn _unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(),
|
|||
"Failed to parse shortcut '{}' for unregistration: {}",
|
||||
binding.current_binding, e
|
||||
);
|
||||
eprintln!("_unregister_shortcut parse error: {}", error_msg);
|
||||
error!("_unregister_shortcut parse error: {}", error_msg);
|
||||
return Err(error_msg);
|
||||
}
|
||||
};
|
||||
|
|
@ -767,7 +768,7 @@ fn _unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) -> Result<(),
|
|||
"Failed to unregister shortcut '{}': {}",
|
||||
binding.current_binding, e
|
||||
);
|
||||
eprintln!("_unregister_shortcut error: {}", error_msg);
|
||||
error!("_unregister_shortcut error: {}", error_msg);
|
||||
error_msg
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::actions::ACTION_MAP;
|
||||
use crate::managers::audio::AudioRecordingManager;
|
||||
use crate::ManagedToggleState;
|
||||
use log::{info, warn};
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ pub use crate::tray::*;
|
|||
/// Centralized cancellation function that can be called from anywhere in the app.
|
||||
/// Handles cancelling both recording and transcription operations and updates UI state.
|
||||
pub fn cancel_current_operation(app: &AppHandle) {
|
||||
println!("Initiating operation cancellation...");
|
||||
info!("Initiating operation cancellation...");
|
||||
|
||||
// First, reset all shortcut toggle states and call stop actions
|
||||
// This is critical for non-push-to-talk mode where shortcuts toggle on/off
|
||||
|
|
@ -28,7 +29,7 @@ pub fn cancel_current_operation(app: &AppHandle) {
|
|||
.collect();
|
||||
|
||||
for binding_id in active_bindings {
|
||||
println!("Stopping active action for binding: {}", binding_id);
|
||||
info!("Stopping active action for binding: {}", binding_id);
|
||||
|
||||
// Call the action's stop method to ensure proper cleanup
|
||||
if let Some(action) = ACTION_MAP.get(&binding_id) {
|
||||
|
|
@ -41,7 +42,7 @@ pub fn cancel_current_operation(app: &AppHandle) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Warning: Failed to lock toggle state manager during cancellation");
|
||||
warn!("Failed to lock toggle state manager during cancellation");
|
||||
}
|
||||
|
||||
// Cancel any ongoing recording
|
||||
|
|
@ -51,5 +52,5 @@ pub fn cancel_current_operation(app: &AppHandle) {
|
|||
// Update tray icon and menu to idle state
|
||||
change_tray_icon(app, crate::tray::TrayIconState::Idle);
|
||||
|
||||
println!("Operation cancellation completed - returned to idle state");
|
||||
info!("Operation cancellation completed - returned to idle state");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { TextDisplay } from "../ui";
|
||||
import { SettingContainer } from "../ui/SettingContainer";
|
||||
import { Button } from "../ui/Button";
|
||||
|
||||
interface AppDataDirectoryProps {
|
||||
descriptionMode?: "tooltip" | "inline";
|
||||
|
|
@ -32,9 +33,13 @@ export const AppDataDirectory: React.FC<AppDataDirectoryProps> = ({
|
|||
loadAppDirectory();
|
||||
}, []);
|
||||
|
||||
const handleCopy = (value: string) => {
|
||||
// Could add a toast notification here if desired
|
||||
console.log("Copied to clipboard:", value);
|
||||
const handleOpen = async () => {
|
||||
if (!appDirPath) return;
|
||||
try {
|
||||
await invoke("open_app_data_dir");
|
||||
} catch (openError) {
|
||||
console.error("Failed to open app data directory:", openError);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
|
|
@ -57,15 +62,27 @@ export const AppDataDirectory: React.FC<AppDataDirectoryProps> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<TextDisplay
|
||||
label="App Data Directory"
|
||||
<SettingContainer
|
||||
title="App Data Directory"
|
||||
description="Main directory where application data, settings, and models are stored"
|
||||
value={appDirPath}
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
copyable={true}
|
||||
monospace={true}
|
||||
onCopy={handleCopy}
|
||||
/>
|
||||
layout="stacked"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 min-w-0 px-2 py-2 bg-mid-gray/10 border border-mid-gray/80 rounded text-xs font-mono break-all">
|
||||
{appDirPath}
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleOpen}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
disabled={!appDirPath}
|
||||
className="px-3 py-2"
|
||||
>
|
||||
Open
|
||||
</Button>
|
||||
</div>
|
||||
</SettingContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import React from "react";
|
||||
import { WordCorrectionThreshold } from "./WordCorrectionThreshold";
|
||||
import { LogDirectory } from "./LogDirectory";
|
||||
import { LogLevelSelector } from "./LogLevelSelector";
|
||||
import { SettingsGroup } from "../../ui/SettingsGroup";
|
||||
import { HistoryLimit } from "../HistoryLimit";
|
||||
import { AlwaysOnMicrophone } from "../AlwaysOnMicrophone";
|
||||
|
|
@ -13,6 +15,8 @@ export const DebugSettings: React.FC = () => {
|
|||
return (
|
||||
<div className="max-w-3xl w-full mx-auto space-y-6">
|
||||
<SettingsGroup title="Debug">
|
||||
<LogDirectory grouped={true} />
|
||||
<LogLevelSelector grouped={true} />
|
||||
<SoundPicker
|
||||
label="Sound Theme"
|
||||
description="Choose a sound theme for recording start and stop feedback"
|
||||
|
|
|
|||
81
src/components/settings/debug/LogDirectory.tsx
Normal file
81
src/components/settings/debug/LogDirectory.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { SettingContainer } from "../../ui/SettingContainer";
|
||||
import { Button } from "../../ui/Button";
|
||||
|
||||
interface LogDirectoryProps {
|
||||
descriptionMode?: "tooltip" | "inline";
|
||||
grouped?: boolean;
|
||||
}
|
||||
|
||||
export const LogDirectory: React.FC<LogDirectoryProps> = ({
|
||||
descriptionMode = "tooltip",
|
||||
grouped = false,
|
||||
}) => {
|
||||
const [logDir, setLogDir] = useState<string>("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadLogDirectory = async () => {
|
||||
try {
|
||||
const result = await invoke<string>("get_log_dir_path");
|
||||
setLogDir(result);
|
||||
} catch (err) {
|
||||
const errorMessage =
|
||||
err && typeof err === "object" && "message" in err
|
||||
? String(err.message)
|
||||
: "Failed to load log directory";
|
||||
setError(errorMessage);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadLogDirectory();
|
||||
}, []);
|
||||
|
||||
const handleOpen = async () => {
|
||||
if (!logDir) return;
|
||||
try {
|
||||
await invoke("open_log_dir");
|
||||
} catch (openError) {
|
||||
console.error("Failed to open log directory:", openError);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingContainer
|
||||
title="Log Directory"
|
||||
description="Location on disk where Handy writes rotated log files"
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
layout="stacked"
|
||||
>
|
||||
{loading ? (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-8 bg-gray-100 rounded" />
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="p-3 bg-red-50 border border-red-200 rounded text-xs text-red-600">
|
||||
Error loading log directory: {error}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 min-w-0 px-2 py-2 bg-mid-gray/10 border border-mid-gray/80 rounded text-xs font-mono break-all">
|
||||
{logDir}
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleOpen}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
disabled={!logDir}
|
||||
className="px-3 py-2"
|
||||
>
|
||||
Open
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</SettingContainer>
|
||||
);
|
||||
};
|
||||
60
src/components/settings/debug/LogLevelSelector.tsx
Normal file
60
src/components/settings/debug/LogLevelSelector.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import React from "react";
|
||||
import { SettingContainer } from "../../ui/SettingContainer";
|
||||
import { Dropdown, type DropdownOption } from "../../ui/Dropdown";
|
||||
import { useSettings } from "../../../hooks/useSettings";
|
||||
|
||||
const LOG_LEVEL_OPTIONS: DropdownOption[] = [
|
||||
{ value: "5", label: "Error" },
|
||||
{ value: "4", label: "Warn" },
|
||||
{ value: "3", label: "Info" },
|
||||
{ value: "2", label: "Debug" },
|
||||
{ value: "1", label: "Trace" },
|
||||
];
|
||||
|
||||
interface LogLevelSelectorProps {
|
||||
descriptionMode?: "tooltip" | "inline";
|
||||
grouped?: boolean;
|
||||
}
|
||||
|
||||
export const LogLevelSelector: React.FC<LogLevelSelectorProps> = ({
|
||||
descriptionMode = "tooltip",
|
||||
grouped = false,
|
||||
}) => {
|
||||
const { settings, updateSetting, isUpdating } = useSettings();
|
||||
const currentLevel = settings?.log_level ?? 2;
|
||||
const isLevelUpdating = isUpdating("log_level");
|
||||
|
||||
const selectedValue = currentLevel.toString();
|
||||
|
||||
const handleSelect = async (value: string) => {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
if (Number.isNaN(parsed) || parsed === currentLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateSetting("log_level", parsed as typeof currentLevel);
|
||||
} catch (error) {
|
||||
console.error("Failed to update log level:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingContainer
|
||||
title="Log Level"
|
||||
description="Choose how verbose Handy should be while logging to disk"
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
layout="horizontal"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<Dropdown
|
||||
options={LOG_LEVEL_OPTIONS}
|
||||
selectedValue={selectedValue}
|
||||
onSelect={handleSelect}
|
||||
disabled={!settings || isLevelUpdating}
|
||||
/>
|
||||
</div>
|
||||
</SettingContainer>
|
||||
);
|
||||
};
|
||||
|
|
@ -1 +1,3 @@
|
|||
export { WordCorrectionThreshold } from "./WordCorrectionThreshold";
|
||||
export { LogDirectory } from "./LogDirectory";
|
||||
export { LogLevelSelector } from "./LogLevelSelector";
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ export const ClipboardHandlingSchema = z.enum([
|
|||
]);
|
||||
export type ClipboardHandling = z.infer<typeof ClipboardHandlingSchema>;
|
||||
|
||||
export const LogLevelSchema = z.number().int().min(1).max(5).default(2);
|
||||
export type LogLevelValue = z.infer<typeof LogLevelSchema>;
|
||||
|
||||
export const RecordingRetentionPeriodSchema = z.enum([
|
||||
"never",
|
||||
"preserve_limit",
|
||||
|
|
@ -96,6 +99,7 @@ export const SettingsSchema = z.object({
|
|||
selected_language: z.string(),
|
||||
overlay_position: OverlayPositionSchema,
|
||||
debug_mode: z.boolean(),
|
||||
log_level: LogLevelSchema.optional().default(2),
|
||||
custom_words: z.array(z.string()).optional().default([]),
|
||||
model_unload_timeout: ModelUnloadTimeoutSchema.optional().default("never"),
|
||||
word_correction_threshold: z.number().optional().default(0.18),
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ const DEFAULT_SETTINGS: Partial<Settings> = {
|
|||
selected_language: "auto",
|
||||
overlay_position: "bottom",
|
||||
debug_mode: false,
|
||||
log_level: 2,
|
||||
custom_words: [],
|
||||
history_limit: 5,
|
||||
recording_retention_period: "preserve_limit",
|
||||
|
|
@ -136,6 +137,7 @@ const settingUpdaters: {
|
|||
invoke("set_post_process_selected_prompt", { id: value }),
|
||||
mute_while_recording: (value) =>
|
||||
invoke("change_mute_while_recording_setting", { enabled: value }),
|
||||
log_level: (value) => invoke("set_log_level", { level: value }),
|
||||
};
|
||||
|
||||
export const useSettingsStore = create<SettingsStore>()(
|
||||
|
|
|
|||
Loading…
Reference in a new issue