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
@@ -18,6 +18,9 @@ export const createSettingsHelpers = (dependencies) => {
} = dependencies;
const PWA_APP_NAME_MAX_LENGTH = 64;
const STT_SERVER_URL_MAX_LENGTH = 2048;
const STT_MODEL_MAX_LENGTH = 256;
const STT_LANGUAGE_MAX_LENGTH = 64;
const PWA_ORIENTATION_VALUES = new Set(['system', 'portrait', 'landscape']);
const MOBILE_KEYBOARD_MODE_VALUES = new Set(['native', 'resize-content']);
@@ -599,6 +602,40 @@ export const createSettingsHelpers = (dependencies) => {
}
}
if (typeof candidate.sttProvider === 'string') {
const provider = candidate.sttProvider.trim();
if (provider === 'browser' || provider === 'server') {
result.sttProvider = provider;
}
}
if (typeof candidate.sttServerUrl === 'string') {
const trimmed = candidate.sttServerUrl.trim();
if (trimmed.length <= STT_SERVER_URL_MAX_LENGTH) {
result.sttServerUrl = trimmed;
}
}
if (typeof candidate.sttModel === 'string') {
const trimmed = candidate.sttModel.trim();
if (trimmed.length <= STT_MODEL_MAX_LENGTH) {
result.sttModel = trimmed;
}
}
if (typeof candidate.sttLanguage === 'string') {
const trimmed = candidate.sttLanguage.trim();
if (trimmed.length <= STT_LANGUAGE_MAX_LENGTH) {
result.sttLanguage = trimmed;
}
}
if (typeof candidate.sttSilenceThresholdDb === 'number' && Number.isFinite(candidate.sttSilenceThresholdDb)) {
result.sttSilenceThresholdDb = Math.max(-100, Math.min(0, candidate.sttSilenceThresholdDb));
}
if (typeof candidate.sttSilenceHoldMs === 'number' && Number.isFinite(candidate.sttSilenceHoldMs)) {
result.sttSilenceHoldMs = Math.max(250, Math.min(10000, Math.round(candidate.sttSilenceHoldMs)));
}
if (typeof candidate.sttTranscribeOnStop === 'boolean') {
result.sttTranscribeOnStop = candidate.sttTranscribeOnStop;
}
return result;
};