Skip to content

Commit

Permalink
Merge branch 'release/2.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
purocean committed Dec 25, 2019
2 parents 1bef46a + e787d83 commit 53f278f
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 48 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@
+ HTML 解析:可以直接在文档里面使用 HTML 代码,也可以使用快捷键 `Ctrl + B + V` 粘贴复制 HTML 为 Markdown
+ docx 导出:后端使用 pandoc 做转换器
+ TOC 支持:生成 TOC 在需要生成目录的地方写入 `[toc]{type: "ol", level: [1,2,3]}` 即可,,示例见 FEATURE.md
+ 复制标题链接:`Ctrl + 单击标题` 复制标题链接路径到剪切板,便于插入到其他文件
+ 嵌入小工具:文档支持内嵌 HTML 小工具,示例见 FEATURE.md
+ 嵌入 Plantuml 图形:需要安装 Java,graphviz ,示例见 FEATURE.md
+ 嵌入 drawio 图形:文档支持内嵌 drawio 图形,示例见 FEATURE.md
+ 嵌入 ECharts 图形:在文档中嵌入 Echarts 图形,示例见 FEATURE.md
+ 嵌入 Mermaid 图形:在文档中嵌入 Mermaid 图形,示例见 FEATURE.md
+ 元素属性书写:可自定义元素的任意属性,示例见 FEATURE.md
+ 表格解析增强:表格支持表格标题多行文本,列表等特性,示例见 FEATURE.md
+ 文档交叉链接跳转:支持在文档中引入其他文档,互相跳转

## 上手使用
+[最新版本](https://github.com/purocean/yn/releases) 下载对应平台应用即可
Expand Down Expand Up @@ -148,14 +150,20 @@ yarn run start

[Windows 商店下载](https://www.microsoft.com/zh-cn/p/yank-note-%e4%b8%80%e6%ac%be%e9%9d%a2%e5%90%91%e7%a8%8b%e5%ba%8f%e5%91%98%e7%9a%84-markdown-%e7%ac%94%e8%ae%b0%e5%ba%94%e7%94%a8/9n08bq8k8681?rtc=1#activetab=pivot:overviewtab)

### [v2.2.0](https://github.com/purocean/yn/releases/tag/v2.2.0) 2019-12-25
1. 增加文档之间跳转功能
1. 增加复制文档标题链接功能
1. 调整文档插入选择面板
1. 修复高分辨率下目录树箭头消失问题

<details>
<summary>展开查看更多版本记录</summary>

### [v2.1.1](https://github.com/purocean/yn/releases/tag/v2.1.1) 2019-12-24
1. 增加在当前目录创建文件菜单
1. 限制快捷跳转列表数量以提高性能
1. 标题栏最大化窗口后移除尺寸调节

<details>
<summary>展开查看更多版本记录</summary>

### [v2.1.0](https://github.com/purocean/yn/releases/tag/v2.1.0) 2019-11-29
1. 增加多标签同时打开多个文件

Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/Filter.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<XMask :show="!!show" @close="show = false">
<QuickOpen @choose-file="show" @close="show = false"></QuickOpen>
<QuickOpen @choose-file="show" @close="show = false" :with-marked="show && show.withMarked"></QuickOpen>
</XMask>
</template>

Expand Down Expand Up @@ -33,14 +33,15 @@ export default {
}
this.show = false
}
this.show.withMarked = false
e.preventDefault()
e.stopPropagation()
} else if (e.key === 'p' && e.ctrlKey) {
this.show = f => {
this.$bus.$emit('switch-repo-by-name', f.repo)
this.$store.commit('app/setCurrentFile', f)
this.show = false
}
this.show.withMarked = true
e.preventDefault()
e.stopPropagation()
}
Expand Down
90 changes: 72 additions & 18 deletions frontend/src/components/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,8 @@ export default {
HighlightLineNumber.lineNumbersBlock(ele)
}
for (let ele of document.getElementsByTagName('a')) {
const href = ele.getAttribute('href')
if (href && href.startsWith('#')) {
ele.onclick = () => {
document.getElementById(href.replace(/^#/, '')).scrollIntoView()
return false
}
}
}
// 渲染完成后触发渲染完成事件
this.$nextTick(() => this.$bus.emit('preview-rendered'))
}, 500, { leading: true })
this.render()
Expand Down Expand Up @@ -202,6 +195,16 @@ export default {
const fileName = file.basename(path)
const filePath = `${basePath}/${path}`
// md 文件不替换
if (fileName.endsWith('.md')) {
return match
}
// 路径中有 hash 不替换
if (path.indexOf('#') > -1) {
return match
}
return `[${alt}](api/attachment/${encodeURIComponent(fileName)}?repo=${repo}&path=${encodeURI(filePath)})`
})
},
Expand Down Expand Up @@ -282,27 +285,78 @@ export default {
},
handleClick (e) {
const target = e.target
console.log(env.isElectron, target.tagName, target.getAttribute('href'), /^(http:|https:|ftp:)\/\//i.test(target.getAttribute('href') || ''))
const preventEvent = () => {
e.preventDefault()
e.stopPropagation()
}
const handleLink = link => {
if (link.tagName === 'A' && link.classList.contains('open')) {
// 系统中打开附件
if (link.classList.contains('open')) {
fetch(link.href.replace('api/attachment', 'api/open'))
e.preventDefault()
e.stopPropagation()
return
return preventEvent()
}
if (env.isElectron && link.tagName === 'A' && /^(http:|https:|ftp:)\/\//i.test(link.getAttribute('href') || '')) {
env.require && env.require('opn')(link.href)
e.preventDefault()
e.stopPropagation()
const href = link.getAttribute('href') || ''
if (/^(http:|https:|ftp:)\/\//i.test(href)) { // 处理外链
// Electron 中打开外链
if (env.isElectron) {
env.require && env.require('opn')(link.href)
preventEvent()
}
} else { // 处理相对链接
if (/(\.md$|\.md#)/.test(href)) { // 处理打开相对 md 文件
const tmp = href.split('#')
let path = tmp[0]
if (path.startsWith('.')) { // 将相对路径转换为绝对路径
path = file.dirname(this.filePath || '') + path.replace('.', '')
}
// 打开文件
this.$store.commit('app/setCurrentFile', {
path,
name: file.basename(path),
repo: this.fileRepo,
type: 'file'
})
// 跳转锚点
const hash = tmp[1]
if (hash) {
this.$bus.once('preview-rendered', () => {
const el = document.getElementById(hash)
if (el) {
// 如果是标题的话,也顺便将编辑器滚动到可视区域
if (hash.startsWith('h-')) {
el.click()
} else {
el.scrollIntoView()
}
}
})
}
preventEvent()
} else if (href && href.startsWith('#')) { // 处理 TOC 跳转
document.getElementById(href.replace(/^#/, '')).scrollIntoView()
preventEvent()
}
}
}
if (target.tagName === 'A') {
handleLink(target)
}
// 复制标题链接
if (['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].indexOf(target.tagName) > -1 && target.id && e.ctrlKey) {
this.$bus.emit('copy-text', encodeURI(this.filePath) + '#' + target.id)
return preventEvent()
}
if (target.tagName === 'IMG') {
const img = target
if (e.ctrlKey && e.shiftKey) { // 转换外链图片到本地
Expand Down
31 changes: 26 additions & 5 deletions frontend/src/components/QuickOpen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,19 @@ import fuzzyMatch from '@/lib/fuzzyMatch'
export default {
name: 'quick-open',
components: {},
props: {
withMarked: {
type: Boolean,
default: true,
},
},
data () {
return {
selected: null,
searchText: '',
currentTab: 'marked',
list: [],
lastFetchTime: 0,
tabs: [
{ key: 'marked', label: '已标记' },
{ key: 'file', label: '快速跳转' },
{ key: 'search', label: '搜索内容' },
]
}
},
created () {
Expand Down Expand Up @@ -216,6 +217,14 @@ export default {
}
},
watch: {
withMarked: {
immediate: true,
handler (val) {
if (!val && this.currentTab === 'marked') {
this.currentTab = 'file'
}
}
},
list () {
this.updateSelected()
},
Expand All @@ -233,6 +242,18 @@ export default {
},
computed: {
...mapState('app', ['currentRepo', 'recentOpenTime', 'tree', 'markedFiles']),
tabs () {
const tabs = [
{ key: 'file', label: '快速跳转' },
{ key: 'search', label: '搜索内容' },
]
if (this.withMarked) {
tabs.unshift({ key: 'marked', label: '已标记' })
}
return tabs
},
files () {
return this.travelFiles(this.tree || [])
},
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/Toast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export default {
},
methods: {
show (type, content, timeout = 2000) {
// TODO 暂时只有 warning type
this.toast = { type, content }
setTimeout(() => {
Expand Down Expand Up @@ -42,4 +41,9 @@ export default {
background: #d46b08;
color: #f9ebeb;
}
.toast-info {
background: #07bd52;
color: #f9ebeb;
}
</style>
1 change: 0 additions & 1 deletion frontend/src/components/TreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ export default {
summary.folder::-webkit-details-marker {
flex: none;
width: 10px;
position: relative;
margin: 0;
margin-right: 5px;
}
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/pages/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,28 @@ export default {
mounted () {
RunPlugin.clearCache()
this.$bus.on('editor-ready', this.init)
this.$bus.on('copy-text', this.copyText)
},
beforeDestroy () {
this.$bus.off('editor-ready', this.init)
this.$bus.off('copy-text', this.copyText)
},
methods: {
copyText (text) {
const input = document.createElement('input')
input.style.position = 'absolute'
input.style.background = 'red'
input.style.left = '-999999px'
input.style.top = '-999999px'
input.style.zIndex = -1000
input.style.opacity = 0
input.value = text
document.body.appendChild(input)
input.select()
document.execCommand('copy')
document.body.removeChild(input)
this.$toast.show('info', '已复制')
},
init () {
if (!this.currentFile) {
this.$store.dispatch('app/showHelp', 'README.md')
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/plugins/SourceLinePlugin.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const SourceLinePlugin = (md) => {
const injectLineNumbers = (tokens, idx, options, env, slf) => {
if (tokens[idx].map) {
const line = tokens[idx].map[0]
tokens[idx].attrJoin('class', 'source-line')
tokens[idx].attrSet('data-source-line', String(line + 1))
}
return slf.renderToken(tokens, idx, options, env, slf)
export const injectLineNumbers = (tokens, idx, options, env, slf) => {
if (tokens[idx].map) {
const line = tokens[idx].map[0]
tokens[idx].attrJoin('class', 'source-line')
tokens[idx].attrSet('data-source-line', String(line + 1))
}
return slf.renderToken(tokens, idx, options, env, slf)
}

const SourceLinePlugin = (md) => {
md.renderer.rules.paragraph_open = injectLineNumbers
md.renderer.rules.heading_open = injectLineNumbers
md.renderer.rules.list_item_open = injectLineNumbers
Expand Down
31 changes: 22 additions & 9 deletions frontend/src/plugins/TocPlugin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// https://github.com/Oktavilla/markdown-it-table-of-contents

import { injectLineNumbers } from './SourceLinePlugin'

const slugify = s => 'h-' + encodeURIComponent(String(s).trim().toLowerCase().replace(/\s+/g, '-'))

const defaults = {
Expand Down Expand Up @@ -109,18 +111,34 @@ export default (md, o) => {
var tokenLength = gstate && gstate.tokens && gstate.tokens.length

while (pos < tokenLength) {
var tocHierarchy = renderChildsTokens(pos, gstate.tokens)
var tocHierarchy = renderChildrenTokens(pos, gstate.tokens)
pos = tocHierarchy[0]
tocBody += tocHierarchy[1]
}

return tocBody
} else {
return renderChildsTokens(0, gstate.tokens)[1]
return renderChildrenTokens(0, gstate.tokens)[1]
}
}

md.renderer.rules.heading_open = function (tokens, idx, opt, env, slf) {
const header = tokens[idx]
const headContent = tokens[idx + 1]
const slug = options.slugify(headContent.content)

if (header.attrIndex('id') < 0) {
header.attrSet('id', slug)
}

if (header.attrIndex('title') < 0) {
header.attrSet('title', 'Ctrl + 单击复制链接')
}

return injectLineNumbers(tokens, idx, opt, env, slf)
}

function renderChildsTokens (pos, tokens) {
function renderChildrenTokens (pos, tokens) {
let headings = []
let buffer = ''
let currentLevel
Expand All @@ -129,7 +147,6 @@ export default (md, o) => {
let i = pos
while (i < size) {
var token = tokens[i]
const header = tokens[i - 2]
var heading = tokens[i - 1]
var level = token.tag && parseInt(token.tag.substr(1, 1))
if (token.type !== 'heading_close' || options.level.indexOf(level) === -1 || heading.type !== 'inline') {
Expand All @@ -140,7 +157,7 @@ export default (md, o) => {
currentLevel = level // We init with the first found level
} else {
if (level > currentLevel) {
subHeadings = renderChildsTokens(i, tokens)
subHeadings = renderChildrenTokens(i, tokens)
buffer += subHeadings[1]
i = subHeadings[0]
continue
Expand All @@ -162,10 +179,6 @@ export default (md, o) => {

const slug = options.slugify(heading.content)

if (header.attrIndex('id') < 0) {
header.attrSet('id', slug)
}

buffer = `<li><a href="#${slug}">`
buffer += typeof options.format === 'function' ? options.format(heading.content) : heading.content
buffer += `</a>`
Expand Down
Loading

0 comments on commit 53f278f

Please sign in to comment.