feat(ui): improve readability and fix workspace regressions

Separate interface typography from document text and enlarge controls across the workspace and public reader. Keep tree menus within the viewport and make constrained dialogs scrollable.

Preserve nested image references, escape moved link paths, synchronize note creation and daily-note opening, and hydrate shared headings. Add regression coverage for settings and workspace behavior.
This commit is contained in:
eezd committed 2026-10-23 10:55:53 +08:00
1 parent 4d1339851b
commit a716794e41
31 files changed
+556 -246

No files matched your search

+33 -7
View File
@@ -15,6 +15,9 @@ const isOpen = computed(() => expanded.has(props.node.path))
const isActive = computed(() => (image.value ? ws.image : ws.active) === props.node.path)
const isRenaming = computed(() => ws.renaming === props.node.path)
const menuOpen = ref(false)
const row = ref<HTMLElement>()
const menuElement = ref<HTMLElement>()
const menuPosition = ref({ left: 0, top: 0 })
const dropTarget = ref(false)
const input = ref<HTMLInputElement>()
const draft = ref('')
@@ -30,8 +33,7 @@ function onTouchStart(e: TouchEvent) {
pressStart = { x: e.touches[0].clientX, y: e.touches[0].clientY }
pressTimer = setTimeout(() => {
longPressed = true
contextSelection(props.node.path)
menuOpen.value = true
showMenu()
}, LONG_PRESS_MS)
}
@@ -98,6 +100,21 @@ function showMenu() {
menuOpen.value = true
}
watch(menuOpen, async (open, _previous, onCleanup) => {
if (!open) return
const close = () => { menuOpen.value = false }
window.addEventListener('resize', close)
onCleanup(() => window.removeEventListener('resize', close))
await nextTick()
if (!menuOpen.value || !row.value || !menuElement.value) return
const anchor = row.value.getBoundingClientRect()
const { width, height } = menuElement.value.getBoundingClientRect()
menuPosition.value = {
left: Math.max(4, Math.min(anchor.right - width, innerWidth - width - 4)),
top: Math.max(4, Math.min(anchor.bottom, innerHeight - height - 4)),
}
})
function cancelRename() {
ws.renaming = null
}
@@ -129,6 +146,7 @@ function onDrop(e: DragEvent) {
<template>
<li class="rm-tree-item">
<div
ref="row"
class="rm-tree-row"
:class="{ active: isActive, selected: selectedPaths.has(node.path), drop: dropTarget, 'menu-open': menuOpen }"
:style="{ paddingLeft: depth * 14 + 8 + 'px' }"
@@ -146,7 +164,7 @@ function onDrop(e: DragEvent) {
@dragleave="dropTarget = false"
@drop="onDrop"
>
<span class="rm-tree-chevron" :class="{ open: isOpen, hidden: !node.dir }"><Icon name="chevron" :size="12" /></span>
<span class="rm-tree-chevron" :class="{ open: isOpen, hidden: !node.dir }"><Icon name="chevron" :size="16" /></span>
<input
v-if="isRenaming"
ref="input"
@@ -157,12 +175,15 @@ function onDrop(e: DragEvent) {
@keydown.esc.prevent="cancelRename"
@blur="commitRename"
/>
<Icon v-if="image" name="image" :size="14" />
<Icon v-if="image" name="image" :size="18" />
<span v-if="!isRenaming" class="rm-tree-name">{{ node.dir ? node.name : node.name.replace(/\.md$/, '') }}</span>
<button class="rm-icon-btn rm-tree-more" title="更多操作" @click.stop="menuOpen ? (menuOpen = false) : showMenu()">
<Icon name="more" />
</button>
<div v-if="menuOpen" class="rm-menu" @click.stop>
</div>
<Teleport to="body">
<div v-if="menuOpen" class="rm-menu-backdrop rm-tree-menu-backdrop" @click.stop="menuOpen = false" @contextmenu.prevent.stop="menuOpen = false" />
<div v-if="menuOpen" ref="menuElement" class="rm-menu rm-tree-menu" :style="{ left: menuPosition.left + 'px', top: menuPosition.top + 'px' }" @click.stop>
<template v-if="node.dir && !assetDir && selectedPaths.size === 1">
<button @click="runMenu('note')"><Icon name="noteAdd" />新建笔记</button>
<button @click="runMenu('folder')"><Icon name="folderAdd" />新建文件夹</button>
@@ -176,10 +197,15 @@ function onDrop(e: DragEvent) {
<button @click="runMenu('export')"><Icon name="note" />导出 Markdown</button>
<button v-if="!assetDir" class="danger" @click="runMenu('delete')"><Icon name="trash" />删除</button>
</div>
<div v-if="menuOpen" class="rm-menu-backdrop" @click.stop="menuOpen = false" @contextmenu.prevent.stop="menuOpen = false" />
</div>
</Teleport>
<ul v-if="node.dir && isOpen" class="rm-tree-list">
<TreeNode v-for="child in node.children" :key="child.path" :node="child" :depth="depth + 1" />
</ul>
</li>
</template>
<style scoped>
.rm-tree-menu { position: fixed; right: auto; z-index: 45; width: max-content; min-width: 0; max-width: calc(100vw - 8px); max-height: calc(100dvh - 8px); box-sizing: border-box; overflow: auto; }
.rm-tree-menu button { flex: none; white-space: nowrap; }
.rm-tree-menu-backdrop { z-index: 44; }
</style>