@incremark/vue 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ThemeProvider.vue.d.ts +25 -0
- package/dist/components/AutoScrollContainer.vue.d.ts +31 -0
- package/dist/components/Incremark.vue.d.ts +36 -0
- package/dist/components/IncremarkBlockquote.vue.d.ts +6 -0
- package/dist/components/IncremarkCode.vue.d.ts +23 -0
- package/dist/components/IncremarkContainer.vue.d.ts +18 -0
- package/dist/components/IncremarkDefault.vue.d.ts +6 -0
- package/dist/components/IncremarkFootnotes.vue.d.ts +2 -0
- package/dist/components/IncremarkHeading.vue.d.ts +6 -0
- package/dist/components/IncremarkHtmlElement.vue.d.ts +20 -0
- package/dist/components/IncremarkInline.vue.d.ts +6 -0
- package/dist/components/IncremarkList.vue.d.ts +6 -0
- package/dist/components/IncremarkMath.vue.d.ts +17 -0
- package/dist/components/IncremarkParagraph.vue.d.ts +6 -0
- package/dist/components/IncremarkRenderer.vue.d.ts +12 -0
- package/dist/components/IncremarkTable.vue.d.ts +6 -0
- package/dist/components/IncremarkThematicBreak.vue.d.ts +2 -0
- package/{src/components/index.ts → dist/components/index.d.ts} +16 -23
- package/dist/composables/index.d.ts +8 -0
- package/dist/composables/useBlockTransformer.d.ts +68 -0
- package/dist/composables/useDefinationsContext.d.ts +9 -0
- package/dist/composables/useDevTools.d.ts +18 -0
- package/dist/composables/useIncremark.d.ts +112 -0
- package/dist/composables/useProvideDefinations.d.ts +16 -0
- package/dist/composables/useStreamRenderer.d.ts +26 -0
- package/dist/composables/useTypewriter.d.ts +37 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +428 -222
- package/dist/index.js.map +1 -1
- package/dist/utils/cursor.d.ts +18 -0
- package/package.json +11 -13
- package/dist/index.css +0 -6
- package/dist/index.css.map +0 -1
- package/src/ThemeProvider.vue +0 -41
- package/src/components/AutoScrollContainer.vue +0 -164
- package/src/components/Incremark.vue +0 -131
- package/src/components/IncremarkBlockquote.vue +0 -18
- package/src/components/IncremarkCode.vue +0 -236
- package/src/components/IncremarkDefault.vue +0 -15
- package/src/components/IncremarkFootnotes.vue +0 -78
- package/src/components/IncremarkHeading.vue +0 -17
- package/src/components/IncremarkHtmlElement.vue +0 -127
- package/src/components/IncremarkInline.vue +0 -187
- package/src/components/IncremarkList.vue +0 -46
- package/src/components/IncremarkMath.vue +0 -105
- package/src/components/IncremarkParagraph.vue +0 -14
- package/src/components/IncremarkRenderer.vue +0 -50
- package/src/components/IncremarkTable.vue +0 -42
- package/src/components/IncremarkThematicBreak.vue +0 -8
- package/src/composables/index.ts +0 -11
- package/src/composables/useBlockTransformer.ts +0 -141
- package/src/composables/useDefinationsContext.ts +0 -16
- package/src/composables/useDevTools.ts +0 -54
- package/src/composables/useIncremark.ts +0 -238
- package/src/composables/useProvideDefinations.ts +0 -61
- package/src/composables/useStreamRenderer.ts +0 -55
- package/src/composables/useTypewriter.ts +0 -205
- package/src/index.ts +0 -78
- package/src/utils/cursor.ts +0 -46
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
/**
|
|
3
|
-
* 脚注列表组件
|
|
4
|
-
*
|
|
5
|
-
* 在文档底部渲染所有脚注定义,按引用出现的顺序排列
|
|
6
|
-
*
|
|
7
|
-
* @component IncremarkFootnotes
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* 样式定义在 @incremark/theme 中的 footnotes.less
|
|
11
|
-
* footnoteReferenceOrder 自动从 context 获取,无需手动传递
|
|
12
|
-
*/
|
|
13
|
-
-->
|
|
14
|
-
<script setup lang="ts">
|
|
15
|
-
import { computed } from 'vue'
|
|
16
|
-
import type { FootnoteDefinition, RootContent } from 'mdast'
|
|
17
|
-
import { useDefinationsContext } from '../composables/useDefinationsContext'
|
|
18
|
-
import IncremarkRenderer from './IncremarkRenderer.vue'
|
|
19
|
-
|
|
20
|
-
const { definations, footnoteDefinitions, footnoteReferenceOrder } = useDefinationsContext()
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 按引用顺序排列的脚注列表
|
|
24
|
-
* 只显示已有定义的脚注
|
|
25
|
-
*/
|
|
26
|
-
const orderedFootnotes = computed<Array<{ identifier: string; definition: FootnoteDefinition }>>(() => {
|
|
27
|
-
return footnoteReferenceOrder.value
|
|
28
|
-
.map(identifier => ({
|
|
29
|
-
identifier,
|
|
30
|
-
definition: footnoteDefinitions.value[identifier]
|
|
31
|
-
}))
|
|
32
|
-
.filter(item => item.definition !== undefined)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 是否有脚注需要显示
|
|
37
|
-
*/
|
|
38
|
-
const hasFootnotes = computed(() => orderedFootnotes.value.length > 0)
|
|
39
|
-
</script>
|
|
40
|
-
|
|
41
|
-
<template>
|
|
42
|
-
<section v-if="hasFootnotes" class="incremark-footnotes">
|
|
43
|
-
<hr class="incremark-footnotes-divider" />
|
|
44
|
-
<ol class="incremark-footnotes-list">
|
|
45
|
-
<li
|
|
46
|
-
v-for="(item, index) in orderedFootnotes"
|
|
47
|
-
:key="item.identifier"
|
|
48
|
-
:id="`fn-${item.identifier}`"
|
|
49
|
-
class="incremark-footnote-item"
|
|
50
|
-
>
|
|
51
|
-
<div class="incremark-footnote-content">
|
|
52
|
-
<!-- 脚注序号 -->
|
|
53
|
-
<span class="incremark-footnote-number">{{ index + 1 }}.</span>
|
|
54
|
-
|
|
55
|
-
<!-- 脚注内容 -->
|
|
56
|
-
<div class="incremark-footnote-body">
|
|
57
|
-
<IncremarkRenderer
|
|
58
|
-
v-for="(child, childIndex) in item.definition.children"
|
|
59
|
-
:key="childIndex"
|
|
60
|
-
:node="(child as RootContent)"
|
|
61
|
-
/>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<!-- 返回链接 -->
|
|
66
|
-
<a
|
|
67
|
-
:href="`#fnref-${item.identifier}`"
|
|
68
|
-
class="incremark-footnote-backref"
|
|
69
|
-
aria-label="返回引用位置"
|
|
70
|
-
>
|
|
71
|
-
↩
|
|
72
|
-
</a>
|
|
73
|
-
</li>
|
|
74
|
-
</ol>
|
|
75
|
-
</section>
|
|
76
|
-
</template>
|
|
77
|
-
|
|
78
|
-
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { Heading } from 'mdast'
|
|
3
|
-
import { computed } from 'vue'
|
|
4
|
-
import IncremarkInline from './IncremarkInline.vue'
|
|
5
|
-
|
|
6
|
-
const props = defineProps<{
|
|
7
|
-
node: Heading
|
|
8
|
-
}>()
|
|
9
|
-
|
|
10
|
-
const tag = computed(() => `h${props.node.depth}`)
|
|
11
|
-
</script>
|
|
12
|
-
|
|
13
|
-
<template>
|
|
14
|
-
<component :is="tag" :class="`incremark-heading h${node.depth}`">
|
|
15
|
-
<IncremarkInline :nodes="node.children" />
|
|
16
|
-
</component>
|
|
17
|
-
</template>
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { RootContent, PhrasingContent } from 'mdast'
|
|
3
|
-
import IncremarkInline from './IncremarkInline.vue'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* HtmlElementNode 类型定义(与 @incremark/core 中的定义一致)
|
|
7
|
-
*/
|
|
8
|
-
interface HtmlElementNode {
|
|
9
|
-
type: 'htmlElement'
|
|
10
|
-
tagName: string
|
|
11
|
-
attrs: Record<string, string>
|
|
12
|
-
children: RootContent[]
|
|
13
|
-
data?: {
|
|
14
|
-
rawHtml?: string
|
|
15
|
-
parsed?: boolean
|
|
16
|
-
originalType?: string
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
defineProps<{
|
|
21
|
-
node: HtmlElementNode
|
|
22
|
-
}>()
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 判断是否是行内元素
|
|
26
|
-
*/
|
|
27
|
-
function isInlineElement(tagName: string): boolean {
|
|
28
|
-
const inlineElements = [
|
|
29
|
-
'a', 'abbr', 'acronym', 'b', 'bdo', 'big', 'br', 'button', 'cite',
|
|
30
|
-
'code', 'dfn', 'em', 'i', 'img', 'input', 'kbd', 'label', 'map',
|
|
31
|
-
'object', 'output', 'q', 'samp', 'script', 'select', 'small',
|
|
32
|
-
'span', 'strong', 'sub', 'sup', 'textarea', 'time', 'tt', 'var'
|
|
33
|
-
]
|
|
34
|
-
return inlineElements.includes(tagName.toLowerCase())
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 判断是否是自闭合元素
|
|
39
|
-
*/
|
|
40
|
-
function isVoidElement(tagName: string): boolean {
|
|
41
|
-
const voidElements = [
|
|
42
|
-
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
43
|
-
'link', 'meta', 'param', 'source', 'track', 'wbr'
|
|
44
|
-
]
|
|
45
|
-
return voidElements.includes(tagName.toLowerCase())
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 判断子节点是否都是行内内容
|
|
50
|
-
*/
|
|
51
|
-
function hasOnlyInlineChildren(children: RootContent[]): boolean {
|
|
52
|
-
if (!children || children.length === 0) return true
|
|
53
|
-
|
|
54
|
-
return children.every(child => {
|
|
55
|
-
const type = child.type
|
|
56
|
-
// 常见的行内类型
|
|
57
|
-
const inlineTypes = ['text', 'strong', 'emphasis', 'inlineCode', 'link', 'image', 'break', 'html', 'htmlElement']
|
|
58
|
-
if (inlineTypes.includes(type)) {
|
|
59
|
-
// 如果是 htmlElement,检查是否是行内元素
|
|
60
|
-
if (type === 'htmlElement') {
|
|
61
|
-
return isInlineElement((child as unknown as HtmlElementNode).tagName)
|
|
62
|
-
}
|
|
63
|
-
return true
|
|
64
|
-
}
|
|
65
|
-
return false
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 将属性对象转换为 HTML 属性字符串(用于 v-bind)
|
|
71
|
-
*/
|
|
72
|
-
function getAttrs(attrs: Record<string, string>): Record<string, string> {
|
|
73
|
-
// 过滤掉可能有问题的属性
|
|
74
|
-
const result: Record<string, string> = {}
|
|
75
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
76
|
-
// 跳过事件属性(已在解析时过滤,这里双重保险)
|
|
77
|
-
if (key.startsWith('on')) continue
|
|
78
|
-
result[key] = value
|
|
79
|
-
}
|
|
80
|
-
return result
|
|
81
|
-
}
|
|
82
|
-
</script>
|
|
83
|
-
|
|
84
|
-
<template>
|
|
85
|
-
<component
|
|
86
|
-
:is="node.tagName"
|
|
87
|
-
v-bind="getAttrs(node.attrs)"
|
|
88
|
-
:class="['incremark-html-element', `incremark-${node.tagName}`]"
|
|
89
|
-
>
|
|
90
|
-
<!-- 自闭合元素没有子节点 -->
|
|
91
|
-
<template v-if="!isVoidElement(node.tagName)">
|
|
92
|
-
<!-- 如果子节点都是行内内容,使用 IncremarkInline -->
|
|
93
|
-
<template v-if="hasOnlyInlineChildren(node.children)">
|
|
94
|
-
<IncremarkInline :nodes="(node.children as PhrasingContent[])" />
|
|
95
|
-
</template>
|
|
96
|
-
|
|
97
|
-
<!-- 否则递归渲染每个子节点 -->
|
|
98
|
-
<template v-else>
|
|
99
|
-
<template v-for="(child, idx) in node.children" :key="idx">
|
|
100
|
-
<!-- 如果子节点是 htmlElement,递归 -->
|
|
101
|
-
<IncremarkHtmlElement
|
|
102
|
-
v-if="child.type === 'htmlElement'"
|
|
103
|
-
:node="(child as unknown as HtmlElementNode)"
|
|
104
|
-
/>
|
|
105
|
-
<!-- 如果是文本节点 -->
|
|
106
|
-
<template v-else-if="child.type === 'text'">
|
|
107
|
-
{{ (child as any).value }}
|
|
108
|
-
</template>
|
|
109
|
-
<!-- 其他类型尝试用 IncremarkInline -->
|
|
110
|
-
<IncremarkInline
|
|
111
|
-
v-else-if="['strong', 'emphasis', 'inlineCode', 'link', 'image', 'break'].includes(child.type)"
|
|
112
|
-
:nodes="[child as PhrasingContent]"
|
|
113
|
-
/>
|
|
114
|
-
<!-- 段落等块级元素 -->
|
|
115
|
-
<template v-else-if="child.type === 'paragraph'">
|
|
116
|
-
<p><IncremarkInline :nodes="((child as any).children as PhrasingContent[])" /></p>
|
|
117
|
-
</template>
|
|
118
|
-
<!-- 其他未知类型,显示原始 -->
|
|
119
|
-
<template v-else>
|
|
120
|
-
<div class="incremark-unknown-child">{{ child.type }}</div>
|
|
121
|
-
</template>
|
|
122
|
-
</template>
|
|
123
|
-
</template>
|
|
124
|
-
</template>
|
|
125
|
-
</component>
|
|
126
|
-
</template>
|
|
127
|
-
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { PhrasingContent, RootContent, ImageReference, LinkReference } from 'mdast'
|
|
3
|
-
import type { TextChunk } from '@incremark/core'
|
|
4
|
-
import {
|
|
5
|
-
type TextNodeWithChunks,
|
|
6
|
-
hasChunks,
|
|
7
|
-
getStableText,
|
|
8
|
-
isHtmlNode
|
|
9
|
-
} from '@incremark/shared'
|
|
10
|
-
import IncremarkMath from './IncremarkMath.vue'
|
|
11
|
-
import IncremarkHtmlElement from './IncremarkHtmlElement.vue'
|
|
12
|
-
import { useDefinationsContext } from '../composables/useDefinationsContext'
|
|
13
|
-
|
|
14
|
-
// Math 节点类型
|
|
15
|
-
interface MathNode {
|
|
16
|
-
type: 'math' | 'inlineMath'
|
|
17
|
-
value: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// HtmlElement 节点类型
|
|
21
|
-
interface HtmlElementNode {
|
|
22
|
-
type: 'htmlElement'
|
|
23
|
-
tagName: string
|
|
24
|
-
attrs: Record<string, string>
|
|
25
|
-
children: RootContent[]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* 类型守卫:检查是否是 htmlElement 节点
|
|
30
|
-
*/
|
|
31
|
-
function isHtmlElementNode(node: PhrasingContent): node is PhrasingContent & HtmlElementNode {
|
|
32
|
-
return (node as unknown as HtmlElementNode).type === 'htmlElement'
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 类型守卫:检查是否是 imageReference 节点
|
|
37
|
-
*/
|
|
38
|
-
function isImageReference(node: PhrasingContent): node is ImageReference {
|
|
39
|
-
return node.type === 'imageReference'
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 类型守卫:检查是否是 linkReference 节点
|
|
44
|
-
*/
|
|
45
|
-
function isLinkReference(node: PhrasingContent): node is LinkReference {
|
|
46
|
-
return node.type === 'linkReference'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const props = defineProps<{
|
|
50
|
-
nodes: PhrasingContent[]
|
|
51
|
-
}>()
|
|
52
|
-
|
|
53
|
-
const {
|
|
54
|
-
definations,
|
|
55
|
-
footnoteDefinitions
|
|
56
|
-
} = useDefinationsContext()
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 获取节点的 chunks(类型安全)
|
|
60
|
-
*/
|
|
61
|
-
function getChunks(node: PhrasingContent): TextChunk[] | undefined {
|
|
62
|
-
if (hasChunks(node)) {
|
|
63
|
-
return (node as TextNodeWithChunks).chunks
|
|
64
|
-
}
|
|
65
|
-
return undefined
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 类型守卫:检查是否是 inlineMath 节点
|
|
70
|
-
* inlineMath 是 mdast-util-math 扩展的类型,不在标准 PhrasingContent 中
|
|
71
|
-
*/
|
|
72
|
-
function isInlineMath(node: PhrasingContent): node is PhrasingContent & MathNode {
|
|
73
|
-
return (node as unknown as MathNode).type === 'inlineMath'
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
</script>
|
|
77
|
-
|
|
78
|
-
<template>
|
|
79
|
-
<template v-for="(node, idx) in nodes" :key="idx">
|
|
80
|
-
<!-- 文本(支持 chunks 渐入动画) -->
|
|
81
|
-
<template v-if="node.type === 'text'">
|
|
82
|
-
<!-- 稳定文本(已经显示过的部分,无动画) -->
|
|
83
|
-
{{ getStableText(node as TextNodeWithChunks) }}
|
|
84
|
-
<!-- 新增的 chunk 部分(带渐入动画) -->
|
|
85
|
-
<span
|
|
86
|
-
v-for="chunk in getChunks(node)"
|
|
87
|
-
:key="chunk.createdAt"
|
|
88
|
-
class="incremark-fade-in"
|
|
89
|
-
>{{ chunk.text }}</span>
|
|
90
|
-
</template>
|
|
91
|
-
|
|
92
|
-
<!-- 行内公式 -->
|
|
93
|
-
<IncremarkMath v-else-if="isInlineMath(node)" :node="(node as unknown as MathNode)" />
|
|
94
|
-
|
|
95
|
-
<!-- htmlElement 节点(结构化的 HTML 元素) -->
|
|
96
|
-
<IncremarkHtmlElement
|
|
97
|
-
v-else-if="isHtmlElementNode(node)"
|
|
98
|
-
:node="(node as unknown as HtmlElementNode)"
|
|
99
|
-
/>
|
|
100
|
-
|
|
101
|
-
<!-- HTML 节点(原始 HTML,如未启用 htmlTree 选项) -->
|
|
102
|
-
<span v-else-if="isHtmlNode(node)" style="display: contents;" v-html="(node as any).value"></span>
|
|
103
|
-
|
|
104
|
-
<!-- 加粗 -->
|
|
105
|
-
<strong v-else-if="node.type === 'strong'">
|
|
106
|
-
<IncremarkInline :nodes="(node.children as PhrasingContent[])" />
|
|
107
|
-
</strong>
|
|
108
|
-
|
|
109
|
-
<!-- 斜体 -->
|
|
110
|
-
<em v-else-if="node.type === 'emphasis'">
|
|
111
|
-
<IncremarkInline :nodes="(node.children as PhrasingContent[])" />
|
|
112
|
-
</em>
|
|
113
|
-
|
|
114
|
-
<!-- 行内代码 -->
|
|
115
|
-
<code v-else-if="node.type === 'inlineCode'" class="incremark-inline-code">{{ (node as any).value }}</code>
|
|
116
|
-
|
|
117
|
-
<!-- 链接 -->
|
|
118
|
-
<a
|
|
119
|
-
v-else-if="node.type === 'link'"
|
|
120
|
-
class="incremark-link"
|
|
121
|
-
:href="node.url"
|
|
122
|
-
target="_blank"
|
|
123
|
-
rel="noopener noreferrer"
|
|
124
|
-
>
|
|
125
|
-
<IncremarkInline :nodes="(node.children as PhrasingContent[])" />
|
|
126
|
-
</a>
|
|
127
|
-
|
|
128
|
-
<!-- 图片 -->
|
|
129
|
-
<img
|
|
130
|
-
v-else-if="node.type === 'image'"
|
|
131
|
-
class="incremark-image"
|
|
132
|
-
:src="node.url"
|
|
133
|
-
:alt="node.alt || ''"
|
|
134
|
-
:title="(node as any).title || undefined"
|
|
135
|
-
loading="lazy"
|
|
136
|
-
/>
|
|
137
|
-
|
|
138
|
-
<!-- 引用式图片(imageReference) -->
|
|
139
|
-
<template v-else-if="isImageReference(node)">
|
|
140
|
-
<img
|
|
141
|
-
v-if="definations[node.identifier]"
|
|
142
|
-
class="incremark-image incremark-reference-image"
|
|
143
|
-
:src="definations[node.identifier].url"
|
|
144
|
-
:alt="(node as ImageReference).alt || ''"
|
|
145
|
-
:title="definations[node.identifier].title || undefined"
|
|
146
|
-
loading="lazy"
|
|
147
|
-
/>
|
|
148
|
-
<!-- 如果没有找到定义,渲染为原始文本(降级处理) -->
|
|
149
|
-
<span v-else class="incremark-image-ref-missing">
|
|
150
|
-
![{{ (node as ImageReference).alt }}][{{ (node as ImageReference).identifier || (node as ImageReference).label }}]
|
|
151
|
-
</span>
|
|
152
|
-
</template>
|
|
153
|
-
|
|
154
|
-
<!-- 引用式链接(linkReference) -->
|
|
155
|
-
<template v-else-if="isLinkReference(node)">
|
|
156
|
-
<a
|
|
157
|
-
v-if="definations[node.identifier]"
|
|
158
|
-
class="incremark-link incremark-reference-link"
|
|
159
|
-
:href="definations[node.identifier].url"
|
|
160
|
-
:title="definations[node.identifier].title || undefined"
|
|
161
|
-
target="_blank"
|
|
162
|
-
rel="noopener noreferrer"
|
|
163
|
-
>
|
|
164
|
-
<IncremarkInline :nodes="((node as LinkReference).children as PhrasingContent[])" />
|
|
165
|
-
</a>
|
|
166
|
-
<!-- 如果没有找到定义,渲染为原始文本(降级处理) -->
|
|
167
|
-
<span v-else class="incremark-link-ref-missing">
|
|
168
|
-
[{{ ((node as LinkReference).children as any[]).map((c: any) => c.value).join('') }}][{{ (node as LinkReference).identifier || (node as LinkReference).label }}]
|
|
169
|
-
</span>
|
|
170
|
-
</template>
|
|
171
|
-
|
|
172
|
-
<!-- 脚注引用(footnoteReference) -->
|
|
173
|
-
<sup v-else-if="node.type === 'footnoteReference'" class="incremark-footnote-ref">
|
|
174
|
-
<a :href="`#fn-${(node as any).identifier}`" :id="`fnref-${(node as any).identifier}`">
|
|
175
|
-
[{{ (node as any).identifier }}]
|
|
176
|
-
</a>
|
|
177
|
-
</sup>
|
|
178
|
-
|
|
179
|
-
<!-- 换行 -->
|
|
180
|
-
<br v-else-if="node.type === 'break'" />
|
|
181
|
-
|
|
182
|
-
<!-- 删除线 -->
|
|
183
|
-
<del v-else-if="node.type === 'delete'">
|
|
184
|
-
<IncremarkInline :nodes="(node.children as PhrasingContent[])" />
|
|
185
|
-
</del>
|
|
186
|
-
</template>
|
|
187
|
-
</template>
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { List, ListItem, PhrasingContent } from 'mdast'
|
|
3
|
-
import { computed } from 'vue'
|
|
4
|
-
import IncremarkInline from './IncremarkInline.vue'
|
|
5
|
-
|
|
6
|
-
const props = defineProps<{
|
|
7
|
-
node: List
|
|
8
|
-
}>()
|
|
9
|
-
|
|
10
|
-
const tag = computed(() => props.node.ordered ? 'ol' : 'ul')
|
|
11
|
-
|
|
12
|
-
function getItemContent(item: ListItem): PhrasingContent[] {
|
|
13
|
-
const firstChild = item.children[0]
|
|
14
|
-
if (firstChild?.type === 'paragraph') {
|
|
15
|
-
return firstChild.children as PhrasingContent[]
|
|
16
|
-
}
|
|
17
|
-
return []
|
|
18
|
-
}
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<template>
|
|
22
|
-
<component :is="tag" class="incremark-list" :class="{ 'task-list': node.children.some(item => item.checked !== null && item.checked !== undefined) }">
|
|
23
|
-
<li
|
|
24
|
-
v-for="(item, index) in node.children"
|
|
25
|
-
:key="index"
|
|
26
|
-
class="incremark-list-item"
|
|
27
|
-
:class="{ 'task-item': item.checked !== null && item.checked !== undefined }"
|
|
28
|
-
>
|
|
29
|
-
<label v-if="item.checked !== null && item.checked !== undefined" class="task-label">
|
|
30
|
-
<input
|
|
31
|
-
type="checkbox"
|
|
32
|
-
:checked="item.checked"
|
|
33
|
-
disabled
|
|
34
|
-
class="checkbox"
|
|
35
|
-
/>
|
|
36
|
-
<span class="task-content">
|
|
37
|
-
<IncremarkInline :nodes="getItemContent(item)" />
|
|
38
|
-
</span>
|
|
39
|
-
</label>
|
|
40
|
-
<template v-else>
|
|
41
|
-
<IncremarkInline :nodes="getItemContent(item)" />
|
|
42
|
-
</template>
|
|
43
|
-
</li>
|
|
44
|
-
</component>
|
|
45
|
-
</template>
|
|
46
|
-
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, ref, watch, shallowRef, onUnmounted } from 'vue'
|
|
3
|
-
|
|
4
|
-
// Math 节点类型(来自 mdast-util-math)
|
|
5
|
-
interface MathNode {
|
|
6
|
-
type: 'math' | 'inlineMath'
|
|
7
|
-
value: string
|
|
8
|
-
data?: {
|
|
9
|
-
hName?: string
|
|
10
|
-
hProperties?: Record<string, any>
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const props = withDefaults(
|
|
15
|
-
defineProps<{
|
|
16
|
-
node: MathNode
|
|
17
|
-
/** 渲染延迟(毫秒),用于流式输入时防抖 */
|
|
18
|
-
renderDelay?: number
|
|
19
|
-
}>(),
|
|
20
|
-
{
|
|
21
|
-
renderDelay: 300
|
|
22
|
-
}
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
const renderedHtml = ref('')
|
|
26
|
-
const renderError = ref('')
|
|
27
|
-
const isLoading = ref(false)
|
|
28
|
-
const katexRef = shallowRef<any>(null)
|
|
29
|
-
let renderTimer: ReturnType<typeof setTimeout> | null = null
|
|
30
|
-
|
|
31
|
-
const isInline = computed(() => props.node.type === 'inlineMath')
|
|
32
|
-
const formula = computed(() => props.node.value)
|
|
33
|
-
|
|
34
|
-
// 带防抖动的渲染
|
|
35
|
-
function scheduleRender() {
|
|
36
|
-
if (!formula.value) {
|
|
37
|
-
renderedHtml.value = ''
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 清除之前的定时器
|
|
42
|
-
if (renderTimer) {
|
|
43
|
-
clearTimeout(renderTimer)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
isLoading.value = true
|
|
47
|
-
|
|
48
|
-
// 防抖动延迟渲染
|
|
49
|
-
renderTimer = setTimeout(() => {
|
|
50
|
-
doRender()
|
|
51
|
-
}, props.renderDelay)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function doRender() {
|
|
55
|
-
if (!formula.value) return
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
// 动态导入 KaTeX
|
|
59
|
-
if (!katexRef.value) {
|
|
60
|
-
// @ts-ignore - katex 是可选依赖
|
|
61
|
-
const katexModule = await import('katex')
|
|
62
|
-
katexRef.value = katexModule.default
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const katex = katexRef.value
|
|
66
|
-
renderedHtml.value = katex.renderToString(formula.value, {
|
|
67
|
-
displayMode: !isInline.value,
|
|
68
|
-
throwOnError: false,
|
|
69
|
-
strict: false
|
|
70
|
-
})
|
|
71
|
-
renderError.value = ''
|
|
72
|
-
} catch (e: any) {
|
|
73
|
-
// 静默失败,可能是公式不完整
|
|
74
|
-
renderError.value = ''
|
|
75
|
-
renderedHtml.value = ''
|
|
76
|
-
} finally {
|
|
77
|
-
isLoading.value = false
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
onUnmounted(() => {
|
|
82
|
-
if (renderTimer) {
|
|
83
|
-
clearTimeout(renderTimer)
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
watch(formula, scheduleRender, { immediate: true })
|
|
88
|
-
</script>
|
|
89
|
-
|
|
90
|
-
<template>
|
|
91
|
-
<!-- 行内公式 -->
|
|
92
|
-
<span v-if="isInline" class="incremark-math-inline">
|
|
93
|
-
<!-- 渲染成功 -->
|
|
94
|
-
<span v-if="renderedHtml && !isLoading" v-html="renderedHtml" />
|
|
95
|
-
<!-- 加载中或未渲染:显示源码 -->
|
|
96
|
-
<code v-else class="math-source">{{ formula }}</code>
|
|
97
|
-
</span>
|
|
98
|
-
<!-- 块级公式 -->
|
|
99
|
-
<div v-else class="incremark-math-block">
|
|
100
|
-
<!-- 渲染成功 -->
|
|
101
|
-
<div v-if="renderedHtml && !isLoading" v-html="renderedHtml" class="math-rendered" />
|
|
102
|
-
<!-- 加载中或未渲染:显示源码 -->
|
|
103
|
-
<pre v-else class="math-source-block"><code>{{ formula }}</code></pre>
|
|
104
|
-
</div>
|
|
105
|
-
</template>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { Paragraph } from 'mdast'
|
|
3
|
-
import IncremarkInline from './IncremarkInline.vue'
|
|
4
|
-
|
|
5
|
-
defineProps<{
|
|
6
|
-
node: Paragraph
|
|
7
|
-
}>()
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<template>
|
|
11
|
-
<p class="incremark-paragraph">
|
|
12
|
-
<IncremarkInline :nodes="node.children" />
|
|
13
|
-
</p>
|
|
14
|
-
</template>
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { RootContent, HTML } from 'mdast'
|
|
3
|
-
import type { Component } from 'vue'
|
|
4
|
-
import IncremarkHeading from './IncremarkHeading.vue'
|
|
5
|
-
import IncremarkParagraph from './IncremarkParagraph.vue'
|
|
6
|
-
import IncremarkCode from './IncremarkCode.vue'
|
|
7
|
-
import IncremarkList from './IncremarkList.vue'
|
|
8
|
-
import IncremarkTable from './IncremarkTable.vue'
|
|
9
|
-
import IncremarkBlockquote from './IncremarkBlockquote.vue'
|
|
10
|
-
import IncremarkThematicBreak from './IncremarkThematicBreak.vue'
|
|
11
|
-
import IncremarkMath from './IncremarkMath.vue'
|
|
12
|
-
import IncremarkHtmlElement from './IncremarkHtmlElement.vue'
|
|
13
|
-
import IncremarkDefault from './IncremarkDefault.vue'
|
|
14
|
-
|
|
15
|
-
const props = defineProps<{
|
|
16
|
-
node: RootContent
|
|
17
|
-
}>()
|
|
18
|
-
|
|
19
|
-
const componentMap: Record<string, Component> = {
|
|
20
|
-
heading: IncremarkHeading,
|
|
21
|
-
paragraph: IncremarkParagraph,
|
|
22
|
-
code: IncremarkCode,
|
|
23
|
-
list: IncremarkList,
|
|
24
|
-
table: IncremarkTable,
|
|
25
|
-
blockquote: IncremarkBlockquote,
|
|
26
|
-
thematicBreak: IncremarkThematicBreak,
|
|
27
|
-
math: IncremarkMath,
|
|
28
|
-
inlineMath: IncremarkMath,
|
|
29
|
-
htmlElement: IncremarkHtmlElement,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getComponent(type: string): Component {
|
|
33
|
-
return componentMap[type] || IncremarkDefault
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 检查是否是 html 节点
|
|
38
|
-
*/
|
|
39
|
-
function isHtmlNode(node: RootContent): node is HTML {
|
|
40
|
-
return node.type === 'html'
|
|
41
|
-
}
|
|
42
|
-
</script>
|
|
43
|
-
|
|
44
|
-
<template>
|
|
45
|
-
<!-- HTML 节点:渲染为代码块显示源代码 -->
|
|
46
|
-
<pre v-if="isHtmlNode(node)" class="incremark-html-code"><code>{{ (node as HTML).value }}</code></pre>
|
|
47
|
-
<!-- 其他节点:使用对应组件 -->
|
|
48
|
-
<component v-else :is="getComponent(node.type)" :node="node" />
|
|
49
|
-
</template>
|
|
50
|
-
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { Table, TableCell, PhrasingContent } from 'mdast'
|
|
3
|
-
import IncremarkInline from './IncremarkInline.vue'
|
|
4
|
-
|
|
5
|
-
defineProps<{
|
|
6
|
-
node: Table
|
|
7
|
-
}>()
|
|
8
|
-
|
|
9
|
-
function getCellContent(cell: TableCell): PhrasingContent[] {
|
|
10
|
-
return cell.children as PhrasingContent[]
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<template>
|
|
15
|
-
<div class="incremark-table-wrapper">
|
|
16
|
-
<table class="incremark-table">
|
|
17
|
-
<thead>
|
|
18
|
-
<tr v-if="node.children[0]">
|
|
19
|
-
<th
|
|
20
|
-
v-for="(cell, cellIndex) in node.children[0].children"
|
|
21
|
-
:key="cellIndex"
|
|
22
|
-
:style="{ textAlign: node.align?.[cellIndex] || 'left' }"
|
|
23
|
-
>
|
|
24
|
-
<IncremarkInline :nodes="getCellContent(cell)" />
|
|
25
|
-
</th>
|
|
26
|
-
</tr>
|
|
27
|
-
</thead>
|
|
28
|
-
<tbody>
|
|
29
|
-
<tr v-for="(row, rowIndex) in node.children.slice(1)" :key="rowIndex">
|
|
30
|
-
<td
|
|
31
|
-
v-for="(cell, cellIndex) in row.children"
|
|
32
|
-
:key="cellIndex"
|
|
33
|
-
:style="{ textAlign: node.align?.[cellIndex] || 'left' }"
|
|
34
|
-
>
|
|
35
|
-
<IncremarkInline :nodes="getCellContent(cell)" />
|
|
36
|
-
</td>
|
|
37
|
-
</tr>
|
|
38
|
-
</tbody>
|
|
39
|
-
</table>
|
|
40
|
-
</div>
|
|
41
|
-
</template>
|
|
42
|
-
|
package/src/composables/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export { useIncremark } from './useIncremark'
|
|
2
|
-
export type { UseIncremarkOptions, TypewriterOptions, TypewriterControls } from './useIncremark'
|
|
3
|
-
|
|
4
|
-
export { useStreamRenderer } from './useStreamRenderer'
|
|
5
|
-
export type { UseStreamRendererOptions } from './useStreamRenderer'
|
|
6
|
-
|
|
7
|
-
export { useDevTools } from './useDevTools'
|
|
8
|
-
export type { UseDevToolsOptions } from './useDevTools'
|
|
9
|
-
|
|
10
|
-
export { useBlockTransformer } from './useBlockTransformer'
|
|
11
|
-
export type { UseBlockTransformerOptions, UseBlockTransformerReturn } from './useBlockTransformer'
|