| |
| |
| |
| |
| |
| |
| |
|
|
| import { isDesktopRuntime } from './runtime'; |
|
|
| const STORAGE_KEY_BROWSER_MODEL = 'wm-ai-flow-browser-model'; |
| const STORAGE_KEY_CLOUD_LLM = 'wm-ai-flow-cloud-llm'; |
| const STORAGE_KEY_MAP_NEWS_FLASH = 'wm-map-news-flash'; |
| const STORAGE_KEY_HEADLINE_MEMORY = 'wm-headline-memory'; |
| const STORAGE_KEY_BADGE_ANIMATION = 'wm-badge-animation'; |
| const STORAGE_KEY_STREAM_QUALITY = 'wm-stream-quality'; |
| const EVENT_NAME = 'ai-flow-changed'; |
| const STREAM_QUALITY_EVENT = 'stream-quality-changed'; |
|
|
| export interface AiFlowSettings { |
| browserModel: boolean; |
| cloudLlm: boolean; |
| mapNewsFlash: boolean; |
| headlineMemory: boolean; |
| badgeAnimation: boolean; |
| } |
|
|
| function readBool(key: string, defaultValue: boolean): boolean { |
| try { |
| const raw = localStorage.getItem(key); |
| if (raw === null) return defaultValue; |
| return raw === 'true'; |
| } catch { |
| return defaultValue; |
| } |
| } |
|
|
| function writeBool(key: string, value: boolean): void { |
| try { |
| localStorage.setItem(key, String(value)); |
| } catch { |
| |
| } |
| } |
|
|
| const STORAGE_KEY_MAP: Record<keyof AiFlowSettings, string> = { |
| browserModel: STORAGE_KEY_BROWSER_MODEL, |
| cloudLlm: STORAGE_KEY_CLOUD_LLM, |
| mapNewsFlash: STORAGE_KEY_MAP_NEWS_FLASH, |
| headlineMemory: STORAGE_KEY_HEADLINE_MEMORY, |
| badgeAnimation: STORAGE_KEY_BADGE_ANIMATION, |
| }; |
|
|
| const DEFAULTS: AiFlowSettings = { |
| browserModel: false, |
| cloudLlm: true, |
| mapNewsFlash: true, |
| headlineMemory: false, |
| badgeAnimation: false, |
| }; |
|
|
| export function getAiFlowSettings(): AiFlowSettings { |
| return { |
| browserModel: readBool(STORAGE_KEY_BROWSER_MODEL, DEFAULTS.browserModel), |
| cloudLlm: readBool(STORAGE_KEY_CLOUD_LLM, DEFAULTS.cloudLlm), |
| mapNewsFlash: readBool(STORAGE_KEY_MAP_NEWS_FLASH, DEFAULTS.mapNewsFlash), |
| headlineMemory: readBool(STORAGE_KEY_HEADLINE_MEMORY, DEFAULTS.headlineMemory), |
| badgeAnimation: readBool(STORAGE_KEY_BADGE_ANIMATION, DEFAULTS.badgeAnimation), |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function isHeadlineMemoryEnabled(): boolean { |
| const headline = readBool(STORAGE_KEY_HEADLINE_MEMORY, DEFAULTS.headlineMemory); |
| if (!headline) return false; |
| if (isDesktopRuntime()) return true; |
| const browser = readBool(STORAGE_KEY_BROWSER_MODEL, DEFAULTS.browserModel); |
| return browser; |
| } |
|
|
| export function setAiFlowSetting(key: keyof AiFlowSettings, value: boolean): void { |
| writeBool(STORAGE_KEY_MAP[key], value); |
| window.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: { key } })); |
| } |
|
|
| export function isAnyAiProviderEnabled(): boolean { |
| const s = getAiFlowSettings(); |
| return s.cloudLlm || s.browserModel; |
| } |
|
|
| export function subscribeAiFlowChange(cb: (changedKey?: keyof AiFlowSettings) => void): () => void { |
| const handler = (e: Event) => { |
| const detail = (e as CustomEvent).detail as { key?: keyof AiFlowSettings } | undefined; |
| cb(detail?.key); |
| }; |
| window.addEventListener(EVENT_NAME, handler); |
| return () => window.removeEventListener(EVENT_NAME, handler); |
| } |
|
|
| |
|
|
| export type StreamQuality = 'auto' | 'small' | 'medium' | 'large' | 'hd720'; |
|
|
| export const STREAM_QUALITY_OPTIONS: { value: StreamQuality; label: string }[] = [ |
| { value: 'auto', label: 'Auto' }, |
| { value: 'small', label: 'Low (360p)' }, |
| { value: 'medium', label: 'Medium (480p)' }, |
| { value: 'large', label: 'High (480p+)' }, |
| { value: 'hd720', label: 'HD (720p)' }, |
| ]; |
|
|
| export function getStreamQuality(): StreamQuality { |
| try { |
| const raw = localStorage.getItem(STORAGE_KEY_STREAM_QUALITY); |
| if (raw && ['auto', 'small', 'medium', 'large', 'hd720'].includes(raw)) return raw as StreamQuality; |
| } catch { } |
| return 'auto'; |
| } |
|
|
| export function setStreamQuality(quality: StreamQuality): void { |
| try { |
| localStorage.setItem(STORAGE_KEY_STREAM_QUALITY, quality); |
| } catch { } |
| window.dispatchEvent(new CustomEvent(STREAM_QUALITY_EVENT, { detail: { quality } })); |
| } |
|
|
| export function subscribeStreamQualityChange(cb: (quality: StreamQuality) => void): () => void { |
| const handler = (e: Event) => { |
| const detail = (e as CustomEvent).detail as { quality: StreamQuality }; |
| cb(detail.quality); |
| }; |
| window.addEventListener(STREAM_QUALITY_EVENT, handler); |
| return () => window.removeEventListener(STREAM_QUALITY_EVENT, handler); |
| } |
|
|