diff --git a/src/renderer/components/Outline.vue b/src/renderer/components/Outline.vue index 88bdcd6b..69e89861 100644 --- a/src/renderer/components/Outline.vue +++ b/src/renderer/components/Outline.vue @@ -1,12 +1,12 @@ @@ -82,6 +103,11 @@ export default defineComponent({ display: flex; border-radius: var(--g-border-radius); cursor: pointer; + overflow-wrap: break-word; + + &[data-activated="true"] { + background: rgba(0, 0, 0, 0.05); + } &:hover { background: rgba(0, 0, 0, 0.1); @@ -100,8 +126,14 @@ export default defineComponent({ } @include dark-theme { - .outline-toc > .heading:hover { - background: rgba(255, 255, 255, 0.14); + .outline-toc > .heading { + &[data-activated="true"] { + background: rgba(255, 255, 255, 0.07); + } + + &:hover { + background: rgba(255, 255, 255, 0.14); + } } } diff --git a/src/renderer/components/Preview.vue b/src/renderer/components/Preview.vue index e7a818d9..d1096efb 100644 --- a/src/renderer/components/Preview.vue +++ b/src/renderer/components/Preview.vue @@ -26,7 +26,7 @@
- +
TOP
@@ -53,6 +53,7 @@ import { DOM_ATTR_NAME } from '@fe/support/constant' import { useI18n } from '@fe/services/i18n' import { getLogger } from '@fe/utils' import type { RenderEnv } from '@fe/types' +import type { AppState } from '@fe/support/store' import Render from './Render.vue' import SvgIcon from './SvgIcon.vue' @@ -69,7 +70,7 @@ export default defineComponent({ setup () { useI18n() - const store = useStore() + const store = useStore() const pinOutline = ref(false) const { currentContent, currentFile, autoPreview, presentation, inComposition } = toRefs(store.state) @@ -181,23 +182,13 @@ export default defineComponent({ triggerHook('VIEW_SCROLL', { e }) } - function syncScroll (line: number) { - if (store.state.showEditor) { - revealLineInCenter(line) - } else { - refViewWrapper.value - ?.querySelector(`.markdown-body [${DOM_ATTR_NAME.SOURCE_LINE_START}="${line}"]`) - ?.scrollIntoView() - } - } - function scrollTopTo (top: number) { refViewWrapper.value!.scrollTo(0, top) } function scrollToTop () { scrollTopTo(0) - syncScroll(1) + revealLineInCenter(1) } function handleDbClick (e: MouseEvent) { @@ -347,7 +338,6 @@ export default defineComponent({ handleClick, handleDbClick, handleContextMenu, - syncScroll, } }, }) diff --git a/src/renderer/services/view.ts b/src/renderer/services/view.ts index 2c2691fd..49f9ed83 100644 --- a/src/renderer/services/view.ts +++ b/src/renderer/services/view.ts @@ -14,10 +14,11 @@ export type MenuItem = Components.ContextMenu.Item export type BuildContextMenu = (items: MenuItem[], e: MouseEvent) => void export type Heading = { tag: string; - class: string[]; + class: string; text: string; level: number; sourceLine: number; + activated?: boolean; } let tmpEnableSyncScroll = true @@ -178,22 +179,62 @@ export function getViewDom () { /** * Get Headings */ -export function getHeadings (): Heading[] { - const tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] - const dom = getViewDom() +export function getHeadings (withActivated = false): Heading[] { + const dom = getViewDom()?.parentElement if (!dom) { return [] } + const tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] + + let viewRect: DOMRect + let breakCheck = false + const isActivated = (nodes: NodeListOf, i: number) => { + if (!withActivated) { + return undefined + } + + if (breakCheck) { + return false + } + + if (!viewRect) { + viewRect = dom.getBoundingClientRect() + } + + const node = nodes[i] + const nodeRect = node.getBoundingClientRect() + + const bottom = viewRect.bottom / 3 * 2 + // in view + if (nodeRect.top >= viewRect.top && nodeRect.top < bottom) { + breakCheck = true + return true + } else if (nodeRect.top < viewRect.top) { // before view + const nextNode = i < nodes.length - 1 ? nodes[i + 1] : undefined + const res = !nextNode || nextNode.getBoundingClientRect().top > bottom + if (res) { + breakCheck = true + } + + return res + } else { // after view + breakCheck = true + } + + return false + } + const nodes = dom.querySelectorAll(tags.join(',')) - return Array.from(nodes).map(node => { + return Array.from(nodes).map((node, i) => { const tag = node.tagName.toLowerCase() return { tag, - class: [...node.classList, 'tag-' + tag], - text: node.innerText, + class: `heading ${node.className} tag-${tag}`, + text: node.textContent || '', level: tags.indexOf(tag), - sourceLine: parseInt(node.dataset.sourceLine || '0') + sourceLine: parseInt(node.dataset.sourceLine || '0'), + activated: isActivated(nodes, i), } }) } diff --git a/src/renderer/views/Main.vue b/src/renderer/views/Main.vue index 515690d5..139d348a 100644 --- a/src/renderer/views/Main.vue +++ b/src/renderer/views/Main.vue @@ -8,7 +8,7 @@