Skip to content

feat(studio): Voices panel + inline ElevenLabs key setup#2

Merged
cuio merged 1 commit intomainfrom
feat/elevenlabs-voices-panel
Apr 25, 2026
Merged

feat(studio): Voices panel + inline ElevenLabs key setup#2
cuio merged 1 commit intomainfrom
feat/elevenlabs-voices-panel

Conversation

@cuio
Copy link
Copy Markdown
Owner

@cuio cuio commented Apr 25, 2026

Summary

Phase 2 of the ElevenLabs integration. Adds a Studio sidebar UI for browsing voices, previewing them, picking a default, and setting the API key directly from the studio (no terminal trip required).

Studio

  • New Voices sidebar tab (`Cmd/Ctrl+3`) with search, refresh, and a scrollable card list. Each card has ▶ play/stop preview and a Use button to set the default voice. Active default is badged.
  • Inline empty-state form when no key is set: password input + Save key (Enter to submit). Shows a clean error if rejected, and a status footer (`Key: project .env [clear]`) once a key is configured.

Core / studio-api

New helpers in `@hyperframes/core/elevenlabs`:

  • `getElevenLabsKeyStatus(projectDir)` → `{hasKey, source}`; never leaks the value.
  • `writeElevenLabsKeyToEnvFile(envPath, value)` preserves surrounding lines, quotes values containing whitespace, writes mode `0600`, and accepts `null` to remove the entry.

New routes (registered alongside Phase 1):

  • `GET /api/projects/:id/elevenlabs/key` — status only
  • `PUT /api/projects/:id/elevenlabs/key` — writes/removes the key in `/.env`, then ensures `.env` is covered by `/.gitignore` so the one-click UX can't lead to a committed secret
  • `GET /api/projects/:id/elevenlabs/settings` — read `tts.defaultVoiceId` from `hyperframes.json`
  • `PATCH /api/projects/:id/elevenlabs/settings` — set/clear `tts.defaultVoiceId`

Notes for reviewers

  • No new runtime dependencies.
  • Key value never crosses the network in either direction. The `GET /key` endpoint returns presence + source only. The `PUT /key` endpoint takes a value but never echoes it back. Voice preview audio is still proxied so `xi-api-key` stays server-side.
  • `.env` write safety: mode `0600`, in-place rewrite preserves other vars/comments/blank lines, values with whitespace get quoted. `.gitignore` is touched best-effort (skipped silently if the project isn't a repo).
  • Source precedence is unchanged from Phase 1: shell env → `/.env` → `~/.hyperframes/.env`. The UI labels which one a key came from.

Test plan

  • Without any key set: open Voices tab → see inline form, paste `sk_…`, click Save → voices populate, status footer shows `Key: project .env`.
  • Click `clear` in the status footer → key removed from `/.env`, voices empty-state returns.
  • With key in shell env (`ELEVENLABS_API_KEY=… bun run dev`): footer shows `shell env` and the clear button is hidden.
  • Pick a voice → `Use` writes `tts.defaultVoiceId` into `hyperframes.json`. Reload → still selected.
  • Save the key → check `/.gitignore` includes `.env`.

🤖 Generated with Claude Code

Phase 2 of the ElevenLabs integration: a sidebar UI for browsing voices,
previewing them, picking a default, and setting the API key directly from
the studio (no terminal trip required).

Studio
- New "Voices" sidebar tab (Cmd/Ctrl+3) with search, refresh, and a
  scrollable card list. Each card has ▶ play/stop preview and a "Use"
  button to pick the default voice. Active default is badged.
- Inline empty-state form when no key is set: password-style input +
  Save key button (Enter to submit). Shows a clear error if the key is
  rejected, and a status footer ("Key: project .env [clear]") once a
  key is configured.

Core / studio-api
- New helpers in @hyperframes/core/elevenlabs:
  - getElevenLabsKeyStatus(projectDir) → {hasKey, source}; never leaks
    the value.
  - writeElevenLabsKeyToEnvFile(envPath, value) preserves surrounding
    lines, quotes values containing whitespace, writes mode 0600, and
    accepts null to remove the entry.
- New routes:
  - GET  /api/projects/:id/elevenlabs/key — status only
  - PUT  /api/projects/:id/elevenlabs/key — writes/removes from
    <project>/.env, then ensures .env is covered by .gitignore so the
    one-click UX can't accidentally lead to a committed secret.
  - GET/PATCH /api/projects/:id/elevenlabs/settings — persists
    tts.defaultVoiceId into hyperframes.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@cuio cuio merged commit 768d000 into main Apr 25, 2026
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