商业产业 Hacker News AI 2026-07-16T02:57 正文 ok 754 words

🔶 开源、支持免费层级的 Whispr:基于 Cloudflare AI 的语音输入工具

英文标题:🔶 Open Source, Free Tier Capable Whispr Using Cloudflare AI

Whispr 是一款语音转文字桌面工具,通过快捷键捕获语音,交由 Whisper 转写,再用 LLM 格式化后直接粘贴到当前位置。它基于 Wails 构建 macOS 客户端,后端使用 Cloudflare Worker 和 Durable Object,免费层即可运行。项目架构清晰,提供云托管和本地处理两种模式,为开发者展示了如何将 Cloudflare AI 与键鼠工作流结合。
打开原文相关 2 · 机会 2
media68.6 KB

中文

一款语音转文字工具,可捕获语音,通过 Whisper 将其转录,并使用 LLM 格式化输出。按下快捷键,说话,松开——格式化后的文本会进入剪贴板,并自动粘贴到你正在输入的应用中。

┌──────────────────────┐ PCM 数据块 ┌──────────────────────────────────┐ 格式化 │ Wails 桌面应用 │ ──WebSocket──▶ │ Cloudflare Worker (Durable Obj) │ ──文本────▶ 剪贴板 → 自动粘贴 │ (Go + React WebView)│ ◀─────────────│ Whisper STT → LLM 格式化器 │ └──────────────────────┘ └──────────────────────────────────┘

  1. 按住 Ctrl+Cmd——捕获焦点上下文,开始录音,顶部中央出现悬浮窗
  2. 对着麦克风说话(语音电平表会显示输入)
  3. 松开 Ctrl+Cmd——音频流上传到云端
  4. Whisper 转录,LLM 格式化,结果拷贝到剪贴板并自动粘贴到原应用中

voicebox/ ├── main.go # Wails 入口,应用菜单 ├── app.go # 应用生命周期,快捷键处理,流程编排 ├── window_darwin.go # macOS 窗口管理(悬浮窗、设置、Dock 点击) ├── window_other.go # 非 macOS 构建的桩 ├── internal/ │ ├── audio/ # PCM 音频捕获 (malgo/miniaudio),RMS 电平 │ ├── pipeline/ # WebSocket 客户端,将音频和焦点上下文发送到 Worker │ ├── accessibility/ # macOS AX API:焦点元素上下文 + 自动粘贴 (Cmd+V) │ ├── config/ # TOML 配置加载与保存 │ ├── hotkey/ # 全局快捷键注册 │ ├── stt/ # STT 提供者接口(桩) │ └── formatter/ # LLM 格式化提供者接口(桩) ├── frontend/ # React + Tailwind 悬浮窗 UI (Vite) │ └── src/ │ ├── App.tsx # 在设置模式与悬浮窗模式之间路由 │ ├── components/ │ │ ├── settings-form.tsx # 配置编辑器 (react-hook-form + zod) │ │ └── title-bar.tsx # 无边框标题栏,含可拖拽区域 │ └── hooks/ │ ├── use-voicebox.ts # voicebox:state / voicebox:mode / voicebox:level 事件 │ └── use-config.ts # GetConfig / SaveConfig / GetConfigPath 绑定 ├── worker/ # Cloudflare Worker (TypeScript) │ ├── src/ │ │ ├── index.ts # 路由:/ws (WebSocket)、/health │ │ ├── session.ts # Durable Object:音频累积 + AI 管道 │ │ ├── prompt.ts # 系统提示词 + 用户消息构造 │ │ ├── wav.ts # PCM 转 WAV 封装 │ │ └── types.ts # 共享类型 │ ├── test/ # Vitest 测试 │ └── wrangler.jsonc # Worker 配置 ├── go.mod └── voicebox.toml # 用户配置 (gitignored)

  • Go 1.24+
  • Node.js + pnpm
  • Wails v2 CLI
  • 拥有 Workers AI 访问权限的 Cloudflare 账号
  • macOS(自动粘贴需要辅助功能权限)

cd worker pnpm install wrangler secret put VOICEBOX_TOKEN # 设置共享密钥 pnpm deploy

配置桌面客户端

首次启动时,VoiceBox 会打开设置窗口。你也可以手动在 ~/.config/voicebox/voicebox.toml 创建配置文件:

[provider] mode = "cloud"

[cloud] worker_url = "https://voicebox..workers.dev" token = "your-shared-secret"

[audio] sample_rate = 16000 channels = 1 chunk_size = 4096

[hotkey] record = "ctrl+cmd"

配置加载顺序为:~/.config/voicebox/voicebox.toml、二进制文件同级目录、./voicebox.toml。

macOS 辅助功能权限

自动粘贴需要 macOS 的辅助功能访问权限。首次使用时系统会提示授权,也可以前往 系统设置 → 隐私与安全性 → 辅助功能 手动授权。

wails dev # 开发模式,支持热重载 wails build # 生产二进制文件

设置窗口 (700×450,居中):启动时、Dock 点击或通过录制菜单打开。在此编辑配置。

悬浮窗 (160×48,顶部居中,浮动):录音时显示。带有语音电平表的录音指示器,处理时显示旋转器,成功时显示勾号。

客户端连接到 GET /ws?token=<auth-token>。

收到 {"type":"ready"} 后,客户端发送包含音频和焦点上下文的 configure 消息,随后流式传输二进制 PCM 数据块:

客户端 服务端 │── connect /ws?token=... ──────▶│ │◀── {"type":"ready"} ──────────│ │── {"type":"configure", ...} ──▶│ │── [binary PCM chunk] ─────────▶│ │── [binary PCM chunk] ─────────▶│ │── {"type":"audio_end"} ───────▶│ │◀── {"type":"processing",...} ──│ │◀── {"type":"result",...} ──────│

configure 消息携带音频参数以及焦点元素上下文(应用名称、bundle ID、元素角色、标题、占位符、当前值),LLM 格式化器利用这些信息定制输出。

  • STT : @cf/openai/whisper-large-v3-turbo

  • 格式化器 : @cf/qwen/qwen3-30b-a3b-fp8

  • STT : faster-whisper

  • 格式化器 : Ollama

  • 提供者接口位于 internal/stt/ 和 internal/formatter/

  • 16kHz 采样率,单声道,PCM 有符号 16 位小端

  • 约 4096 字节数据块(约 128ms 每块)

  • 最大录音长度:约 25 MiB(约 13 分钟)

桌面应用

wails dev # 开发服务器 (Go + Vite 热重载) wails build # 生产构建 go vet ./... # Go 静态检查 go test ./internal/... # Go 测试

前端

cd frontend && pnpm install && pnpm build

Worker

cd worker pnpm dev # 本地开发服务器 pnpm lint # 类型检查 pnpm format # prettier pnpm test # vitest pnpm deploy # 部署到 Cloudflare

English / 原文

Voice-to-text tool that captures speech, transcribes it via Whisper, and formats the output with an LLM. Press a hotkey, speak, release — formatted text lands in your clipboard and is auto-pasted into whatever you were typing in.

┌──────────────────────┐  PCM chunks   ┌──────────────────────────────────┐   formatted
│  Wails Desktop App   │ ──WebSocket──▶ │  Cloudflare Worker (Durable Obj) │ ──text────▶ Clipboard → Auto-paste
│  (Go + React WebView)│ ◀─────────────│  Whisper STT → LLM Formatter     │
└──────────────────────┘               └──────────────────────────────────┘
  1. Hold Ctrl+Cmd — focus context is captured, recording starts, overlay appears at top-center
  2. Speak into your microphone (voice level meter shows input)
  3. Release Ctrl+Cmd — audio streams to the cloud
  4. Whisper transcribes, LLM formats, result is copied to clipboard and auto-pasted into the originating app
voicebox/
├── main.go                 # Wails entrypoint, app menu
├── app.go                  # App lifecycle, hotkey handlers, pipeline orchestration
├── window_darwin.go        # macOS window management (overlay, settings, dock click)
├── window_other.go         # Stub for non-macOS builds
├── internal/
│   ├── audio/              # PCM audio capture (malgo/miniaudio), RMS level
│   ├── pipeline/           # WebSocket client, streams audio + focus context to worker
│   ├── accessibility/      # macOS AX API: focused element context + auto-paste (Cmd+V)
│   ├── config/             # TOML config loading and saving
│   ├── hotkey/             # Global hotkey registration
│   ├── stt/                # STT provider interface (stubs)
│   └── formatter/          # LLM formatting provider interface (stubs)
├── frontend/               # React + Tailwind overlay UI (Vite)
│   └── src/
│       ├── App.tsx         # Routes between settings mode and overlay mode
│       ├── components/
│       │   ├── settings-form.tsx  # Config editor (react-hook-form + zod)
│       │   └── title-bar.tsx      # Frameless title bar with drag region
│       └── hooks/
│           ├── use-voicebox.ts    # voicebox:state / voicebox:mode / voicebox:level events
│           └── use-config.ts      # GetConfig / SaveConfig / GetConfigPath bindings
├── worker/                 # Cloudflare Worker (TypeScript)
│   ├── src/
│   │   ├── index.ts        # Router: /ws (WebSocket), /health
│   │   ├── session.ts      # Durable Object: audio accumulation + AI pipeline
│   │   ├── prompt.ts       # System prompt + user message builder
│   │   ├── wav.ts          # PCM-to-WAV wrapper
│   │   └── types.ts        # Shared types
│   ├── test/               # Vitest tests
│   └── wrangler.jsonc      # Worker configuration
├── go.mod
└── voicebox.toml           # User config (gitignored)
  • Go 1.24+

  • Node.js + pnpm

  • Wails v2 CLI

  • A Cloudflare account with Workers AI access

  • macOS (accessibility permission required for auto-paste)

    cd worker pnpm install wrangler secret put VOICEBOX_TOKEN # set a shared secret pnpm deploy

Configure the Desktop Client

On first launch, VoiceBox opens a settings window. You can also create the config manually at ~/.config/voicebox/voicebox.toml:

[provider]
mode = "cloud"

[cloud]
worker_url = "https://voicebox.<your-subdomain>.workers.dev"
token = "your-shared-secret"

[audio]
sample_rate = 16000
channels = 1
chunk_size = 4096

[hotkey]
record = "ctrl+cmd"

Config is loaded from (in order): ~/.config/voicebox/voicebox.toml, next to the binary, then ./voicebox.toml.

macOS Accessibility Permission

Auto-paste requires macOS Accessibility access. On first use, macOS will prompt for permission, or you can grant it manually in System Settings → Privacy & Security → Accessibility.

wails dev      # dev mode with hot reload
wails build    # production binary

Settings (700×450, centered): Opens on launch, dock click, or via the Recording menu. Edit config here.

Overlay (160×48, top-center, floating): Appears during recording. Shows recording indicator with voice level meter, spinner while processing, checkmark on success.

Client connects to GET /ws?token=<auth-token>.

After receiving {"type":"ready"}, the client sends a configure message with audio and focus context, then streams binary PCM chunks:

Client                          Server
  │── connect /ws?token=... ──────▶│
  │◀── {"type":"ready"} ──────────│
  │── {"type":"configure", ...} ──▶│
  │── [binary PCM chunk] ─────────▶│
  │── [binary PCM chunk] ─────────▶│
  │── {"type":"audio_end"} ───────▶│
  │◀── {"type":"processing",...} ──│
  │◀── {"type":"result",...} ──────│

The configure message carries audio params and focused element context (app name, bundle ID, element role, title, placeholder, current value) used by the LLM formatter to tailor output.

  • STT : @cf/openai/whisper-large-v3-turbo

  • Formatter : @cf/qwen/qwen3-30b-a3b-fp8

  • STT : faster-whisper

  • Formatter : Ollama

  • Provider interfaces exist at internal/stt/ and internal/formatter/

  • 16kHz sample rate, mono, PCM signed 16-bit LE

  • ~4096 byte chunks (~128ms each)

  • Max recording: ~25 MiB (~13 minutes)

    Desktop app

    wails dev # dev server (Go + Vite hot reload) wails build # production build go vet ./... # lint Go go test ./internal/... # test Go

    Frontend

    cd frontend && pnpm install && pnpm build

    Worker

    cd worker pnpm dev # local dev server pnpm lint # type-check pnpm format # prettier pnpm test # vitest pnpm deploy # deploy to Cloudflare