Sync speech recognition settings across devices (#1217)

* fix: sync STT settings across devices
Persist speech recognition preferences through shared OpenChamber settings so mobile and desktop stay in sync.

* fix bot review

* fix(voice): sync transcribe-on-stop setting

---------

Co-authored-by: Konstantin Zolin <kzolin@alfabank.ru>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
kostazol
2026-05-13 15:45:03 +03:00
committed by GitHub
co-authored by Konstantin Zolin Bohdan Triapitsyn
parent c1948fe691
commit a2c88f0142
4 changed files with 177 additions and 0 deletions
+7
View File
@@ -166,6 +166,13 @@ export type DesktopSettings = {
responseStyleEnabled?: boolean;
responseStylePreset?: 'concise' | 'detailed' | 'mentor' | 'pushback' | 'noFiller' | 'matchEnergy' | 'warmPeer' | 'custom';
responseStyleCustomInstructions?: string;
sttProvider?: 'browser' | 'server';
sttServerUrl?: string;
sttModel?: string;
sttLanguage?: string;
sttSilenceThresholdDb?: number;
sttSilenceHoldMs?: number;
sttTranscribeOnStop?: boolean;
};
type TauriGlobal = {
+72
View File
@@ -96,6 +96,27 @@ const persistToLocalStorage = (settings: DesktopSettings) => {
if (typeof settings.mobileKeyboardMode === 'string') {
setStoredMobileKeyboardMode(settings.mobileKeyboardMode);
}
if (settings.sttProvider === 'browser' || settings.sttProvider === 'server') {
localStorage.setItem('sttProvider', settings.sttProvider);
}
if (typeof settings.sttServerUrl === 'string') {
localStorage.setItem('sttServerUrl', settings.sttServerUrl);
}
if (typeof settings.sttModel === 'string') {
localStorage.setItem('sttModel', settings.sttModel);
}
if (typeof settings.sttLanguage === 'string') {
localStorage.setItem('sttLanguage', settings.sttLanguage);
}
if (typeof settings.sttSilenceThresholdDb === 'number' && Number.isFinite(settings.sttSilenceThresholdDb)) {
localStorage.setItem('sttSilenceThresholdDb', String(settings.sttSilenceThresholdDb));
}
if (typeof settings.sttSilenceHoldMs === 'number' && Number.isFinite(settings.sttSilenceHoldMs)) {
localStorage.setItem('sttSilenceHoldMs', String(settings.sttSilenceHoldMs));
}
if (typeof settings.sttTranscribeOnStop === 'boolean') {
localStorage.setItem('sttTranscribeOnStop', String(settings.sttTranscribeOnStop));
}
};
type PersistApi = {
@@ -316,6 +337,9 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
const configStore = typeof window !== 'undefined'
? window.__zustand_config_store__?.getState?.() ?? null
: null;
const configStoreApi = typeof window !== 'undefined'
? window.__zustand_config_store__ ?? null
: null;
const queueStore = useMessageQueueStore.getState();
if (typeof settings.showReasoningTraces === 'boolean' && settings.showReasoningTraces !== store.showReasoningTraces) {
@@ -474,6 +498,33 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
store.setMobileKeyboardMode(mode);
}
}
if (configStoreApi && configStore) {
const nextConfigState: Partial<typeof configStore> = {};
if ((settings.sttProvider === 'browser' || settings.sttProvider === 'server') && settings.sttProvider !== configStore.sttProvider) {
nextConfigState.sttProvider = settings.sttProvider;
}
if (typeof settings.sttServerUrl === 'string' && settings.sttServerUrl !== configStore.sttServerUrl) {
nextConfigState.sttServerUrl = settings.sttServerUrl;
}
if (typeof settings.sttModel === 'string' && settings.sttModel !== configStore.sttModel) {
nextConfigState.sttModel = settings.sttModel;
}
if (typeof settings.sttLanguage === 'string' && settings.sttLanguage !== configStore.sttLanguage) {
nextConfigState.sttLanguage = settings.sttLanguage;
}
if (typeof settings.sttSilenceThresholdDb === 'number' && Number.isFinite(settings.sttSilenceThresholdDb) && settings.sttSilenceThresholdDb !== configStore.sttSilenceThresholdDb) {
nextConfigState.sttSilenceThresholdDb = settings.sttSilenceThresholdDb;
}
if (typeof settings.sttSilenceHoldMs === 'number' && Number.isFinite(settings.sttSilenceHoldMs) && settings.sttSilenceHoldMs !== configStore.sttSilenceHoldMs) {
nextConfigState.sttSilenceHoldMs = settings.sttSilenceHoldMs;
}
if (typeof settings.sttTranscribeOnStop === 'boolean' && settings.sttTranscribeOnStop !== configStore.sttTranscribeOnStop) {
nextConfigState.sttTranscribeOnStop = settings.sttTranscribeOnStop;
}
if (Object.keys(nextConfigState).length > 0) {
configStoreApi.setState(nextConfigState);
}
}
if (Array.isArray(settings.favoriteModels)) {
const current = store.favoriteModels;
@@ -980,6 +1031,27 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
if (typeof candidate.responseStyleCustomInstructions === 'string') {
result.responseStyleCustomInstructions = candidate.responseStyleCustomInstructions;
}
if (candidate.sttProvider === 'browser' || candidate.sttProvider === 'server') {
result.sttProvider = candidate.sttProvider;
}
if (typeof candidate.sttServerUrl === 'string') {
result.sttServerUrl = candidate.sttServerUrl.trim();
}
if (typeof candidate.sttModel === 'string') {
result.sttModel = candidate.sttModel.trim();
}
if (typeof candidate.sttLanguage === 'string') {
result.sttLanguage = candidate.sttLanguage.trim();
}
if (typeof candidate.sttSilenceThresholdDb === 'number' && Number.isFinite(candidate.sttSilenceThresholdDb)) {
result.sttSilenceThresholdDb = candidate.sttSilenceThresholdDb;
}
if (typeof candidate.sttSilenceHoldMs === 'number' && Number.isFinite(candidate.sttSilenceHoldMs)) {
result.sttSilenceHoldMs = candidate.sttSilenceHoldMs;
}
if (typeof candidate.sttTranscribeOnStop === 'boolean') {
result.sttTranscribeOnStop = candidate.sttTranscribeOnStop;
}
return result;
};