Polish sidebar controls, font input and tab menus

This commit is contained in:
eezd committed 2026-10-21 15:57:33 +08:00
1 parent d253536009
commit 7344bb19dc
5 files changed
+27 -11

No files matched your search

+13 -4
View File
@@ -6,7 +6,7 @@ import Icon, { type IconName } from './Icon.vue'
import AppearanceSettings from './AppearanceSettings.vue'
import PluginsPanel from './PluginsPanel.vue'
import { installApp, installPrompt } from './pwa'
import { CONTENT_WIDTHS, FONT_SIZES, TAB_SIZES, loadSettings, settings, updateSettings, type Settings } from './settings'
import { CONTENT_WIDTHS, TAB_SIZES, loadSettings, settings, updateSettings, type Settings } from './settings'
import SharePanel from './SharePanel.vue'
import TokensPanel from './TokensPanel.vue'
import TrashPanel from './TrashPanel.vue'
@@ -73,6 +73,17 @@ async function change(patch: Partial<Settings>) {
}
}
function changeFont(event: Event) {
const input = event.target as HTMLInputElement
const value = input.valueAsNumber
if (!Number.isFinite(value) || value <= 0) {
toast('字号必须为正数')
input.value = String(settings.fontSize)
return
}
if (value !== settings.fontSize) void change({ fontSize: value })
}
// ---- 修改登录密码 ----
const pw = ref({ current: '', next: '', confirm: '' })
const pwError = ref('')
@@ -119,9 +130,7 @@ async function changePassword() {
<AppearanceSettings />
<div class="rm-setting">
<span class="rm-setting-label">字号</span>
<select :value="settings.fontSize" aria-label="字号" @change="change({ fontSize: Number(($event.target as HTMLSelectElement).value) })">
<option v-for="n in FONT_SIZES" :key="n" :value="n">{{ n }} px</option>
</select>
<label class="rm-font-input"><input type="number" step="any" :value="settings.fontSize" aria-label="字号" @blur="changeFont" @keydown.enter="($event.target as HTMLInputElement).blur()" /> px</label>
</div>
<div class="rm-setting">
<span class="rm-setting-label">正文宽度</span>
-1
View File
@@ -54,7 +54,6 @@ function onRootDrop(e: DragEvent) {
<p v-if="!tree.length" class="rm-tree-empty">还没有笔记</p>
</div>
<div class="rm-sidebar-foot">
<button class="rm-foot-btn" title="命令面板(F1)" @click="ws.switcher = '>'"><Icon name="command" />命令</button>
<button class="rm-foot-btn" title="设置(Ctrl+,)" @click="ws.settings = 'index'"><Icon name="settings" />设置</button>
<button class="rm-foot-btn" @click="emit('logout')"><Icon name="logout" />退出</button>
</div>
+10 -4
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import { nextTick, ref } from 'vue'
import Icon from './Icon.vue'
import { noteTitle } from './paths'
import { closeTab, ws } from './workspace'
@@ -43,8 +43,14 @@ function onDragEnd() {
// ---- 右键菜单 ----
const menu = ref<{ path: string; x: number; y: number } | null>(null)
function openMenu(e: MouseEvent, path: string) {
menu.value = { path, x: Math.min(e.clientX, innerWidth - 200), y: e.clientY }
const menuElement = ref<HTMLElement>()
async function openMenu(e: MouseEvent, path: string) {
menu.value = { path, x: e.clientX, y: e.clientY }
await nextTick()
if (menu.value?.path !== path || !menuElement.value) return
const { width, height } = menuElement.value.getBoundingClientRect()
menu.value.x = Math.max(4, Math.min(e.clientX, innerWidth - width - 4))
menu.value.y = Math.max(4, Math.min(e.clientY, innerHeight - height - 4))
}
async function closeMany(keep: (index: number, path: string) => boolean) {
@@ -101,7 +107,7 @@ function closeRight(path: string) {
</button>
<template v-if="menu">
<div class="rm-menu-backdrop" @click="menu = null" @contextmenu.prevent="menu = null" />
<div class="rm-menu rm-tab-menu" :style="{ left: menu.x + 'px', top: menu.y + 'px' }">
<div ref="menuElement" class="rm-menu rm-tab-menu" :style="{ left: menu.x + 'px', top: menu.y + 'px' }">
<button @click="closeMany((_, p) => p !== menu!.path)">关闭</button>
<button @click="closeOthers(menu.path)">关闭其他标签页</button>
<button @click="closeRight(menu.path)">关闭右侧标签页</button>
+3
View File
@@ -535,3 +535,6 @@ button {
.rm-plugin-tabs { display: flex; flex: none; gap: 4px; padding: 8px; overflow-x: auto; }
.rm-plugin-tabs .on { background: var(--rm-active); color: var(--rm-accent-strong); }
@media (pointer: coarse) { .rm-plugin-row .rm-btn, .rm-plugin-tabs .rm-btn { min-height: 44px; } }
.rm-font-input input { width: 80px; padding: 7px; border: 1px solid var(--rm-divider); border-radius: 6px; background: var(--rm-bg); color: var(--rm-text); }
.rm-tab-menu { right: auto; width: max-content; max-width: calc(100vw - 8px); }
+1 -2
View File
@@ -4,7 +4,6 @@ import { reactive } from 'vue'
import { api } from './api'
import { applyAppearance, appearancePaused, sanitizeAppearance, type Appearance } from '../themes/appearance'
export const FONT_SIZES = [14, 15, 16, 17, 18, 20, 22]
export const TAB_SIZES = [2, 4, 8]
// 0 为全宽
export const CONTENT_WIDTHS = [
@@ -27,7 +26,7 @@ export const settings = reactive<Settings>(sanitize(JSON.parse(localStorage.getI
function sanitize(raw: Record<string, unknown>): Settings {
const pick = <T>(v: unknown, allowed: T[], def: T) => (allowed.includes(v as T) ? (v as T) : def)
return {
fontSize: pick(raw.fontSize, FONT_SIZES, DEFAULTS.fontSize),
fontSize: typeof raw.fontSize === 'number' && Number.isFinite(raw.fontSize) && raw.fontSize > 0 ? raw.fontSize : DEFAULTS.fontSize,
spellcheck: typeof raw.spellcheck === 'boolean' ? raw.spellcheck : DEFAULTS.spellcheck,
tabSize: pick(raw.tabSize, TAB_SIZES, DEFAULTS.tabSize),
...sanitizeAppearance(raw),