textflowkit 0.1.9__tar.gz → 0.1.11__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 (219) hide show
  1. {textflowkit-0.1.9 → textflowkit-0.1.11}/.github/workflows/ci.yml +1 -1
  2. {textflowkit-0.1.9 → textflowkit-0.1.11}/CHANGELOG.md +92 -0
  3. {textflowkit-0.1.9 → textflowkit-0.1.11}/CONTRIBUTING.md +7 -1
  4. {textflowkit-0.1.9 → textflowkit-0.1.11}/PKG-INFO +138 -24
  5. {textflowkit-0.1.9 → textflowkit-0.1.11}/README.md +131 -23
  6. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/adapters.md +118 -4
  7. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/architecture.md +73 -4
  8. textflowkit-0.1.11/docs/assets/architecture-overview.svg +213 -0
  9. textflowkit-0.1.11/docs/assets/live-streaming-flow.svg +50 -0
  10. textflowkit-0.1.11/docs/assets/local-ui-flow.svg +155 -0
  11. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/index.html +46 -9
  12. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/install.md +53 -1
  13. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/release-checklist.md +48 -1
  14. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/roadmap.md +39 -0
  15. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/site-deployment.md +10 -2
  16. textflowkit-0.1.11/docs/streaming.md +514 -0
  17. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/user-manual.md +144 -17
  18. {textflowkit-0.1.9 → textflowkit-0.1.11}/pyproject.toml +17 -2
  19. textflowkit-0.1.11/scripts/smoke_installed_wheel.py +436 -0
  20. textflowkit-0.1.11/src/textflowkit/__init__.py +41 -0
  21. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/adapters/http_server.py +258 -2
  22. textflowkit-0.1.11/src/textflowkit/adapters/streaming_ws.py +1276 -0
  23. textflowkit-0.1.11/src/textflowkit/core/stream_worker.py +227 -0
  24. textflowkit-0.1.11/src/textflowkit/core/streaming.py +1198 -0
  25. textflowkit-0.1.11/src/textflowkit/core/streaming_assets.py +542 -0
  26. textflowkit-0.1.11/src/textflowkit/core/streaming_events.py +330 -0
  27. textflowkit-0.1.11/src/textflowkit/core/streaming_process.py +520 -0
  28. textflowkit-0.1.11/src/textflowkit/ui/__init__.py +13 -0
  29. textflowkit-0.1.11/src/textflowkit/ui/app.py +711 -0
  30. textflowkit-0.1.11/src/textflowkit/ui/capabilities.py +195 -0
  31. textflowkit-0.1.11/src/textflowkit/ui/launcher.py +711 -0
  32. textflowkit-0.1.11/src/textflowkit/ui/ownership.py +602 -0
  33. textflowkit-0.1.11/src/textflowkit/ui/paths.py +179 -0
  34. textflowkit-0.1.11/src/textflowkit/ui/security.py +200 -0
  35. textflowkit-0.1.11/src/textflowkit/ui/static/app.css +285 -0
  36. textflowkit-0.1.11/src/textflowkit/ui/static/app.js +756 -0
  37. textflowkit-0.1.11/src/textflowkit/ui/static/index.html +226 -0
  38. textflowkit-0.1.11/src/textflowkit/ui/static/streaming-capture-worklet.js +156 -0
  39. textflowkit-0.1.11/src/textflowkit/ui/static/streaming-example.html +699 -0
  40. textflowkit-0.1.11/src/textflowkit/ui/uploads.py +247 -0
  41. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_landing_commands.py +28 -11
  42. textflowkit-0.1.11/tests/test_streaming_assets.py +320 -0
  43. textflowkit-0.1.11/tests/test_streaming_browser_lifecycle.py +1347 -0
  44. textflowkit-0.1.11/tests/test_streaming_core.py +654 -0
  45. textflowkit-0.1.11/tests/test_streaming_example.py +371 -0
  46. textflowkit-0.1.11/tests/test_streaming_repairs.py +950 -0
  47. textflowkit-0.1.11/tests/test_streaming_transport.py +837 -0
  48. textflowkit-0.1.11/tests/test_streaming_u5_repairs.py +917 -0
  49. textflowkit-0.1.11/tests/test_streaming_ui_integration.py +190 -0
  50. textflowkit-0.1.11/tests/test_ui_assets_and_capabilities.py +294 -0
  51. textflowkit-0.1.11/tests/test_ui_consoleless_logging.py +88 -0
  52. textflowkit-0.1.11/tests/test_ui_existing_routes_unchanged.py +86 -0
  53. textflowkit-0.1.11/tests/test_ui_launcher.py +224 -0
  54. textflowkit-0.1.11/tests/test_ui_ownership_and_shutdown.py +477 -0
  55. textflowkit-0.1.11/tests/test_ui_packaging.py +164 -0
  56. textflowkit-0.1.11/tests/test_ui_real_serve.py +88 -0
  57. textflowkit-0.1.11/tests/test_ui_security.py +168 -0
  58. textflowkit-0.1.11/tests/test_ui_submission_and_download.py +260 -0
  59. textflowkit-0.1.11/tests/test_ui_uploads.py +201 -0
  60. textflowkit-0.1.9/docs/assets/architecture-overview.svg +0 -189
  61. textflowkit-0.1.9/scripts/smoke_installed_wheel.py +0 -219
  62. textflowkit-0.1.9/src/textflowkit/__init__.py +0 -8
  63. {textflowkit-0.1.9 → textflowkit-0.1.11}/.dockerignore +0 -0
  64. {textflowkit-0.1.9 → textflowkit-0.1.11}/.env.example +0 -0
  65. {textflowkit-0.1.9 → textflowkit-0.1.11}/.gitattributes +0 -0
  66. {textflowkit-0.1.9 → textflowkit-0.1.11}/.github/dependabot.yml +0 -0
  67. {textflowkit-0.1.9 → textflowkit-0.1.11}/.github/workflows/publish-pypi.yml +0 -0
  68. {textflowkit-0.1.9 → textflowkit-0.1.11}/.gitignore +0 -0
  69. {textflowkit-0.1.9 → textflowkit-0.1.11}/.pre-commit-config.yaml +0 -0
  70. {textflowkit-0.1.9 → textflowkit-0.1.11}/Dockerfile +0 -0
  71. {textflowkit-0.1.9 → textflowkit-0.1.11}/LEGAL.md +0 -0
  72. {textflowkit-0.1.9 → textflowkit-0.1.11}/LICENSE +0 -0
  73. {textflowkit-0.1.9 → textflowkit-0.1.11}/SECURITY.md +0 -0
  74. {textflowkit-0.1.9 → textflowkit-0.1.11}/compose.yaml +0 -0
  75. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/.nojekyll +0 -0
  76. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/404.html +0 -0
  77. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/assets/whistle-resume.svg +0 -0
  78. {textflowkit-0.1.9 → textflowkit-0.1.11}/docs/sources.md +0 -0
  79. {textflowkit-0.1.9 → textflowkit-0.1.11}/scripts/fetch_published_fonts.py +0 -0
  80. {textflowkit-0.1.9 → textflowkit-0.1.11}/scripts/smoke_live_youtube.py +0 -0
  81. {textflowkit-0.1.9 → textflowkit-0.1.11}/scripts/verify_readme_release.py +0 -0
  82. {textflowkit-0.1.9 → textflowkit-0.1.11}/scripts/verify_release_ci.py +0 -0
  83. {textflowkit-0.1.9 → textflowkit-0.1.11}/scripts/verify_release_versions.py +0 -0
  84. {textflowkit-0.1.9 → textflowkit-0.1.11}/scripts/write_release_manifest.py +0 -0
  85. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/adapters/__init__.py +0 -0
  86. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/adapters/mcp_server.py +0 -0
  87. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/assets/README.md +0 -0
  88. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/assets/selftest-speech.wav +0 -0
  89. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/cli.py +0 -0
  90. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/__init__.py +0 -0
  91. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/batch.py +0 -0
  92. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/bind.py +0 -0
  93. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/cancel.py +0 -0
  94. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/checkpoint.py +0 -0
  95. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/diarize.py +0 -0
  96. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/engine.py +0 -0
  97. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/executor.py +0 -0
  98. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/jobs.py +0 -0
  99. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/model.py +0 -0
  100. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/paths.py +0 -0
  101. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/pipeline.py +0 -0
  102. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/retrieval.py +0 -0
  103. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/runner.py +0 -0
  104. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/service.py +0 -0
  105. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/sqlite_store.py +0 -0
  106. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/startup.py +0 -0
  107. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/submission.py +0 -0
  108. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/timeutil.py +0 -0
  109. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/translate.py +0 -0
  110. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/whistle.py +0 -0
  111. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/core/whistle_assets.py +0 -0
  112. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/__init__.py +0 -0
  113. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/_cue_layout.py +0 -0
  114. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/_cue_text.py +0 -0
  115. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/_rename_excl.py +0 -0
  116. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/_rename_noreplace.py +0 -0
  117. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/docx.py +0 -0
  118. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/markdown.py +0 -0
  119. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/pdf.py +0 -0
  120. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/srt.py +0 -0
  121. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/txt.py +0 -0
  122. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/render/vtt.py +0 -0
  123. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/sources/__init__.py +0 -0
  124. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/sources/acquire.py +0 -0
  125. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/sources/detect.py +0 -0
  126. {textflowkit-0.1.9 → textflowkit-0.1.11}/src/textflowkit/sources/scratch.py +0 -0
  127. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/__init__.py +0 -0
  128. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/ollama_stub.py +0 -0
  129. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_acquire.py +0 -0
  130. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_adapters.py +0 -0
  131. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_admission_ownership.py +0 -0
  132. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_atomic_resume.py +0 -0
  133. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_atomic_resume_aba.py +0 -0
  134. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_batch.py +0 -0
  135. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_batch_item_boundary.py +0 -0
  136. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_batch_resume_shared.py +0 -0
  137. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_bind.py +0 -0
  138. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cancellation_finalization.py +0 -0
  139. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_checkpoint.py +0 -0
  140. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_checkpoint_single_copy.py +0 -0
  141. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cli.py +0 -0
  142. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cli_batch_feedback.py +0 -0
  143. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cli_owning_recovery.py +0 -0
  144. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cli_resume_batch.py +0 -0
  145. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cli_stage_feedback.py +0 -0
  146. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cli_stdout_format.py +0 -0
  147. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_confined_decoder_boundary.py +0 -0
  148. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_confined_indirect_media.py +0 -0
  149. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_confined_resume_boundary.py +0 -0
  150. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_container_example.py +0 -0
  151. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_cue_layout.py +0 -0
  152. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_diarize.py +0 -0
  153. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_diarize_example_flags.py +0 -0
  154. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_diarize_token_docs.py +0 -0
  155. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_docx_publication_identity.py +0 -0
  156. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_done_output_integrity.py +0 -0
  157. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_empty_formats_resume.py +0 -0
  158. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_engine_adapters.py +0 -0
  159. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_engine_cache.py +0 -0
  160. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_engine_faster.py +0 -0
  161. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_engine_quiet.py +0 -0
  162. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_executor.py +0 -0
  163. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_executor_store_failure.py +0 -0
  164. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_export.py +0 -0
  165. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_export_file_mode.py +0 -0
  166. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_export_preflight.py +0 -0
  167. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_faster_whisper_docs.py +0 -0
  168. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_fetch_published_fonts.py +0 -0
  169. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_font_notices.py +0 -0
  170. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_http_host_boundary.py +0 -0
  171. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_http_trusted_proxy.py +0 -0
  172. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_job_progress.py +0 -0
  173. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_jobs.py +0 -0
  174. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_landing_page.py +0 -0
  175. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_legal_summary.py +0 -0
  176. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_linux_no_hardlink.py +0 -0
  177. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_live_smoke_script.py +0 -0
  178. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_macos_no_hardlink.py +0 -0
  179. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_mcp_batch_cookie_docs.py +0 -0
  180. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_mcp_http_boundary.py +0 -0
  181. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_mcp_protocol_cleanliness.py +0 -0
  182. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_mcp_terminal_guidance.py +0 -0
  183. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_mcp_tool_descriptions.py +0 -0
  184. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_mcp_work_root.py +0 -0
  185. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_model.py +0 -0
  186. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_partial_output_resume.py +0 -0
  187. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_partial_pdf_resume.py +0 -0
  188. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_paths.py +0 -0
  189. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_pipeline_integrity.py +0 -0
  190. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_pipeline_resume_setup.py +0 -0
  191. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_production_browser_cookies.py +0 -0
  192. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_publish_ci_gate.py +0 -0
  193. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_publish_fonts_wiring.py +0 -0
  194. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_publish_manifest_wiring.py +0 -0
  195. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_publish_workflow_gate.py +0 -0
  196. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_readme_layout.py +0 -0
  197. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_readme_release_guard.py +0 -0
  198. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_release_boundaries.py +0 -0
  199. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_release_manifest.py +0 -0
  200. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_release_surfaces.py +0 -0
  201. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_render.py +0 -0
  202. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_retrieval.py +0 -0
  203. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_sdist_allowlist.py +0 -0
  204. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_service_profile.py +0 -0
  205. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_site_not_found.py +0 -0
  206. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_sources.py +0 -0
  207. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_stage_reuse_resume.py +0 -0
  208. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_startup_recovery.py +0 -0
  209. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_stdio_protocol.py +0 -0
  210. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_submission.py +0 -0
  211. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_submission_validation.py +0 -0
  212. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_translate.py +0 -0
  213. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_translate_completeness.py +0 -0
  214. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_verify_release_versions.py +0 -0
  215. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_whistle_assets.py +0 -0
  216. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_whistle_defaults.py +0 -0
  217. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_whistle_engine.py +0 -0
  218. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_whistle_job_resume.py +0 -0
  219. {textflowkit-0.1.9 → textflowkit-0.1.11}/tests/test_windows_no_hardlink.py +0 -0
@@ -90,6 +90,6 @@ jobs:
90
90
  python -m pip install "setuptools>=83" "build>=1.2"
91
91
  python -m build --wheel
92
92
  python -m build --wheel --outdir dist packages/textflowkit-fonts
93
- - name: Smoke all three installed entry points
93
+ - name: Smoke all four installed entry points (CLI, MCP, HTTP, served UI)
94
94
  shell: bash
95
95
  run: python scripts/smoke_installed_wheel.py dist/textflowkit-*.whl dist/textflowkit_fonts-*.whl
@@ -1,5 +1,97 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.1.11 — 2026-10-06
4
+
5
+ Optional native live streaming supports **Windows x86_64 only**. Core version:
6
+ 0.1.11; unchanged optional fonts: 0.1.6. Existing standalone Whistle defaults,
7
+ file/URL jobs, CLI, and MCP behavior are unchanged. The public site remains static
8
+ documentation and serves no streams. Local source validation does not establish
9
+ published-package verification or cross-platform live support.
10
+
11
+
12
+ ### Added
13
+
14
+ - **Public `StreamingSession` API.** Bounded PCM feed, typed incremental events,
15
+ flush/finish, cancellation, canonical final `Transcript`, and context-manager
16
+ cleanup. One owned native child per session binds directly to the separately
17
+ pinned `libneedle3.dll`; no upstream Python SDK or telemetry module is loaded.
18
+ Live sessions are ephemeral, with no replay or durable resume.
19
+ - **Live microphone streaming (opt-in, loopback-only).** When
20
+ `TEXTFLOWKIT_STREAMING=1` is set, the developer HTTP app serves a `/stream`
21
+ WebSocket and the local UI serves `/api/stream`, both transcribing audio as it
22
+ arrives. A runnable, self-hosted browser example is at `/streaming-example`
23
+ (and `/api/streaming-example` under the UI); its `AudioWorklet` resamples any
24
+ device rate to 16 kHz mono with continuous phase. Off by default: with the
25
+ switch unset the endpoint does not exist. The standalone `textflowkit` CLI is
26
+ unaffected — it does not open a microphone or a streaming socket. Requires the
27
+ new `streaming` extra (`pip install "textflowkit[streaming]"`), which adds the
28
+ `websockets` dependency. Auth is fail-closed: the UI accepts only its own origin
29
+ and capability, the standalone API requires the API token (even in developer
30
+ mode), and a separate origin may stream only if the operator lists it in
31
+ `TEXTFLOWKIT_STREAMING_ORIGINS` (a browser needs its own origin listed, including
32
+ the standalone example's). See [docs/streaming.md](docs/streaming.md).
33
+ **Bounded live runs verified on this machine's native engine** — 65 s and 300 s
34
+ through the product core on Windows x86_64. A separate assembled UI WebSocket
35
+ run fed prerecorded PCM at real time for 12 s. Browser lifecycle and resampling
36
+ checks used fake audio; **physical microphone capture was not verified**. Only
37
+ English was measured natively; the other six accepted languages and live
38
+ Linux/macOS were not exercised. No public remote streaming is provided.
39
+
40
+ ## v0.1.10 — 2026-10-06
41
+
42
+ **The local browser interface ships.** The loopback-only browser workspace
43
+ (`textflowkit-ui`) is now part of the core package rather than source-only, so a
44
+ regular install of this release carries the command. Core version: 0.1.10;
45
+ unchanged optional fonts: 0.1.6.
46
+
47
+ ### Added
48
+
49
+ - **Local browser interface in the core package.** `textflowkit-ui` serves a
50
+ point-and-click workspace on **this machine only**: it binds `127.0.0.1` (never
51
+ off the machine, whatever `TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a
52
+ browser once serving. It mounts the existing developer HTTP app under `/api`
53
+ **unchanged** — the same routes, submission contract, and job store — and adds
54
+ only transport: a loopback session layer, a streamed upload, a fixed-format
55
+ download, in-workspace playback, and a capability endpoint. It adds **no new
56
+ dependency** beyond the existing `http` extra (FastAPI + uvicorn).
57
+ - **Durable jobs, cancel and resume in the workspace.** Jobs and checkpoints are
58
+ stored durably under the per-user data directory unless
59
+ `TEXTFLOWKIT_DB` / `TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT` is set.
60
+ A dropped file is streamed and capped at **2 GiB** by default
61
+ (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it). A running job can be
62
+ cancelled, and a durable job resumed.
63
+ - **Windows desktop shortcut.** `textflowkit-ui --create-shortcut` creates a Start
64
+ Menu entry that launches with `pythonw` (no console) and needs no `pywin32`.
65
+ Under `pythonw` a startup failure is logged under the per-user data directory
66
+ and shown in a message box rather than failing silently.
67
+ - **Controlled shutdown that drains.** **Stop server** asks this process's own
68
+ uvicorn server to exit through its supported `should_exit` flag; the normal
69
+ lifespan teardown **waits for current jobs to finish** before draining the
70
+ worker pool rather than interrupting them, so a running job's saved progress
71
+ survives and is resumable. It never sends a signal to a pid.
72
+
73
+ ### Changed
74
+
75
+ - **Documentation surfaces.** README, user manual, install notes, developer
76
+ manual, architecture guide, roadmap, release checklist, site deployment, the
77
+ landing page, and the checked-in SVG diagrams now describe the local browser
78
+ interface as a shipped feature and a fifth door, and remove the earlier
79
+ "unreleased candidate / install from source only" wording.
80
+ - **No endpoint-breaking change.** The CLI, Python API, MCP server, and JSON HTTP
81
+ adapter are unchanged in this release; the optional engines, diarization, and
82
+ translation keep their documented dependency requirements.
83
+
84
+ ### Verification boundary
85
+
86
+ Automated coverage runs the UI's routes, packaging, ownership/shutdown, and
87
+ security boundaries. A **native Windows** machine, in a real browser, proved the
88
+ workspace end to end (drop a file, submit, download, stop). That is one platform's
89
+ browser proof, **not** a live Linux or macOS UI run — those platforms are covered
90
+ by automated tests only, and cross-platform CI (Windows/Linux/macOS) is a separate
91
+ release gate. **No live optional-backend (translation or
92
+ diarization) run through the UI is claimed.** The engine and packaging changes of
93
+ v0.1.9 are unchanged by this release.
94
+
3
95
  ## v0.1.9 — 2026-10-05
4
96
 
5
97
  **Whistle is now the default engine.** This release ships the Whistle integration
@@ -14,9 +14,15 @@ python -m venv .venv
14
14
  # Windows: .venv\Scripts\activate
15
15
  # POSIX: source .venv/bin/activate
16
16
  pip install ./packages/textflowkit-fonts # local optional PDF font companion
17
- pip install -e ".[dev,mcp,http,export]"
17
+ pip install -e ".[dev,mcp,http,streaming,export,whisper]"
18
18
  ```
19
19
 
20
+ The full suite needs both `streaming` (WebSocket transport) and `whisper`
21
+ (optional standalone engine) dependencies. Use a development environment; for
22
+ an existing AMD ROCm torch stack, follow [the install guide](docs/install.md)
23
+ to preserve that stack. Native live streaming supports Windows x86_64 only;
24
+ ordinary standalone platform support and defaults remain unchanged.
25
+
20
26
  ## Test and lint — the commands CI runs
21
27
 
22
28
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: textflowkit
3
- Version: 0.1.9
3
+ Version: 0.1.11
4
4
  Summary: Cross-platform media transcription toolkit: URL or file in, timestamped transcripts and subtitle files out.
5
5
  Project-URL: Homepage, https://www.textflowkit.org/
6
6
  Project-URL: Issues, https://github.com/scottconverse/textflowkit/issues
@@ -32,6 +32,7 @@ Requires-Dist: python-docx>=1.1; extra == 'all'
32
32
  Requires-Dist: reportlab>=4.0; extra == 'all'
33
33
  Requires-Dist: textflowkit-fonts<0.2,>=0.1.5; extra == 'all'
34
34
  Requires-Dist: uvicorn>=0.30; extra == 'all'
35
+ Requires-Dist: websockets<16,>=15; extra == 'all'
35
36
  Provides-Extra: dev
36
37
  Requires-Dist: build>=1.2; extra == 'dev'
37
38
  Requires-Dist: hatchling>=1.25; extra == 'dev'
@@ -53,6 +54,11 @@ Requires-Dist: pydantic>=2.7; extra == 'http'
53
54
  Requires-Dist: uvicorn>=0.30; extra == 'http'
54
55
  Provides-Extra: mcp
55
56
  Requires-Dist: mcp>=2.0; extra == 'mcp'
57
+ Provides-Extra: streaming
58
+ Requires-Dist: fastapi>=0.115; extra == 'streaming'
59
+ Requires-Dist: pydantic>=2.7; extra == 'streaming'
60
+ Requires-Dist: uvicorn>=0.30; extra == 'streaming'
61
+ Requires-Dist: websockets<16,>=15; extra == 'streaming'
56
62
  Provides-Extra: whisper
57
63
  Requires-Dist: openai-whisper>=20240930; extra == 'whisper'
58
64
  Description-Content-Type: text/markdown
@@ -67,7 +73,7 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
67
73
  [User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md) ·
68
74
  [Developer and integration manual](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md)
69
75
 
70
- **Current release: [v0.1.9](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.9).**
76
+ **Current release: [v0.1.11](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.11).**
71
77
 
72
78
  The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
73
79
  Pages. GitHub remains the source and CI host; the website does not run the
@@ -128,10 +134,11 @@ DOCX and PDF are available through the optional `export` extra.
128
134
 
129
135
  ## Architecture
130
136
 
131
- ![TextFlowKit shared-core architecture: four thin entry points (CLI, Python, MCP, HTTP) feed one core that acquires and decodes media, transcribes with the default Whistle engine or an explicitly selected openai-whisper, keeps job state and resume checkpoints in an optional SQLite store, adds optional speaker or translation postprocessing, and publishes TXT, SRT, VTT, JSON, Markdown, and optional DOCX/PDF exports.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.9/docs/assets/architecture-overview.svg)
137
+ ![TextFlowKit shared-core architecture: five thin entry points feed one core. The CLI, Python API, MCP, HTTP API, and the local browser UI (a fifth door that mounts the existing HTTP app under /api and shares the same job store, adding transport only, not a new pipeline) sit above one core that acquires and decodes media, transcribes with the default Whistle engine or an explicitly selected openai-whisper, keeps job state and resume checkpoints in an optional SQLite store, adds optional speaker or translation postprocessing, and publishes TXT, SRT, VTT, JSON, Markdown, and optional DOCX/PDF exports.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.11/docs/assets/architecture-overview.svg)
132
138
 
133
- The design principle is **one engine, three doors**. Everything of substance lives in
134
- the core; the interfaces are thin.
139
+ The design principle is **one shared core, thin doors**. Everything of substance
140
+ lives in the core; the interfaces are thin — the local browser UI is a fifth door
141
+ that adds only transport over the existing HTTP app and shared job store.
135
142
 
136
143
  | Layer | Path | Responsibility |
137
144
  |---|---|---|
@@ -141,6 +148,7 @@ the core; the interfaces are thin.
141
148
  | **CLI** | `src/textflowkit/cli.py` | Reference interface (subprocess-friendly) |
142
149
  | **MCP** | `src/textflowkit/adapters/mcp_server.py` | stdio + Streamable HTTP, for AI harnesses |
143
150
  | **HTTP** | `src/textflowkit/adapters/http_server.py` | JSON API, for software products and web frontends |
151
+ | **Local UI** | `src/textflowkit/ui` | Loopback-only browser workspace over the same HTTP app and job store |
144
152
 
145
153
  Because the core owns the pipeline, adding a door is cheap — and adding a platform
146
154
  means writing one source adapter, not another tool.
@@ -148,6 +156,32 @@ means writing one source adapter, not another tool.
148
156
  For the job lifecycle, process ownership, checkpoints, and output publication,
149
157
  see [docs/architecture.md](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md).
150
158
 
159
+ ## Live microphone streaming (opt-in)
160
+
161
+ In v0.1.11, live streaming supports **Windows x86_64 only**. When
162
+ `TEXTFLOWKIT_STREAMING=1` is set, the developer HTTP app serves a `/stream`
163
+ WebSocket and the local UI serves `/api/stream`, both transcribing a live microphone
164
+ as you speak. A **runnable, self-hosted browser example** ships at
165
+ `/streaming-example` (and `/api/streaming-example` under the UI): it captures with an
166
+ `AudioWorklet`, resamples any device rate to 16 kHz mono, and shows the transcript.
167
+ No CDN, no analytics, no external fetch. Requires the `streaming` extra:
168
+
169
+ ```bash
170
+ python -m pip install "textflowkit[streaming]==0.1.11"
171
+ ```
172
+
173
+ It is **off by default** — with the switch unset the endpoint does not exist — and
174
+ **loopback-only**: auth is fail-closed, so the UI accepts only its own origin and
175
+ capability, the standalone API requires `TEXTFLOWKIT_API_TOKEN` (even in developer
176
+ mode), and a separate origin — including the standalone browser example's own origin —
177
+ may stream only if listed in `TEXTFLOWKIT_STREAMING_ORIGINS`. The standalone
178
+ `textflowkit` CLI is unaffected: it does not open a microphone or a streaming socket.
179
+ **Bounded live runs are verified on the native engine here** — 65 s and 300 s
180
+ end-to-end on Windows x86_64; the transport and the browser resampler are covered by
181
+ tests, and a physical browser microphone has not yet been captured (the browser proof
182
+ drives the page against a fake audio device). See
183
+ [docs/streaming.md](https://github.com/scottconverse/textflowkit/blob/main/docs/streaming.md).
184
+
151
185
  ## Recognized sources
152
186
 
153
187
  YouTube · TikTok · Facebook · Instagram · Vimeo · Twitch · Bilibili · Rumble ·
@@ -294,7 +328,7 @@ textflowkit export ./transcript.json --format vtt
294
328
  needs no PyTorch, so a fresh `pip install textflowkit` stays small and pulls no
295
329
  torch; `openai-whisper` is an explicitly selectable opt-in engine.
296
330
 
297
- ![Whistle bounded block run and durable resume: a decoded PCM WAV is split into 26-second cores with up to 2 seconds of context (each clip ≤ 30 s), each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.9/docs/assets/whistle-resume.svg)
331
+ ![Whistle bounded block run and durable resume: a decoded PCM WAV is split into 26-second cores with up to 2 seconds of context (each clip ≤ 30 s), each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.11/docs/assets/whistle-resume.svg)
298
332
 
299
333
  - **CPU only, no torch.** It runs a pinned native binary and downloads one pinned
300
334
  model on first use. Naming a GPU device is refused rather than silently
@@ -311,7 +345,7 @@ torch; `openai-whisper` is an explicitly selectable opt-in engine.
311
345
  of context on each side, so every standalone clip is ≤ 30 seconds (the native
312
346
  CLI's limit). Overlap words are selected by core midpoint rather than text
313
347
  deduplication, so repeated spoken phrases are preserved.
314
- - **Streaming is not used.** The native `--audio-stream` mode failed its
348
+ - **Standalone streaming is not used.** The native `--audio-stream` mode failed its
315
349
  coverage and is never passed; only the committed standalone output is parsed.
316
350
  - **Durable block resume.** With `TEXTFLOWKIT_DB` set, a long run writes partial
317
351
  per-block checkpoints; a resume checks the source and decoded-WAV hashes and the
@@ -397,6 +431,66 @@ contract checks cover it, and running an actual build is deliberately out of
397
431
  scope here, so no image is claimed to build or start. See
398
432
  [the container example](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#container-example-dockerfile-and-compose).
399
433
 
434
+ ## Local browser interface
435
+
436
+ For point-and-click use there is a local browser workspace that runs on **this
437
+ machine only**, reuses the same pipeline and job store, and adds no dependency of
438
+ its own beyond the `http` extra. It ships with the core package as of v0.1.10:
439
+ a regular install of the current release carries it.
440
+
441
+ ```bash
442
+ python -m pip install 'textflowkit[http,export]==0.1.11'
443
+ textflowkit-ui
444
+ ```
445
+
446
+ On Windows you can create a desktop shortcut when you want one:
447
+
448
+ ```powershell
449
+ textflowkit-ui --create-shortcut # Start Menu entry (pythonw, no console)
450
+ textflowkit-ui --create-shortcut --shortcut-dir 'C:\path\to\folder'
451
+ ```
452
+
453
+ The shortcut is created only on request and needs no `pywin32`. Under `pythonw`
454
+ there is no console, so a startup failure is written to a log file under the
455
+ per-user data directory and shown in a message box rather than failing silently.
456
+ The normal operator path is to launch from that shortcut; the console command
457
+ above is equivalent.
458
+
459
+ **This is a local app, not a public website.** The [landing page](https://www.textflowkit.org/)
460
+ is static documentation served by Cloudflare Pages; it does **not** run the
461
+ transcription engine and offers no upload form. The browser workspace exists only
462
+ on the machine that installed it.
463
+
464
+ - **Loopback only.** It binds `127.0.0.1` (never off the machine, whatever
465
+ `TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a browser once it is serving.
466
+ - **One owner per database.** A second launch against the same database opens the
467
+ UI already running rather than starting a competing server. **Stop server** in
468
+ the workspace shuts the local server down without a terminal, waiting for the
469
+ current jobs to finish rather than interrupting them. Shutdown **drains** — it
470
+ lets in-flight jobs complete; it does **not** auto-cancel them. Cancel a job
471
+ first to stop it early.
472
+ - **Durable jobs with cancel and resume.** Jobs and checkpoints are stored
473
+ durably under the per-user data directory unless you set `TEXTFLOWKIT_DB` /
474
+ `TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT`. A dropped file is capped
475
+ at **2 GiB** by default (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it).
476
+ In-workspace playback uses the browser's native player on the **currently
477
+ selected** media, so it depends on what that browser can decode; it is a
478
+ convenience preview, not a format guarantee.
479
+ - **No UI analytics.** The page loads no CDN asset and no analytics, and the
480
+ session token lives only in the page — never in a URL, cookie, or log line.
481
+ - **What uses the network.** The page itself loads nothing remote. A **URL
482
+ source** is downloaded from the internet; an engine's **model** may be
483
+ downloaded on first use; and **remote or cloud translation** (an advanced
484
+ option) sends transcript text to that backend. With local media and a local
485
+ translation model, no transcript leaves the machine.
486
+ - `--port`, `--no-browser`, and `--version` are available.
487
+
488
+ The other doors are unchanged: the CLI, Python API, MCP, and HTTP adapters keep
489
+ their existing behavior, and the optional engines, diarization, and translation
490
+ retain their documented dependency requirements. See the
491
+ [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md#7-local-browser-interface)
492
+ and [architecture guide](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md#one-owner-per-database).
493
+
400
494
  ## Durable, bounded, cancellable
401
495
 
402
496
  ```bash
@@ -411,23 +505,43 @@ boundary. See [docs/adapters.md](https://github.com/scottconverse/textflowkit/bl
411
505
 
412
506
  The MCP and HTTP adapters are **job-based**: submission returns a job id
413
507
  immediately and clients poll for completion. The CLI uses the same core but
414
- waits for the result. This lets AI harnesses, software products, and a future
415
- web frontend share the job contract without blocking a request.
508
+ waits for the result. This lets AI harnesses, software products, and the local
509
+ browser interface share the job contract without blocking a request.
416
510
 
417
511
  ## Status
418
512
 
419
- **v0.1.9 release.** Core, CLI, MCP, and HTTP have automated
420
- coverage. This release makes **Whistle the default engine** — a native CPU-only
421
- transcriber that needs no PyTorch — and moves `openai-whisper` to the optional
422
- `whisper` extra, selected explicitly with `--engine whisper` or
423
- `engine="whisper"`. Legacy engine aliases are preserved, so older saved jobs and
424
- `transcribe()` calls still decode. Whistle refuses an unsupported platform,
425
- language, or GPU request instead of silently falling back. Its engine's
426
- verification boundary is in [Whistle (default engine)](#whistle-default-engine)
427
- above. Release publication uses the tag workflow, which requires successful
428
- exact-commit main CI before PyPI uploads and creates the public GitHub release
429
- only afterward. Check the linked release for artifacts and workflow status; local
430
- source verification is not a fresh PyPI-install or individual-harness receipt.
513
+ **v0.1.11 release.** Optional live streaming adds a transport-neutral
514
+ `StreamingSession` Python API, incremental transcript events, and authenticated
515
+ loopback WebSockets (`/stream` on the developer HTTP API and `/api/stream` on the
516
+ local UI), with a packaged browser microphone example. The native live capability
517
+ supports **Windows x86_64 only** and uses one owned child with a separately pinned
518
+ `libneedle3.dll`. The `streaming` extra supplies the WebSocket dependency;
519
+ `TEXTFLOWKIT_STREAMING=1` enables the server routes. Streaming is off by default.
520
+
521
+ Existing file and URL transcription, CLI, MCP, durable jobs, and local browser
522
+ workspace defaults remain unchanged. Whistle stays the CPU-only standalone default;
523
+ `openai-whisper` remains an explicit opt-in engine. The local browser workspace
524
+ introduced in v0.1.10 still needs only the `http` extra for its ordinary job flow.
525
+ Fonts remain 0.1.6. Live sessions are ephemeral and have no durable resume.
526
+
527
+ **Streaming evidence.** Local English PCM runs exercised the native core for 65 s
528
+ and 300 s; a separate assembled UI WebSocket run fed prerecorded PCM at real time
529
+ for 12 s and returned incremental events and a final transcript. Browser lifecycle
530
+ and resampling checks used fake audio. Physical browser microphone capture, live
531
+ Linux/macOS, and the six other accepted languages were not verified. The public
532
+ website remains static documentation and serves no streaming endpoint. Local source
533
+ validation does not establish published-package or exact-commit CI results.
534
+
535
+ **Verification limit.** The local browser interface is covered by an automated
536
+ suite, and the UI's behavior was proved on a **native Windows** machine in a real
537
+ browser. No live optional-backend (translation/diarization) run through the UI is
538
+ claimed, and **no live Linux or macOS UI run was performed here** — those
539
+ platforms are covered by automated tests only, and CI across Windows, Linux, and
540
+ macOS is a separate release gate. Release publication uses the tag
541
+ workflow, which requires successful exact-commit main CI before PyPI uploads and
542
+ creates the public GitHub release only afterward. Check the linked release for
543
+ artifacts and workflow status; local source verification is not a fresh
544
+ PyPI-install or individual-harness receipt.
431
545
 
432
546
  The v0.1.8 release carried four fixes for the 2026-10-01 post-release audit-lite
433
547
  (findings AL-001 – AL-004): CLI owning-process startup recovery so an interrupted
@@ -439,8 +553,8 @@ that precede it. The v0.1.7 source candidate was verified with real CLI, HTTP an
439
553
  MCP speech, seven-format exports and completed resume; the v0.1.8 runtime fixes
440
554
  were independently checked through fresh-process CLI restart probes, real SQLite
441
555
  setup-failure tests, and the full Windows test suite. Those checks are not new
442
- individual-harness receipts. That is the previous release's record, not evidence
443
- for v0.1.9.
556
+ individual-harness receipts. That is the previous releases' record, not evidence
557
+ for v0.1.11.
444
558
 
445
559
  The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
446
560
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
@@ -459,7 +573,7 @@ and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
459
573
  [fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
460
574
  live — that is the v0.1.6 release's evidence, not a receipt for a later release.
461
575
  Everything from here to the end of this section is the v0.1.6 release's
462
- historical record as published: it is not re-verified for v0.1.9, whose separate release evidence is not supplied by these historical paragraphs.
576
+ historical record as published: it is not re-verified for v0.1.11, whose separate release evidence is not supplied by these historical paragraphs.
463
577
  Merged-main CI passed 16/16 on the tagged commit; the published wheel and
464
578
  sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
465
579
  Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
@@ -8,7 +8,7 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
8
8
  [User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md) ·
9
9
  [Developer and integration manual](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md)
10
10
 
11
- **Current release: [v0.1.9](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.9).**
11
+ **Current release: [v0.1.11](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.11).**
12
12
 
13
13
  The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
14
14
  Pages. GitHub remains the source and CI host; the website does not run the
@@ -69,10 +69,11 @@ DOCX and PDF are available through the optional `export` extra.
69
69
 
70
70
  ## Architecture
71
71
 
72
- ![TextFlowKit shared-core architecture: four thin entry points (CLI, Python, MCP, HTTP) feed one core that acquires and decodes media, transcribes with the default Whistle engine or an explicitly selected openai-whisper, keeps job state and resume checkpoints in an optional SQLite store, adds optional speaker or translation postprocessing, and publishes TXT, SRT, VTT, JSON, Markdown, and optional DOCX/PDF exports.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.9/docs/assets/architecture-overview.svg)
72
+ ![TextFlowKit shared-core architecture: five thin entry points feed one core. The CLI, Python API, MCP, HTTP API, and the local browser UI (a fifth door that mounts the existing HTTP app under /api and shares the same job store, adding transport only, not a new pipeline) sit above one core that acquires and decodes media, transcribes with the default Whistle engine or an explicitly selected openai-whisper, keeps job state and resume checkpoints in an optional SQLite store, adds optional speaker or translation postprocessing, and publishes TXT, SRT, VTT, JSON, Markdown, and optional DOCX/PDF exports.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.11/docs/assets/architecture-overview.svg)
73
73
 
74
- The design principle is **one engine, three doors**. Everything of substance lives in
75
- the core; the interfaces are thin.
74
+ The design principle is **one shared core, thin doors**. Everything of substance
75
+ lives in the core; the interfaces are thin — the local browser UI is a fifth door
76
+ that adds only transport over the existing HTTP app and shared job store.
76
77
 
77
78
  | Layer | Path | Responsibility |
78
79
  |---|---|---|
@@ -82,6 +83,7 @@ the core; the interfaces are thin.
82
83
  | **CLI** | `src/textflowkit/cli.py` | Reference interface (subprocess-friendly) |
83
84
  | **MCP** | `src/textflowkit/adapters/mcp_server.py` | stdio + Streamable HTTP, for AI harnesses |
84
85
  | **HTTP** | `src/textflowkit/adapters/http_server.py` | JSON API, for software products and web frontends |
86
+ | **Local UI** | `src/textflowkit/ui` | Loopback-only browser workspace over the same HTTP app and job store |
85
87
 
86
88
  Because the core owns the pipeline, adding a door is cheap — and adding a platform
87
89
  means writing one source adapter, not another tool.
@@ -89,6 +91,32 @@ means writing one source adapter, not another tool.
89
91
  For the job lifecycle, process ownership, checkpoints, and output publication,
90
92
  see [docs/architecture.md](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md).
91
93
 
94
+ ## Live microphone streaming (opt-in)
95
+
96
+ In v0.1.11, live streaming supports **Windows x86_64 only**. When
97
+ `TEXTFLOWKIT_STREAMING=1` is set, the developer HTTP app serves a `/stream`
98
+ WebSocket and the local UI serves `/api/stream`, both transcribing a live microphone
99
+ as you speak. A **runnable, self-hosted browser example** ships at
100
+ `/streaming-example` (and `/api/streaming-example` under the UI): it captures with an
101
+ `AudioWorklet`, resamples any device rate to 16 kHz mono, and shows the transcript.
102
+ No CDN, no analytics, no external fetch. Requires the `streaming` extra:
103
+
104
+ ```bash
105
+ python -m pip install "textflowkit[streaming]==0.1.11"
106
+ ```
107
+
108
+ It is **off by default** — with the switch unset the endpoint does not exist — and
109
+ **loopback-only**: auth is fail-closed, so the UI accepts only its own origin and
110
+ capability, the standalone API requires `TEXTFLOWKIT_API_TOKEN` (even in developer
111
+ mode), and a separate origin — including the standalone browser example's own origin —
112
+ may stream only if listed in `TEXTFLOWKIT_STREAMING_ORIGINS`. The standalone
113
+ `textflowkit` CLI is unaffected: it does not open a microphone or a streaming socket.
114
+ **Bounded live runs are verified on the native engine here** — 65 s and 300 s
115
+ end-to-end on Windows x86_64; the transport and the browser resampler are covered by
116
+ tests, and a physical browser microphone has not yet been captured (the browser proof
117
+ drives the page against a fake audio device). See
118
+ [docs/streaming.md](https://github.com/scottconverse/textflowkit/blob/main/docs/streaming.md).
119
+
92
120
  ## Recognized sources
93
121
 
94
122
  YouTube · TikTok · Facebook · Instagram · Vimeo · Twitch · Bilibili · Rumble ·
@@ -235,7 +263,7 @@ textflowkit export ./transcript.json --format vtt
235
263
  needs no PyTorch, so a fresh `pip install textflowkit` stays small and pulls no
236
264
  torch; `openai-whisper` is an explicitly selectable opt-in engine.
237
265
 
238
- ![Whistle bounded block run and durable resume: a decoded PCM WAV is split into 26-second cores with up to 2 seconds of context (each clip ≤ 30 s), each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.9/docs/assets/whistle-resume.svg)
266
+ ![Whistle bounded block run and durable resume: a decoded PCM WAV is split into 26-second cores with up to 2 seconds of context (each clip ≤ 30 s), each core runs as one owned child with forced telemetry-off flags, a partial checkpoint is written after each core, and a resume validates the source and decoded-WAV hashes then re-runs only the unfinished cores.](https://raw.githubusercontent.com/scottconverse/textflowkit/v0.1.11/docs/assets/whistle-resume.svg)
239
267
 
240
268
  - **CPU only, no torch.** It runs a pinned native binary and downloads one pinned
241
269
  model on first use. Naming a GPU device is refused rather than silently
@@ -252,7 +280,7 @@ torch; `openai-whisper` is an explicitly selectable opt-in engine.
252
280
  of context on each side, so every standalone clip is ≤ 30 seconds (the native
253
281
  CLI's limit). Overlap words are selected by core midpoint rather than text
254
282
  deduplication, so repeated spoken phrases are preserved.
255
- - **Streaming is not used.** The native `--audio-stream` mode failed its
283
+ - **Standalone streaming is not used.** The native `--audio-stream` mode failed its
256
284
  coverage and is never passed; only the committed standalone output is parsed.
257
285
  - **Durable block resume.** With `TEXTFLOWKIT_DB` set, a long run writes partial
258
286
  per-block checkpoints; a resume checks the source and decoded-WAV hashes and the
@@ -338,6 +366,66 @@ contract checks cover it, and running an actual build is deliberately out of
338
366
  scope here, so no image is claimed to build or start. See
339
367
  [the container example](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#container-example-dockerfile-and-compose).
340
368
 
369
+ ## Local browser interface
370
+
371
+ For point-and-click use there is a local browser workspace that runs on **this
372
+ machine only**, reuses the same pipeline and job store, and adds no dependency of
373
+ its own beyond the `http` extra. It ships with the core package as of v0.1.10:
374
+ a regular install of the current release carries it.
375
+
376
+ ```bash
377
+ python -m pip install 'textflowkit[http,export]==0.1.11'
378
+ textflowkit-ui
379
+ ```
380
+
381
+ On Windows you can create a desktop shortcut when you want one:
382
+
383
+ ```powershell
384
+ textflowkit-ui --create-shortcut # Start Menu entry (pythonw, no console)
385
+ textflowkit-ui --create-shortcut --shortcut-dir 'C:\path\to\folder'
386
+ ```
387
+
388
+ The shortcut is created only on request and needs no `pywin32`. Under `pythonw`
389
+ there is no console, so a startup failure is written to a log file under the
390
+ per-user data directory and shown in a message box rather than failing silently.
391
+ The normal operator path is to launch from that shortcut; the console command
392
+ above is equivalent.
393
+
394
+ **This is a local app, not a public website.** The [landing page](https://www.textflowkit.org/)
395
+ is static documentation served by Cloudflare Pages; it does **not** run the
396
+ transcription engine and offers no upload form. The browser workspace exists only
397
+ on the machine that installed it.
398
+
399
+ - **Loopback only.** It binds `127.0.0.1` (never off the machine, whatever
400
+ `TEXTFLOWKIT_ALLOW_REMOTE` is set to) and opens a browser once it is serving.
401
+ - **One owner per database.** A second launch against the same database opens the
402
+ UI already running rather than starting a competing server. **Stop server** in
403
+ the workspace shuts the local server down without a terminal, waiting for the
404
+ current jobs to finish rather than interrupting them. Shutdown **drains** — it
405
+ lets in-flight jobs complete; it does **not** auto-cancel them. Cancel a job
406
+ first to stop it early.
407
+ - **Durable jobs with cancel and resume.** Jobs and checkpoints are stored
408
+ durably under the per-user data directory unless you set `TEXTFLOWKIT_DB` /
409
+ `TEXTFLOWKIT_WORK_ROOT` / `TEXTFLOWKIT_OUTPUT_ROOT`. A dropped file is capped
410
+ at **2 GiB** by default (`TEXTFLOWKIT_UI_MAX_UPLOAD_BYTES` overrides it).
411
+ In-workspace playback uses the browser's native player on the **currently
412
+ selected** media, so it depends on what that browser can decode; it is a
413
+ convenience preview, not a format guarantee.
414
+ - **No UI analytics.** The page loads no CDN asset and no analytics, and the
415
+ session token lives only in the page — never in a URL, cookie, or log line.
416
+ - **What uses the network.** The page itself loads nothing remote. A **URL
417
+ source** is downloaded from the internet; an engine's **model** may be
418
+ downloaded on first use; and **remote or cloud translation** (an advanced
419
+ option) sends transcript text to that backend. With local media and a local
420
+ translation model, no transcript leaves the machine.
421
+ - `--port`, `--no-browser`, and `--version` are available.
422
+
423
+ The other doors are unchanged: the CLI, Python API, MCP, and HTTP adapters keep
424
+ their existing behavior, and the optional engines, diarization, and translation
425
+ retain their documented dependency requirements. See the
426
+ [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md#7-local-browser-interface)
427
+ and [architecture guide](https://github.com/scottconverse/textflowkit/blob/main/docs/architecture.md#one-owner-per-database).
428
+
341
429
  ## Durable, bounded, cancellable
342
430
 
343
431
  ```bash
@@ -352,23 +440,43 @@ boundary. See [docs/adapters.md](https://github.com/scottconverse/textflowkit/bl
352
440
 
353
441
  The MCP and HTTP adapters are **job-based**: submission returns a job id
354
442
  immediately and clients poll for completion. The CLI uses the same core but
355
- waits for the result. This lets AI harnesses, software products, and a future
356
- web frontend share the job contract without blocking a request.
443
+ waits for the result. This lets AI harnesses, software products, and the local
444
+ browser interface share the job contract without blocking a request.
357
445
 
358
446
  ## Status
359
447
 
360
- **v0.1.9 release.** Core, CLI, MCP, and HTTP have automated
361
- coverage. This release makes **Whistle the default engine** — a native CPU-only
362
- transcriber that needs no PyTorch — and moves `openai-whisper` to the optional
363
- `whisper` extra, selected explicitly with `--engine whisper` or
364
- `engine="whisper"`. Legacy engine aliases are preserved, so older saved jobs and
365
- `transcribe()` calls still decode. Whistle refuses an unsupported platform,
366
- language, or GPU request instead of silently falling back. Its engine's
367
- verification boundary is in [Whistle (default engine)](#whistle-default-engine)
368
- above. Release publication uses the tag workflow, which requires successful
369
- exact-commit main CI before PyPI uploads and creates the public GitHub release
370
- only afterward. Check the linked release for artifacts and workflow status; local
371
- source verification is not a fresh PyPI-install or individual-harness receipt.
448
+ **v0.1.11 release.** Optional live streaming adds a transport-neutral
449
+ `StreamingSession` Python API, incremental transcript events, and authenticated
450
+ loopback WebSockets (`/stream` on the developer HTTP API and `/api/stream` on the
451
+ local UI), with a packaged browser microphone example. The native live capability
452
+ supports **Windows x86_64 only** and uses one owned child with a separately pinned
453
+ `libneedle3.dll`. The `streaming` extra supplies the WebSocket dependency;
454
+ `TEXTFLOWKIT_STREAMING=1` enables the server routes. Streaming is off by default.
455
+
456
+ Existing file and URL transcription, CLI, MCP, durable jobs, and local browser
457
+ workspace defaults remain unchanged. Whistle stays the CPU-only standalone default;
458
+ `openai-whisper` remains an explicit opt-in engine. The local browser workspace
459
+ introduced in v0.1.10 still needs only the `http` extra for its ordinary job flow.
460
+ Fonts remain 0.1.6. Live sessions are ephemeral and have no durable resume.
461
+
462
+ **Streaming evidence.** Local English PCM runs exercised the native core for 65 s
463
+ and 300 s; a separate assembled UI WebSocket run fed prerecorded PCM at real time
464
+ for 12 s and returned incremental events and a final transcript. Browser lifecycle
465
+ and resampling checks used fake audio. Physical browser microphone capture, live
466
+ Linux/macOS, and the six other accepted languages were not verified. The public
467
+ website remains static documentation and serves no streaming endpoint. Local source
468
+ validation does not establish published-package or exact-commit CI results.
469
+
470
+ **Verification limit.** The local browser interface is covered by an automated
471
+ suite, and the UI's behavior was proved on a **native Windows** machine in a real
472
+ browser. No live optional-backend (translation/diarization) run through the UI is
473
+ claimed, and **no live Linux or macOS UI run was performed here** — those
474
+ platforms are covered by automated tests only, and CI across Windows, Linux, and
475
+ macOS is a separate release gate. Release publication uses the tag
476
+ workflow, which requires successful exact-commit main CI before PyPI uploads and
477
+ creates the public GitHub release only afterward. Check the linked release for
478
+ artifacts and workflow status; local source verification is not a fresh
479
+ PyPI-install or individual-harness receipt.
372
480
 
373
481
  The v0.1.8 release carried four fixes for the 2026-10-01 post-release audit-lite
374
482
  (findings AL-001 – AL-004): CLI owning-process startup recovery so an interrupted
@@ -380,8 +488,8 @@ that precede it. The v0.1.7 source candidate was verified with real CLI, HTTP an
380
488
  MCP speech, seven-format exports and completed resume; the v0.1.8 runtime fixes
381
489
  were independently checked through fresh-process CLI restart probes, real SQLite
382
490
  setup-failure tests, and the full Windows test suite. Those checks are not new
383
- individual-harness receipts. That is the previous release's record, not evidence
384
- for v0.1.9.
491
+ individual-harness receipts. That is the previous releases' record, not evidence
492
+ for v0.1.11.
385
493
 
386
494
  The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
387
495
  for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
@@ -400,7 +508,7 @@ and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
400
508
  [fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
401
509
  live — that is the v0.1.6 release's evidence, not a receipt for a later release.
402
510
  Everything from here to the end of this section is the v0.1.6 release's
403
- historical record as published: it is not re-verified for v0.1.9, whose separate release evidence is not supplied by these historical paragraphs.
511
+ historical record as published: it is not re-verified for v0.1.11, whose separate release evidence is not supplied by these historical paragraphs.
404
512
  Merged-main CI passed 16/16 on the tagged commit; the published wheel and
405
513
  sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
406
514
  Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI