Backend: - tiers.py: real license validation via Heimdall (self-hosted key or cloud user_id), 30-min cache, BYOK exception for translation - session_store.py: create_session() accepts tier/user_id/window_ms/transcribe_lang/ num_speakers; tier-aware _should_reap() (Free: reap on subscriber leave, Paid: activity TTL); auto-snapshot paid sessions with history on reap; voice-ready probe on session start - services/translator.py: DeepL-backed label translation, CF-managed and BYOK key paths, per-session label cache - models/session.py, scene_event.py, service_event.py: new event types and session fields - api/snapshots.py: GET /sessions/history + POST /session/resume (Paid tier only) - api/dev.py: dev-only /dev/voice/stop|start|restart proxy to cf-orch agent - migrations/002_session_snapshots.sql: session_snapshots table Frontend: - ComposeBar.vue: pre-session settings panel (window, language, speakers, Elcor toggle) with slide transition; passes options to startSession() - stores/settings.ts: persistent user preferences (localStorage), survives page reload - stores/session.ts: voiceReady/voiceError/voiceWarning states; SceneEvent/AccentEvent/ ServiceEvent types; TTL timers for environ/queue badges; startSession() accepts opts - composables/useToneStream.ts: handlers for scene-event, accent-event, service-event SSE - NowPanel.vue: diarization speaker identity badge, acoustic scene badge, privacy risk indicator - HistoryStrip.vue: per-speaker colour chips in history strip - components/DevPanel.vue: dev-only cf-voice stop/restart controls + thread view toggle - App.vue: DevPanel in footer (DEV only), threadView flag switches v0.1.x/v0.2.x preview Tests: - tests/test_pinning.py: 20 tests covering tier-aware reaping, snapshot creation, and translator behaviour - tests/test_tiers.py: 17 tests covering key validation, cloud resolution, caching, require_paid() gate, BYOK exception - All 72 tests passing
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
// stores/settings.ts — persistent user preferences, survives page reload via localStorage.
|
||
//
|
||
// All settings here apply to the NEXT session start. Mid-session changes take
|
||
// effect on the following session (the ComposeBar shows current-session values
|
||
// as read-only badges while a session is running).
|
||
|
||
import { defineStore } from "pinia";
|
||
import { ref, watch } from "vue";
|
||
|
||
const STORAGE_KEY = "linnet:settings:v1";
|
||
|
||
interface PersistedSettings {
|
||
windowMs: number;
|
||
transcribeLang: string;
|
||
elcor: boolean;
|
||
numSpeakers: number; // 0 = auto-detect
|
||
threadView: boolean; // dev-only: preview v0.2.x thread UI (toggled from DevPanel)
|
||
}
|
||
|
||
const DEFAULTS: PersistedSettings = {
|
||
windowMs: 1000,
|
||
transcribeLang: "",
|
||
elcor: false,
|
||
numSpeakers: 0,
|
||
threadView: false,
|
||
};
|
||
|
||
function loadFromStorage(): PersistedSettings {
|
||
try {
|
||
const raw = localStorage.getItem(STORAGE_KEY);
|
||
if (raw) {
|
||
// Merge with defaults so new fields added after initial save get their defaults
|
||
return { ...DEFAULTS, ...(JSON.parse(raw) as Partial<PersistedSettings>) };
|
||
}
|
||
} catch {
|
||
// corrupt or missing — fall through to defaults
|
||
}
|
||
return { ...DEFAULTS };
|
||
}
|
||
|
||
export const useSettingsStore = defineStore("settings", () => {
|
||
const saved = loadFromStorage();
|
||
|
||
const windowMs = ref<number>(saved.windowMs);
|
||
const transcribeLang = ref<string>(saved.transcribeLang);
|
||
const elcor = ref<boolean>(saved.elcor);
|
||
// 0 = auto; 1–8 = fixed count passed to pyannote as min_speakers=max_speakers
|
||
const numSpeakers = ref<number>(saved.numSpeakers);
|
||
// Dev-only: show v0.2.x thread UI instead of v0.1.x NowPanel/HistoryStrip.
|
||
// Toggled from DevPanel; not exposed in user-facing settings.
|
||
const threadView = ref<boolean>(saved.threadView);
|
||
|
||
// Persist to localStorage whenever any setting changes
|
||
watch(
|
||
[windowMs, transcribeLang, elcor, numSpeakers, threadView],
|
||
() => {
|
||
localStorage.setItem(
|
||
STORAGE_KEY,
|
||
JSON.stringify({
|
||
windowMs: windowMs.value,
|
||
transcribeLang: transcribeLang.value,
|
||
elcor: elcor.value,
|
||
numSpeakers: numSpeakers.value,
|
||
threadView: threadView.value,
|
||
}),
|
||
);
|
||
},
|
||
{ deep: false },
|
||
);
|
||
|
||
return { windowMs, transcribeLang, elcor, numSpeakers, threadView };
|
||
});
|