voice-bird-cli 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voice-bird-cli
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "voice-bird-cli"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Voice Bird CLI - Terminal audio streaming for voice transcription"
9
9
  authors = [{ name = "Dzmitry Rekun" }]
10
10
  readme = "voice-bird-cli/README.md"
@@ -2,6 +2,12 @@
2
2
  # It is not intended for manual editing.
3
3
  version = 4
4
4
 
5
+ [[package]]
6
+ name = "adler2"
7
+ version = "2.0.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
10
+
5
11
  [[package]]
6
12
  name = "aho-corasick"
7
13
  version = "1.1.4"
@@ -54,6 +60,26 @@ version = "1.0.100"
54
60
  source = "registry+https://github.com/rust-lang/crates.io-index"
55
61
  checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
56
62
 
63
+ [[package]]
64
+ name = "arboard"
65
+ version = "3.6.1"
66
+ source = "registry+https://github.com/rust-lang/crates.io-index"
67
+ checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
68
+ dependencies = [
69
+ "clipboard-win",
70
+ "image",
71
+ "log",
72
+ "objc2",
73
+ "objc2-app-kit",
74
+ "objc2-core-foundation",
75
+ "objc2-core-graphics",
76
+ "objc2-foundation",
77
+ "parking_lot",
78
+ "percent-encoding",
79
+ "windows-sys 0.60.2",
80
+ "x11rb",
81
+ ]
82
+
57
83
  [[package]]
58
84
  name = "autocfg"
59
85
  version = "1.5.0"
@@ -114,12 +140,24 @@ version = "3.19.1"
114
140
  source = "registry+https://github.com/rust-lang/crates.io-index"
115
141
  checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
116
142
 
143
+ [[package]]
144
+ name = "bytemuck"
145
+ version = "1.25.0"
146
+ source = "registry+https://github.com/rust-lang/crates.io-index"
147
+ checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
148
+
117
149
  [[package]]
118
150
  name = "byteorder"
119
151
  version = "1.5.0"
120
152
  source = "registry+https://github.com/rust-lang/crates.io-index"
121
153
  checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
122
154
 
155
+ [[package]]
156
+ name = "byteorder-lite"
157
+ version = "0.1.0"
158
+ source = "registry+https://github.com/rust-lang/crates.io-index"
159
+ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
160
+
123
161
  [[package]]
124
162
  name = "bytes"
125
163
  version = "1.11.0"
@@ -207,6 +245,15 @@ dependencies = [
207
245
  "libloading",
208
246
  ]
209
247
 
248
+ [[package]]
249
+ name = "clipboard-win"
250
+ version = "5.4.1"
251
+ source = "registry+https://github.com/rust-lang/crates.io-index"
252
+ checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
253
+ dependencies = [
254
+ "error-code",
255
+ ]
256
+
210
257
  [[package]]
211
258
  name = "combine"
212
259
  version = "4.6.7"
@@ -391,6 +438,15 @@ dependencies = [
391
438
  "libc",
392
439
  ]
393
440
 
441
+ [[package]]
442
+ name = "crc32fast"
443
+ version = "1.5.0"
444
+ source = "registry+https://github.com/rust-lang/crates.io-index"
445
+ checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
446
+ dependencies = [
447
+ "cfg-if",
448
+ ]
449
+
394
450
  [[package]]
395
451
  name = "crossterm"
396
452
  version = "0.28.1"
@@ -416,6 +472,12 @@ dependencies = [
416
472
  "winapi",
417
473
  ]
418
474
 
475
+ [[package]]
476
+ name = "crunchy"
477
+ version = "0.2.4"
478
+ source = "registry+https://github.com/rust-lang/crates.io-index"
479
+ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
480
+
419
481
  [[package]]
420
482
  name = "crypto-common"
421
483
  version = "0.1.7"
@@ -509,6 +571,16 @@ version = "0.2.0"
509
571
  source = "registry+https://github.com/rust-lang/crates.io-index"
510
572
  checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
511
573
 
574
+ [[package]]
575
+ name = "dispatch2"
576
+ version = "0.3.0"
577
+ source = "registry+https://github.com/rust-lang/crates.io-index"
578
+ checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
579
+ dependencies = [
580
+ "bitflags 2.10.0",
581
+ "objc2",
582
+ ]
583
+
512
584
  [[package]]
513
585
  name = "either"
514
586
  version = "1.15.0"
@@ -531,18 +603,72 @@ dependencies = [
531
603
  "windows-sys 0.61.2",
532
604
  ]
533
605
 
606
+ [[package]]
607
+ name = "error-code"
608
+ version = "3.3.2"
609
+ source = "registry+https://github.com/rust-lang/crates.io-index"
610
+ checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
611
+
534
612
  [[package]]
535
613
  name = "fastrand"
536
614
  version = "2.3.0"
537
615
  source = "registry+https://github.com/rust-lang/crates.io-index"
538
616
  checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
539
617
 
618
+ [[package]]
619
+ name = "fax"
620
+ version = "0.2.6"
621
+ source = "registry+https://github.com/rust-lang/crates.io-index"
622
+ checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
623
+ dependencies = [
624
+ "fax_derive",
625
+ ]
626
+
627
+ [[package]]
628
+ name = "fax_derive"
629
+ version = "0.2.0"
630
+ source = "registry+https://github.com/rust-lang/crates.io-index"
631
+ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
632
+ dependencies = [
633
+ "proc-macro2",
634
+ "quote",
635
+ "syn",
636
+ ]
637
+
638
+ [[package]]
639
+ name = "fdeflate"
640
+ version = "0.3.7"
641
+ source = "registry+https://github.com/rust-lang/crates.io-index"
642
+ checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
643
+ dependencies = [
644
+ "simd-adler32",
645
+ ]
646
+
647
+ [[package]]
648
+ name = "fern"
649
+ version = "0.7.1"
650
+ source = "registry+https://github.com/rust-lang/crates.io-index"
651
+ checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29"
652
+ dependencies = [
653
+ "log",
654
+ ]
655
+
540
656
  [[package]]
541
657
  name = "find-msvc-tools"
542
658
  version = "0.1.8"
543
659
  source = "registry+https://github.com/rust-lang/crates.io-index"
544
660
  checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
545
661
 
662
+ [[package]]
663
+ name = "flate2"
664
+ version = "1.1.9"
665
+ source = "registry+https://github.com/rust-lang/crates.io-index"
666
+ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
667
+ dependencies = [
668
+ "crc32fast",
669
+ "miniz_oxide",
670
+ ]
671
+
546
672
  [[package]]
547
673
  name = "foldhash"
548
674
  version = "0.1.5"
@@ -651,6 +777,16 @@ dependencies = [
651
777
  "version_check",
652
778
  ]
653
779
 
780
+ [[package]]
781
+ name = "gethostname"
782
+ version = "1.1.0"
783
+ source = "registry+https://github.com/rust-lang/crates.io-index"
784
+ checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
785
+ dependencies = [
786
+ "rustix 1.1.3",
787
+ "windows-link",
788
+ ]
789
+
654
790
  [[package]]
655
791
  name = "getrandom"
656
792
  version = "0.2.17"
@@ -680,6 +816,17 @@ version = "0.3.3"
680
816
  source = "registry+https://github.com/rust-lang/crates.io-index"
681
817
  checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
682
818
 
819
+ [[package]]
820
+ name = "half"
821
+ version = "2.7.1"
822
+ source = "registry+https://github.com/rust-lang/crates.io-index"
823
+ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
824
+ dependencies = [
825
+ "cfg-if",
826
+ "crunchy",
827
+ "zerocopy",
828
+ ]
829
+
683
830
  [[package]]
684
831
  name = "hashbrown"
685
832
  version = "0.15.5"
@@ -755,6 +902,20 @@ version = "1.0.1"
755
902
  source = "registry+https://github.com/rust-lang/crates.io-index"
756
903
  checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
757
904
 
905
+ [[package]]
906
+ name = "image"
907
+ version = "0.25.9"
908
+ source = "registry+https://github.com/rust-lang/crates.io-index"
909
+ checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
910
+ dependencies = [
911
+ "bytemuck",
912
+ "byteorder-lite",
913
+ "moxcms",
914
+ "num-traits",
915
+ "png",
916
+ "tiff",
917
+ ]
918
+
758
919
  [[package]]
759
920
  name = "indexmap"
760
921
  version = "2.13.0"
@@ -963,6 +1124,16 @@ version = "0.2.1"
963
1124
  source = "registry+https://github.com/rust-lang/crates.io-index"
964
1125
  checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
965
1126
 
1127
+ [[package]]
1128
+ name = "miniz_oxide"
1129
+ version = "0.8.9"
1130
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1131
+ checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
1132
+ dependencies = [
1133
+ "adler2",
1134
+ "simd-adler32",
1135
+ ]
1136
+
966
1137
  [[package]]
967
1138
  name = "mio"
968
1139
  version = "1.1.1"
@@ -975,6 +1146,16 @@ dependencies = [
975
1146
  "windows-sys 0.61.2",
976
1147
  ]
977
1148
 
1149
+ [[package]]
1150
+ name = "moxcms"
1151
+ version = "0.7.11"
1152
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1153
+ checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
1154
+ dependencies = [
1155
+ "num-traits",
1156
+ "pxfm",
1157
+ ]
1158
+
978
1159
  [[package]]
979
1160
  name = "native-tls"
980
1161
  version = "0.2.14"
@@ -1110,12 +1291,70 @@ dependencies = [
1110
1291
  "objc2-encode",
1111
1292
  ]
1112
1293
 
1294
+ [[package]]
1295
+ name = "objc2-app-kit"
1296
+ version = "0.3.2"
1297
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1298
+ checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
1299
+ dependencies = [
1300
+ "bitflags 2.10.0",
1301
+ "objc2",
1302
+ "objc2-core-graphics",
1303
+ "objc2-foundation",
1304
+ ]
1305
+
1306
+ [[package]]
1307
+ name = "objc2-core-foundation"
1308
+ version = "0.3.2"
1309
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1310
+ checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
1311
+ dependencies = [
1312
+ "bitflags 2.10.0",
1313
+ "dispatch2",
1314
+ "objc2",
1315
+ ]
1316
+
1317
+ [[package]]
1318
+ name = "objc2-core-graphics"
1319
+ version = "0.3.2"
1320
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1321
+ checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
1322
+ dependencies = [
1323
+ "bitflags 2.10.0",
1324
+ "dispatch2",
1325
+ "objc2",
1326
+ "objc2-core-foundation",
1327
+ "objc2-io-surface",
1328
+ ]
1329
+
1113
1330
  [[package]]
1114
1331
  name = "objc2-encode"
1115
1332
  version = "4.1.0"
1116
1333
  source = "registry+https://github.com/rust-lang/crates.io-index"
1117
1334
  checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
1118
1335
 
1336
+ [[package]]
1337
+ name = "objc2-foundation"
1338
+ version = "0.3.2"
1339
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1340
+ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
1341
+ dependencies = [
1342
+ "bitflags 2.10.0",
1343
+ "objc2",
1344
+ "objc2-core-foundation",
1345
+ ]
1346
+
1347
+ [[package]]
1348
+ name = "objc2-io-surface"
1349
+ version = "0.3.2"
1350
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1351
+ checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
1352
+ dependencies = [
1353
+ "bitflags 2.10.0",
1354
+ "objc2",
1355
+ "objc2-core-foundation",
1356
+ ]
1357
+
1119
1358
  [[package]]
1120
1359
  name = "objc_exception"
1121
1360
  version = "0.1.2"
@@ -1233,6 +1472,12 @@ version = "1.0.15"
1233
1472
  source = "registry+https://github.com/rust-lang/crates.io-index"
1234
1473
  checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
1235
1474
 
1475
+ [[package]]
1476
+ name = "percent-encoding"
1477
+ version = "2.3.2"
1478
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1479
+ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
1480
+
1236
1481
  [[package]]
1237
1482
  name = "pin-project-lite"
1238
1483
  version = "0.2.16"
@@ -1251,6 +1496,19 @@ version = "0.3.32"
1251
1496
  source = "registry+https://github.com/rust-lang/crates.io-index"
1252
1497
  checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
1253
1498
 
1499
+ [[package]]
1500
+ name = "png"
1501
+ version = "0.18.1"
1502
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1503
+ checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
1504
+ dependencies = [
1505
+ "bitflags 2.10.0",
1506
+ "crc32fast",
1507
+ "fdeflate",
1508
+ "flate2",
1509
+ "miniz_oxide",
1510
+ ]
1511
+
1254
1512
  [[package]]
1255
1513
  name = "ppv-lite86"
1256
1514
  version = "0.2.21"
@@ -1287,6 +1545,21 @@ dependencies = [
1287
1545
  "unicode-ident",
1288
1546
  ]
1289
1547
 
1548
+ [[package]]
1549
+ name = "pxfm"
1550
+ version = "0.1.27"
1551
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1552
+ checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
1553
+ dependencies = [
1554
+ "num-traits",
1555
+ ]
1556
+
1557
+ [[package]]
1558
+ name = "quick-error"
1559
+ version = "2.0.1"
1560
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1561
+ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
1562
+
1290
1563
  [[package]]
1291
1564
  name = "quote"
1292
1565
  version = "1.0.43"
@@ -1635,6 +1908,12 @@ dependencies = [
1635
1908
  "libc",
1636
1909
  ]
1637
1910
 
1911
+ [[package]]
1912
+ name = "simd-adler32"
1913
+ version = "0.3.8"
1914
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1915
+ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
1916
+
1638
1917
  [[package]]
1639
1918
  name = "slab"
1640
1919
  version = "0.4.11"
@@ -1761,6 +2040,20 @@ dependencies = [
1761
2040
  "syn",
1762
2041
  ]
1763
2042
 
2043
+ [[package]]
2044
+ name = "tiff"
2045
+ version = "0.10.3"
2046
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2047
+ checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
2048
+ dependencies = [
2049
+ "fax",
2050
+ "flate2",
2051
+ "half",
2052
+ "quick-error",
2053
+ "weezl",
2054
+ "zune-jpeg",
2055
+ ]
2056
+
1764
2057
  [[package]]
1765
2058
  name = "tokio"
1766
2059
  version = "1.49.0"
@@ -1944,16 +2237,19 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
1944
2237
 
1945
2238
  [[package]]
1946
2239
  name = "voice-bird-cli"
1947
- version = "0.2.0"
2240
+ version = "0.2.2"
1948
2241
  dependencies = [
1949
2242
  "anyhow",
2243
+ "arboard",
1950
2244
  "chrono",
1951
2245
  "cpal",
1952
2246
  "crossterm",
1953
2247
  "dirs",
2248
+ "fern",
1954
2249
  "futures-util",
1955
2250
  "hound",
1956
2251
  "http",
2252
+ "log",
1957
2253
  "ratatui",
1958
2254
  "rubato",
1959
2255
  "screencapturekit",
@@ -2060,6 +2356,12 @@ dependencies = [
2060
2356
  "wasm-bindgen",
2061
2357
  ]
2062
2358
 
2359
+ [[package]]
2360
+ name = "weezl"
2361
+ version = "0.1.12"
2362
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2363
+ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
2364
+
2063
2365
  [[package]]
2064
2366
  name = "winapi"
2065
2367
  version = "0.3.9"
@@ -2546,6 +2848,23 @@ version = "0.51.0"
2546
2848
  source = "registry+https://github.com/rust-lang/crates.io-index"
2547
2849
  checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
2548
2850
 
2851
+ [[package]]
2852
+ name = "x11rb"
2853
+ version = "0.13.2"
2854
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2855
+ checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
2856
+ dependencies = [
2857
+ "gethostname",
2858
+ "rustix 1.1.3",
2859
+ "x11rb-protocol",
2860
+ ]
2861
+
2862
+ [[package]]
2863
+ name = "x11rb-protocol"
2864
+ version = "0.13.2"
2865
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2866
+ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
2867
+
2549
2868
  [[package]]
2550
2869
  name = "zerocopy"
2551
2870
  version = "0.8.33"
@@ -2571,3 +2890,18 @@ name = "zmij"
2571
2890
  version = "1.0.15"
2572
2891
  source = "registry+https://github.com/rust-lang/crates.io-index"
2573
2892
  checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2"
2893
+
2894
+ [[package]]
2895
+ name = "zune-core"
2896
+ version = "0.4.12"
2897
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2898
+ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
2899
+
2900
+ [[package]]
2901
+ name = "zune-jpeg"
2902
+ version = "0.4.21"
2903
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2904
+ checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
2905
+ dependencies = [
2906
+ "zune-core",
2907
+ ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "voice-bird-cli"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  edition = "2021"
5
5
  description = "Voice Bird CLI - Terminal audio streaming for voice transcription"
6
6
  readme = "README.md"
@@ -25,6 +25,13 @@ tokio-tungstenite = { version = "0.23", features = ["connect", "native-tls"], de
25
25
  futures-util = "0.3"
26
26
  http = "1.0"
27
27
 
28
+ # Logging
29
+ log = "0.4"
30
+ fern = "0.7"
31
+
32
+ # Clipboard
33
+ arboard = "3"
34
+
28
35
  # Utilities
29
36
  anyhow = "1.0"
30
37
  serde = { version = "1.0", features = ["derive"] }
@@ -1,3 +1,4 @@
1
+ use std::path::PathBuf;
1
2
  use std::sync::{Arc, Mutex};
2
3
  use uuid::Uuid;
3
4
 
@@ -69,6 +70,12 @@ pub struct App {
69
70
 
70
71
  /// Status message to display
71
72
  pub status_message: Option<String>,
73
+
74
+ /// Shared error channel — recording threads write errors here
75
+ pub error_channel: Arc<Mutex<Option<String>>>,
76
+
77
+ /// Path to the current log file
78
+ pub log_path: Option<PathBuf>,
72
79
  }
73
80
 
74
81
  impl App {
@@ -90,6 +97,8 @@ impl App {
90
97
  api_key_input: String::new(),
91
98
  should_quit: false,
92
99
  status_message: None,
100
+ error_channel: Arc::new(Mutex::new(None)),
101
+ log_path: None,
93
102
  }
94
103
  }
95
104
 
@@ -191,6 +200,15 @@ impl App {
191
200
  self.api_key_input.clear();
192
201
  }
193
202
 
203
+ /// Check for errors from recording threads and update status
204
+ pub fn check_error(&mut self) {
205
+ if let Ok(mut err) = self.error_channel.lock() {
206
+ if let Some(msg) = err.take() {
207
+ self.status = RecordingStatus::Error(msg);
208
+ }
209
+ }
210
+ }
211
+
194
212
  /// Toggle help display
195
213
  pub fn toggle_help(&mut self) {
196
214
  self.mode = if self.mode == AppMode::Help {
@@ -0,0 +1,47 @@
1
+ use anyhow::Result;
2
+ use chrono::Local;
3
+ use std::fs;
4
+ use std::path::PathBuf;
5
+
6
+ /// Initialize file-based logging for the CLI.
7
+ ///
8
+ /// Logs to `~/.voice-bird-cli/logs/voice_bird_cli_YYYY-MM-DD_HH-MM-SS.log`.
9
+ /// All levels (DEBUG+) go to file only — nothing to terminal (TUI owns the screen).
10
+ /// Returns the log file path so the app can display it.
11
+ pub fn init() -> Result<PathBuf> {
12
+ let logs_dir = dirs::home_dir()
13
+ .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?
14
+ .join(".voice-bird-cli")
15
+ .join("logs");
16
+
17
+ fs::create_dir_all(&logs_dir)?;
18
+
19
+ let log_filename = format!(
20
+ "voice_bird_cli_{}.log",
21
+ Local::now().format("%Y-%m-%d_%H-%M-%S")
22
+ );
23
+ let log_path = logs_dir.join(log_filename);
24
+
25
+ let log_file = fs::OpenOptions::new()
26
+ .create(true)
27
+ .write(true)
28
+ .truncate(true)
29
+ .open(&log_path)?;
30
+
31
+ fern::Dispatch::new()
32
+ .format(|out, message, record| {
33
+ out.finish(format_args!(
34
+ "[{} {} {}] {}",
35
+ Local::now().format("%Y-%m-%d %H:%M:%S"),
36
+ record.level(),
37
+ record.target(),
38
+ message
39
+ ))
40
+ })
41
+ .level(log::LevelFilter::Debug)
42
+ .chain(log_file)
43
+ .apply()
44
+ .map_err(|e| anyhow::anyhow!("Failed to initialize logger: {}", e))?;
45
+
46
+ Ok(log_path)
47
+ }
@@ -1,6 +1,7 @@
1
1
  mod app;
2
2
  mod audio;
3
3
  mod config;
4
+ mod logger;
4
5
  mod platform;
5
6
  mod streaming;
6
7
  mod ui;
@@ -21,6 +22,17 @@ use uuid::Uuid;
21
22
  use app::{App, AppMode, RecordingStatus, ActiveSession};
22
23
 
23
24
  fn main() -> Result<()> {
25
+ // Initialize file logger before TUI takes over the screen
26
+ let log_path = match logger::init() {
27
+ Ok(path) => Some(path),
28
+ Err(e) => {
29
+ eprintln!("Warning: failed to initialize logger: {}", e);
30
+ None
31
+ }
32
+ };
33
+
34
+ log::info!("Voice Bird CLI starting");
35
+
24
36
  // Setup terminal
25
37
  enable_raw_mode()?;
26
38
  let mut stdout = io::stdout();
@@ -30,8 +42,11 @@ fn main() -> Result<()> {
30
42
 
31
43
  // Create app and run
32
44
  let mut app = App::new();
45
+ app.log_path = log_path;
33
46
  app.refresh_sessions();
34
47
 
48
+ log::info!("Found {} audio sessions", app.sessions.len());
49
+
35
50
  let result = run_app(&mut terminal, &mut app);
36
51
 
37
52
  // Restore terminal
@@ -44,9 +59,12 @@ fn main() -> Result<()> {
44
59
  terminal.show_cursor()?;
45
60
 
46
61
  if let Err(e) = result {
62
+ log::error!("Application error: {}", e);
47
63
  eprintln!("Error: {}", e);
48
64
  }
49
65
 
66
+ log::info!("Voice Bird CLI exiting");
67
+
50
68
  Ok(())
51
69
  }
52
70
 
@@ -55,6 +73,9 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> Result<()>
55
73
  // Update duration if recording
56
74
  app.update_duration();
57
75
 
76
+ // Check for errors from recording threads
77
+ app.check_error();
78
+
58
79
  // Draw UI
59
80
  terminal.draw(|f| ui::render(f, app))?;
60
81
 
@@ -109,6 +130,12 @@ fn handle_normal_mode(app: &mut App, key: KeyCode) {
109
130
  KeyCode::Enter => {
110
131
  toggle_recording(app);
111
132
  }
133
+ KeyCode::Char('l') => {
134
+ copy_error_to_clipboard(app);
135
+ }
136
+ KeyCode::Char('L') => {
137
+ copy_log_path_to_clipboard(app);
138
+ }
112
139
  _ => {}
113
140
  }
114
141
  }
@@ -140,6 +167,36 @@ fn handle_help_mode(app: &mut App, key: KeyCode) {
140
167
  }
141
168
  }
142
169
 
170
+ fn copy_error_to_clipboard(app: &mut App) {
171
+ if let RecordingStatus::Error(ref msg) = app.status {
172
+ let text = msg.clone();
173
+ match arboard::Clipboard::new().and_then(|mut cb| cb.set_text(&text)) {
174
+ Ok(()) => {
175
+ app.status_message = Some("Error copied to clipboard".to_string());
176
+ }
177
+ Err(e) => {
178
+ log::warn!("Failed to copy to clipboard: {}", e);
179
+ app.status_message = Some(format!("Clipboard error: {}", e));
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ fn copy_log_path_to_clipboard(app: &mut App) {
186
+ if let Some(ref path) = app.log_path {
187
+ let text = path.display().to_string();
188
+ match arboard::Clipboard::new().and_then(|mut cb| cb.set_text(&text)) {
189
+ Ok(()) => {
190
+ app.status_message = Some("Log path copied to clipboard".to_string());
191
+ }
192
+ Err(e) => {
193
+ log::warn!("Failed to copy to clipboard: {}", e);
194
+ app.status_message = Some(format!("Clipboard error: {}", e));
195
+ }
196
+ }
197
+ }
198
+ }
199
+
143
200
  fn toggle_recording(app: &mut App) {
144
201
  match &app.status {
145
202
  RecordingStatus::Idle | RecordingStatus::Error(_) => {
@@ -176,6 +233,7 @@ fn start_recording(app: &mut App) {
176
233
  let session_id = Uuid::new_v4();
177
234
  let stop_signal = Arc::new(Mutex::new(false));
178
235
  let audio_level = app.audio_level.clone();
236
+ let error_channel = app.error_channel.clone();
179
237
 
180
238
  let server_url_clone = server_url.clone();
181
239
  let api_key_clone = api_key.clone();
@@ -184,6 +242,11 @@ fn start_recording(app: &mut App) {
184
242
  let stop_signal_clone = stop_signal.clone();
185
243
  let audio_level_clone = audio_level.clone();
186
244
 
245
+ log::info!(
246
+ "Starting recording: session={}, device={}, input={}",
247
+ session_id_str, session_clone.device_name, session_clone.is_input
248
+ );
249
+
187
250
  // Spawn recording thread
188
251
  std::thread::spawn(move || {
189
252
  let result = if session_clone.is_input {
@@ -209,7 +272,11 @@ fn start_recording(app: &mut App) {
209
272
  };
210
273
 
211
274
  if let Err(e) = result {
212
- eprintln!("Recording error: {}", e);
275
+ let msg = format!("Recording error: {}", e);
276
+ log::error!("{}", msg);
277
+ if let Ok(mut err) = error_channel.lock() {
278
+ *err = Some(msg);
279
+ }
213
280
  }
214
281
  });
215
282
 
@@ -229,6 +296,8 @@ fn start_recording(app: &mut App) {
229
296
  }
230
297
 
231
298
  fn stop_all_sessions(app: &mut App) {
299
+ log::info!("Stopping all sessions");
300
+
232
301
  for session in &app.active_sessions {
233
302
  if let Ok(mut stop) = session.stop_signal.lock() {
234
303
  *stop = true;
@@ -46,16 +46,21 @@ fn check_macos_version() -> Result<String> {
46
46
 
47
47
  /// Enumerate audio sessions on macOS using ScreenCaptureKit
48
48
  pub fn enumerate_audio_sessions() -> Result<Vec<AudioSession>> {
49
- check_macos_version()?;
49
+ let version = check_macos_version()?;
50
+
51
+ log::info!("Enumerating macOS audio sessions (macOS {})", version);
50
52
 
51
53
  let content = SCShareableContent::get()
52
- .map_err(|e| anyhow::anyhow!(
53
- "Screen Recording permission is required for audio capture.\n\n\
54
- Grant permission in:\n \
55
- System Settings > Privacy & Security > Screen Recording\n\n\
56
- After granting permission, restart the application.\n\
57
- Error: {:?}", e
58
- ))?;
54
+ .map_err(|e| {
55
+ log::error!("SCShareableContent::get() failed: {:?}", e);
56
+ anyhow::anyhow!(
57
+ "Screen Recording permission is required for audio capture.\n\n\
58
+ Grant permission in:\n \
59
+ System Settings > Privacy & Security > Screen Recording\n\n\
60
+ After granting permission, restart the application.\n\
61
+ Error: {:?}", e
62
+ )
63
+ })?;
59
64
 
60
65
  let mut sessions = Vec::new();
61
66
 
@@ -122,14 +127,22 @@ pub fn start_output_recording(
122
127
  ) -> Result<()> {
123
128
  let macos_version = check_macos_version()?;
124
129
 
130
+ log::info!(
131
+ "Starting macOS output recording: app={}, device={}, macOS {}",
132
+ session.app_name, session.device_name, macos_version
133
+ );
134
+
125
135
  let content = SCShareableContent::get()
126
- .map_err(|e| anyhow::anyhow!(
127
- "Screen Recording permission is required for audio capture.\n\n\
128
- Grant permission in:\n \
129
- System Settings > Privacy & Security > Screen Recording\n\n\
130
- After granting permission, restart the application.\n\
131
- Error: {:?}", e
132
- ))?;
136
+ .map_err(|e| {
137
+ log::error!("SCShareableContent::get() failed in start_output_recording: {:?}", e);
138
+ anyhow::anyhow!(
139
+ "Screen Recording permission is required for audio capture.\n\n\
140
+ Grant permission in:\n \
141
+ System Settings > Privacy & Security > Screen Recording\n\n\
142
+ After granting permission, restart the application.\n\
143
+ Error: {:?}", e
144
+ )
145
+ })?;
133
146
 
134
147
  let (tx, rx) = mpsc::channel::<Vec<f32>>();
135
148
 
@@ -245,6 +258,7 @@ pub fn start_output_recording(
245
258
  stream.add_output_handler(handler, SCStreamOutputType::Audio);
246
259
  stream.start_capture()
247
260
  .map_err(|e| {
261
+ log::error!("SCStream::start_capture() failed: {:?}", e);
248
262
  let err_str = format!("{:?}", e);
249
263
  let hint = if err_str.contains("CoreGraphicsErrorDomain") || err_str.contains("1003") {
250
264
  format!(
@@ -40,12 +40,22 @@ fn render_title(frame: &mut Frame, area: Rect, app: &App) {
40
40
  Span::styled(" [API Key: Not Set]", Style::default().fg(Color::Yellow))
41
41
  };
42
42
 
43
+ let log_info = if let Some(ref path) = app.log_path {
44
+ Span::styled(
45
+ format!(" [log: {}]", path.display()),
46
+ Style::default().fg(Color::DarkGray),
47
+ )
48
+ } else {
49
+ Span::raw("")
50
+ };
51
+
43
52
  let title = Line::from(vec![
44
53
  Span::styled(
45
54
  format!(" Voice Bird CLI v{} ", env!("CARGO_PKG_VERSION")),
46
55
  Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
47
56
  ),
48
57
  api_status,
58
+ log_info,
49
59
  Span::raw(" "),
50
60
  Span::styled("[q]", Style::default().fg(Color::DarkGray)),
51
61
  Span::raw("uit "),
@@ -185,6 +195,20 @@ fn render_controls(frame: &mut Frame, area: Rect, app: &App) {
185
195
  Span::raw("onfig "),
186
196
  ]);
187
197
 
198
+ // Show copy controls when there's an error or log path
199
+ if matches!(app.status, RecordingStatus::Error(_)) {
200
+ all_controls.extend(vec![
201
+ Span::styled("[l]", Style::default().fg(Color::Yellow)),
202
+ Span::raw(" copy error "),
203
+ ]);
204
+ }
205
+ if app.log_path.is_some() {
206
+ all_controls.extend(vec![
207
+ Span::styled("[L]", Style::default().fg(Color::Yellow)),
208
+ Span::raw(" copy log path "),
209
+ ]);
210
+ }
211
+
188
212
  let controls_line = Line::from(all_controls);
189
213
 
190
214
  let block = Block::default()
@@ -259,6 +283,10 @@ fn render_help_dialog(frame: &mut Frame) {
259
283
  " Configuration:",
260
284
  " c Configure API key",
261
285
  "",
286
+ " Diagnostics:",
287
+ " l Copy error text to clipboard",
288
+ " L Copy log file path to clipboard",
289
+ "",
262
290
  " Other:",
263
291
  " ? Toggle this help",
264
292
  " q Quit application",