mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-18 14:20:42 -07:00
feat(capture): dictation, personalities, 0.5.0
Ships the Capture release end to end. Global-hotkey dictation with synthetic paste into the focused app on macOS and Windows, an on-screen pill across recording / transcribing / refining, customizable push-to- talk and toggle chords, and an accessibility-permission prompt scoped to Settings → Captures with inline re-check feedback. Voice profiles gain optional personalities that power compose / rewrite / respond actions via a local Qwen3 LLM — shared with refinement, so there is one local LLM in the app, not two. Refinement hardened with deterministic Whisper-loop collapse before the LLM sees the transcript, per-capture flag snapshots for re-runs, and a ten-transcript evaluation harness across every bundled refinement size. Version bump 0.4.5 → 0.5.0. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
ed2eec591a
commit
87c582ad54
@@ -77,6 +77,7 @@ function makeProfileSchema(t: (key: string) => string) {
|
||||
name: z.string().min(1, t('profileForm.validation.nameRequired')).max(100),
|
||||
description: z.string().max(500).optional(),
|
||||
language: z.enum(LANGUAGE_CODES as [LanguageCode, ...LanguageCode[]]),
|
||||
personality: z.string().max(2000).optional(),
|
||||
sampleFile: z.instanceof(File).optional(),
|
||||
referenceText: z.string().max(1000).optional(),
|
||||
avatarFile: z.instanceof(File).optional(),
|
||||
@@ -100,6 +101,7 @@ type ProfileFormValues = {
|
||||
name: string;
|
||||
description?: string;
|
||||
language: LanguageCode;
|
||||
personality?: string;
|
||||
sampleFile?: File;
|
||||
referenceText?: string;
|
||||
avatarFile?: File;
|
||||
@@ -166,6 +168,7 @@ export function ProfileForm() {
|
||||
name: '',
|
||||
description: '',
|
||||
language: 'en',
|
||||
personality: '',
|
||||
sampleFile: undefined,
|
||||
referenceText: '',
|
||||
avatarFile: undefined,
|
||||
@@ -331,6 +334,7 @@ export function ProfileForm() {
|
||||
name: editingProfile.name,
|
||||
description: editingProfile.description || '',
|
||||
language: editingProfile.language as LanguageCode,
|
||||
personality: editingProfile.personality || '',
|
||||
sampleFile: undefined,
|
||||
referenceText: undefined,
|
||||
avatarFile: undefined,
|
||||
@@ -344,6 +348,7 @@ export function ProfileForm() {
|
||||
name: profileFormDraft.name,
|
||||
description: profileFormDraft.description,
|
||||
language: profileFormDraft.language as LanguageCode,
|
||||
personality: profileFormDraft.personality || '',
|
||||
referenceText: profileFormDraft.referenceText,
|
||||
sampleFile: undefined,
|
||||
avatarFile: undefined,
|
||||
@@ -368,6 +373,7 @@ export function ProfileForm() {
|
||||
name: '',
|
||||
description: '',
|
||||
language: 'en',
|
||||
personality: '',
|
||||
sampleFile: undefined,
|
||||
referenceText: undefined,
|
||||
avatarFile: undefined,
|
||||
@@ -493,6 +499,7 @@ export function ProfileForm() {
|
||||
description: data.description,
|
||||
language: data.language,
|
||||
default_engine: defaultEngine || undefined,
|
||||
personality: data.personality?.trim() ? data.personality.trim() : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -558,6 +565,7 @@ export function ProfileForm() {
|
||||
preset_engine: selectedPresetEngine,
|
||||
preset_voice_id: selectedPresetVoiceId,
|
||||
default_engine: selectedPresetEngine,
|
||||
personality: data.personality?.trim() ? data.personality.trim() : undefined,
|
||||
});
|
||||
|
||||
// Handle avatar upload if provided
|
||||
@@ -654,6 +662,7 @@ export function ProfileForm() {
|
||||
description: data.description,
|
||||
language: data.language,
|
||||
default_engine: defaultEngine || undefined,
|
||||
personality: data.personality?.trim() ? data.personality.trim() : undefined,
|
||||
});
|
||||
|
||||
// Convert non-WAV uploads to WAV so the backend can always use soundfile.
|
||||
@@ -756,6 +765,7 @@ export function ProfileForm() {
|
||||
name: values.name || '',
|
||||
description: values.description || '',
|
||||
language: values.language || 'en',
|
||||
personality: values.personality || '',
|
||||
referenceText: values.referenceText || '',
|
||||
sampleMode,
|
||||
};
|
||||
@@ -1182,6 +1192,27 @@ export function ProfileForm() {
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="personality"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Personality</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Optional. Who this voice is and how they talk. E.g. "a grumpy pirate who only speaks in nautical metaphors". Used by Compose, Rewrite, and the Speak API."
|
||||
className="min-h-[96px]"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Leave blank to hide the Compose and Rewrite buttons on the generate page.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="language"
|
||||
|
||||
Reference in New Issue
Block a user