mirror of
https://github.com/purocean/yn.git
synced 2026-09-24 15:46:02 +08:00
feat: 单独保存预览窗口滚动条位置
This commit is contained in:
@@ -5,19 +5,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { debounce } from 'lodash-es'
|
||||
import { defineComponent, nextTick, onBeforeMount, onMounted, ref, toRefs, watch } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useBus } from '@fe/support/bus'
|
||||
import Storage from '@fe/utils/storage'
|
||||
import { isEncrypted, saveDoc, toUri } from '@fe/context/document'
|
||||
import { setScrollToTop, whenEditorReady } from '@fe/context/editor'
|
||||
import { getActionHandler } from '@fe/context/action'
|
||||
import { Doc } from '@fe/support/types'
|
||||
import { whenEditorReady } from '@fe/context/editor'
|
||||
import type { Doc } from '@fe/support/types'
|
||||
import MonacoEditor from './MonacoEditor.vue'
|
||||
|
||||
const FILE_POSITION_KEY = 'filePosition'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'editor',
|
||||
components: { MonacoEditor },
|
||||
@@ -32,29 +27,12 @@ export default defineComponent({
|
||||
|
||||
const getEditor = () => refEditor.value
|
||||
|
||||
function saveFileOpenPosition (top: number) {
|
||||
if (currentFile.value) {
|
||||
const map = Storage.get(FILE_POSITION_KEY, {})
|
||||
map[`${currentFile.value.repo}|${currentFile.value.path}`] = top
|
||||
Storage.set(FILE_POSITION_KEY, map)
|
||||
}
|
||||
}
|
||||
|
||||
const saveFileOpenPositionDebounce = debounce((line: number) => {
|
||||
saveFileOpenPosition(line)
|
||||
}, 1000)
|
||||
|
||||
function setCurrentValue ({ uri, value }: { uri: string; value: any}) {
|
||||
if (toUri(currentFile.value) === uri) {
|
||||
store.commit('setCurrentContent', value)
|
||||
}
|
||||
}
|
||||
|
||||
function syncScrollView (line: number, top: number) {
|
||||
getActionHandler('view.reveal-line')(line)
|
||||
saveFileOpenPositionDebounce(top)
|
||||
}
|
||||
|
||||
function clearTimer () {
|
||||
if (timer) {
|
||||
window.clearTimeout(timer)
|
||||
@@ -108,14 +86,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
getEditor().setModel(toUri(current), current?.content ?? '\n')
|
||||
|
||||
if (current && current.status) {
|
||||
// 切换文件时候保留定位
|
||||
nextTick(() => {
|
||||
const top = Storage.get(FILE_POSITION_KEY, {})[`${current.repo}|${current.path}`] || 1
|
||||
setScrollToTop(top)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function resize () {
|
||||
@@ -142,12 +112,6 @@ export default defineComponent({
|
||||
changeFile(currentFile.value)
|
||||
}
|
||||
|
||||
editor.onDidScrollChange(() => {
|
||||
const line = Math.max(1, editor.getVisibleRanges()[0].startLineNumber - 2)
|
||||
const top = editor.getScrollTop()
|
||||
syncScrollView(line, top)
|
||||
})
|
||||
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, () => {
|
||||
saveFile()
|
||||
})
|
||||
|
||||
@@ -147,14 +147,19 @@ export default defineComponent({
|
||||
|
||||
function handleScroll (e: any) {
|
||||
scrollTop.value = e.target.scrollTop
|
||||
triggerHook('ON_VIEW_SCROLL', e)
|
||||
}
|
||||
|
||||
function syncScroll (line: number) {
|
||||
revealLineInCenter(line)
|
||||
}
|
||||
|
||||
function scrollTopTo (top: number) {
|
||||
refViewWrapper.value!.scrollTo(0, top)
|
||||
}
|
||||
|
||||
function scrollToTop () {
|
||||
refViewWrapper.value!.scrollTo(0, 0)
|
||||
scrollTopTo(0)
|
||||
syncScroll(1)
|
||||
}
|
||||
|
||||
@@ -168,7 +173,7 @@ export default defineComponent({
|
||||
|
||||
function revealLine (line: number) {
|
||||
if (line <= 1) {
|
||||
refViewWrapper.value!.scrollTo(0, 0)
|
||||
scrollTopTo(0)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -213,6 +218,7 @@ export default defineComponent({
|
||||
triggerHook('ON_VIEW_MOUNTED', { getViewDom })
|
||||
registerAction({ name: 'view.refresh', handler: render })
|
||||
registerAction({ name: 'view.reveal-line', handler: revealLine })
|
||||
registerAction({ name: 'view.scroll-top-to', handler: scrollTopTo })
|
||||
window.addEventListener('keydown', keydownHandler, true)
|
||||
bus.on('global.resize', resizeHandler)
|
||||
resizeHandler()
|
||||
@@ -221,6 +227,7 @@ export default defineComponent({
|
||||
onBeforeUnmount(() => {
|
||||
removeAction('view.refresh')
|
||||
removeAction('view.reveal-line')
|
||||
removeAction('view.scroll-top-to')
|
||||
window.removeEventListener('keydown', keydownHandler)
|
||||
bus.off('global.resize', resizeHandler)
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ import markdownMermaid from '@fe/plugins/markdown-mermaid'
|
||||
import markdownApplet from '@fe/plugins/markdown-applet'
|
||||
import markdownEcharts from '@fe/plugins/markdown-echarts'
|
||||
import markdownLuckysheet from '@fe/plugins/markdown-luckysheet'
|
||||
import viewSyncScroll from '@fe/plugins/view-sync-scroll'
|
||||
import syncScroll from '@fe/plugins/sync-scroll'
|
||||
|
||||
export default [
|
||||
documentHistoryStack,
|
||||
@@ -71,5 +71,5 @@ export default [
|
||||
markdownApplet,
|
||||
markdownEcharts,
|
||||
markdownLuckysheet,
|
||||
viewSyncScroll,
|
||||
syncScroll,
|
||||
]
|
||||
|
||||
@@ -33,6 +33,7 @@ const ctx = {
|
||||
statusBar,
|
||||
layout,
|
||||
editor,
|
||||
view,
|
||||
theme,
|
||||
storage,
|
||||
embed
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import { getActionHandler } from './action'
|
||||
|
||||
export type HookType = 'ON_VIEW_ELEMENT_CLICK'
|
||||
| 'ON_VIEW_ELEMENT_DBCLICK'
|
||||
| 'ON_VIEW_KEY_DOWN'
|
||||
| 'ON_VIEW_SCROLL'
|
||||
| 'ON_VIEW_RENDER'
|
||||
| 'ON_VIEW_RENDERED'
|
||||
| 'ON_VIEW_MOUNTED'
|
||||
| 'ON_VIEW_FILE_CHANGE'
|
||||
| 'ON_VIEW_BEFORE_CONVERT'
|
||||
|
||||
export type ActionName = 'view.refresh' | 'view.reveal-line'
|
||||
export type ActionName = 'view.refresh' | 'view.reveal-line' | 'view.scroll-top-to'
|
||||
|
||||
export function refresh () {
|
||||
getActionHandler('view.refresh')()
|
||||
}
|
||||
|
||||
export function revealLine (line: number) {
|
||||
getActionHandler('view.reveal-line')(line)
|
||||
}
|
||||
|
||||
export function scrollTopTo (top: number) {
|
||||
getActionHandler('view.scroll-top-to')(top)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import StateCore from 'markdown-it/lib/rules_core/state_core'
|
||||
import Token from 'markdown-it/lib/token'
|
||||
import { Plugin, Ctx } from '@fe/context/plugin'
|
||||
import store from '@fe/support/store'
|
||||
import { sleep } from '@fe/utils'
|
||||
import { isElectron, nodeRequire } from '@fe/utils/env'
|
||||
import { basename, dirname, join } from '@fe/utils/path'
|
||||
import { switchDoc } from '@fe/context/document'
|
||||
@@ -43,12 +44,11 @@ const handleLink = (link: HTMLAnchorElement) => {
|
||||
name: basename(path),
|
||||
repo: fileRepo,
|
||||
type: 'file'
|
||||
})
|
||||
|
||||
// 跳转锚点
|
||||
const hash = tmp.slice(1).join('#')
|
||||
if (hash) {
|
||||
setTimeout(() => {
|
||||
}).then(async () => {
|
||||
const hash = tmp.slice(1).join('#')
|
||||
// 跳转锚点
|
||||
if (hash) {
|
||||
await sleep(50)
|
||||
const el = document.getElementById(hash) ||
|
||||
document.getElementById(encodeURIComponent(hash))
|
||||
|
||||
@@ -59,8 +59,8 @@ const handleLink = (link: HTMLAnchorElement) => {
|
||||
}
|
||||
el.scrollIntoView()
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
} else if (href && href.startsWith('#')) { // 处理 TOC 跳转
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { debounce } from 'lodash-es'
|
||||
import type { Plugin, Ctx } from '@fe/context/plugin'
|
||||
import type { Doc } from '@fe/support/types'
|
||||
import { sleep } from '@fe/utils'
|
||||
|
||||
export default {
|
||||
name: 'sync-scroll',
|
||||
register: (ctx: Ctx) => {
|
||||
type ScrollTop = { editor?: number, view?: number }
|
||||
|
||||
const STORAGE_KEY = 'plugin.scroll-position'
|
||||
let enableSyncScroll = true
|
||||
|
||||
function saveScrollPosition (scrollTop: ScrollTop) {
|
||||
const key = ctx.doc.toUri(ctx.store.state.currentFile)
|
||||
const data: Record<string, ScrollTop> = ctx.storage.get(STORAGE_KEY, {})
|
||||
data[key] = { ...data[key], ...scrollTop }
|
||||
ctx.storage.set(STORAGE_KEY, data)
|
||||
}
|
||||
|
||||
// 切换文件后恢复滚动位置
|
||||
let xTimer: any
|
||||
ctx.bus.on('doc.switched', async (file?: Doc) => {
|
||||
if (file) {
|
||||
await sleep(0)
|
||||
|
||||
const key = ctx.doc.toUri(ctx.store.state.currentFile)
|
||||
const data: Record<string, ScrollTop> = ctx.storage.get(STORAGE_KEY, {})
|
||||
const position = data[key] || { editor: 0, view: 0 }
|
||||
|
||||
await ctx.editor.whenEditorReady()
|
||||
|
||||
clearTimeout(xTimer)
|
||||
enableSyncScroll = false
|
||||
ctx.editor.setScrollToTop(position.editor || 1)
|
||||
|
||||
if (typeof position.view === 'number') {
|
||||
await sleep(0)
|
||||
ctx.view.scrollTopTo(position.view)
|
||||
}
|
||||
|
||||
xTimer = setTimeout(() => {
|
||||
enableSyncScroll = true
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
|
||||
ctx.editor.whenEditorReady().then(({ editor }) => {
|
||||
const savePosition = debounce(saveScrollPosition, 500)
|
||||
editor.onDidScrollChange(() => {
|
||||
const line = Math.max(1, editor.getVisibleRanges()[0].startLineNumber - 2)
|
||||
const top = editor.getScrollTop()
|
||||
if (enableSyncScroll) {
|
||||
ctx.view.revealLine(line)
|
||||
}
|
||||
savePosition({ editor: top })
|
||||
})
|
||||
})
|
||||
|
||||
const savePosition = debounce(saveScrollPosition, 500)
|
||||
ctx.registerHook('ON_VIEW_SCROLL', (e: WheelEvent) => {
|
||||
if (ctx.store.state.currentFile?.status) {
|
||||
const { scrollTop } = e.target as HTMLElement
|
||||
savePosition({ view: scrollTop })
|
||||
}
|
||||
})
|
||||
|
||||
ctx.registerHook('ON_VIEW_ELEMENT_CLICK', async (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
if (
|
||||
ctx.store.state.showEditor &&
|
||||
!ctx.store.state.presentation &&
|
||||
target.classList.contains('source-line') &&
|
||||
window.getSelection()!.toString().length < 1
|
||||
) {
|
||||
ctx.editor.revealLineInCenter(parseInt(target.dataset.sourceLine || '0'))
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
} as Plugin
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Plugin, Ctx } from '@fe/context/plugin'
|
||||
|
||||
export default {
|
||||
name: 'view-sync-scroll',
|
||||
register: (ctx: Ctx) => {
|
||||
ctx.registerHook('ON_VIEW_ELEMENT_CLICK', async (e: MouseEvent) => {
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
if (
|
||||
ctx.store.state.showEditor &&
|
||||
!ctx.store.state.presentation &&
|
||||
target.classList.contains('source-line') &&
|
||||
window.getSelection()!.toString().length < 1
|
||||
) {
|
||||
ctx.editor.revealLineInCenter(parseInt(target.dataset.sourceLine || '0'))
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
} as Plugin
|
||||
@@ -42,3 +42,9 @@ export const getLogger = (subject: string) => {
|
||||
error: logger('error')
|
||||
}
|
||||
}
|
||||
|
||||
export function sleep (ms: number) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user