feat: update AGENTS.md with new components and API details; enhance FileContent.vue with Plyr video support; add VideoPlayer.vue for video playback; improve MilkdownEditor.vue for video file handling; refine TTSPlayer.vue volume controls; optimize settings store for localStorage; implement video upload support in uploadBlock.js; create utility functions for Plyr player management.

This commit is contained in:
“ydy0615”
2026-06-13 11:03:40 +08:00
parent 2283020e51
commit 4813196b0a
16 changed files with 767 additions and 275 deletions
+52 -134
View File
@@ -2,165 +2,83 @@ import { defineStore } from 'pinia'
import { ref, watch, computed } from 'vue'
import { translations } from '../utils/i18n'
// Settings persisted in localStorage under 'llm-in-text-settings'
const SETTINGS_KEY = 'llm-in-text-settings'
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
const proThinking = ref('medium') // 'low' | 'medium' | 'high'
// 3. Privacy
// --- 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'
const privacyMode = ref(true)
// 4. Preferences
const language = ref('auto')
const currency = ref('auto')
// 5. Background
const backgroundType = ref('default') // 'default' | 'warm' | 'reading' | 'image'
const backgroundImage = ref('')
const backgroundOpacity = ref(0.2) // 0.05 - 0.50
// TTS Voice
const backgroundOpacity = ref(0.2) // 0.05 - 0.50
const ttsInstruct = ref('')
// --- Getters ---
// --- Computed getters (derived from reactive state) ---
const uiLanguage = computed(() => {
if (language.value !== 'auto') {
return language.value
}
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'
return ['zh', 'en', 'ja', 'ko', 'de', 'fr'].includes(sysLang) ? sysLang : 'en'
})
const detectedTimezone = computed(() => {
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
})
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 || '')
// 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['en'],
...(translations[uiLanguage.value] || {}),
}
})
const initialMarkdown = computed(() => {
const lang = uiLanguage.value
return translations[lang]?.initialMarkdown || translations['en']?.initialMarkdown || ''
})
// --- Actions/Logic ---
// Load from localStorage
// --- localStorage persistence ---
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 (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) {
if (data.backgroundType === 'color') backgroundType.value = 'default'
else 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 {
// Failed to load settings, use defaults
}
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 */ }
}
// Save to localStorage
const saveSettings = () => {
try {
const data = {
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,
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,
ttsInstruct: ttsInstruct.value,
}
localStorage.setItem('llm-in-text-settings', JSON.stringify(data))
} catch {
// Failed to save settings
}
}))
} catch { /* silently fail */ }
}
// Reset to defaults
const resetSettings = () => {
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()
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()
}
// Auto-save watchers
watch(
[
theme,
modelThinking,
debounceMs,
proThinking,
privacyMode,
language,
currency,
backgroundType,
backgroundImage,
backgroundOpacity,
ttsInstruct,
],
() => {
saveSettings()
}
)
// Watch all reactive refs and auto-save on change
watch([theme, modelThinking, debounceMs, proThinking, privacyMode, language, currency
, backgroundType, backgroundImage, backgroundOpacity, ttsInstruct], saveSettings)
// Initialize
loadSettings()
loadSettings() // Initialize from localStorage
return {
theme,
modelThinking,
debounceMs,
proThinking,
privacyMode,
language,
currency,
backgroundType,
backgroundImage,
backgroundOpacity,
ttsInstruct,
detectedTimezone,
uiLanguage,
t,
initialMarkdown,
resetSettings
}
return { theme, modelThinking, debounceMs, proThinking, privacyMode, language
, currency, backgroundType, backgroundImage, backgroundOpacity, ttsInstruct
, detectedTimezone, uiLanguage, t, initialMarkdown, resetSettings }
})