diff --git a/src/renderer/components/ActionBar.vue b/src/renderer/components/ActionBar.vue
new file mode 100644
index 00000000..49d3df07
--- /dev/null
+++ b/src/renderer/components/ActionBar.vue
@@ -0,0 +1,137 @@
+
+
+
+
{{$t(showOutline ? 'outline' : 'files')}}
+
+
+
+
+
+
+
diff --git a/src/renderer/components/Layout.vue b/src/renderer/components/Layout.vue
index d296c780..ea9349ea 100644
--- a/src/renderer/components/Layout.vue
+++ b/src/renderer/components/Layout.vue
@@ -231,6 +231,18 @@ export default defineComponent({
flex: none;
height: 100%;
border-right: 1px solid var(--g-color-86);
+ display: flex;
+ flex-direction: column;
+ background: var(--g-color-98);
+
+ ::v-deep(.action-bar .btns) {
+ opacity: 0;
+ transition: 0.2s;
+ }
+
+ &:hover ::v-deep(.action-bar .btns) {
+ opacity: 1;
+ }
}
.sash-right {
diff --git a/src/renderer/components/Outline.vue b/src/renderer/components/Outline.vue
new file mode 100644
index 00000000..88bdcd6b
--- /dev/null
+++ b/src/renderer/components/Outline.vue
@@ -0,0 +1,107 @@
+
+
+
Empty
+
+ {{ head.text }}
+ {{head.tag}}
+
+
+
+
+
+
+
diff --git a/src/renderer/components/Preview.vue b/src/renderer/components/Preview.vue
index 3f6853ff..e7a818d9 100644
--- a/src/renderer/components/Preview.vue
+++ b/src/renderer/components/Preview.vue
@@ -26,15 +26,7 @@
-
- {{ head.text }}
- {{head.tag}}
-
+
syncScroll(head.sourceLine)" />
TOP
@@ -55,7 +47,7 @@ import { registerHook, removeHook, triggerHook } from '@fe/core/hook'
import { registerAction, removeAction } from '@fe/core/action'
import { revealLineInCenter } from '@fe/services/editor'
import { showExport, toUri } from '@fe/services/document'
-import { getContextMenuItems } from '@fe/services/view'
+import { getContextMenuItems, getHeadings, Heading } from '@fe/services/view'
import { useContextMenu } from '@fe/support/ui/context-menu'
import { DOM_ATTR_NAME } from '@fe/support/constant'
import { useI18n } from '@fe/services/i18n'
@@ -64,6 +56,7 @@ import type { RenderEnv } from '@fe/types'
import Render from './Render.vue'
import SvgIcon from './SvgIcon.vue'
+import Outline from './Outline.vue'
import 'highlight.js/styles/atom-one-dark.css'
import 'katex/dist/katex.min.css'
@@ -72,7 +65,7 @@ const logger = getLogger('preview')
export default defineComponent({
name: 'xview',
- components: { Render, SvgIcon },
+ components: { Render, SvgIcon, Outline },
setup () {
useI18n()
@@ -93,13 +86,7 @@ export default defineComponent({
const width = ref(1024)
const height = ref(1024)
- const heads = ref<{
- tag: string;
- class: string[];
- text: string;
- level: number;
- sourceLine: number;
- }[]>([])
+ const heads = ref([])
const todoCount = ref(0)
const todoDoneCount = ref(0)
const scrollTop = ref(0)
@@ -122,18 +109,7 @@ export default defineComponent({
}
function updateOutline () {
- const tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
- const nodes = refView.value!.querySelectorAll(tags.join(','))
- heads.value = Array.from(nodes).map(node => {
- const tag = node.tagName.toLowerCase()
- return {
- tag,
- class: [...node.classList, 'tag-' + tag],
- text: node.innerText,
- level: tags.indexOf(tag),
- sourceLine: parseInt(node.dataset.sourceLine || '0')
- }
- })
+ heads.value = getHeadings()
}
function updateTodoCount () {
@@ -447,25 +423,6 @@ export default defineComponent({
cursor: pointer;
overflow: auto;
overflow-x: hidden;
- padding-bottom: 1em;
-
- & > div {
- font-size: 14px;
- line-height: 18px;
- padding: 7px .5em;
- display: flex;
- border-radius: var(--g-border-radius);
-
- &:hover {
- background: var(--g-color-75);
- }
-
- .tag-name {
- color: var(--g-color-60);
- font-size: 12px;
- padding-left: 0.5em;
- }
- }
}
.outline-title {
diff --git a/src/renderer/components/SvgIcon.vue b/src/renderer/components/SvgIcon.vue
index 28220170..d7c8023b 100644
--- a/src/renderer/components/SvgIcon.vue
+++ b/src/renderer/components/SvgIcon.vue
@@ -48,6 +48,7 @@ export default defineComponent({
'star-solid': '',
'star-regular': '',
'pen-solid': '',
+ 'folder-tree-solid': '',
}
const name = props.name!
diff --git a/src/renderer/components/Tree.vue b/src/renderer/components/Tree.vue
index 85d39764..59ac8229 100644
--- a/src/renderer/components/Tree.vue
+++ b/src/renderer/components/Tree.vue
@@ -92,7 +92,6 @@ export default defineComponent({
width: 100%;
box-sizing: border-box;
overflow: auto;
- background: var(--g-color-98);
padding-bottom: 20px;
}
diff --git a/src/renderer/plugins/document-history-stack.ts b/src/renderer/plugins/document-history-stack.ts
index 9bb19f54..34156816 100644
--- a/src/renderer/plugins/document-history-stack.ts
+++ b/src/renderer/plugins/document-history-stack.ts
@@ -93,6 +93,7 @@ export default {
flat: true,
title: ctx.i18n.t('control-center.navigation.back', ctx.command.getKeysLabel(backId)),
disabled: idx <= 0,
+ showInActionBar: true,
onClick: () => ctx.action.getActionHandler(backId)()
},
{
@@ -101,6 +102,7 @@ export default {
flat: true,
title: ctx.i18n.t('control-center.navigation.forward', ctx.command.getKeysLabel(forwardId)),
disabled: idx >= stack.length - 1,
+ showInActionBar: true,
onClick: () => ctx.action.getActionHandler(forwardId)()
},
)
diff --git a/src/renderer/plugins/markdown-heading-number.ts b/src/renderer/plugins/markdown-heading-number.ts
index 43ad520b..9a1994db 100644
--- a/src/renderer/plugins/markdown-heading-number.ts
+++ b/src/renderer/plugins/markdown-heading-number.ts
@@ -46,31 +46,31 @@ export default {
counter(h6counter) ".\\0000a0\\0000a0";
}
- .markdown-view .outline .tag-h1.show-number { counter-reset: outline-h2counter; }
- .markdown-view .outline .tag-h2.show-number { counter-reset: outline-h3counter; }
- .markdown-view .outline .tag-h3.show-number { counter-reset: outline-h4counter; }
- .markdown-view .outline .tag-h4.show-number { counter-reset: outline-h5counter; }
- .markdown-view .outline .tag-h5.show-number { counter-reset: outline-h6counter; }
+ .outline-toc .tag-h1.show-number { counter-reset: outline-h2counter; }
+ .outline-toc .tag-h2.show-number { counter-reset: outline-h3counter; }
+ .outline-toc .tag-h3.show-number { counter-reset: outline-h4counter; }
+ .outline-toc .tag-h4.show-number { counter-reset: outline-h5counter; }
+ .outline-toc .tag-h5.show-number { counter-reset: outline-h6counter; }
- .markdown-view .outline .tag-h2.show-number:before {
+ .outline-toc .tag-h2.show-number:before {
counter-increment: outline-h2counter;
content: counter(outline-h2counter) ".\\0000a0\\0000a0";
}
- .markdown-view .outline .tag-h3.show-number:before {
+ .outline-toc .tag-h3.show-number:before {
counter-increment: outline-h3counter;
content: counter(outline-h2counter) "."
counter(outline-h3counter) ".\\0000a0\\0000a0";
}
- .markdown-view .outline .tag-h4.show-number:before {
+ .outline-toc .tag-h4.show-number:before {
counter-increment: outline-h4counter;
content: counter(outline-h2counter) "."
counter(outline-h3counter) "."
counter(outline-h4counter) ".\\0000a0\\0000a0";
}
- .markdown-view .outline .tag-h5.show-number:before {
+ .outline-toc .tag-h5.show-number:before {
counter-increment: outline-h5counter;
content: counter(outline-h2counter) "."
counter(outline-h3counter) "."
@@ -78,7 +78,7 @@ export default {
counter(outline-h5counter) ".\\0000a0\\0000a0";
}
- .markdown-view .outline .tag-h6.show-number:before {
+ .outline-toc .tag-h6.show-number:before {
counter-increment: outline-h6counter;
content: counter(outline-h2counter) "."
counter(outline-h3counter) "."
diff --git a/src/renderer/plugins/status-bar-control-center.ts b/src/renderer/plugins/status-bar-control-center.ts
index a2d48671..8c48cfe9 100644
--- a/src/renderer/plugins/status-bar-control-center.ts
+++ b/src/renderer/plugins/status-bar-control-center.ts
@@ -111,6 +111,7 @@ export default {
icon: 'search-solid',
flat: true,
title: ctx.i18n.t('control-center.navigation.goto', ctx.command.getKeysLabel('filter.show-quick-open')),
+ showInActionBar: true,
onClick: () => {
ctx.action.getActionHandler('filter.show-quick-open')()
ctx.controlCenter.toggle(false)
diff --git a/src/renderer/services/control-center.ts b/src/renderer/services/control-center.ts
index bb9b61e5..510b4da2 100644
--- a/src/renderer/services/control-center.ts
+++ b/src/renderer/services/control-center.ts
@@ -12,6 +12,7 @@ export type Item = {
icon: string,
title: string,
onClick?: () => void,
+ showInActionBar?: boolean,
}
export type SchemaItem = { items: Item[] }
@@ -26,6 +27,7 @@ export type SchemaTapper = (schema: Schema) => void
const _refresh = debounce(() => {
getActionHandler('control-center.refresh')()
+ getActionHandler('action-bar.refresh')()
}, 10)
/**
diff --git a/src/renderer/services/layout.ts b/src/renderer/services/layout.ts
index 466b23c6..d8d0957d 100644
--- a/src/renderer/services/layout.ts
+++ b/src/renderer/services/layout.ts
@@ -67,6 +67,13 @@ export function toggleXterm (visible?: boolean) {
}
})
}
+/**
+ * Toggle outline visible.
+ * @param visible
+ */
+export function toggleOutline (visible?: boolean) {
+ store.commit('setShowOutline', typeof visible === 'boolean' ? visible : !store.state.showOutline)
+}
registerAction({ name: 'layout.toggle-side', handler: toggleSide, keys: [Alt, 'e'] })
registerAction({ name: 'layout.toggle-editor', handler: toggleEditor, keys: [Alt, 'x'] })
diff --git a/src/renderer/services/view.ts b/src/renderer/services/view.ts
index 83793f7e..2c2691fd 100644
--- a/src/renderer/services/view.ts
+++ b/src/renderer/services/view.ts
@@ -12,6 +12,13 @@ import { switchDoc } from './document'
export type MenuItem = Components.ContextMenu.Item
export type BuildContextMenu = (items: MenuItem[], e: MouseEvent) => void
+export type Heading = {
+ tag: string;
+ class: string[];
+ text: string;
+ level: number;
+ sourceLine: number;
+}
let tmpEnableSyncScroll = true
let syncScrollTimer: any
@@ -168,6 +175,29 @@ export function getViewDom () {
return getActionHandler('view.get-view-dom')()
}
+/**
+ * Get Headings
+ */
+export function getHeadings (): Heading[] {
+ const tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
+ const dom = getViewDom()
+ if (!dom) {
+ return []
+ }
+
+ const nodes = dom.querySelectorAll(tags.join(','))
+ return Array.from(nodes).map(node => {
+ const tag = node.tagName.toLowerCase()
+ return {
+ tag,
+ class: [...node.classList, 'tag-' + tag],
+ text: node.innerText,
+ level: tags.indexOf(tag),
+ sourceLine: parseInt(node.dataset.sourceLine || '0')
+ }
+ })
+}
+
/**
* Get render env.
* @returns
diff --git a/src/renderer/support/store.ts b/src/renderer/support/store.ts
index c655e0a3..d4af0c4d 100644
--- a/src/renderer/support/store.ts
+++ b/src/renderer/support/store.ts
@@ -11,6 +11,7 @@ export const initState = {
showView: storage.get('showView', true),
showEditor: storage.get('showEditor', true),
showXterm: false,
+ showOutline: false,
autoPreview: true,
syncScroll: true,
showSetting: false,
@@ -88,6 +89,9 @@ export default createStore({
setShowXterm (state, data) {
state.showXterm = data
},
+ setShowOutline (state, data) {
+ state.showOutline = data
+ },
setInComposition (state, data) {
state.inComposition = data
},
diff --git a/src/renderer/views/Main.vue b/src/renderer/views/Main.vue
index df0bd3e0..515690d5 100644
--- a/src/renderer/views/Main.vue
+++ b/src/renderer/views/Main.vue
@@ -7,7 +7,9 @@
-
+
+
+
@@ -29,10 +31,12 @@
diff --git a/src/share/i18n/languages/en.ts b/src/share/i18n/languages/en.ts
index b58abfae..ad38397b 100644
--- a/src/share/i18n/languages/en.ts
+++ b/src/share/i18n/languages/en.ts
@@ -26,6 +26,8 @@ const data = {
'close': 'Close',
'discard': 'Discard',
'edit': 'Edit',
+ 'outline': 'Outline',
+ 'files': 'Files',
'premium': {
'need-purchase': '[%s] Premium is required',
'buy-license': 'Buy License',
diff --git a/src/share/i18n/languages/zh-CN.ts b/src/share/i18n/languages/zh-CN.ts
index 0706926c..265b33c9 100644
--- a/src/share/i18n/languages/zh-CN.ts
+++ b/src/share/i18n/languages/zh-CN.ts
@@ -27,6 +27,8 @@ const data: BaseLanguage = {
'close': '关闭',
'edit': '编辑',
'discard': '放弃',
+ 'outline': '目录',
+ 'files': '文件',
'premium': {
'need-purchase': '[%s] 需要高级版',
'buy-license': '立即购买',