From 06be0038cf8c94424d16669031f7209619500c81 Mon Sep 17 00:00:00 2001 From: eezd Date: Thu, 22 Oct 2026 15:01:39 +0800 Subject: [PATCH] feat(ui): add typography preferences and contextual formatting --- internal/api/themes.go | 2 +- web/src/app/AppearanceSettings.vue | 4 ++- web/src/app/MobileToolbar.vue | 39 ++++++++++++++++++++++++------ web/src/app/SettingsDialog.vue | 36 +++++++++++++++++++++++---- web/src/app/app.css | 35 +++++++++++++++++++++++++++ web/src/app/settings.ts | 14 ++++------- web/src/app/workspace.ts | 4 +-- web/src/editor/livepreview.ts | 3 +++ web/src/themes/base.css | 2 +- web/src/themes/typography.ts | 32 ++++++++++++++++++++++++ 10 files changed, 145 insertions(+), 26 deletions(-) create mode 100644 web/src/themes/typography.ts diff --git a/internal/api/themes.go b/internal/api/themes.go index 0325e4b..458dd9f 100644 --- a/internal/api/themes.go +++ b/internal/api/themes.go @@ -154,7 +154,7 @@ func (s *Server) publicAppearance(w http.ResponseWriter, r *http.Request) { return } out := map[string]any{} - for _, key := range []string{"colorMode", "fontSize", "contentWidth"} { + for _, key := range []string{"colorMode", "fontSize", "contentWidth", "textFont", "codeFont", "lineHeight", "paragraphSpacing", "glassOpacity"} { if value, ok := settings[key]; ok { out[key] = value } diff --git a/web/src/app/AppearanceSettings.vue b/web/src/app/AppearanceSettings.vue index 26af0a7..ebadbf8 100644 --- a/web/src/app/AppearanceSettings.vue +++ b/web/src/app/AppearanceSettings.vue @@ -1,6 +1,7 @@ diff --git a/web/src/app/SettingsDialog.vue b/web/src/app/SettingsDialog.vue index b8af265..8538411 100644 --- a/web/src/app/SettingsDialog.vue +++ b/web/src/app/SettingsDialog.vue @@ -4,6 +4,7 @@ import { api, ApiError } from './api' import { runCommand } from './commands' import Icon, { type IconName } from './Icon.vue' import AppearanceSettings from './AppearanceSettings.vue' +import { CODE_FONTS, TEXT_FONTS } from '../themes/typography' import PluginsPanel from './PluginsPanel.vue' import { installApp, installPrompt } from './pwa' import { CONTENT_WIDTHS, TAB_SIZES, loadSettings, settings, updateSettings, type Settings } from './settings' @@ -14,8 +15,8 @@ import TrashPanel from './TrashPanel.vue' import { toast, ws, type SettingsSection } from './workspace' const SECTIONS: { id: SettingsSection; title: string; icon: IconName }[] = [ - { id: 'appearance', title: '外观', icon: 'palette' }, - { id: 'editor', title: '编辑器', icon: 'edit' }, + { id: 'appearance', title: '常规与外观', icon: 'palette' }, + { id: 'editor', title: '编辑与阅读排版', icon: 'edit' }, { id: 'plugins', title: '插件', icon: 'command' }, { id: 'account', title: '账户', icon: 'user' }, { id: 'shortcuts', title: '快捷键', icon: 'command' }, @@ -30,6 +31,8 @@ if (!buildVersion) throw new Error('缺少应用构建版本信息') const section = computed(() => (ws.settings === 'index' ? 'appearance' : ws.settings!)) // 手机上一次只显示分组列表或分组内容 +const dailyTemplates = computed(() => ws.entries.filter(entry => !entry.dir && entry.path.endsWith('.md'))) +const missingDailyTemplate = computed(() => settings.dailyTemplate && !dailyTemplates.value.some(entry => entry.path === settings.dailyTemplate)) const showList = ref(ws.settings === 'index') const current = computed(() => SECTIONS.find((s) => s.id === section.value)!) @@ -53,7 +56,7 @@ async function change(patch: Partial) { await updateSettings(patch) } catch (err) { toast('保存设置失败:' + (err instanceof ApiError ? err.message : '网络错误,请检查连接')) - void loadSettings().catch(() => {}) + void loadSettings().catch(error => toast('重新读取设置失败:' + (error instanceof Error ? error.message : '网络错误'))) } } @@ -68,6 +71,15 @@ function changeFont(event: Event) { if (value !== settings.fontSize) void change({ fontSize: value }) } +function changeFolder(key: 'dailyFolder' | 'templatesFolder', event: Event) { + const input = event.target as HTMLInputElement + const path = input.value.trim() + if ((key === 'templatesFolder' && !path) || path && path.split('/').some(part => !part || part.startsWith('.') || /[\\\0]/.test(part))) { + toast('请输入有效的仓库相对目录'); input.value = settings[key]; return + } + void change({ [key]: path }) +} + // ---- 修改登录密码 ---- const pw = ref({ current: '', next: '', confirm: '' }) const pwError = ref('') @@ -95,23 +107,28 @@ async function changePassword() {