Add source editing toggle beside the outline button

This commit is contained in:
eezd committed 2026-10-21 19:14:45 +08:00
1 parent 73ff88c3c3
commit 15e036b9cd
7 files changed
+37 -11

No files matched your search

+1
View File
@@ -22,6 +22,7 @@ const paths = {
palette: 'M12 22a10 10 0 1 1 10-10c0 2.8-2.2 4-4 4h-2a2 2 0 0 0-1.5 3.3A1.7 1.7 0 0 1 12 22zM7.5 10.5h.01M12 7.5h.01M16.5 10.5h.01',
user: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z',
outline: 'M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01',
source: 'm8 6-6 6 6 6m8-12 6 6-6 6',
command: 'M18 3a3 3 0 0 0-3 3v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 0 0 0-6z',
info: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 11v6M12 7h.01',
}
+9 -4
View File
@@ -3,7 +3,7 @@ import { nextTick, ref } from 'vue'
import { commandTitle } from './commands'
import Icon from './Icon.vue'
import { noteTitle } from './paths'
import { closeTab, ws } from './workspace'
import { closeTab, toggleSourceMode, ws } from './workspace'
// 拖动排序:拖到某个标签的左半边插到它前面,右半边插到它后面
const TAB_TYPE = 'application/x-rawmark-tab'
@@ -103,9 +103,14 @@ function closeRight(path: string) {
<Icon name="close" :size="12" />
</button>
</div>
<button v-if="ws.active" class="rm-icon-btn rm-tabs-outline" :class="{ on: ws.outlineOpen }" :title="commandTitle('view.outline')" @click="ws.outlineOpen = !ws.outlineOpen">
<Icon name="outline" />
</button>
<div v-if="ws.active" class="rm-tabs-actions">
<button class="rm-icon-btn" :title="ws.sourceMode ? '关闭源码编辑' : '开启源码编辑'" :aria-pressed="ws.sourceMode" @mousedown.prevent @click="toggleSourceMode">
<Icon name="source" />
</button>
<button class="rm-icon-btn rm-tabs-outline" :class="{ on: ws.outlineOpen }" :title="commandTitle('view.outline')" @click="ws.outlineOpen = !ws.outlineOpen">
<Icon name="outline" />
</button>
</div>
<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' }">
+2 -1
View File
@@ -18,7 +18,7 @@ import { noteTitle } from './paths'
import { SIDEBAR_WIDTH, settings, updateSettings } from './settings'
import Sidebar from './Sidebar.vue'
import TabBar from './TabBar.vue'
import { activeTab, flushAll, readyToReload, startWorkspace, stopWorkspace, toast, ws } from './workspace'
import { activeTab, flushAll, readyToReload, startWorkspace, stopWorkspace, toast, toggleSourceMode, ws } from './workspace'
const emit = defineEmits<{ logout: [] }>()
const title = computed(() => (ws.active ? noteTitle(ws.active) : 'RawMark'))
@@ -110,6 +110,7 @@ onBeforeUnmount(() => {
<button class="rm-icon-btn" title="目录" @click="ws.sidebarOpen = true"><Icon name="menu" :size="20" /></button>
<button class="rm-mobile-title" title="切换笔记" @click="ws.switcher = ''">{{ title }}</button>
<span class="rm-save-state" :class="{ on: saving }" />
<button v-if="ws.active" class="rm-icon-btn" :title="ws.sourceMode ? '关闭源码编辑' : '开启源码编辑'" :aria-pressed="ws.sourceMode" @mousedown.prevent @click="toggleSourceMode"><Icon name="source" :size="20" /></button>
<button v-if="ws.active" class="rm-icon-btn" title="大纲" @click="ws.outlineOpen = !ws.outlineOpen"><Icon name="outline" :size="20" /></button>
<button class="rm-icon-btn" title="快速打开" @click="ws.switcher = ''"><Icon name="search" :size="20" /></button>
<button class="rm-icon-btn" title="命令" @click="ws.switcher = '>'"><Icon name="command" :size="20" /></button>
+3 -2
View File
@@ -55,6 +55,7 @@ button {
flex: none;
}
.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-text); }
.rm-icon-btn[aria-pressed="true"] { background: var(--rm-active); color: var(--rm-accent-strong); }
.rm-splash,
.rm-empty {
@@ -284,8 +285,8 @@ button {
}
.rm-tabs.empty { display: none; }
.rm-tabs-side,
.rm-tabs-outline { flex: none; align-self: center; margin-bottom: 4px; }
.rm-tabs-outline { margin-left: auto; position: sticky; right: 0; background: var(--rm-bg); }
.rm-tabs-actions { flex: none; align-self: center; margin-bottom: 4px; }
.rm-tabs-actions { display: flex; margin-left: auto; position: sticky; right: 0; background: var(--rm-bg); }
.rm-tabs-outline.on { color: var(--rm-accent-strong); }
.rm-tab.dragging { opacity: 0.5; }
.rm-tab.drop-before { box-shadow: inset 2px 0 0 var(--rm-accent); }
+15 -1
View File
@@ -5,7 +5,7 @@ import { EditorView } from '@codemirror/view'
import { nextTick, reactive, watch } from 'vue'
import { editorCommands } from '../editor/shortcuts'
import { refreshImages } from '../editor/livepreview'
import { createEditorState, insertImageFiles, pasteImages, languageCompartment, pluginCompartment, pluginEditorExtensions, pluginLanguage, preferencesCompartment } from '../editor'
import { createEditorState, insertImageFiles, pasteImages, languageCompartment, pluginCompartment, pluginEditorExtensions, pluginLanguage, preferencesCompartment, previewCompartment, previewExtensions } from '../editor'
import { handleShortcut, registerCommand } from './commands'
import { api, ApiError, encodePath, type Entry } from './api'
import { drafts } from './drafts'
@@ -73,6 +73,7 @@ export const ws = reactive({
sidebarCollapsed: layout.sidebarCollapsed ?? false,
// 大纲面板(电脑上在右侧常驻,手机上为浮层)
outlineOpen: layout.outlineOpen ?? false,
sourceMode: false,
// 最近打开的笔记,最新的在前
recent: JSON.parse(localStorage.getItem(RECENT_KEY) ?? '[]') as string[],
toasts: [] as Toast[],
@@ -149,6 +150,17 @@ export function editorView(): EditorView | null {
return view
}
export function toggleSourceMode() {
ws.sourceMode = !ws.sourceMode
const effects = previewCompartment.reconfigure(previewExtensions(ws.sourceMode))
for (const [path, state] of editorStates) {
if (path === ws.active && view) {
view.dispatch({ effects })
editorStates.set(path, view.state)
} else editorStates.set(path, state.update({ effects }).state)
}
}
/** 从相册/文件中选择图片插入到光标处 */
export function insertImagesFromPicker() {
const v = view
@@ -186,6 +198,7 @@ function buildState(path: string, doc: string): EditorState {
shortcuts: [],
spellcheck: settings.spellcheck,
tabSize: settings.tabSize,
sourceMode: ws.sourceMode,
resolveImage: (src) => {
const url = resolveAssetUrl(ref.path, src)
const entry = ws.entries.find((e) => url === '/api/v1/raw/' + encodePath(e.path))
@@ -1068,6 +1081,7 @@ function registerBuiltinCommands() {
{ id: 'app.settings', title: '设置', keys: ['Mod+,'], run: () => (ws.settings = 'index') },
{ id: 'view.sidebar', title: '显示/隐藏侧栏', keys: ['Mod+\\'], run: toggleSidebar },
{ id: 'view.outline', title: '显示/隐藏大纲', keys: ['Mod+Shift+O'], when: hasNote, run: () => (ws.outlineOpen = !ws.outlineOpen) },
{ id: 'view.source', title: '开启/关闭源码编辑', when: hasNote, run: toggleSourceMode },
{ id: 'app.trash', title: '打开回收站', run: () => (ws.settings = 'trash') },
{ id: 'note.share', title: '分享当前笔记', when: hasNote, run: () => (ws.sharing = ws.active) },
{ id: 'app.images', title: '管理根目录图片', run: () => (ws.images = '') },