mirror of
https://github.com/doocs/md.git
synced 2024-11-24 19:10:34 +08:00
parent
832360419c
commit
bd31da9e5f
965
package-lock.json
generated
965
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,7 @@
|
|||||||
"juice": "^8.0.0",
|
"juice": "^8.0.0",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
"lucide-vue-next": "^0.428.0",
|
"lucide-vue-next": "^0.428.0",
|
||||||
"marked": "^4.0.18",
|
"marked": "^14.1.0",
|
||||||
"marked-katex-extension": "^5.1.1",
|
"marked-katex-extension": "^5.1.1",
|
||||||
"minio": "7.1.3",
|
"minio": "7.1.3",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
import { nextTick, reactive, ref } from 'vue'
|
import { nextTick, reactive, ref } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { ElNotification } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
import CodeMirror from 'codemirror'
|
|
||||||
import { Command } from 'lucide-vue-next'
|
|
||||||
|
|
||||||
import PostInfo from './PostInfo.vue'
|
import PostInfo from './PostInfo.vue'
|
||||||
import FileDropdown from './FileDropdown.vue'
|
import FileDropdown from './FileDropdown.vue'
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { Check } from 'lucide-vue-next'
|
import { Check } from 'lucide-vue-next'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes['class'] }>()
|
const props = defineProps<ContextMenuCheckboxItemProps & { class?: HTMLAttributes[`class`] }>()
|
||||||
const emits = defineEmits<ContextMenuCheckboxItemEmits>()
|
const emits = defineEmits<ContextMenuCheckboxItemEmits>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
@ -30,7 +30,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
|||||||
props.class,
|
props.class,
|
||||||
)"
|
)"
|
||||||
>
|
>
|
||||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span class="absolute left-2 h-3.5 w-3.5 flex items-center justify-center">
|
||||||
<ContextMenuItemIndicator>
|
<ContextMenuItemIndicator>
|
||||||
<Check class="h-4 w-4" />
|
<Check class="h-4 w-4" />
|
||||||
</ContextMenuItemIndicator>
|
</ContextMenuItemIndicator>
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} from 'radix-vue'
|
} from 'radix-vue'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes['class'] }>()
|
const props = defineProps<ContextMenuContentProps & { class?: HTMLAttributes[`class`] }>()
|
||||||
const emits = defineEmits<ContextMenuContentEmits>()
|
const emits = defineEmits<ContextMenuContentEmits>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'radix-vue'
|
} from 'radix-vue'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
const props = defineProps<ContextMenuItemProps & { class?: HTMLAttributes[`class`], inset?: boolean }>()
|
||||||
const emits = defineEmits<ContextMenuItemEmits>()
|
const emits = defineEmits<ContextMenuItemEmits>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
|
@ -3,7 +3,7 @@ import { type HTMLAttributes, computed } from 'vue'
|
|||||||
import { ContextMenuLabel, type ContextMenuLabelProps } from 'radix-vue'
|
import { ContextMenuLabel, type ContextMenuLabelProps } from 'radix-vue'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
const props = defineProps<ContextMenuLabelProps & { class?: HTMLAttributes[`class`], inset?: boolean }>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
const { class: _, ...delegated } = props
|
const { class: _, ...delegated } = props
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { Circle } from 'lucide-vue-next'
|
import { Circle } from 'lucide-vue-next'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes['class'] }>()
|
const props = defineProps<ContextMenuRadioItemProps & { class?: HTMLAttributes[`class`] }>()
|
||||||
const emits = defineEmits<ContextMenuRadioItemEmits>()
|
const emits = defineEmits<ContextMenuRadioItemEmits>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
@ -30,7 +30,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
|||||||
props.class,
|
props.class,
|
||||||
)"
|
)"
|
||||||
>
|
>
|
||||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span class="absolute left-2 h-3.5 w-3.5 flex items-center justify-center">
|
||||||
<ContextMenuItemIndicator>
|
<ContextMenuItemIndicator>
|
||||||
<Circle class="h-2 w-2 fill-current" />
|
<Circle class="h-2 w-2 fill-current" />
|
||||||
</ContextMenuItemIndicator>
|
</ContextMenuItemIndicator>
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
} from 'radix-vue'
|
} from 'radix-vue'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes['class'] }>()
|
const props = defineProps<ContextMenuSeparatorProps & { class?: HTMLAttributes[`class`] }>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
const { class: _, ...delegated } = props
|
const { class: _, ...delegated } = props
|
||||||
|
@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'vue'
|
|||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
class?: HTMLAttributes['class']
|
class?: HTMLAttributes[`class`]
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'radix-vue'
|
} from 'radix-vue'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes['class'] }>()
|
const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes[`class`] }>()
|
||||||
const emits = defineEmits<DropdownMenuSubContentEmits>()
|
const emits = defineEmits<DropdownMenuSubContentEmits>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
import { ChevronRight } from 'lucide-vue-next'
|
import { ChevronRight } from 'lucide-vue-next'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes['class'], inset?: boolean }>()
|
const props = defineProps<ContextMenuSubTriggerProps & { class?: HTMLAttributes[`class`], inset?: boolean }>()
|
||||||
|
|
||||||
const delegatedProps = computed(() => {
|
const delegatedProps = computed(() => {
|
||||||
const { class: _, ...delegated } = props
|
const { class: _, ...delegated } = props
|
||||||
|
@ -381,7 +381,7 @@ const graceTheme = mergeTheme(defaultTheme, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
thead: {
|
thead: {
|
||||||
'background': `linear-gradient(45deg, rgba(0, 152, 116, 0.9), rgba(0, 192, 146, 0.9))`,
|
'background': `rgba(0, 0, 0, 0.05)`,
|
||||||
'color': `#fff`,
|
'color': `#fff`,
|
||||||
'font-weight': `bold`,
|
'font-weight': `bold`,
|
||||||
},
|
},
|
||||||
|
@ -52,14 +52,19 @@ class WxRenderer extends Renderer {
|
|||||||
|
|
||||||
getStyles = (tokenName, addition = ``) => {
|
getStyles = (tokenName, addition = ``) => {
|
||||||
const dict = this.styleMapping[tokenName]
|
const dict = this.styleMapping[tokenName]
|
||||||
if (!dict)
|
if (!dict) {
|
||||||
return ``
|
return ``
|
||||||
|
}
|
||||||
const styles = Object.entries(dict)
|
const styles = Object.entries(dict)
|
||||||
.map(([key, value]) => `${key}:${value}`)
|
.map(([key, value]) => `${key}:${value}`)
|
||||||
.join(`;`)
|
.join(`;`)
|
||||||
return `style="${styles}${addition}"`
|
return `style="${styles}${addition}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
styledContent = (styleLabel, content, label = styleLabel) => {
|
||||||
|
return `<${label} ${this.getStyles(styleLabel)}>${content}</${label}>`
|
||||||
|
}
|
||||||
|
|
||||||
addFootnote = (title, link) => {
|
addFootnote = (title, link) => {
|
||||||
this.footnotes.push([++this.footnoteIndex, title, link])
|
this.footnotes.push([++this.footnoteIndex, title, link])
|
||||||
return this.footnoteIndex
|
return this.footnoteIndex
|
||||||
@ -77,9 +82,8 @@ class WxRenderer extends Renderer {
|
|||||||
: `<code style="font-size: 90%; opacity: 0.6;">[${index}]</code> ${title}: <i style="word-break: break-all">${link}</i><br/>`,
|
: `<code style="font-size: 90%; opacity: 0.6;">[${index}]</code> ${title}: <i style="word-break: break-all">${link}</i><br/>`,
|
||||||
)
|
)
|
||||||
.join(`\n`)
|
.join(`\n`)
|
||||||
return `<h4 ${this.getStyles(`h4`)}>引用链接</h4><p ${this.getStyles(
|
|
||||||
`footnotes`,
|
return this.styledContent(`h4`, `引用链接`) + this.styledContent(`footnotes`, footnoteArray, `p`)
|
||||||
)}>${footnoteArray}</p>`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildAddition = () => `
|
buildAddition = () => `
|
||||||
@ -104,23 +108,29 @@ class WxRenderer extends Renderer {
|
|||||||
this.styleMapping = this.buildTheme(this.opts.theme)
|
this.styleMapping = this.buildTheme(this.opts.theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
heading = (text, level) => {
|
heading({ tokens, depth }) {
|
||||||
const tag = `h${level}`
|
const text = this.parser.parseInline(tokens)
|
||||||
return `<${tag} ${this.getStyles(tag)}>${text}</${tag}>`
|
const tag = `h${depth}`
|
||||||
|
return this.styledContent(tag, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
paragraph = (text) => {
|
paragraph({ tokens }) {
|
||||||
|
const text = this.parser.parseInline(tokens)
|
||||||
const isFigureImage = text.includes(`<figure`) && text.includes(`<img`)
|
const isFigureImage = text.includes(`<figure`) && text.includes(`<img`)
|
||||||
const isEmpty = text.trim() === ``
|
const isEmpty = text.trim() === ``
|
||||||
return isFigureImage ? text : isEmpty ? `` : `<p ${this.getStyles(`p`)}>${text}</p>`
|
if (isFigureImage || isEmpty) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
return this.styledContent(`p`, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote = (text) => {
|
blockquote({ tokens }) {
|
||||||
|
let text = this.parser.parse(tokens)
|
||||||
text = text.replace(/<p.*?>/g, `<p ${this.getStyles(`blockquote_p`)}>`)
|
text = text.replace(/<p.*?>/g, `<p ${this.getStyles(`blockquote_p`)}>`)
|
||||||
return `<blockquote ${this.getStyles(`blockquote`)}>${text}</blockquote>`
|
return this.styledContent(`blockquote`, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
code = (text, lang = ``) => {
|
code({ text, lang }) {
|
||||||
if (lang.startsWith(`mermaid`)) {
|
if (lang.startsWith(`mermaid`)) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.mermaid?.run()
|
window.mermaid?.run()
|
||||||
@ -129,41 +139,43 @@ class WxRenderer extends Renderer {
|
|||||||
}
|
}
|
||||||
const langText = lang.split(` `)[0]
|
const langText = lang.split(` `)[0]
|
||||||
const language = hljs.getLanguage(langText) ? langText : `plaintext`
|
const language = hljs.getLanguage(langText) ? langText : `plaintext`
|
||||||
text = hljs.highlight(text, { language }).value
|
let highlighted = hljs.highlight(text, { language }).value
|
||||||
text = text
|
highlighted = highlighted
|
||||||
.replace(/\r\n/g, `<br/>`)
|
.replace(/\r\n/g, `<br/>`)
|
||||||
.replace(/\n/g, `<br/>`)
|
.replace(/\n/g, `<br/>`)
|
||||||
.replace(/(>[^<]+)|(^[^<]+)/g, (str) => {
|
.replace(/(>[^<]+)|(^[^<]+)/g, str => str.replace(/\s/g, ` `))
|
||||||
return str.replace(/\s/g, ` `)
|
|
||||||
})
|
|
||||||
|
|
||||||
return `<pre class="hljs code__pre" ${this.getStyles(
|
return `<pre class="hljs code__pre" ${this.getStyles(
|
||||||
`code_pre`,
|
`code_pre`,
|
||||||
)}><code class="language-${lang}" ${this.getStyles(
|
)}><code class="language-${lang}" ${this.getStyles(
|
||||||
`code`,
|
`code`,
|
||||||
)}>${text}</code></pre>`
|
)}>${highlighted}</code></pre>`
|
||||||
}
|
}
|
||||||
|
|
||||||
codespan = text => `<code ${this.getStyles(`codespan`)}>${text}</code>`
|
codespan({ text }) {
|
||||||
|
return this.styledContent(`codespan`, text, `code`)
|
||||||
listitem = text => `<li ${this.getStyles(`listitem`)}><span><%s/></span>${text}</li>`
|
|
||||||
|
|
||||||
list = (text, ordered) => {
|
|
||||||
text = text.replace(/<\/*p.*?>/g, ``).replace(/<\/*p>/g, ``)
|
|
||||||
|
|
||||||
const segments = text.split(`<%s/>`)
|
|
||||||
|
|
||||||
if (!ordered) {
|
|
||||||
return `<ul ${this.getStyles(`ul`)}>${segments.join(`• `)}</ul>`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderedText = segments.map((segment, i) => (i > 0 ? `${i}. ` : ``) + segment).join(``)
|
listitem(tokens, prefix) {
|
||||||
return `<ol ${this.getStyles(`ol`)}>${orderedText}</ol>`
|
return `<li ${this.getStyles(`listitem`)}>${prefix}${this.parser.parseInline(tokens)}</li>`
|
||||||
}
|
}
|
||||||
|
|
||||||
image = (href, title, text) => {
|
list({ ordered, items }) {
|
||||||
const createSubText = s => s ? `<figcaption ${this.getStyles(`figcaption`)}>${s}</figcaption>` : ``
|
const listItems = []
|
||||||
const transform = {
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const { tokens } = items[i]
|
||||||
|
const prefix = ordered ? `${i + 1}. ` : `• `
|
||||||
|
listItems.push(this.listitem(tokens, prefix))
|
||||||
|
}
|
||||||
|
const label = ordered ? `ol` : `ul`
|
||||||
|
return this.styledContent(label, listItems.join(``))
|
||||||
|
}
|
||||||
|
|
||||||
|
image({ href, title, text }) {
|
||||||
|
const createSubText = s =>
|
||||||
|
s ? `<figcaption ${this.getStyles(`figcaption`)}>${s}</figcaption>` : ``
|
||||||
|
const transform
|
||||||
|
= {
|
||||||
'alt': () => text,
|
'alt': () => text,
|
||||||
'title': () => title,
|
'title': () => title,
|
||||||
'alt-title': () => text || title,
|
'alt-title': () => text || title,
|
||||||
@ -173,42 +185,57 @@ class WxRenderer extends Renderer {
|
|||||||
const subText = createSubText(transform())
|
const subText = createSubText(transform())
|
||||||
const figureStyles = this.getStyles(`figure`)
|
const figureStyles = this.getStyles(`figure`)
|
||||||
const imgStyles = this.getStyles(`image`)
|
const imgStyles = this.getStyles(`image`)
|
||||||
|
|
||||||
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`
|
return `<figure ${figureStyles}><img ${imgStyles} src="${href}" title="${title}" alt="${text}"/>${subText}</figure>`
|
||||||
}
|
}
|
||||||
|
|
||||||
link = (href, title, text) => {
|
link({ href, title, text }) {
|
||||||
if (href.startsWith(`https://mp.weixin.qq.com`)) {
|
if (href.startsWith(`https://mp.weixin.qq.com`)) {
|
||||||
return `<a href="${href}" title="${title || text}" ${this.getStyles(
|
return `<a href="${href}" title="${title || text}" ${this.getStyles(
|
||||||
`wx_link`,
|
`wx_link`,
|
||||||
)}>${text}</a>`
|
)}>${text}</a>`
|
||||||
}
|
}
|
||||||
if (href === text)
|
if (href === text) {
|
||||||
return text
|
return text
|
||||||
|
}
|
||||||
if (this.opts.status) {
|
if (this.opts.status) {
|
||||||
const ref = this.addFootnote(title || text, href)
|
const ref = this.addFootnote(title || text, href)
|
||||||
return `<span ${this.getStyles(
|
return `<span ${this.getStyles(
|
||||||
`link`,
|
`link`,
|
||||||
)}>${text}<sup>[${ref}]</sup></span>`
|
)}>${text}<sup>[${ref}]</sup></span>`
|
||||||
}
|
}
|
||||||
return `<span ${this.getStyles(`link`)}>${text}</span>`
|
return this.styledContent(`link`, text, `span`)
|
||||||
}
|
}
|
||||||
|
|
||||||
strong = text => `<strong ${this.getStyles(`strong`)}>${text}</strong>`
|
strong({ text }) {
|
||||||
|
return this.styledContent(`strong`, text)
|
||||||
|
}
|
||||||
|
|
||||||
em = text => `<span style="font-style: italic;">${text}</span>`
|
em({ text }) {
|
||||||
|
return `<span style="font-style: italic;">${text}</span>`
|
||||||
|
}
|
||||||
|
|
||||||
table = (header, body) => `
|
table({ header, rows }) {
|
||||||
|
const headerRow = header.map(cell => this.styledContent(`td`, cell.text)).join(``)
|
||||||
|
const body = rows.map((row) => {
|
||||||
|
const rowContent = row.map(cell => this.styledContent(`td`, cell.text)).join(``)
|
||||||
|
return this.styledContent(`tr`, rowContent)
|
||||||
|
}).join(``)
|
||||||
|
return `
|
||||||
<section style="padding:0 8px;">
|
<section style="padding:0 8px;">
|
||||||
<table class="preview-table">
|
<table class="preview-table">
|
||||||
<thead ${this.getStyles(`thead`)}>${header}</thead>
|
<thead ${this.getStyles(`thead`)}>${headerRow}</thead>
|
||||||
<tbody>${body}</tbody>
|
<tbody>${body}</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>`
|
</section>`
|
||||||
|
}
|
||||||
|
|
||||||
tablecell = text => `<td ${this.getStyles(`td`)}>${text}</td>`
|
tablecell({ text }) {
|
||||||
|
return this.styledContent(`td`, text)
|
||||||
|
}
|
||||||
|
|
||||||
hr = () => `<hr ${this.getStyles(`hr`)}/>`
|
hr(_) {
|
||||||
|
return this.styledContent(`hr`, ``)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WxRenderer
|
export default WxRenderer
|
||||||
|
@ -15,21 +15,14 @@ import RunLoading from '@/components/RunLoading.vue'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
ContextMenuCheckboxItem,
|
|
||||||
ContextMenuContent,
|
ContextMenuContent,
|
||||||
ContextMenuItem,
|
ContextMenuItem,
|
||||||
ContextMenuLabel,
|
|
||||||
ContextMenuRadioGroup,
|
|
||||||
ContextMenuRadioItem,
|
|
||||||
ContextMenuSeparator,
|
ContextMenuSeparator,
|
||||||
ContextMenuShortcut,
|
ContextMenuShortcut,
|
||||||
ContextMenuSub,
|
|
||||||
ContextMenuSubContent,
|
|
||||||
ContextMenuSubTrigger,
|
|
||||||
ContextMenuTrigger,
|
ContextMenuTrigger,
|
||||||
} from '@/components/ui/context-menu'
|
} from '@/components/ui/context-menu'
|
||||||
|
|
||||||
import { altKey, altSign, ctrlKey, ctrlSign, shiftKey, shiftSign } from '@/config'
|
import { altKey, altSign, ctrlKey, shiftKey, shiftSign } from '@/config'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
checkImage,
|
checkImage,
|
||||||
|
Loading…
Reference in New Issue
Block a user