feat: add privacy mode, thinking levels, PWA support, and i18n

- Add privacy mode to hide IP and user preferences from AI requests
- Add model thinking levels (low/medium/high) for context analysis depth
- Add PWA support with service worker, manifest, and app icons
- Add SettingsPanel for user preferences (theme, background, language)
- Add i18n translations for en/zh/ja/ko/de/fr
- Add Pinia store for centralized settings management
- Update backend to support user preferences and thinking levels
- Update config to use absolute API URLs
This commit is contained in:
2026-02-19 10:22:27 +08:00
parent d2b64ad5d6
commit aa6133e3ed
24 changed files with 1291 additions and 222 deletions
+156
View File
@@ -0,0 +1,156 @@
import { defineStore } from 'pinia'
import { ref, watch, computed } from 'vue'
import { translations } from '../utils/i18n'
export const useSettingsStore = defineStore('settings', () => {
// --- State ---
// 1. Theme (handled partly by useTheme, but we keep a ref here for the UI)
const theme = ref('system') // 'light' | 'dark' | 'system'
// 2. Model Behavior
const modelThinking = ref('low') // 'low' | 'medium' | 'high'
const debounceMs = ref(1000) // 1000 - 5000
// 3. Privacy
const privacyMode = ref(false)
// 4. Preferences
const language = ref('auto')
const currency = ref('auto')
// const timezone = ref('auto') // removed
// 5. Background
const backgroundType = ref('default') // 'default' | 'warm' | 'reading' | 'image'
// const backgroundColor = ref('#ffffff') // removed
const backgroundImage = ref('')
const backgroundOpacity = ref(0.2) // 0.05 - 0.50
// --- Getters ---
const uiLanguage = computed(() => {
if (language.value !== 'auto') {
return language.value
}
const sysLang = (navigator.language || navigator.userLanguage || 'en').split('-')[0]
const supported = ['zh', 'en', 'ja', 'ko', 'de', 'fr']
return supported.includes(sysLang) ? sysLang : 'en'
})
const detectedTimezone = computed(() => {
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
})
// We can't easily detect currency by IP on the frontend without an external API.
// We will let the backend handle 'auto' currency if needed, or stick to auto label.
const t = computed(() => {
return translations[uiLanguage.value] || translations['en']
})
// --- Actions/Logic ---
// Load from localStorage
const loadSettings = () => {
try {
const stored = localStorage.getItem('llm-in-text-settings')
if (stored) {
const data = JSON.parse(stored)
if (data.theme) theme.value = data.theme
if (data.modelThinking) modelThinking.value = data.modelThinking
if (data.debounceMs) debounceMs.value = data.debounceMs
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.timezone) timezone.value = data.timezone // timezone legacy ignore
if (data.backgroundType) {
// migrate color to default if needed, or mapped
if (data.backgroundType === 'color') backgroundType.value = 'default'
else backgroundType.value = data.backgroundType
}
// if (data.backgroundColor) backgroundColor.value = data.backgroundColor // removed
if (data.backgroundImage) backgroundImage.value = data.backgroundImage
if (data.backgroundOpacity) backgroundOpacity.value = data.backgroundOpacity
}
} catch (e) {
console.error('Failed to load settings', e)
}
}
// Save to localStorage
const saveSettings = () => {
try {
const data = {
theme: theme.value,
modelThinking: modelThinking.value,
debounceMs: debounceMs.value,
privacyMode: privacyMode.value,
language: language.value,
currency: currency.value,
// timezone: timezone.value, // removed
backgroundType: backgroundType.value,
// backgroundColor: backgroundColor.value, // removed
backgroundImage: backgroundImage.value,
backgroundOpacity: backgroundOpacity.value,
}
localStorage.setItem('llm-in-text-settings', JSON.stringify(data))
} catch (e) {
console.error('Failed to save settings', e)
}
}
// Reset to defaults
const resetSettings = () => {
theme.value = 'system'
modelThinking.value = 'low'
debounceMs.value = 1000
privacyMode.value = false
language.value = 'auto'
currency.value = 'auto'
// timezone.value = 'auto' // removed
backgroundType.value = 'default'
// backgroundColor.value = '#ffffff' // removed
backgroundImage.value = ''
backgroundOpacity.value = 0.2
saveSettings()
}
// Auto-save watchers
watch(
[
theme,
modelThinking,
debounceMs,
privacyMode,
language,
currency,
// timezone, // removed
backgroundType,
// backgroundColor, // removed
backgroundImage,
backgroundOpacity,
],
() => {
saveSettings()
}
)
// Initialize
loadSettings()
return {
theme,
modelThinking,
debounceMs,
privacyMode,
language,
currency,
// timezone, // removed
backgroundType,
// backgroundColor, // removed
backgroundImage,
backgroundOpacity,
uiLanguage,
t,
resetSettings
}
})