diff --git a/CHANGELOG.md b/CHANGELOG.md index 326725b..3a7f618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.2] - 2026-06-12 + +### Added + +- πŸŽ™οΈ **Voice memos.** Record audio memos directly from the "+" menu or βŒ˜β‡§M. Recordings are saved to your workspace as audio files with an auto-generated markdown transcript. Uses a save-first architecture with IndexedDB backup so your recording is never lost, even if the network drops. Transcription is powered by any OpenAI-compatible STT API (Whisper, etc.), configurable in Settings > Audio. +- πŸ”Š **Audio admin settings.** New Audio tab in admin settings with separate controls for enabling voice memos, toggling auto-transcription, and configuring STT credentials (base URL, API key, model). + +### Changed + +- 🎨 **Consistent admin input styling.** Standardized all input fields in the Configuration admin panel to match the Browser settings design. Uniform height, background, focus states, and label sizing across all admin tabs. + ## [0.3.1] - 2026-06-12 ### Added diff --git a/cptr/app.py b/cptr/app.py index 53dbea3..52bf426 100644 --- a/cptr/app.py +++ b/cptr/app.py @@ -7,6 +7,7 @@ from cptr.routers import ( admin_router, + audio_router, auth_router, automations_router, bridge_router, @@ -209,6 +210,7 @@ async def get_config(): # Routers app.include_router(admin_router) +app.include_router(audio_router) app.include_router(auth_router) app.include_router(automations_router) app.include_router(bridge_router) diff --git a/cptr/frontend/package.json b/cptr/frontend/package.json index 923b45b..3b44424 100644 --- a/cptr/frontend/package.json +++ b/cptr/frontend/package.json @@ -1,7 +1,7 @@ { "name": "frontend", "private": true, - "version": "0.3.1", + "version": "0.3.2", "type": "module", "scripts": { "dev": "vite dev", diff --git a/cptr/frontend/src/lib/components/Admin/AudioSettings.svelte b/cptr/frontend/src/lib/components/Admin/AudioSettings.svelte new file mode 100644 index 0000000..8ae2435 --- /dev/null +++ b/cptr/frontend/src/lib/components/Admin/AudioSettings.svelte @@ -0,0 +1,152 @@ + + +
+

Audio

+ + {#if loading} +
+ {:else} + +

Voice Memos

+ +
+ +

+ Record voice memos from the "+" menu. +

+ + +

+ {transcribeEnabled ? 'Recordings are transcribed to markdown via STT.' : 'Recordings are saved as audio only.'} +

+ +
+ Recording quality + +
+

+ {quality === 'high' ? 'Best quality, larger files.' : quality === 'medium' ? 'Balanced quality and size.' : 'Smallest files, optimized for speech.'} +

+
+ + +

Speech-to-Text

+ +
+
+ + +
+
+ + +
+
+ + +
+

+ Compatible with OpenAI's audio/transcriptions API. +

+
+ + +
+ +
+ {/if} +
diff --git a/cptr/frontend/src/lib/components/Admin/Settings.svelte b/cptr/frontend/src/lib/components/Admin/Settings.svelte index bafd500..592f7c5 100644 --- a/cptr/frontend/src/lib/components/Admin/Settings.svelte +++ b/cptr/frontend/src/lib/components/Admin/Settings.svelte @@ -127,12 +127,12 @@

{$t('admin.webAutoHint')}

{:else if provider === 'exa'}
- saveKey('web.exa_api_key', exaKey)} @@ -144,12 +144,12 @@
{:else if provider === 'tavily'}
- saveKey('web.tavily_api_key', tavilyKey)} @@ -161,12 +161,12 @@
{:else if provider === 'brave'}
- saveKey('web.brave_api_key', braveKey)} @@ -182,12 +182,12 @@

{:else if provider === 'perplexity'}
- saveKey('web.perplexity_api_key', perplexityKey)} @@ -200,12 +200,12 @@ {:else if provider === 'chat_completions'}
- saveKey('web.chat_completions_base_url', ccBaseUrl)} @@ -213,12 +213,12 @@ />
- saveKey('web.chat_completions_api_key', ccKey)} @@ -226,12 +226,12 @@ />
- saveKey('web.chat_completions_model', ccModel)} diff --git a/cptr/frontend/src/lib/components/GroupTabBar.svelte b/cptr/frontend/src/lib/components/GroupTabBar.svelte index 3db0391..cf4e553 100644 --- a/cptr/frontend/src/lib/components/GroupTabBar.svelte +++ b/cptr/frontend/src/lib/components/GroupTabBar.svelte @@ -21,12 +21,14 @@ } from '$lib/stores'; import { openChatTab } from '$lib/stores'; import { chatEnabled, streamingChatTabs } from '$lib/stores/chat'; + import { voiceMemosEnabled, showVoiceMemo } from '$lib/stores/audio'; import { keybindings, formatChord } from '$lib/stores/keybindings'; import Icon from './Icon.svelte'; import Spinner from './common/Spinner.svelte'; import DropdownMenu from './DropdownMenu.svelte'; import { tooltip } from '$lib/tooltip'; import { t } from '$lib/i18n'; + import VoiceMemoModal from './VoiceMemoModal.svelte'; interface Props { group: EditorGroup; @@ -159,7 +161,19 @@ onclick: () => { openTerminalTab(group.id); } - } + }, + ...($voiceMemosEnabled + ? [ + { + label: 'Voice Memo', + icon: 'microphone', + shortcut: formatChord($keybindings.voiceMemo), + onclick: () => { + showVoiceMemo.set(true); + } + } + ] + : []) ]); const contextMenuItems = $derived.by(() => { @@ -391,6 +405,14 @@ /> {/if} +{#if $showVoiceMemo} + showVoiceMemo.set(false)} + /> +{/if} +