linnet/frontend/src/stores/settings.ts
pyr0ball b92285b4b3 feat: tier-aware sessions, session pinning, translation, audio settings, voice status
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
2026-06-24 15:25:46 -07:00

72 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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; 18 = 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 };
});