Skip to content

feat: add voice status widget showing Claude Code voice input state#357

Open
t-benoit wants to merge 1 commit intosirmalloc:mainfrom
t-benoit:feat/voice-status-widget
Open

feat: add voice status widget showing Claude Code voice input state#357
t-benoit wants to merge 1 commit intosirmalloc:mainfrom
t-benoit:feat/voice-status-widget

Conversation

@t-benoit
Copy link
Copy Markdown
Contributor

@t-benoit t-benoit commented May 6, 2026

Summary

  • Adds a new voice-status widget that shows whether Claude Code's voice input feature is enabled, read from the user's Claude settings.
  • Supports four configurable display formats (icon, icon-text, text, word) plus an optional Nerd Font toggle, modeled on the existing vim-mode widget.
  • Reads the effective voice.enabled value from Claude Code's full layered settings stack (project-local → project → user-local → user-global), so the widget reflects the same state Claude Code itself
    uses.

Display

Standard rendering uses a constant 🎤 emoji plus an ASCII state indicator to keep widget width stable:

Format OFF ON
icon (default) 🎤 ○ 🎤 ◉
icon-text 🎤 off 🎤 on
text off on
word voice off voice on

With Nerd Font enabled (n in the editor), renders the microphone (U+F130) and microphone-slash (U+F131) glyphs from FontAwesome, providing a natively-differentiated icon for ON vs OFF.

Implementation notes

  • New helper getVoiceConfig(cwd) in src/utils/claude-settings.ts reads up to four candidate files in priority order — project-local first, user-global last — and short-circuits on the first one that
    defines voice.enabled. Typical case is a single read; layers without the field don't clobber lower-priority defined values; malformed JSON on a layer is silently skipped (treated as "no override on that
    layer").
  • Widget hides itself (render() returns null) when no candidate file exists at all, so users without a Claude installation see nothing rather than a misleading "off".
  • The voice.mode field (hold / toggle) is intentionally not exposed — kept available for a possible follow-up widget.

Test plan

  • bun test — full suite green (1195 tests; 17 new on getVoiceConfig covering layer precedence, malformed files, missing fields, and CLAUDE_CONFIG_DIR; 31 new on the widget covering all formats ×
    states × Nerd Font modes plus preview / raw value / null-config paths)
  • bun run lint — clean
  • In the TUI (bun run start): add the Voice Status widget, cycle formats with f, toggle Nerd Font with n, verify preview rendering
  • Toggle /voice in Claude Code with the widget visible in the statusline; confirm the rendered state flips between ON/OFF without restart
  • Verify graceful behavior when ~/.claude/settings.json is absent or malformed (widget hides, no crash)
  • Verify project-local voice.enabled correctly overrides a user-global value (manual: write <cwd>/.claude/settings.local.json with {"voice":{"enabled":false}} while user-global has true,
    confirm widget shows OFF)

@t-benoit t-benoit marked this pull request as ready for review May 6, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant