2026-02-19 10:22:27 +08:00
|
|
|
import { defineStore } from 'pinia'
|
|
|
|
|
import { ref, watch, computed } from 'vue'
|
|
|
|
|
import { translations } from '../utils/i18n'
|
|
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
// Settings persisted in localStorage under 'llm-in-text-settings'
|
|
|
|
|
const SETTINGS_KEY = 'llm-in-text-settings'
|
2026-02-19 10:22:27 +08:00
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
export const useSettingsStore = defineStore('settings', () => {
|
|
|
|
|
// --- Reactive state (all auto-tracked by watch below) ---
|
|
|
|
|
const theme = ref('system') // 'light' | 'dark' | 'system'
|
|
|
|
|
const modelThinking = ref('low') // 'low' | 'medium' | 'high'
|
|
|
|
|
const debounceMs = ref(1000) // 1000 - 5000
|
|
|
|
|
const proThinking = ref('medium') // 'low' | 'medium' | 'high'
|
2026-04-04 20:05:40 +08:00
|
|
|
const privacyMode = ref(true)
|
2026-02-19 10:22:27 +08:00
|
|
|
const language = ref('auto')
|
|
|
|
|
const currency = ref('auto')
|
|
|
|
|
const backgroundType = ref('default') // 'default' | 'warm' | 'reading' | 'image'
|
|
|
|
|
const backgroundImage = ref('')
|
2026-06-13 11:03:40 +08:00
|
|
|
const backgroundOpacity = ref(0.2) // 0.05 - 0.50
|
2026-04-07 23:38:23 +08:00
|
|
|
const ttsInstruct = ref('')
|
2026-02-19 10:22:27 +08:00
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
// --- Computed getters (derived from reactive state) ---
|
2026-02-19 10:22:27 +08:00
|
|
|
const uiLanguage = computed(() => {
|
2026-06-13 11:03:40 +08:00
|
|
|
if (language.value !== 'auto') return language.value
|
2026-02-19 10:22:27 +08:00
|
|
|
const sysLang = (navigator.language || navigator.userLanguage || 'en').split('-')[0]
|
2026-06-13 11:03:40 +08:00
|
|
|
return ['zh', 'en', 'ja', 'ko', 'de', 'fr'].includes(sysLang) ? sysLang : 'en'
|
2026-02-19 10:22:27 +08:00
|
|
|
})
|
|
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
const detectedTimezone = computed(() => Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC')
|
|
|
|
|
const t = computed(() => ({ ...translations['en'], ...(translations[uiLanguage.value] || {}) }))
|
|
|
|
|
const initialMarkdown = computed(() => translations[uiLanguage.value]?.initialMarkdown || translations['en']?.initialMarkdown || '')
|
2026-02-19 10:22:27 +08:00
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
// --- localStorage persistence ---
|
2026-02-19 10:22:27 +08:00
|
|
|
const loadSettings = () => {
|
|
|
|
|
try {
|
2026-06-13 11:03:40 +08:00
|
|
|
const stored = localStorage.getItem(SETTINGS_KEY)
|
|
|
|
|
if (!stored) return
|
|
|
|
|
const data = JSON.parse(stored)
|
|
|
|
|
// Restore each field with type guards
|
|
|
|
|
if (data.theme) theme.value = data.theme
|
|
|
|
|
if (data.modelThinking) modelThinking.value = data.modelThinking
|
|
|
|
|
if (data.debounceMs) debounceMs.value = data.debounceMs
|
|
|
|
|
if (data.proThinking) proThinking.value = data.proThinking
|
|
|
|
|
if (typeof data.privacyMode === 'boolean') privacyMode.value = data.privacyMode
|
|
|
|
|
if (data.language) language.value = data.language
|
|
|
|
|
if (data.currency) currency.value = data.currency
|
|
|
|
|
if (data.backgroundType === 'color') backgroundType.value = 'default'
|
|
|
|
|
else if (data.backgroundType) backgroundType.value = data.backgroundType
|
|
|
|
|
if (data.backgroundImage) backgroundImage.value = data.backgroundImage
|
|
|
|
|
if (data.backgroundOpacity) backgroundOpacity.value = data.backgroundOpacity
|
|
|
|
|
if (typeof data.ttsInstruct === 'string') ttsInstruct.value = data.ttsInstruct
|
|
|
|
|
} catch { /* use defaults */ }
|
2026-02-19 10:22:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const saveSettings = () => {
|
|
|
|
|
try {
|
2026-06-13 11:03:40 +08:00
|
|
|
localStorage.setItem(SETTINGS_KEY, JSON.stringify({
|
|
|
|
|
theme: theme.value, modelThinking: modelThinking.value,
|
|
|
|
|
debounceMs: debounceMs.value, proThinking: proThinking.value,
|
|
|
|
|
privacyMode: privacyMode.value, language: language.value,
|
|
|
|
|
currency: currency.value, backgroundType: backgroundType.value,
|
|
|
|
|
backgroundImage: backgroundImage.value, backgroundOpacity: backgroundOpacity.value,
|
2026-04-07 23:38:23 +08:00
|
|
|
ttsInstruct: ttsInstruct.value,
|
2026-06-13 11:03:40 +08:00
|
|
|
}))
|
|
|
|
|
} catch { /* silently fail */ }
|
2026-02-19 10:22:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const resetSettings = () => {
|
2026-06-13 11:03:40 +08:00
|
|
|
theme.value = 'system'; modelThinking.value = 'low'; debounceMs.value = 1000
|
|
|
|
|
proThinking.value = 'medium'; privacyMode.value = false; language.value = 'auto'
|
|
|
|
|
currency.value = 'auto'; backgroundType.value = 'default'; backgroundImage.value = ''
|
|
|
|
|
backgroundOpacity.value = 0.2; ttsInstruct.value = ''; saveSettings()
|
2026-02-19 10:22:27 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
// Watch all reactive refs and auto-save on change
|
|
|
|
|
watch([theme, modelThinking, debounceMs, proThinking, privacyMode, language, currency
|
|
|
|
|
, backgroundType, backgroundImage, backgroundOpacity, ttsInstruct], saveSettings)
|
2026-02-19 10:22:27 +08:00
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
loadSettings() // Initialize from localStorage
|
2026-02-19 10:22:27 +08:00
|
|
|
|
2026-06-13 11:03:40 +08:00
|
|
|
return { theme, modelThinking, debounceMs, proThinking, privacyMode, language
|
|
|
|
|
, currency, backgroundType, backgroundImage, backgroundOpacity, ttsInstruct
|
|
|
|
|
, detectedTimezone, uiLanguage, t, initialMarkdown, resetSettings }
|
2026-02-19 10:22:27 +08:00
|
|
|
})
|