Add persistent pinned tabs protected from bulk closing
This commit is contained in:
1 parent
19d0feac23
commit
82d08b9d73
4 files changed
+19
-13
No files matched your search
@@ -11,6 +11,7 @@ const paths = {
|
||||
chevron: 'm9 6 6 6-6 6',
|
||||
more: 'M12 6h.01M12 12h.01M12 18h.01',
|
||||
close: 'M18 6 6 18M6 6l12 12',
|
||||
pin: 'M16 9V3H8v6l-3 3v2h6v7h2v-7h6v-2z',
|
||||
trash: 'M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6',
|
||||
logout: 'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9',
|
||||
restore: 'M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5',
|
||||
|
||||
+12
-10
@@ -3,7 +3,7 @@ import { nextTick, ref } from 'vue'
|
||||
import { commandTitle } from './commands'
|
||||
import Icon from './Icon.vue'
|
||||
import { noteTitle } from './paths'
|
||||
import { closeTab, toggleSourceMode, ws } from './workspace'
|
||||
import { closeTab, toggleSourceMode, ws, type Tab } from './workspace'
|
||||
|
||||
// 拖动排序:拖到某个标签的左半边插到它前面,右半边插到它后面
|
||||
const TAB_TYPE = 'application/x-rawmark-tab'
|
||||
@@ -42,20 +42,20 @@ function onDragEnd() {
|
||||
}
|
||||
|
||||
// ---- 右键菜单 ----
|
||||
const menu = ref<{ path: string; x: number; y: number } | null>(null)
|
||||
const menu = ref<{ tab: Tab; x: number; y: number } | null>(null)
|
||||
|
||||
const menuElement = ref<HTMLElement>()
|
||||
async function openMenu(e: MouseEvent, path: string) {
|
||||
menu.value = { path, x: e.clientX, y: e.clientY }
|
||||
async function openMenu(e: MouseEvent, tab: Tab) {
|
||||
menu.value = { tab, x: e.clientX, y: e.clientY }
|
||||
await nextTick()
|
||||
if (menu.value?.path !== path || !menuElement.value) return
|
||||
if (menu.value?.tab !== tab || !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) {
|
||||
const paths = ws.tabs.filter((t, i) => !keep(i, t.path)).map((t) => t.path)
|
||||
const paths = ws.tabs.filter((t, i) => !t.pinned && !keep(i, t.path)).map((t) => t.path)
|
||||
menu.value = null
|
||||
for (const p of paths) await closeTab(p)
|
||||
}
|
||||
@@ -91,12 +91,13 @@ function closeRight(path: string) {
|
||||
draggable="true"
|
||||
@click="ws.active = tab.path"
|
||||
@auxclick.prevent="$event.button === 1 && closeTab(tab.path)"
|
||||
@contextmenu.prevent="openMenu($event, tab.path)"
|
||||
@contextmenu.prevent="openMenu($event, tab)"
|
||||
@dragstart="onDragStart($event, tab.path)"
|
||||
@dragover="onDragOver($event, tab.path)"
|
||||
@drop="onDrop"
|
||||
@dragend="onDragEnd"
|
||||
>
|
||||
<span v-if="tab.pinned" class="rm-tab-pin" title="已固定"><Icon name="pin" :size="12" /></span>
|
||||
<span class="rm-tab-title">{{ noteTitle(tab.path) }}</span>
|
||||
<button class="rm-tab-close" :class="{ dirty: tab.dirty }" title="关闭" @click.stop="closeTab(tab.path)">
|
||||
<span class="rm-dirty-dot" />
|
||||
@@ -114,9 +115,10 @@ function closeRight(path: string) {
|
||||
<template v-if="menu">
|
||||
<div class="rm-menu-backdrop" @click="menu = null" @contextmenu.prevent="menu = null" />
|
||||
<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>
|
||||
<button @click="menu.tab.pinned = !menu.tab.pinned; menu = null">{{ menu.tab.pinned ? '取消固定' : '固定' }}</button>
|
||||
<button @click="closeTab(menu.tab.path); menu = null">关闭</button>
|
||||
<button @click="closeOthers(menu.tab.path)">关闭其他标签页</button>
|
||||
<button @click="closeRight(menu.tab.path)">关闭右侧标签页</button>
|
||||
</div>
|
||||
</template>
|
||||
</nav>
|
||||
|
||||
@@ -308,6 +308,7 @@ button {
|
||||
.rm-tab:hover { background: var(--rm-hover); }
|
||||
.rm-tab.active { background: var(--rm-active); color: var(--rm-text); }
|
||||
.rm-tab-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.rm-tab-pin { display: inline-flex; flex: none; color: var(--rm-accent-strong); }
|
||||
.rm-tab-close {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
|
||||
@@ -22,6 +22,7 @@ export type TabStatus = 'loading' | 'ready' | 'conflict' | 'deleted' | 'error'
|
||||
|
||||
export interface Tab {
|
||||
path: string
|
||||
pinned?: boolean
|
||||
// 最近一次与服务器一致的版本
|
||||
rev: string
|
||||
status: TabStatus
|
||||
@@ -957,7 +958,7 @@ export async function startWorkspace() {
|
||||
const settingsLoaded = loadSettings().catch((err) => toast('读取设置失败:' + errorText(err)))
|
||||
await refreshTree()
|
||||
await settingsLoaded
|
||||
const saved = JSON.parse(localStorage.getItem(TABS_KEY) ?? '{"paths":[],"active":null}') as { paths: string[]; active: string | null }
|
||||
const saved = JSON.parse(localStorage.getItem(TABS_KEY) ?? '{"paths":[],"active":null}') as { paths: string[]; active: string | null; pinned?: string[] }
|
||||
const exists = new Set(ws.entries.map((e) => e.path))
|
||||
await Promise.all(saved.paths.filter((p) => exists.has(p)).map(openNote))
|
||||
try {
|
||||
@@ -965,6 +966,7 @@ export async function startWorkspace() {
|
||||
} catch (err) {
|
||||
toast('恢复本地草稿失败:' + errorText(err))
|
||||
}
|
||||
for (const tab of ws.tabs) tab.pinned = saved.pinned?.includes(tab.path) ?? false
|
||||
ws.active = saved.active && tabOf(saved.active) ? saved.active : (ws.tabs[0]?.path ?? null)
|
||||
let eventQueue = Promise.resolve()
|
||||
let stopped = false
|
||||
@@ -991,8 +993,8 @@ export async function startWorkspace() {
|
||||
if (listenersInstalled) return
|
||||
listenersInstalled = true
|
||||
watch(
|
||||
() => [ws.tabs.map((t) => t.path), ws.active] as const,
|
||||
([paths, active]) => localStorage.setItem(TABS_KEY, JSON.stringify({ paths, active })),
|
||||
() => ({ paths: ws.tabs.map((t) => t.path), active: ws.active, pinned: ws.tabs.filter((t) => t.pinned).map((t) => t.path) }),
|
||||
(tabs) => localStorage.setItem(TABS_KEY, JSON.stringify(tabs)),
|
||||
// 启动期间(如等待目录刷新时)打开的标签页也要立即记录
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user