python-voiceio 1.4.0__tar.gz → 1.6.0__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.
Files changed (206) hide show
  1. python_voiceio-1.6.0/PKG-INFO +138 -0
  2. python_voiceio-1.6.0/README.md +85 -0
  3. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/pyproject.toml +1 -1
  4. python_voiceio-1.6.0/python_voiceio.egg-info/PKG-INFO +138 -0
  5. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/SOURCES.txt +5 -0
  6. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_clipboard_read.py +32 -0
  7. python_voiceio-1.6.0/tests/test_clipboard_typer.py +204 -0
  8. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_pipeline.py +102 -0
  9. python_voiceio-1.6.0/tests/test_media.py +170 -0
  10. python_voiceio-1.6.0/tests/test_modifier_guard.py +204 -0
  11. python_voiceio-1.6.0/voiceio/__init__.py +1 -0
  12. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/app.py +8 -2
  13. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/learn.py +32 -12
  14. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/clipboard_read.py +33 -3
  15. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/config.py +18 -0
  16. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/health.py +5 -0
  17. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/train.py +11 -1
  18. python_voiceio-1.6.0/voiceio/media.py +89 -0
  19. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/__init__.py +13 -1
  20. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/base.py +25 -1
  21. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/clipboard.py +107 -19
  22. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/manager.py +7 -5
  23. python_voiceio-1.6.0/voiceio/typers/modifier_guard.py +97 -0
  24. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/pynput_type.py +4 -7
  25. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/wtype.py +4 -7
  26. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/xdotool.py +4 -7
  27. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/ydotool.py +4 -7
  28. python_voiceio-1.4.0/PKG-INFO +0 -320
  29. python_voiceio-1.4.0/README.md +0 -267
  30. python_voiceio-1.4.0/python_voiceio.egg-info/PKG-INFO +0 -320
  31. python_voiceio-1.4.0/voiceio/__init__.py +0 -1
  32. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/LICENSE +0 -0
  33. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/dependency_links.txt +0 -0
  34. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/entry_points.txt +0 -0
  35. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/requires.txt +0 -0
  36. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/top_level.txt +0 -0
  37. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/setup.cfg +0 -0
  38. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_app_wiring.py +0 -0
  39. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_audio_quality.py +0 -0
  40. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_audio_source.py +0 -0
  41. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_backend_probes.py +0 -0
  42. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_cli.py +0 -0
  43. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_commands.py +0 -0
  44. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_concurrency_lockdown.py +0 -0
  45. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_config.py +0 -0
  46. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_corrections.py +0 -0
  47. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_decoders.py +0 -0
  48. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_engine.py +0 -0
  49. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_evdev_uaccess.py +0 -0
  50. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_fallback.py +0 -0
  51. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_fcitx_bridge.py +0 -0
  52. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_health.py +0 -0
  53. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_hints.py +0 -0
  54. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_history.py +0 -0
  55. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_engine_limits.py +0 -0
  56. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_engine_listener.py +0 -0
  57. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_pending.py +0 -0
  58. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_pids.py +0 -0
  59. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_ping.py +0 -0
  60. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_session.py +0 -0
  61. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_textlimit.py +0 -0
  62. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_typer.py +0 -0
  63. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_clean.py +0 -0
  64. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_import.py +0 -0
  65. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_metrics.py +0 -0
  66. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_quiet.py +0 -0
  67. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_review.py +0 -0
  68. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_targets.py +0 -0
  69. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_llm.py +0 -0
  70. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_llm_api.py +0 -0
  71. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_numbers.py +0 -0
  72. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_pipeline.py +0 -0
  73. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_platform.py +0 -0
  74. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_postcorrect.py +0 -0
  75. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_postprocess.py +0 -0
  76. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_prebuffer.py +0 -0
  77. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_prompt.py +0 -0
  78. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_recorder_integration.py +0 -0
  79. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_retention.py +0 -0
  80. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_robustness.py +0 -0
  81. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_security_hardening.py +0 -0
  82. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_server.py +0 -0
  83. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_service.py +0 -0
  84. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_setup.py +0 -0
  85. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_streaming.py +0 -0
  86. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_suggest.py +0 -0
  87. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_tokens.py +0 -0
  88. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_transcriber.py +0 -0
  89. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_tts.py +0 -0
  90. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_typer_manager.py +0 -0
  91. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_vad.py +0 -0
  92. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_vocabulary.py +0 -0
  93. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_wordfreq.py +0 -0
  94. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_worker_decode.py +0 -0
  95. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/__main__.py +0 -0
  96. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/audio_source.py +0 -0
  97. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/backends.py +0 -0
  98. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/__init__.py +0 -0
  99. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/configcmd.py +0 -0
  100. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/corrections.py +0 -0
  101. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/doctor.py +0 -0
  102. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/history.py +0 -0
  103. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/models.py +0 -0
  104. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/servicecmd.py +0 -0
  105. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/uninstall.py +0 -0
  106. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/vocab.py +0 -0
  107. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/commands.py +0 -0
  108. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/consent.py +0 -0
  109. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/__init__.py +0 -0
  110. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/bias.py +0 -0
  111. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/__init__.py +0 -0
  112. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/base.py +0 -0
  113. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/catalog.py +0 -0
  114. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/faster_whisper/__init__.py +0 -0
  115. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/faster_whisper/worker.py +0 -0
  116. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/lanes.py +0 -0
  117. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/sherpa.py +0 -0
  118. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/whispercpp.py +0 -0
  119. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/engine.py +0 -0
  120. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/pipeline.py +0 -0
  121. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/corrections.py +0 -0
  122. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/data/60-voiceio-uaccess.rules +0 -0
  123. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/data/__init__.py +0 -0
  124. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/demo.py +0 -0
  125. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/display.py +0 -0
  126. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/feedback.py +0 -0
  127. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hints.py +0 -0
  128. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/history.py +0 -0
  129. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/__init__.py +0 -0
  130. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/base.py +0 -0
  131. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/chain.py +0 -0
  132. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/evdev.py +0 -0
  133. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/pynput_backend.py +0 -0
  134. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/socket_backend.py +0 -0
  135. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/__init__.py +0 -0
  136. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/engine.py +0 -0
  137. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/install.py +0 -0
  138. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/pending.py +0 -0
  139. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/session.py +0 -0
  140. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/textlimit.py +0 -0
  141. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/__init__.py +0 -0
  142. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/augment.py +0 -0
  143. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/clean.py +0 -0
  144. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/evaluate.py +0 -0
  145. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/importer.py +0 -0
  146. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/metrics.py +0 -0
  147. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/quiet.py +0 -0
  148. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/review.py +0 -0
  149. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/store.py +0 -0
  150. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/teacher.py +0 -0
  151. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/llm.py +0 -0
  152. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/llm_api.py +0 -0
  153. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/models/__init__.py +0 -0
  154. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/models/silero_vad.onnx +0 -0
  155. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/numbers.py +0 -0
  156. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/pidlock.py +0 -0
  157. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/platform.py +0 -0
  158. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/postcorrect.py +0 -0
  159. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/postprocess.py +0 -0
  160. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/privacy.py +0 -0
  161. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/prompt.py +0 -0
  162. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/recorder.py +0 -0
  163. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/retention.py +0 -0
  164. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/__init__.py +0 -0
  165. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/__main__.py +0 -0
  166. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/app.py +0 -0
  167. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/config.py +0 -0
  168. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/runtime.py +0 -0
  169. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/service.py +0 -0
  170. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/__init__.py +0 -0
  171. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/configfile.py +0 -0
  172. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/extras.py +0 -0
  173. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/hotkey.py +0 -0
  174. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/noninteractive.py +0 -0
  175. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/speech.py +0 -0
  176. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/steps.py +0 -0
  177. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/system.py +0 -0
  178. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/ui.py +0 -0
  179. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/__init__.py +0 -0
  180. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/commit.wav +0 -0
  181. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/start.wav +0 -0
  182. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/stop.wav +0 -0
  183. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/streaming.py +0 -0
  184. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/suggest.py +0 -0
  185. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tokens.py +0 -0
  186. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/__init__.py +0 -0
  187. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/_icons.py +0 -0
  188. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/_indicator.py +0 -0
  189. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/_pystray.py +0 -0
  190. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/__init__.py +0 -0
  191. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/base.py +0 -0
  192. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/chain.py +0 -0
  193. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/edge_engine.py +0 -0
  194. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/espeak.py +0 -0
  195. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/piper_engine.py +0 -0
  196. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/player.py +0 -0
  197. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/chain.py +0 -0
  198. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/collecting.py +0 -0
  199. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/fcitx_bridge.py +0 -0
  200. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/ibus.py +0 -0
  201. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ui.py +0 -0
  202. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/vad.py +0 -0
  203. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/vocab_stats.py +0 -0
  204. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/vocabulary.py +0 -0
  205. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/watchdog.py +0 -0
  206. {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/wordfreq.py +0 -0
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-voiceio
3
+ Version: 1.6.0
4
+ Summary: Voice dictation for Linux. Speak → text, locally, instantly.
5
+ Author: Hugo Montenegro
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://voiceio.dev/
8
+ Project-URL: Repository, https://github.com/Hugo0/voiceio
9
+ Project-URL: Issues, https://github.com/Hugo0/voiceio/issues
10
+ Project-URL: Changelog, https://github.com/Hugo0/voiceio/releases
11
+ Keywords: voice,speech-to-text,whisper,linux,dictation,wayland,ibus
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: X11 Applications
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: faster-whisper>=1.0.0
22
+ Requires-Dist: numpy>=1.24.0
23
+ Requires-Dist: onnxruntime>=1.16.0
24
+ Requires-Dist: wordfreq>=3.0
25
+ Provides-Extra: desktop
26
+ Requires-Dist: sounddevice>=0.4.6; extra == "desktop"
27
+ Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "desktop"
28
+ Requires-Dist: pynput>=1.7.6; extra == "desktop"
29
+ Requires-Dist: pystray>=0.19; extra == "desktop"
30
+ Requires-Dist: Pillow>=10.0; extra == "desktop"
31
+ Provides-Extra: linux
32
+ Requires-Dist: sounddevice>=0.4.6; extra == "linux"
33
+ Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "linux"
34
+ Requires-Dist: pynput>=1.7.6; extra == "linux"
35
+ Requires-Dist: pystray>=0.19; extra == "linux"
36
+ Requires-Dist: Pillow>=10.0; extra == "linux"
37
+ Provides-Extra: server
38
+ Requires-Dist: aiohttp>=3.9; extra == "server"
39
+ Provides-Extra: tts
40
+ Requires-Dist: piper-tts>=1.3.0; extra == "tts"
41
+ Requires-Dist: edge-tts>=6.1.0; extra == "tts"
42
+ Provides-Extra: train
43
+ Requires-Dist: torch>=2.2; extra == "train"
44
+ Requires-Dist: transformers>=4.40; extra == "train"
45
+ Requires-Dist: peft>=0.10; extra == "train"
46
+ Provides-Extra: sherpa
47
+ Requires-Dist: sherpa-onnx>=1.12; extra == "sherpa"
48
+ Provides-Extra: dev
49
+ Requires-Dist: pytest>=7.0; extra == "dev"
50
+ Requires-Dist: pytest-mock; extra == "dev"
51
+ Requires-Dist: ruff<0.18,>=0.15; extra == "dev"
52
+ Dynamic: license-file
53
+
54
+ # voiceio
55
+
56
+ [![CI](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml/badge.svg)](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml)
57
+ [![PyPI](https://img.shields.io/pypi/v/python-voiceio)](https://pypi.org/project/python-voiceio/)
58
+ [![Python](https://img.shields.io/pypi/pyversions/python-voiceio)](https://pypi.org/project/python-voiceio/)
59
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
60
+
61
+ **Voice dictation for Linux that learns you.** Local, private, open source, and it fine-tunes itself on your own voice.
62
+
63
+ [![voiceio: dictation that learns you](https://voiceio.dev/og.png)](https://voiceio.dev)
64
+
65
+ **[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
66
+
67
+ - **Types anywhere.** Press your hotkey, speak, and the words stream into the focused app with a live underlined preview (IBus), on Wayland and X11: GNOME, KDE, Hyprland, sway, i3.
68
+ - **Runs on your machine.** Whisper decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
69
+ - **Learns your words.** Keep your recordings (opt-in, on your disk) and voiceio fine-tunes its model on them while the machine is idle. It switches only when the new model wins on clips it never trained on. On the author's voice: 12.1% → 4.9% word errors after one night on a laptop CPU.
70
+
71
+ ## Install
72
+
73
+ **With a coding agent.** Paste this into Claude Code, Codex or any agent with a shell:
74
+
75
+ ```text
76
+ Install voiceio on this machine following https://voiceio.dev/llms.txt. Ask me which hotkey I want and whether to keep my recordings on this computer so voiceio can learn my voice. Finish with voiceio doctor and fix whatever it reports.
77
+ ```
78
+
79
+ **By hand:**
80
+
81
+ ```bash
82
+ sudo apt install pipx build-essential python3-dev portaudio19-dev ibus gir1.2-ibus-1.0 python3-gi # Debian/Ubuntu
83
+ sudo dnf install pipx gcc gcc-c++ make python3-devel portaudio-devel ibus ibus-libs python3-gobject # Fedora
84
+ sudo pacman -S python-pipx base-devel portaudio ibus python-gobject # Arch
85
+
86
+ pipx install 'python-voiceio[desktop]'
87
+ voiceio setup # model, hotkey, autostart
88
+ ```
89
+
90
+ Then press your hotkey, speak, and press it again. `voiceio doctor` shows what works and `voiceio doctor --fix` repairs what it can. NixOS, other extras and installing from source: [docs/linux.md](docs/linux.md). Agent runbook: [INSTALL.md](INSTALL.md).
91
+
92
+ ## Make it yours
93
+
94
+ ```bash
95
+ voiceio vocab add Kalshi # a name, recognized from the next dictation
96
+ voiceio corrections add kalchi Kalshi # fix whatever still comes out wrong
97
+ voiceio learn schedule on # learn from your recordings while the machine is idle
98
+ voiceio learn status # what it learned and how each model scores
99
+ ```
100
+
101
+ Scheduled learning runs daily or weekly at a time you pick, only on an idle machine on mains power, with a notification you can stop. It never switches models unless the new one wins, and `voiceio learn rollback` undoes a switch. Details: [docs/learning.md](docs/learning.md).
102
+
103
+ ## Configure
104
+
105
+ Everything lives in `~/.config/voiceio/config.toml`; [config.example.toml](config.example.toml) documents every option. `voiceio config set section.key value` edits it in place.
106
+
107
+ ## Docs
108
+
109
+ | | |
110
+ |---|---|
111
+ | [Learning your voice](docs/learning.md) | Vocabulary, corrections, labeling, fine-tuning, evaluation, schedule |
112
+ | [Choosing a model](docs/models.md) | Whisper sizes, Parakeet, whisper.cpp servers, licenses |
113
+ | [Commands](docs/commands.md) | Every `voiceio` command |
114
+ | [Your data](docs/data.md) | What is stored where, and what is off by default |
115
+ | [Phone dictation](docs/phone.md) | Run voiceio headless on a home server |
116
+ | [Linux support and troubleshooting](docs/linux.md) | Desktops, backends, NixOS, fixes |
117
+ | [Comparisons](https://voiceio.dev/vs/) | voiceio vs Voxtype, Wispr Flow, Superwhisper, Dragon and others |
118
+
119
+ ## Roadmap
120
+
121
+ - **Unbounded vocabulary**: a decoder whose biasing has no token cap (see [model choice](CONTRIBUTING.md#model-choice-why-whisper-and-what-would-change-it)).
122
+ - **GPU on AMD and Intel**: let setup run a Vulkan whisper.cpp server for you.
123
+ - **Typing without IBus on GNOME and KDE**: a libei backend.
124
+ - **Distro packages**: AUR, .deb and .rpm.
125
+ - **One model everywhere**: sync your fine-tune across your machines.
126
+
127
+ ## Contributing
128
+
129
+ Open an issue before a large PR. [CONTRIBUTING.md](CONTRIBUTING.md) covers the architecture, conventions and the reasoning behind the model choice. Install reports and ideas are welcome on the [feedback board](https://voiceio.dev/#feedback).
130
+
131
+ ## Credits
132
+
133
+ - Speech recognition: [faster-whisper](https://github.com/SYSTRAN/faster-whisper) and [CTranslate2](https://github.com/OpenNMT/CTranslate2), with OpenAI's [Whisper](https://github.com/openai/whisper) models. The in-browser demo runs [Moonshine](https://github.com/usefulsensors/moonshine) through [transformers.js](https://github.com/huggingface/transformers.js).
134
+ - Ideas borrowed with thanks: modifier release, paste keys and media pausing from [Voxtype](https://github.com/peteonrails/voxtype); the landing page's look from Romario Kavin's [KOLlateral](https://github.com/RomarioKavin1/kollateral).
135
+
136
+ ## License
137
+
138
+ MIT. Model weights are not part of voiceio and carry their own licenses (see [docs/models.md](docs/models.md)).
@@ -0,0 +1,85 @@
1
+ # voiceio
2
+
3
+ [![CI](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml/badge.svg)](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/python-voiceio)](https://pypi.org/project/python-voiceio/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/python-voiceio)](https://pypi.org/project/python-voiceio/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ **Voice dictation for Linux that learns you.** Local, private, open source, and it fine-tunes itself on your own voice.
9
+
10
+ [![voiceio: dictation that learns you](https://voiceio.dev/og.png)](https://voiceio.dev)
11
+
12
+ **[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
13
+
14
+ - **Types anywhere.** Press your hotkey, speak, and the words stream into the focused app with a live underlined preview (IBus), on Wayland and X11: GNOME, KDE, Hyprland, sway, i3.
15
+ - **Runs on your machine.** Whisper decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
16
+ - **Learns your words.** Keep your recordings (opt-in, on your disk) and voiceio fine-tunes its model on them while the machine is idle. It switches only when the new model wins on clips it never trained on. On the author's voice: 12.1% → 4.9% word errors after one night on a laptop CPU.
17
+
18
+ ## Install
19
+
20
+ **With a coding agent.** Paste this into Claude Code, Codex or any agent with a shell:
21
+
22
+ ```text
23
+ Install voiceio on this machine following https://voiceio.dev/llms.txt. Ask me which hotkey I want and whether to keep my recordings on this computer so voiceio can learn my voice. Finish with voiceio doctor and fix whatever it reports.
24
+ ```
25
+
26
+ **By hand:**
27
+
28
+ ```bash
29
+ sudo apt install pipx build-essential python3-dev portaudio19-dev ibus gir1.2-ibus-1.0 python3-gi # Debian/Ubuntu
30
+ sudo dnf install pipx gcc gcc-c++ make python3-devel portaudio-devel ibus ibus-libs python3-gobject # Fedora
31
+ sudo pacman -S python-pipx base-devel portaudio ibus python-gobject # Arch
32
+
33
+ pipx install 'python-voiceio[desktop]'
34
+ voiceio setup # model, hotkey, autostart
35
+ ```
36
+
37
+ Then press your hotkey, speak, and press it again. `voiceio doctor` shows what works and `voiceio doctor --fix` repairs what it can. NixOS, other extras and installing from source: [docs/linux.md](docs/linux.md). Agent runbook: [INSTALL.md](INSTALL.md).
38
+
39
+ ## Make it yours
40
+
41
+ ```bash
42
+ voiceio vocab add Kalshi # a name, recognized from the next dictation
43
+ voiceio corrections add kalchi Kalshi # fix whatever still comes out wrong
44
+ voiceio learn schedule on # learn from your recordings while the machine is idle
45
+ voiceio learn status # what it learned and how each model scores
46
+ ```
47
+
48
+ Scheduled learning runs daily or weekly at a time you pick, only on an idle machine on mains power, with a notification you can stop. It never switches models unless the new one wins, and `voiceio learn rollback` undoes a switch. Details: [docs/learning.md](docs/learning.md).
49
+
50
+ ## Configure
51
+
52
+ Everything lives in `~/.config/voiceio/config.toml`; [config.example.toml](config.example.toml) documents every option. `voiceio config set section.key value` edits it in place.
53
+
54
+ ## Docs
55
+
56
+ | | |
57
+ |---|---|
58
+ | [Learning your voice](docs/learning.md) | Vocabulary, corrections, labeling, fine-tuning, evaluation, schedule |
59
+ | [Choosing a model](docs/models.md) | Whisper sizes, Parakeet, whisper.cpp servers, licenses |
60
+ | [Commands](docs/commands.md) | Every `voiceio` command |
61
+ | [Your data](docs/data.md) | What is stored where, and what is off by default |
62
+ | [Phone dictation](docs/phone.md) | Run voiceio headless on a home server |
63
+ | [Linux support and troubleshooting](docs/linux.md) | Desktops, backends, NixOS, fixes |
64
+ | [Comparisons](https://voiceio.dev/vs/) | voiceio vs Voxtype, Wispr Flow, Superwhisper, Dragon and others |
65
+
66
+ ## Roadmap
67
+
68
+ - **Unbounded vocabulary**: a decoder whose biasing has no token cap (see [model choice](CONTRIBUTING.md#model-choice-why-whisper-and-what-would-change-it)).
69
+ - **GPU on AMD and Intel**: let setup run a Vulkan whisper.cpp server for you.
70
+ - **Typing without IBus on GNOME and KDE**: a libei backend.
71
+ - **Distro packages**: AUR, .deb and .rpm.
72
+ - **One model everywhere**: sync your fine-tune across your machines.
73
+
74
+ ## Contributing
75
+
76
+ Open an issue before a large PR. [CONTRIBUTING.md](CONTRIBUTING.md) covers the architecture, conventions and the reasoning behind the model choice. Install reports and ideas are welcome on the [feedback board](https://voiceio.dev/#feedback).
77
+
78
+ ## Credits
79
+
80
+ - Speech recognition: [faster-whisper](https://github.com/SYSTRAN/faster-whisper) and [CTranslate2](https://github.com/OpenNMT/CTranslate2), with OpenAI's [Whisper](https://github.com/openai/whisper) models. The in-browser demo runs [Moonshine](https://github.com/usefulsensors/moonshine) through [transformers.js](https://github.com/huggingface/transformers.js).
81
+ - Ideas borrowed with thanks: modifier release, paste keys and media pausing from [Voxtype](https://github.com/peteonrails/voxtype); the landing page's look from Romario Kavin's [KOLlateral](https://github.com/RomarioKavin1/kollateral).
82
+
83
+ ## License
84
+
85
+ MIT. Model weights are not part of voiceio and carry their own licenses (see [docs/models.md](docs/models.md)).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-voiceio"
7
- version = "1.4.0"
7
+ version = "1.6.0"
8
8
  description = "Voice dictation for Linux. Speak → text, locally, instantly."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-voiceio
3
+ Version: 1.6.0
4
+ Summary: Voice dictation for Linux. Speak → text, locally, instantly.
5
+ Author: Hugo Montenegro
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://voiceio.dev/
8
+ Project-URL: Repository, https://github.com/Hugo0/voiceio
9
+ Project-URL: Issues, https://github.com/Hugo0/voiceio/issues
10
+ Project-URL: Changelog, https://github.com/Hugo0/voiceio/releases
11
+ Keywords: voice,speech-to-text,whisper,linux,dictation,wayland,ibus
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: X11 Applications
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: faster-whisper>=1.0.0
22
+ Requires-Dist: numpy>=1.24.0
23
+ Requires-Dist: onnxruntime>=1.16.0
24
+ Requires-Dist: wordfreq>=3.0
25
+ Provides-Extra: desktop
26
+ Requires-Dist: sounddevice>=0.4.6; extra == "desktop"
27
+ Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "desktop"
28
+ Requires-Dist: pynput>=1.7.6; extra == "desktop"
29
+ Requires-Dist: pystray>=0.19; extra == "desktop"
30
+ Requires-Dist: Pillow>=10.0; extra == "desktop"
31
+ Provides-Extra: linux
32
+ Requires-Dist: sounddevice>=0.4.6; extra == "linux"
33
+ Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "linux"
34
+ Requires-Dist: pynput>=1.7.6; extra == "linux"
35
+ Requires-Dist: pystray>=0.19; extra == "linux"
36
+ Requires-Dist: Pillow>=10.0; extra == "linux"
37
+ Provides-Extra: server
38
+ Requires-Dist: aiohttp>=3.9; extra == "server"
39
+ Provides-Extra: tts
40
+ Requires-Dist: piper-tts>=1.3.0; extra == "tts"
41
+ Requires-Dist: edge-tts>=6.1.0; extra == "tts"
42
+ Provides-Extra: train
43
+ Requires-Dist: torch>=2.2; extra == "train"
44
+ Requires-Dist: transformers>=4.40; extra == "train"
45
+ Requires-Dist: peft>=0.10; extra == "train"
46
+ Provides-Extra: sherpa
47
+ Requires-Dist: sherpa-onnx>=1.12; extra == "sherpa"
48
+ Provides-Extra: dev
49
+ Requires-Dist: pytest>=7.0; extra == "dev"
50
+ Requires-Dist: pytest-mock; extra == "dev"
51
+ Requires-Dist: ruff<0.18,>=0.15; extra == "dev"
52
+ Dynamic: license-file
53
+
54
+ # voiceio
55
+
56
+ [![CI](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml/badge.svg)](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml)
57
+ [![PyPI](https://img.shields.io/pypi/v/python-voiceio)](https://pypi.org/project/python-voiceio/)
58
+ [![Python](https://img.shields.io/pypi/pyversions/python-voiceio)](https://pypi.org/project/python-voiceio/)
59
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
60
+
61
+ **Voice dictation for Linux that learns you.** Local, private, open source, and it fine-tunes itself on your own voice.
62
+
63
+ [![voiceio: dictation that learns you](https://voiceio.dev/og.png)](https://voiceio.dev)
64
+
65
+ **[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
66
+
67
+ - **Types anywhere.** Press your hotkey, speak, and the words stream into the focused app with a live underlined preview (IBus), on Wayland and X11: GNOME, KDE, Hyprland, sway, i3.
68
+ - **Runs on your machine.** Whisper decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
69
+ - **Learns your words.** Keep your recordings (opt-in, on your disk) and voiceio fine-tunes its model on them while the machine is idle. It switches only when the new model wins on clips it never trained on. On the author's voice: 12.1% → 4.9% word errors after one night on a laptop CPU.
70
+
71
+ ## Install
72
+
73
+ **With a coding agent.** Paste this into Claude Code, Codex or any agent with a shell:
74
+
75
+ ```text
76
+ Install voiceio on this machine following https://voiceio.dev/llms.txt. Ask me which hotkey I want and whether to keep my recordings on this computer so voiceio can learn my voice. Finish with voiceio doctor and fix whatever it reports.
77
+ ```
78
+
79
+ **By hand:**
80
+
81
+ ```bash
82
+ sudo apt install pipx build-essential python3-dev portaudio19-dev ibus gir1.2-ibus-1.0 python3-gi # Debian/Ubuntu
83
+ sudo dnf install pipx gcc gcc-c++ make python3-devel portaudio-devel ibus ibus-libs python3-gobject # Fedora
84
+ sudo pacman -S python-pipx base-devel portaudio ibus python-gobject # Arch
85
+
86
+ pipx install 'python-voiceio[desktop]'
87
+ voiceio setup # model, hotkey, autostart
88
+ ```
89
+
90
+ Then press your hotkey, speak, and press it again. `voiceio doctor` shows what works and `voiceio doctor --fix` repairs what it can. NixOS, other extras and installing from source: [docs/linux.md](docs/linux.md). Agent runbook: [INSTALL.md](INSTALL.md).
91
+
92
+ ## Make it yours
93
+
94
+ ```bash
95
+ voiceio vocab add Kalshi # a name, recognized from the next dictation
96
+ voiceio corrections add kalchi Kalshi # fix whatever still comes out wrong
97
+ voiceio learn schedule on # learn from your recordings while the machine is idle
98
+ voiceio learn status # what it learned and how each model scores
99
+ ```
100
+
101
+ Scheduled learning runs daily or weekly at a time you pick, only on an idle machine on mains power, with a notification you can stop. It never switches models unless the new one wins, and `voiceio learn rollback` undoes a switch. Details: [docs/learning.md](docs/learning.md).
102
+
103
+ ## Configure
104
+
105
+ Everything lives in `~/.config/voiceio/config.toml`; [config.example.toml](config.example.toml) documents every option. `voiceio config set section.key value` edits it in place.
106
+
107
+ ## Docs
108
+
109
+ | | |
110
+ |---|---|
111
+ | [Learning your voice](docs/learning.md) | Vocabulary, corrections, labeling, fine-tuning, evaluation, schedule |
112
+ | [Choosing a model](docs/models.md) | Whisper sizes, Parakeet, whisper.cpp servers, licenses |
113
+ | [Commands](docs/commands.md) | Every `voiceio` command |
114
+ | [Your data](docs/data.md) | What is stored where, and what is off by default |
115
+ | [Phone dictation](docs/phone.md) | Run voiceio headless on a home server |
116
+ | [Linux support and troubleshooting](docs/linux.md) | Desktops, backends, NixOS, fixes |
117
+ | [Comparisons](https://voiceio.dev/vs/) | voiceio vs Voxtype, Wispr Flow, Superwhisper, Dragon and others |
118
+
119
+ ## Roadmap
120
+
121
+ - **Unbounded vocabulary**: a decoder whose biasing has no token cap (see [model choice](CONTRIBUTING.md#model-choice-why-whisper-and-what-would-change-it)).
122
+ - **GPU on AMD and Intel**: let setup run a Vulkan whisper.cpp server for you.
123
+ - **Typing without IBus on GNOME and KDE**: a libei backend.
124
+ - **Distro packages**: AUR, .deb and .rpm.
125
+ - **One model everywhere**: sync your fine-tune across your machines.
126
+
127
+ ## Contributing
128
+
129
+ Open an issue before a large PR. [CONTRIBUTING.md](CONTRIBUTING.md) covers the architecture, conventions and the reasoning behind the model choice. Install reports and ideas are welcome on the [feedback board](https://voiceio.dev/#feedback).
130
+
131
+ ## Credits
132
+
133
+ - Speech recognition: [faster-whisper](https://github.com/SYSTRAN/faster-whisper) and [CTranslate2](https://github.com/OpenNMT/CTranslate2), with OpenAI's [Whisper](https://github.com/openai/whisper) models. The in-browser demo runs [Moonshine](https://github.com/usefulsensors/moonshine) through [transformers.js](https://github.com/huggingface/transformers.js).
134
+ - Ideas borrowed with thanks: modifier release, paste keys and media pausing from [Voxtype](https://github.com/peteonrails/voxtype); the landing page's look from Romario Kavin's [KOLlateral](https://github.com/RomarioKavin1/kollateral).
135
+
136
+ ## License
137
+
138
+ MIT. Model weights are not part of voiceio and carry their own licenses (see [docs/models.md](docs/models.md)).
@@ -13,6 +13,7 @@ tests/test_audio_source.py
13
13
  tests/test_backend_probes.py
14
14
  tests/test_cli.py
15
15
  tests/test_clipboard_read.py
16
+ tests/test_clipboard_typer.py
16
17
  tests/test_commands.py
17
18
  tests/test_concurrency_lockdown.py
18
19
  tests/test_config.py
@@ -42,6 +43,8 @@ tests/test_learn_review.py
42
43
  tests/test_learn_targets.py
43
44
  tests/test_llm.py
44
45
  tests/test_llm_api.py
46
+ tests/test_media.py
47
+ tests/test_modifier_guard.py
45
48
  tests/test_numbers.py
46
49
  tests/test_pipeline.py
47
50
  tests/test_platform.py
@@ -84,6 +87,7 @@ voiceio/hints.py
84
87
  voiceio/history.py
85
88
  voiceio/llm.py
86
89
  voiceio/llm_api.py
90
+ voiceio/media.py
87
91
  voiceio/numbers.py
88
92
  voiceio/pidlock.py
89
93
  voiceio/platform.py
@@ -189,6 +193,7 @@ voiceio/typers/collecting.py
189
193
  voiceio/typers/fcitx_bridge.py
190
194
  voiceio/typers/ibus.py
191
195
  voiceio/typers/manager.py
196
+ voiceio/typers/modifier_guard.py
192
197
  voiceio/typers/pynput_type.py
193
198
  voiceio/typers/wtype.py
194
199
  voiceio/typers/xdotool.py
@@ -171,3 +171,35 @@ def test_copy_text_wayland_falls_back_to_wl_copy():
171
171
  from voiceio.clipboard_read import copy_text
172
172
  assert copy_text("hello") is True
173
173
  assert calls == ["xclip", "wl-copy"]
174
+
175
+
176
+ def test_read_clipboard_keeps_text_exact_and_skips_primary():
177
+ """Restoring needs the clipboard byte for byte, never the mouse selection."""
178
+ with patch("voiceio.clipboard_read.detect", return_value=_mock_platform(display_server="x11")), \
179
+ patch("shutil.which", return_value="/usr/bin/xclip"), \
180
+ patch("subprocess.run") as mock_run:
181
+ mock_run.return_value = MagicMock(returncode=0, stdout=" two lines\nhere\n")
182
+ from voiceio.clipboard_read import read_clipboard
183
+ assert read_clipboard() == " two lines\nhere\n"
184
+ assert mock_run.call_args[0][0] == ["xclip", "-o", "-selection", "clipboard"]
185
+
186
+
187
+ def test_read_clipboard_wayland_falls_back_to_wl_paste():
188
+ with patch("voiceio.clipboard_read.detect", return_value=_mock_platform(display_server="wayland")), \
189
+ patch("shutil.which", return_value="/usr/bin/tool"), \
190
+ patch("subprocess.run") as mock_run:
191
+ mock_run.side_effect = [
192
+ MagicMock(returncode=1, stdout=""),
193
+ MagicMock(returncode=0, stdout="wayland text"),
194
+ ]
195
+ from voiceio.clipboard_read import read_clipboard
196
+ assert read_clipboard() == "wayland text"
197
+ assert mock_run.call_args[0][0] == ["wl-paste", "--no-newline"]
198
+
199
+
200
+ def test_read_clipboard_empty_is_none():
201
+ with patch("voiceio.clipboard_read.detect", return_value=_mock_platform(display_server="x11")), \
202
+ patch("shutil.which", return_value="/usr/bin/xclip"), \
203
+ patch("subprocess.run", return_value=MagicMock(returncode=0, stdout="")):
204
+ from voiceio.clipboard_read import read_clipboard
205
+ assert read_clipboard() is None
@@ -0,0 +1,204 @@
1
+ """Clipboard typer: configurable paste keystroke and clipboard restore."""
2
+ from __future__ import annotations
3
+
4
+ import subprocess
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ import pytest
8
+
9
+ from voiceio import config
10
+ from voiceio.typers import clipboard as clip_mod, typer_options
11
+ from voiceio.typers.clipboard import ClipboardTyper, parse_paste_keys, paste_command
12
+
13
+
14
+ @pytest.mark.parametrize("spec,expected", [
15
+ ("ctrl+v", (["ctrl"], "v")),
16
+ ("Ctrl + Shift + V", (["ctrl", "shift"], "v")),
17
+ ("shift+insert", (["shift"], "insert")),
18
+ ])
19
+ def test_parse_paste_keys(spec, expected):
20
+ assert parse_paste_keys(spec) == expected
21
+
22
+
23
+ @pytest.mark.parametrize("spec", ["ctrl+q", "hyper+v", "ctrl+ctrl+v", ""])
24
+ def test_unknown_paste_keys_fall_back_to_ctrl_v(spec, caplog):
25
+ assert parse_paste_keys(spec) == (["ctrl"], "v")
26
+ assert "paste_keys" in caplog.text
27
+
28
+
29
+ def test_paste_command_per_tool():
30
+ mods, key = ["ctrl", "shift"], "v"
31
+ assert paste_command("ydotool", mods, key) == \
32
+ ["ydotool", "key", "29:1", "42:1", "47:1", "47:0", "42:0", "29:0"]
33
+ assert paste_command("wtype", mods, key) == \
34
+ ["wtype", "-M", "ctrl", "-M", "shift", "-k", "v", "-m", "shift", "-m", "ctrl"]
35
+ assert paste_command("xdotool", mods, key) == \
36
+ ["xdotool", "key", "--clearmodifiers", "ctrl+shift+v"]
37
+ assert paste_command("xdotool", ["shift"], "insert") == \
38
+ ["xdotool", "key", "--clearmodifiers", "shift+Insert"]
39
+
40
+
41
+ def _wayland_typer(monkeypatch, tool="ydotool", **kwargs) -> ClipboardTyper:
42
+ monkeypatch.setattr(clip_mod.sys, "platform", "linux")
43
+ monkeypatch.setenv("WAYLAND_DISPLAY", "wayland-0")
44
+ available = {"wl-copy", tool}
45
+ monkeypatch.setattr(clip_mod.shutil, "which",
46
+ lambda name: f"/usr/bin/{name}" if name in available else None)
47
+ return ClipboardTyper(**kwargs)
48
+
49
+
50
+ @pytest.mark.parametrize("tool,expected", [
51
+ ("ydotool", ["ydotool", "key", "29:1", "42:1", "47:1", "47:0", "42:0", "29:0"]),
52
+ ("wtype", ["wtype", "-M", "ctrl", "-M", "shift", "-k", "v", "-m", "shift", "-m", "ctrl"]),
53
+ ])
54
+ def test_configured_keys_are_what_gets_pressed(monkeypatch, tool, expected):
55
+ typer = _wayland_typer(monkeypatch, tool, paste_keys="ctrl+shift+v")
56
+ with patch.object(clip_mod.subprocess, "run") as run:
57
+ typer.type_text("hello")
58
+ assert run.call_args_list[0].args[0] == ["wl-copy", "--"]
59
+ assert run.call_args_list[1].args[0] == expected
60
+
61
+
62
+ def test_default_paste_is_ctrl_v(monkeypatch):
63
+ typer = _wayland_typer(monkeypatch)
64
+ with patch.object(clip_mod.subprocess, "run") as run:
65
+ typer.type_text("hello")
66
+ assert run.call_args_list[1].args[0] == ["ydotool", "key", "29:1", "47:1", "47:0", "29:0"]
67
+
68
+
69
+ def test_failed_paste_releases_the_configured_modifiers(monkeypatch):
70
+ typer = _wayland_typer(monkeypatch, paste_keys="ctrl+shift+v")
71
+
72
+ def run(cmd, **kwargs):
73
+ if cmd[:2] == ["ydotool", "key"] and cmd[2].endswith(":1"):
74
+ raise subprocess.TimeoutExpired(cmd, 3)
75
+ return MagicMock(returncode=0)
76
+
77
+ with patch.object(clip_mod.subprocess, "run", side_effect=run) as mock_run, \
78
+ pytest.raises(subprocess.TimeoutExpired):
79
+ typer.type_text("hello")
80
+ assert mock_run.call_args.args[0] == ["ydotool", "key", "29:0", "42:0"]
81
+
82
+
83
+ class FakeClipboard:
84
+ def __init__(self, text):
85
+ self.text = text
86
+ self.reads = 0
87
+ self.writes = []
88
+
89
+ def read(self):
90
+ self.reads += 1
91
+ return self.text
92
+
93
+ def copy(self, text):
94
+ self.writes.append(text)
95
+ self.text = text
96
+ return True
97
+
98
+
99
+ @pytest.fixture
100
+ def clipboard(monkeypatch):
101
+ board = FakeClipboard("user's own text\n")
102
+ monkeypatch.setattr(clip_mod.clipboard_read, "read_clipboard", board.read)
103
+ monkeypatch.setattr(clip_mod.clipboard_read, "copy_text", board.copy)
104
+ monkeypatch.setattr(clip_mod, "RESTORE_DELAY_SECS", 0.01)
105
+ return board
106
+
107
+
108
+ def _copying_run(board):
109
+ """subprocess.run stand-in: wl-copy puts its input on the fake board."""
110
+ def run(cmd, input=None, **kwargs):
111
+ if cmd[0] == "wl-copy":
112
+ board.text = input.decode()
113
+ return MagicMock(returncode=0)
114
+ return run
115
+
116
+
117
+ def _settle(typer):
118
+ """Wait for a scheduled restore (None: it already ran)."""
119
+ timer = typer._restore_timer
120
+ if timer is not None:
121
+ timer.join(timeout=2)
122
+
123
+
124
+ def _paste(typer, board, *texts):
125
+ """Paste each text, then let the restore timer run."""
126
+ with patch.object(clip_mod.subprocess, "run", side_effect=_copying_run(board)):
127
+ for text in texts:
128
+ typer.type_text(text)
129
+ _settle(typer)
130
+
131
+
132
+ def test_restores_the_users_clipboard_after_pasting(monkeypatch, clipboard):
133
+ typer = _wayland_typer(monkeypatch, restore_clipboard=True)
134
+ _paste(typer, clipboard, "dictated")
135
+ assert clipboard.writes == ["user's own text\n"]
136
+
137
+
138
+ def test_streaming_burst_saves_once_and_restores_once(monkeypatch, clipboard):
139
+ typer = _wayland_typer(monkeypatch, restore_clipboard=True)
140
+ monkeypatch.setattr(clip_mod, "RESTORE_DELAY_SECS", 5)
141
+ with patch.object(clip_mod.subprocess, "run", side_effect=_copying_run(clipboard)):
142
+ typer.type_text("one ")
143
+ typer.type_text("two")
144
+ assert clipboard.reads == 1 # saved before the first paste only
145
+ typer._restore_timer.cancel()
146
+ typer._restore_saved(typer._restore_seq)
147
+ assert clipboard.writes == ["user's own text\n"]
148
+
149
+
150
+ def test_superseded_restore_does_nothing(monkeypatch, clipboard):
151
+ typer = _wayland_typer(monkeypatch, restore_clipboard=True)
152
+ typer._saved, typer._pasted, typer._restore_seq = "old", "x", 2
153
+ clipboard.text = "x"
154
+ typer._restore_saved(1)
155
+ assert clipboard.writes == []
156
+
157
+
158
+ def test_no_restore_over_something_copied_since(monkeypatch, clipboard):
159
+ typer = _wayland_typer(monkeypatch, restore_clipboard=True)
160
+
161
+ def run(cmd, input=None, **kwargs):
162
+ clipboard.text = "copied by the user meanwhile"
163
+ return MagicMock(returncode=0)
164
+
165
+ with patch.object(clip_mod.subprocess, "run", side_effect=run):
166
+ typer.type_text("dictated")
167
+ _settle(typer)
168
+ assert clipboard.writes == []
169
+
170
+
171
+ def test_empty_clipboard_is_left_with_the_transcript(monkeypatch, clipboard):
172
+ clipboard.text = None
173
+ typer = _wayland_typer(monkeypatch, restore_clipboard=True)
174
+ _paste(typer, clipboard, "dictated")
175
+ assert clipboard.writes == []
176
+
177
+
178
+ def test_restore_disabled_never_reads_the_clipboard(monkeypatch, clipboard):
179
+ typer = _wayland_typer(monkeypatch)
180
+ with patch.object(clip_mod.subprocess, "run"):
181
+ typer.type_text("dictated")
182
+ assert clipboard.reads == 0
183
+ assert typer._restore_timer is None
184
+
185
+
186
+ @pytest.mark.parametrize("copy,notify,expected", [
187
+ ("off", False, True),
188
+ ("final", False, False), # the transcript is meant to stay on the clipboard
189
+ ("live", False, False),
190
+ ("off", True, False), # the notification says "copied to clipboard"
191
+ ])
192
+ def test_restore_only_when_nothing_keeps_the_transcript(copy, notify, expected):
193
+ cfg = config.Config()
194
+ cfg.output.copy_to_clipboard = copy
195
+ cfg.feedback.notify_clipboard = notify
196
+ assert typer_options(cfg)["restore_clipboard"] is expected
197
+
198
+
199
+ def test_options_reach_the_clipboard_typer(linux_wayland_gnome):
200
+ from voiceio.typers import create_typer_backend
201
+ typer = create_typer_backend("clipboard", linux_wayland_gnome,
202
+ paste_keys="shift+insert", restore_clipboard=True)
203
+ assert (typer._paste_mods, typer._paste_key) == (["shift"], "insert")
204
+ assert typer._restore_clipboard is True