diff --git a/docs/notes/theme/guide/markdown/内容注释.md b/docs/notes/theme/guide/markdown/内容注释.md index 6506dba6..ef5804a0 100644 --- a/docs/notes/theme/guide/markdown/内容注释.md +++ b/docs/notes/theme/guide/markdown/内容注释.md @@ -7,7 +7,7 @@ permalink: /guide/markdown/annotation/ ## 描述 -Annotation(注释) 是 Markdown 中的一种特殊的语法,用于在文档中添加额外的信息、说明或者提示。 +==Annotation(注释)== 是 Markdown 中的一种特殊的语法,用于在文档中添加额外的信息、说明或者提示。 注释不会直接显示在文档中,需要用户手动点击才会显示。 @@ -34,49 +34,120 @@ export default defineUserConfig({ ## 语法 -Annotation(注释) 语法有两个部分组成: +==Annotation(注释)== 语法由两个部分组成: -- **行内注释:** 在行内通过 `[+label]` 语法插入注释。 -- **定义注释:** 在文档单独一行中使用 `[+label]: 内容` 语法定义注释。 +### 行内注释 + +在行内通过 `[+label]` 语法插入注释标签。 + +注释标签由 `[+` + `label` + `]` 组成。为方便与内容做区分,在 `[+label]` 的左边边缘应该有一个空格。 + +`label` 为注释的标签,可以是任意字符串。 ::: important 符号 `+` 是必须的 - -- 行内注释语法由 `[+` + `label` + `]` 组成。 -- 定义注释语法由 `[+` + `label` + `]:` + `内容` 组成。 ::: +### 定义注释 + +在文档的单独区域中使用 `[+label]:` 语法开始定义注释。 + +注释定义区域由 `[+` + `label` + `]:` + `内容` 组成。 + +`label` 应该与上述的 `[+label]` 一致,用于标记注释的标签。 + +**内容** 可以跟随在 `:` 之后开始写: + +```md +[+label]: 这里是内容,可以使用 **Markdown** 语法。 +``` + +**内容** 也可以从下一行开始写,但需要添加缩进,多行时应该保持一致的缩进。 + +```md +[+label]: + 这里是内容。 + 缩进一致,此行也是内容。 + + 即使上一行空行,但此行缩进也是一致的,也是内容。 + 可以使用 **Markdown** 语法。 + +此行不再缩进,该标签的注释定义在上一行结束。 +``` + +定义注释的内容不会直接渲染在文档中,而是在 行内注释 的 `[+label]` 被点击后呈现。 + +## 示例 + +### 示例一 + **输入:** ```md 站点由 VuePress [+vuepress] 驱动。 -[+vuepress]: VuePress 是一个 [静态站点生成器](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) 。 专为构建快速、以内容为中心的站点而设计。 +[+vuepress]: + VuePress 是一个 [静态站点生成器](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) 。 + 专为构建快速、以内容为中心的站点而设计。 ``` **输出:** 站点由 VuePress [+vuepress] 驱动。 -[+vuepress]: VuePress 是一个 [静态站点生成器](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) 。 专为构建快速、以内容为中心的站点而设计。 +[+vuepress]: + VuePress 是一个 [静态站点生成器](https://en.wikipedia.org/wiki/Static_site_generator) (SSG) 。 + 专为构建快速、以内容为中心的站点而设计。 -**还可以为 `label` 定义多个注释,多个定义将会以列表的形式渲染。** +### 示例二 + +**同一个 `label` 定义多个注释,多个定义以列表的形式渲染。** **输入:** ```md 中国古代 **四大名著** [+名著] 家喻户晓。 -[+名著]: **《三国演义》:** 以三国时期的历史为背景,描写了魏、蜀、吴三国之间的政治、军事斗争,塑造了诸葛亮、曹操、关羽、刘备等众多历史人物形象。 -[+名著]: **《西游记》:** 讲述了唐僧师徒四人(孙悟空、猪八戒、沙僧、白龙马)西天取经的故事,充满了神话色彩和奇幻冒险。 -[+名著]: **《红楼梦》:** 以贾、史、王、薛四大家族的兴衰为背景,描写了贾宝玉、林黛玉、薛宝钗等人的爱情悲剧,展现了封建社会的腐朽与没落。 -[+名著]: **《水浒传》:** 描写了北宋末年以宋江为首的108位好汉在梁山泊聚义,反抗朝廷的故事,展现了官逼民反的社会现实。 +[+名著]: + **《三国演义》:** + + 以三国时期的历史为背景,描写了魏、蜀、吴三国之间的政治、军事斗争,塑造了诸葛亮、曹操、关羽、刘备等众多历史人物形象。 + +[+名著]: + **《西游记》:** + + 讲述了唐僧师徒四人(孙悟空、猪八戒、沙僧、白龙马)西天取经的故事,充满了神话色彩和奇幻冒险。 + +[+名著]: + **《红楼梦》:** + + 以贾、史、王、薛四大家族的兴衰为背景,描写了贾宝玉、林黛玉、薛宝钗等人的爱情悲剧,展现了封建社会的腐朽与没落。 + +[+名著]: + **《水浒传》:** + + 描写了北宋末年以宋江为首的108位好汉在梁山泊聚义,反抗朝廷的故事,展现了官逼民反的社会现实。 ``` **输出:** 中国古代 **四大名著** [+名著] 家喻户晓。 -[+名著]: **《三国演义》:** 以三国时期的历史为背景,描写了魏、蜀、吴三国之间的政治、军事斗争,塑造了诸葛亮、曹操、关羽、刘备等众多历史人物形象。 -[+名著]: **《西游记》:** 讲述了唐僧师徒四人(孙悟空、猪八戒、沙僧、白龙马)西天取经的故事,充满了神话色彩和奇幻冒险。 -[+名著]: **《红楼梦》:** 以贾、史、王、薛四大家族的兴衰为背景,描写了贾宝玉、林黛玉、薛宝钗等人的爱情悲剧,展现了封建社会的腐朽与没落。 -[+名著]: **《水浒传》:** 描写了北宋末年以宋江为首的108位好汉在梁山泊聚义,反抗朝廷的故事,展现了官逼民反的社会现实。 +[+名著]: + **《三国演义》:** + + 以三国时期的历史为背景,描写了魏、蜀、吴三国之间的政治、军事斗争,塑造了诸葛亮、曹操、关羽、刘备等众多历史人物形象。 + +[+名著]: + **《西游记》:** + + 讲述了唐僧师徒四人(孙悟空、猪八戒、沙僧、白龙马)西天取经的故事,充满了神话色彩和奇幻冒险。 + +[+名著]: + **《红楼梦》:** + + 以贾、史、王、薛四大家族的兴衰为背景,描写了贾宝玉、林黛玉、薛宝钗等人的爱情悲剧,展现了封建社会的腐朽与没落。 + +[+名著]: + **《水浒传》:** + + 描写了北宋末年以宋江为首的108位好汉在梁山泊聚义,反抗朝廷的故事,展现了官逼民反的社会现实。 diff --git a/plugins/plugin-md-power/src/client/components/Annotation.vue b/plugins/plugin-md-power/src/client/components/Annotation.vue index 4966a08b..ab31402f 100644 --- a/plugins/plugin-md-power/src/client/components/Annotation.vue +++ b/plugins/plugin-md-power/src/client/components/Annotation.vue @@ -24,32 +24,41 @@ function updatePosition() { return const { x: _x, y: _y, width: w, height: h } = button.value.getBoundingClientRect() const x = _x + w / 2 - const y = _y + h + const y = _y + h / 2 const { width, height } = popover.value.getBoundingClientRect() const { clientWidth, clientHeight } = document.documentElement position.value.x = x + width + 16 > clientWidth ? clientWidth - x - width - 16 : 0 - position.value.y = y + height + 16 > clientHeight ? clientHeight - y - height - 16 : 0 + + if (y > clientHeight - 16) { + active.value = false + } + else { + position.value.y = y + height + 16 > clientHeight ? clientHeight - y - height - 16 : 0 + } } watch(active, () => nextTick(updatePosition)) useEventListener('resize', updatePosition) +useEventListener('scroll', updatePosition, { passive: true }) @@ -94,13 +103,15 @@ useEventListener('resize', updatePosition) max-width: min(calc(100vw - 32px), 360px); max-height: 360px; padding: 8px 12px; - overflow-y: auto; + overflow: auto; font-size: 14px; + font-weight: normal; background-color: var(--vp-c-bg); border: solid 1px var(--vp-c-divider); border-radius: 4px; box-shadow: var(--vp-shadow-2); - transform: translateX(var(--vp-annotation-x, 0)) translateY(var(--vp-annotation-y, 0)); + transform: translateX(var(--vp-annotation-x, 0)) translateY(var(--vp-annotation-y, 0)) translateZ(0); + will-change: transform; } .annotations-popover.list { @@ -117,4 +128,25 @@ useEventListener('resize', updatePosition) border-radius: 4px; box-shadow: var(--vp-shadow-1); } + +.annotations-popover :deep(p) { + margin: 12px 0; + line-height: 24px; +} + +.annotations-popover :deep(:first-child) { + margin-top: 4px; +} + +.annotations-popover :deep(:last-child) { + margin-bottom: 4px; +} + +.annotations-popover.list :deep(:first-child) { + margin-top: 8px; +} + +.annotations-popover.list :deep(:last-child) { + margin-bottom: 8px; +} diff --git a/plugins/plugin-md-power/src/node/inline/annotation.ts b/plugins/plugin-md-power/src/node/inline/annotation.ts index e5023a8e..9a261082 100644 --- a/plugins/plugin-md-power/src/node/inline/annotation.ts +++ b/plugins/plugin-md-power/src/node/inline/annotation.ts @@ -8,12 +8,14 @@ import type Token from 'markdown-it/lib/token.mjs' interface AnnotationToken extends Token { meta: { label: string - annotations: string[] } } interface AnnotationEnv extends Record { - annotations: Record + annotations: Record } interface AnnotationStateBlock extends StateBlock { @@ -55,7 +57,7 @@ const annotationDef: RuleBlock = ( } if ( - // empty footnote label + // empty annotation label pos === start + 2 || pos + 1 >= max || state.src.charAt(++pos) !== ':' @@ -68,16 +70,30 @@ const annotationDef: RuleBlock = ( pos++ - state.env.annotations ??= {} - + const data = state.env.annotations ??= {} const label = state.src.slice(start + 2, pos - 2) - const annotation = state.src.slice(pos, max).trim() - state.env.annotations[`:${label}`] ??= [] + let annotation = state.src.slice(pos, max).trim() - state.env.annotations[`:${label}`].push(annotation) + // 处理多行注释 + let nextLine = startLine + 1 + while (nextLine < endLine) { + const nextStart = state.bMarks[nextLine] + state.tShift[nextLine] + const nextMax = state.eMarks[nextLine] + const source = state.src.slice(nextStart, nextMax).trim() - state.line += 1 + // 行不为空,且行缩进小于块缩进,则跳出 + if (state.sCount[nextLine] < state.blkIndent + 2 && source !== '') + break + + annotation += `\n${source}` + nextLine++ + } + + const current = data[`:${label}`] ??= { sources: [], rendered: [] } + current.sources.push(annotation) + + state.line = nextLine return true } @@ -120,7 +136,7 @@ const annotationRef: RuleInline = ( pos++ const label = state.src.slice(start + 2, pos - 1) - const annotations = state.env.annotations?.[`:${label}`] ?? [] + const annotations = state.env.annotations?.[`:${label}`]?.sources ?? [] if (annotations.length === 0) return false @@ -128,10 +144,7 @@ const annotationRef: RuleInline = ( if (!silent) { const refToken = state.push('annotation_ref', '', 0) - refToken.meta = { - label, - annotations, - } as AnnotationToken['meta'] + refToken.meta = { label } as AnnotationToken['meta'] } state.pos = pos @@ -144,13 +157,17 @@ export const annotationPlugin: PluginSimple = (md) => { md.renderer.rules.annotation_ref = ( tokens: AnnotationToken[], idx: number, + _, + env: AnnotationEnv, ) => { - const { label = '', annotations = [] } = tokens[idx].meta ?? {} - return ` - ${annotations.map((annotation, i) => { - return `` - }).join('\n')} - ` + const label = tokens[idx].meta.label + const data = env.annotations[`:${label}`] + + return `${ + data.sources.map((source, i) => { + const annotation = data.rendered[i] ??= md.render(source, env) + return `` + }).join('')}` } md.inline.ruler.before('image', 'annotation_ref', annotationRef)