diff --git a/apps/app/resource/locales/en_US/sandbox-markdown.md b/apps/app/resource/locales/en_US/sandbox-markdown.md index 78d72984dc0..236fd2dac5c 100644 --- a/apps/app/resource/locales/en_US/sandbox-markdown.md +++ b/apps/app/resource/locales/en_US/sandbox-markdown.md @@ -33,6 +33,26 @@ > Advises about risks or negative outcomes of certain actions. ``` +You can also use [directive syntax](https://talk.commonmark.org/t/generic-directives-plugins-syntax/444). + +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: + +```markdown +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: +``` + # Quote text - Use quoted expressions by putting `>` at the beginning of the paragraph diff --git a/apps/app/resource/locales/fr_FR/sandbox-markdown.md b/apps/app/resource/locales/fr_FR/sandbox-markdown.md index 3f0a25cbd71..af073ea4213 100644 --- a/apps/app/resource/locales/fr_FR/sandbox-markdown.md +++ b/apps/app/resource/locales/fr_FR/sandbox-markdown.md @@ -33,6 +33,26 @@ > Advises about risks or negative outcomes of certain actions. ``` +Vous pouvez également utiliser la [syntaxe de directive](https://talk.commonmark.org/t/generic-directives-plugins-syntax/444). + +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: + +```markdown +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: +``` + # Autres ## Citations diff --git a/apps/app/resource/locales/ja_JP/sandbox-markdown.md b/apps/app/resource/locales/ja_JP/sandbox-markdown.md index 7f86abaa553..c524624edd7 100644 --- a/apps/app/resource/locales/ja_JP/sandbox-markdown.md +++ b/apps/app/resource/locales/ja_JP/sandbox-markdown.md @@ -33,6 +33,26 @@ > Advises about risks or negative outcomes of certain actions. ``` +[directive](https://talk.commonmark.org/t/generic-directives-plugins-syntax/444) を使って記述することもできます。 + +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: + +```markdown +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: +``` + # テキストの引用 - 行頭に `>` を記述することで引用表現を記述できます diff --git a/apps/app/resource/locales/zh_CN/sandbox-markdown.md b/apps/app/resource/locales/zh_CN/sandbox-markdown.md index 4603059afd1..2625c865165 100644 --- a/apps/app/resource/locales/zh_CN/sandbox-markdown.md +++ b/apps/app/resource/locales/zh_CN/sandbox-markdown.md @@ -33,6 +33,26 @@ > Advises about risks or negative outcomes of certain actions. ``` +您还可以使用[directive 语法](https://talk.commonmark.org/t/generic-directives-plugins-syntax/444)。 + +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: + +```markdown +:::note +Useful information that users should know, even when skimming content. +::: + +:::tip[Custom Label] +Useful information that users should know, even when skimming content. +::: +``` + # 引用 - 在段落开头放置 `>` 即可使用带引号的表达式 diff --git a/apps/app/src/features/callout/components/CalloutViewer.tsx b/apps/app/src/features/callout/components/CalloutViewer.tsx index 7e92a1fbfec..8c84efd1557 100644 --- a/apps/app/src/features/callout/components/CalloutViewer.tsx +++ b/apps/app/src/features/callout/components/CalloutViewer.tsx @@ -13,7 +13,7 @@ type CALLOUT_TO = { [key in Callout]: string; } -const CALLOUT_TO_TITLE: CALLOUT_TO = { +const CALLOUT_TO_TYPE: CALLOUT_TO = { note: 'Note', tip: 'Tip', important: 'Important', @@ -36,12 +36,15 @@ const CALLOUT_TO_ICON: CALLOUT_TO = { type CalloutViewerProps = { children: ReactNode, node: Element, - name: string + type: string, + label?: string, } export const CalloutViewer = React.memo((props: CalloutViewerProps): JSX.Element => { - const { node, name, children } = props; + const { + node, type, label, children, + } = props; if (node == null) { return <>; @@ -49,13 +52,13 @@ export const CalloutViewer = React.memo((props: CalloutViewerProps): JSX.Element return (
-
+
- {CALLOUT_TO_ICON[name]} + {CALLOUT_TO_ICON[type]}
- {CALLOUT_TO_TITLE[name]} + {label ?? CALLOUT_TO_TYPE[type]}
diff --git a/apps/app/src/features/callout/services/callout.spec.ts b/apps/app/src/features/callout/services/callout.spec.ts new file mode 100644 index 00000000000..e2fa1fd5dd0 --- /dev/null +++ b/apps/app/src/features/callout/services/callout.spec.ts @@ -0,0 +1,80 @@ +import type { ContainerDirective } from 'mdast-util-directive'; +import remarkDirective from 'remark-directive'; +import remarkParse from 'remark-parse'; +import { unified } from 'unified'; +import { visit } from 'unist-util-visit'; +import { describe, it, expect } from 'vitest'; + +import * as callout from './callout'; + +describe('remarkPlugin', () => { + it('should transform containerDirective to callout', () => { + const processor = unified() + .use(remarkParse) + .use(remarkDirective) + .use(callout.remarkPlugin); + + const markdown = ` +:::info +This is an info callout. +::: + `; + + const tree = processor.parse(markdown); + processor.runSync(tree); + + let calloutNode; + visit(tree, 'containerDirective', (node) => { + calloutNode = node; + }); + + expect(calloutNode).toBeDefined(); + + assert(calloutNode?.data?.hName != null); + assert(calloutNode?.data?.hProperties != null); + + expect(calloutNode.data.hName).toBe('callout'); + expect(calloutNode.data.hProperties.type).toBe('info'); + expect(calloutNode.data.hProperties.label).toBe('info'); + + assert('children' in calloutNode.children[0]); + assert('value' in calloutNode.children[0].children[0]); + + expect(calloutNode.children[0].children[0].value).toBe('This is an info callout.'); + }); + + it('should transform containerDirective to callout with custom label', () => { + const processor = unified() + .use(remarkParse) + .use(remarkDirective) + .use(callout.remarkPlugin); + + const markdown = ` +:::info[CUSTOM LABEL] +This is an info callout. +::: + `; + + const tree = processor.parse(markdown); + processor.runSync(tree); + + let calloutNode: ContainerDirective | undefined; + visit(tree, 'containerDirective', (node) => { + calloutNode = node; + }); + + expect(calloutNode).toBeDefined(); + + assert(calloutNode?.data?.hName != null); + assert(calloutNode?.data?.hProperties != null); + + expect(calloutNode.data.hName).toBe('callout'); + expect(calloutNode.data.hProperties.type).toBe('info'); + expect(calloutNode.data.hProperties.label).toBe('CUSTOM LABEL'); + + assert('children' in calloutNode.children[0]); + assert('value' in calloutNode.children[0].children[0]); + + expect(calloutNode.children[0].children[0].value).toBe('This is an info callout.'); + }); +}); diff --git a/apps/app/src/features/callout/services/callout.ts b/apps/app/src/features/callout/services/callout.ts index 74cf5db8716..86e1dd205f8 100644 --- a/apps/app/src/features/callout/services/callout.ts +++ b/apps/app/src/features/callout/services/callout.ts @@ -1,3 +1,4 @@ +import type { Paragraph, Text } from 'mdast'; import type { ContainerDirective } from 'mdast-util-directive'; import type { Plugin } from 'unified'; import { visit } from 'unist-util-visit'; @@ -8,11 +9,26 @@ export const remarkPlugin: Plugin = () => { return (tree) => { visit(tree, 'containerDirective', (node: ContainerDirective) => { if (AllCallout.some(name => name === node.name.toLowerCase())) { + const type = node.name.toLowerCase(); const data = node.data ?? (node.data = {}); + + // extract directive label + const paragraphs = (node.children ?? []).filter((child): child is Paragraph => child.type === 'paragraph'); + const paragraphForDirectiveLabel = paragraphs.find(p => p.data?.directiveLabel); + const label = paragraphForDirectiveLabel != null + ? (paragraphForDirectiveLabel.children[0] as Text).value + : undefined; + // remove directive label from children + if (paragraphForDirectiveLabel != null) { + node.children.splice(node.children.indexOf(paragraphForDirectiveLabel), 1); + } + data.hName = 'callout'; data.hProperties = { - name: node.name.toLocaleLowerCase(), + type, + label: label ?? type, }; + } }); }; @@ -20,4 +36,7 @@ export const remarkPlugin: Plugin = () => { export const sanitizeOption = { tagNames: ['callout'], + attributes: { + callout: ['type', 'label'], + }, };