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:
committed by
GitHub
co-authored by
Konstantin Zolin
Bohdan Triapitsyn
parent
c1948fe691
commit
a2c88f0142
@@ -166,6 +166,13 @@ export type DesktopSettings = {
|
|||||||
responseStyleEnabled?: boolean;
|
responseStyleEnabled?: boolean;
|
||||||
responseStylePreset?: 'concise' | 'detailed' | 'mentor' | 'pushback' | 'noFiller' | 'matchEnergy' | 'warmPeer' | 'custom';
|
responseStylePreset?: 'concise' | 'detailed' | 'mentor' | 'pushback' | 'noFiller' | 'matchEnergy' | 'warmPeer' | 'custom';
|
||||||
responseStyleCustomInstructions?: string;
|
responseStyleCustomInstructions?: string;
|
||||||
|
sttProvider?: 'browser' | 'server';
|
||||||
|
sttServerUrl?: string;
|
||||||
|
sttModel?: string;
|
||||||
|
sttLanguage?: string;
|
||||||
|
sttSilenceThresholdDb?: number;
|
||||||
|
sttSilenceHoldMs?: number;
|
||||||
|
sttTranscribeOnStop?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TauriGlobal = {
|
type TauriGlobal = {
|
||||||
|
|||||||
@@ -96,6 +96,27 @@ const persistToLocalStorage = (settings: DesktopSettings) => {
|
|||||||
if (typeof settings.mobileKeyboardMode === 'string') {
|
if (typeof settings.mobileKeyboardMode === 'string') {
|
||||||
setStoredMobileKeyboardMode(settings.mobileKeyboardMode);
|
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 = {
|
type PersistApi = {
|
||||||
@@ -316,6 +337,9 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
|
|||||||
const configStore = typeof window !== 'undefined'
|
const configStore = typeof window !== 'undefined'
|
||||||
? window.__zustand_config_store__?.getState?.() ?? null
|
? window.__zustand_config_store__?.getState?.() ?? null
|
||||||
: null;
|
: null;
|
||||||
|
const configStoreApi = typeof window !== 'undefined'
|
||||||
|
? window.__zustand_config_store__ ?? null
|
||||||
|
: null;
|
||||||
const queueStore = useMessageQueueStore.getState();
|
const queueStore = useMessageQueueStore.getState();
|
||||||
|
|
||||||
if (typeof settings.showReasoningTraces === 'boolean' && settings.showReasoningTraces !== store.showReasoningTraces) {
|
if (typeof settings.showReasoningTraces === 'boolean' && settings.showReasoningTraces !== store.showReasoningTraces) {
|
||||||
@@ -474,6 +498,33 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
|
|||||||
store.setMobileKeyboardMode(mode);
|
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)) {
|
if (Array.isArray(settings.favoriteModels)) {
|
||||||
const current = store.favoriteModels;
|
const current = store.favoriteModels;
|
||||||
@@ -980,6 +1031,27 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
|
|||||||
if (typeof candidate.responseStyleCustomInstructions === 'string') {
|
if (typeof candidate.responseStyleCustomInstructions === 'string') {
|
||||||
result.responseStyleCustomInstructions = candidate.responseStyleCustomInstructions;
|
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;
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,12 +17,30 @@ import { parseModelIdentifier } from "@/lib/modelIdentifier";
|
|||||||
|
|
||||||
const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
||||||
const MODELS_DEV_PROXY_URL = "/api/openchamber/models-metadata";
|
const MODELS_DEV_PROXY_URL = "/api/openchamber/models-metadata";
|
||||||
|
const STT_SILENCE_THRESHOLD_DB_MIN = -100;
|
||||||
|
const STT_SILENCE_THRESHOLD_DB_MAX = 0;
|
||||||
|
const STT_SILENCE_HOLD_MS_MIN = 250;
|
||||||
|
const STT_SILENCE_HOLD_MS_MAX = 10000;
|
||||||
|
|
||||||
const FALLBACK_PROVIDER_ID = "opencode";
|
const FALLBACK_PROVIDER_ID = "opencode";
|
||||||
const FALLBACK_MODEL_ID = "big-pickle";
|
const FALLBACK_MODEL_ID = "big-pickle";
|
||||||
const GIT_UTILITY_PROVIDER_ID = "zen";
|
const GIT_UTILITY_PROVIDER_ID = "zen";
|
||||||
const GIT_UTILITY_PREFERRED_MODEL_ID = "big-pickle";
|
const GIT_UTILITY_PREFERRED_MODEL_ID = "big-pickle";
|
||||||
|
|
||||||
|
const normalizeSttSilenceThresholdDb = (value: unknown): number | undefined => {
|
||||||
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return Math.max(STT_SILENCE_THRESHOLD_DB_MIN, Math.min(STT_SILENCE_THRESHOLD_DB_MAX, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeSttSilenceHoldMs = (value: unknown): number | undefined => {
|
||||||
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return Math.max(STT_SILENCE_HOLD_MS_MIN, Math.min(STT_SILENCE_HOLD_MS_MAX, Math.round(value)));
|
||||||
|
};
|
||||||
|
|
||||||
interface OpenChamberDefaults {
|
interface OpenChamberDefaults {
|
||||||
defaultModel?: string;
|
defaultModel?: string;
|
||||||
defaultVariant?: string;
|
defaultVariant?: string;
|
||||||
@@ -32,6 +50,12 @@ interface OpenChamberDefaults {
|
|||||||
defaultFileViewerPreview?: boolean;
|
defaultFileViewerPreview?: boolean;
|
||||||
zenModel?: string;
|
zenModel?: string;
|
||||||
messageStreamTransport?: 'auto' | 'ws' | 'sse';
|
messageStreamTransport?: 'auto' | 'ws' | 'sse';
|
||||||
|
sttProvider?: 'browser' | 'server';
|
||||||
|
sttServerUrl?: string;
|
||||||
|
sttModel?: string;
|
||||||
|
sttLanguage?: string;
|
||||||
|
sttSilenceThresholdDb?: number;
|
||||||
|
sttSilenceHoldMs?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
||||||
@@ -53,6 +77,12 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
data?.messageStreamTransport === 'ws' || data?.messageStreamTransport === 'sse' || data?.messageStreamTransport === 'auto'
|
data?.messageStreamTransport === 'ws' || data?.messageStreamTransport === 'sse' || data?.messageStreamTransport === 'auto'
|
||||||
? data.messageStreamTransport
|
? data.messageStreamTransport
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const sttProvider = data?.sttProvider === 'browser' || data?.sttProvider === 'server' ? data.sttProvider : undefined;
|
||||||
|
const sttServerUrl = typeof data?.sttServerUrl === 'string' ? data.sttServerUrl.trim() : undefined;
|
||||||
|
const sttModel = typeof data?.sttModel === 'string' ? data.sttModel.trim() : undefined;
|
||||||
|
const sttLanguage = typeof data?.sttLanguage === 'string' ? data.sttLanguage.trim() : undefined;
|
||||||
|
const sttSilenceThresholdDb = normalizeSttSilenceThresholdDb(data?.sttSilenceThresholdDb);
|
||||||
|
const sttSilenceHoldMs = normalizeSttSilenceHoldMs(data?.sttSilenceHoldMs);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
||||||
@@ -63,6 +93,12 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
defaultFileViewerPreview,
|
defaultFileViewerPreview,
|
||||||
zenModel: zenModel.length > 0 ? zenModel : undefined,
|
zenModel: zenModel.length > 0 ? zenModel : undefined,
|
||||||
messageStreamTransport,
|
messageStreamTransport,
|
||||||
|
sttProvider,
|
||||||
|
sttServerUrl,
|
||||||
|
sttModel,
|
||||||
|
sttLanguage,
|
||||||
|
sttSilenceThresholdDb,
|
||||||
|
sttSilenceHoldMs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -89,6 +125,12 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
data?.messageStreamTransport === 'ws' || data?.messageStreamTransport === 'sse' || data?.messageStreamTransport === 'auto'
|
data?.messageStreamTransport === 'ws' || data?.messageStreamTransport === 'sse' || data?.messageStreamTransport === 'auto'
|
||||||
? data.messageStreamTransport
|
? data.messageStreamTransport
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const sttProvider = data?.sttProvider === 'browser' || data?.sttProvider === 'server' ? data.sttProvider : undefined;
|
||||||
|
const sttServerUrl = typeof data?.sttServerUrl === 'string' ? data.sttServerUrl.trim() : undefined;
|
||||||
|
const sttModel = typeof data?.sttModel === 'string' ? data.sttModel.trim() : undefined;
|
||||||
|
const sttLanguage = typeof data?.sttLanguage === 'string' ? data.sttLanguage.trim() : undefined;
|
||||||
|
const sttSilenceThresholdDb = normalizeSttSilenceThresholdDb(data?.sttSilenceThresholdDb);
|
||||||
|
const sttSilenceHoldMs = normalizeSttSilenceHoldMs(data?.sttSilenceHoldMs);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
|
||||||
@@ -99,6 +141,12 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
|
|||||||
defaultFileViewerPreview,
|
defaultFileViewerPreview,
|
||||||
zenModel: zenModel.length > 0 ? zenModel : undefined,
|
zenModel: zenModel.length > 0 ? zenModel : undefined,
|
||||||
messageStreamTransport,
|
messageStreamTransport,
|
||||||
|
sttProvider,
|
||||||
|
sttServerUrl,
|
||||||
|
sttModel,
|
||||||
|
sttLanguage,
|
||||||
|
sttSilenceThresholdDb,
|
||||||
|
sttSilenceHoldMs,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return {};
|
return {};
|
||||||
@@ -1300,6 +1348,12 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
settingsDefaultFileViewerPreview: openChamberDefaults.defaultFileViewerPreview ?? false,
|
settingsDefaultFileViewerPreview: openChamberDefaults.defaultFileViewerPreview ?? false,
|
||||||
settingsZenModel: resolvedZenModel,
|
settingsZenModel: resolvedZenModel,
|
||||||
settingsMessageStreamTransport: openChamberDefaults.messageStreamTransport ?? state.settingsMessageStreamTransport ?? 'auto',
|
settingsMessageStreamTransport: openChamberDefaults.messageStreamTransport ?? state.settingsMessageStreamTransport ?? 'auto',
|
||||||
|
sttProvider: openChamberDefaults.sttProvider ?? state.sttProvider,
|
||||||
|
sttServerUrl: openChamberDefaults.sttServerUrl ?? state.sttServerUrl,
|
||||||
|
sttModel: openChamberDefaults.sttModel ?? state.sttModel,
|
||||||
|
sttLanguage: openChamberDefaults.sttLanguage ?? state.sttLanguage,
|
||||||
|
sttSilenceThresholdDb: openChamberDefaults.sttSilenceThresholdDb ?? state.sttSilenceThresholdDb,
|
||||||
|
sttSilenceHoldMs: openChamberDefaults.sttSilenceHoldMs ?? state.sttSilenceHoldMs,
|
||||||
directoryScoped: {
|
directoryScoped: {
|
||||||
...state.directoryScoped,
|
...state.directoryScoped,
|
||||||
[directoryKey]: nextSnapshot,
|
[directoryKey]: nextSnapshot,
|
||||||
@@ -1837,6 +1891,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttProvider', provider);
|
localStorage.setItem('sttProvider', provider);
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttProvider: provider }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSttServerUrl: (url: string) => {
|
setSttServerUrl: (url: string) => {
|
||||||
@@ -1844,6 +1899,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttServerUrl', url);
|
localStorage.setItem('sttServerUrl', url);
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttServerUrl: url }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSttModel: (model: string) => {
|
setSttModel: (model: string) => {
|
||||||
@@ -1851,6 +1907,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttModel', model);
|
localStorage.setItem('sttModel', model);
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttModel: model }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSttLanguage: (lang: string) => {
|
setSttLanguage: (lang: string) => {
|
||||||
@@ -1858,6 +1915,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttLanguage', lang);
|
localStorage.setItem('sttLanguage', lang);
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttLanguage: lang }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSttSilenceThresholdDb: (db: number) => {
|
setSttSilenceThresholdDb: (db: number) => {
|
||||||
@@ -1865,6 +1923,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttSilenceThresholdDb', String(db));
|
localStorage.setItem('sttSilenceThresholdDb', String(db));
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttSilenceThresholdDb: db }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSttSilenceHoldMs: (ms: number) => {
|
setSttSilenceHoldMs: (ms: number) => {
|
||||||
@@ -1872,6 +1931,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttSilenceHoldMs', String(ms));
|
localStorage.setItem('sttSilenceHoldMs', String(ms));
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttSilenceHoldMs: ms }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSttTranscribeOnStop: (enabled: boolean) => {
|
setSttTranscribeOnStop: (enabled: boolean) => {
|
||||||
@@ -1879,6 +1939,7 @@ export const useConfigStore = create<ConfigStore>()(
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
localStorage.setItem('sttTranscribeOnStop', String(enabled));
|
localStorage.setItem('sttTranscribeOnStop', String(enabled));
|
||||||
}
|
}
|
||||||
|
updateDesktopSettings({ sttTranscribeOnStop: enabled }).catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
setShowMessageTTSButtons: (show: boolean) => {
|
setShowMessageTTSButtons: (show: boolean) => {
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ export const createSettingsHelpers = (dependencies) => {
|
|||||||
} = dependencies;
|
} = dependencies;
|
||||||
|
|
||||||
const PWA_APP_NAME_MAX_LENGTH = 64;
|
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 PWA_ORIENTATION_VALUES = new Set(['system', 'portrait', 'landscape']);
|
||||||
const MOBILE_KEYBOARD_MODE_VALUES = new Set(['native', 'resize-content']);
|
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;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user