mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-28 21:42:27 +08:00
feat(Markdown): disable indented code blocks (#7288)
* feat(Markdown): disable indented code blocks * chore: update remark/rehype packages
This commit is contained in:
parent
e4f3ea2378
commit
614bde8e37
@ -190,7 +190,7 @@
|
||||
"react-hotkeys-hook": "^4.6.1",
|
||||
"react-i18next": "^14.1.2",
|
||||
"react-infinite-scroll-component": "^6.1.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-router": "6",
|
||||
"react-router-dom": "6",
|
||||
@ -199,10 +199,10 @@
|
||||
"redux": "^5.0.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
"rehype-katex": "^7.0.1",
|
||||
"rehype-mathjax": "^7.0.0",
|
||||
"rehype-mathjax": "^7.1.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-cjk-friendly": "^1.1.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-cjk-friendly": "^1.2.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-math": "^6.0.0",
|
||||
"remove-markdown": "^0.6.2",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
|
||||
@ -41,11 +41,10 @@ const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
||||
return (
|
||||
<EditorContainer style={{ height }}>
|
||||
<InputArea value={inputValue} onChange={handleChange} placeholder={placeholder} autoFocus={autoFocus} />
|
||||
<PreviewArea>
|
||||
<PreviewArea className="markdown">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkCjkFriendly, remarkMath]}
|
||||
rehypePlugins={[rehypeRaw, rehypeKatex]}
|
||||
className="markdown">
|
||||
rehypePlugins={[rehypeRaw, rehypeKatex]}>
|
||||
{inputValue || t('settings.provider.notes.markdown_editor_default_value')}
|
||||
</ReactMarkdown>
|
||||
</PreviewArea>
|
||||
|
||||
@ -75,8 +75,8 @@ const AgentsPage: FC = () => {
|
||||
{agent.description && <AgentDescription>{agent.description}</AgentDescription>}
|
||||
|
||||
{agent.prompt && (
|
||||
<AgentPrompt>
|
||||
<ReactMarkdown className="markdown">{agent.prompt}</ReactMarkdown>{' '}
|
||||
<AgentPrompt className="markdown">
|
||||
<ReactMarkdown>{agent.prompt}</ReactMarkdown>
|
||||
</AgentPrompt>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@ -24,6 +24,7 @@ import remarkMath from 'remark-math'
|
||||
|
||||
import CodeBlock from './CodeBlock'
|
||||
import Link from './Link'
|
||||
import remarkDisableConstructs from './plugins/remarkDisableConstructs'
|
||||
import Table from './Table'
|
||||
|
||||
const ALLOWED_ELEMENTS =
|
||||
@ -40,7 +41,7 @@ const Markdown: FC<Props> = ({ block }) => {
|
||||
const { mathEngine } = useSettings()
|
||||
|
||||
const remarkPlugins = useMemo(() => {
|
||||
const plugins = [remarkGfm, remarkCjkFriendly]
|
||||
const plugins = [remarkGfm, remarkCjkFriendly, remarkDisableConstructs(['codeIndented'])]
|
||||
if (mathEngine !== 'none') {
|
||||
plugins.push(remarkMath)
|
||||
}
|
||||
@ -105,20 +106,21 @@ const Markdown: FC<Props> = ({ block }) => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
rehypePlugins={rehypePlugins}
|
||||
remarkPlugins={remarkPlugins}
|
||||
className="markdown"
|
||||
components={components}
|
||||
disallowedElements={DISALLOWED_ELEMENTS}
|
||||
urlTransform={urlTransform}
|
||||
remarkRehypeOptions={{
|
||||
footnoteLabel: t('common.footnotes'),
|
||||
footnoteLabelTagName: 'h4',
|
||||
footnoteBackContent: ' '
|
||||
}}>
|
||||
{messageContent}
|
||||
</ReactMarkdown>
|
||||
<div className="markdown">
|
||||
<ReactMarkdown
|
||||
rehypePlugins={rehypePlugins}
|
||||
remarkPlugins={remarkPlugins}
|
||||
components={components}
|
||||
disallowedElements={DISALLOWED_ELEMENTS}
|
||||
urlTransform={urlTransform}
|
||||
remarkRehypeOptions={{
|
||||
footnoteLabel: t('common.footnotes'),
|
||||
footnoteLabelTagName: 'h4',
|
||||
footnoteBackContent: ' '
|
||||
}}>
|
||||
{messageContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -103,6 +103,12 @@ vi.mock('rehype-katex', () => ({ __esModule: true, default: vi.fn() }))
|
||||
vi.mock('rehype-mathjax', () => ({ __esModule: true, default: vi.fn() }))
|
||||
vi.mock('rehype-raw', () => ({ __esModule: true, default: vi.fn() }))
|
||||
|
||||
// Mock custom plugins
|
||||
vi.mock('../plugins/remarkDisableConstructs', () => ({
|
||||
__esModule: true,
|
||||
default: vi.fn()
|
||||
}))
|
||||
|
||||
// Mock ReactMarkdown with realistic rendering
|
||||
vi.mock('react-markdown', () => ({
|
||||
__esModule: true,
|
||||
@ -162,12 +168,16 @@ describe('Markdown', () => {
|
||||
describe('rendering', () => {
|
||||
it('should render markdown content with correct structure', () => {
|
||||
const block = createMainTextBlock({ content: 'Test content' })
|
||||
render(<Markdown block={block} />)
|
||||
const { container } = render(<Markdown block={block} />)
|
||||
|
||||
const markdown = screen.getByTestId('markdown-content')
|
||||
expect(markdown).toBeInTheDocument()
|
||||
expect(markdown).toHaveClass('markdown')
|
||||
expect(markdown).toHaveTextContent('Test content')
|
||||
// Check that the outer container has the markdown class
|
||||
const markdownContainer = container.querySelector('.markdown')
|
||||
expect(markdownContainer).toBeInTheDocument()
|
||||
|
||||
// Check that the markdown content is rendered inside
|
||||
const markdownContent = screen.getByTestId('markdown-content')
|
||||
expect(markdownContent).toBeInTheDocument()
|
||||
expect(markdownContent).toHaveTextContent('Test content')
|
||||
})
|
||||
|
||||
it('should handle empty content gracefully', () => {
|
||||
|
||||
@ -3,55 +3,58 @@
|
||||
exports[`Markdown > rendering > should match snapshot 1`] = `
|
||||
<div
|
||||
class="markdown"
|
||||
data-testid="markdown-content"
|
||||
>
|
||||
# Test Markdown
|
||||
<div
|
||||
data-testid="markdown-content"
|
||||
>
|
||||
# Test Markdown
|
||||
|
||||
This is **bold** text.
|
||||
<span
|
||||
data-testid="has-link-component"
|
||||
>
|
||||
link
|
||||
</span>
|
||||
<div
|
||||
data-testid="has-code-component"
|
||||
>
|
||||
<div
|
||||
data-id="code-block-1"
|
||||
data-testid="code-block"
|
||||
<span
|
||||
data-testid="has-link-component"
|
||||
>
|
||||
<code>
|
||||
test code
|
||||
</code>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-testid="has-table-component"
|
||||
>
|
||||
link
|
||||
</span>
|
||||
<div
|
||||
data-block-id="test-block-1"
|
||||
data-testid="table-component"
|
||||
data-testid="has-code-component"
|
||||
>
|
||||
<table>
|
||||
test table
|
||||
</table>
|
||||
<button
|
||||
data-testid="copy-table-button"
|
||||
type="button"
|
||||
<div
|
||||
data-id="code-block-1"
|
||||
data-testid="code-block"
|
||||
>
|
||||
Copy Table
|
||||
</button>
|
||||
<code>
|
||||
test code
|
||||
</code>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-testid="has-table-component"
|
||||
>
|
||||
<div
|
||||
data-block-id="test-block-1"
|
||||
data-testid="table-component"
|
||||
>
|
||||
<table>
|
||||
test table
|
||||
</table>
|
||||
<button
|
||||
data-testid="copy-table-button"
|
||||
type="button"
|
||||
>
|
||||
Copy Table
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
data-testid="has-img-component"
|
||||
>
|
||||
img
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
data-testid="has-img-component"
|
||||
>
|
||||
img
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -0,0 +1,155 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import remarkDisableConstructs from '../remarkDisableConstructs'
|
||||
|
||||
describe('disableIndentedCode', () => {
|
||||
const renderMarkdown = (markdown: string, constructs: string[] = ['codeIndented']) => {
|
||||
return render(<ReactMarkdown remarkPlugins={[remarkDisableConstructs(constructs)]}>{markdown}</ReactMarkdown>)
|
||||
}
|
||||
|
||||
describe('normal path', () => {
|
||||
it('should disable indented code blocks while preserving other code types', () => {
|
||||
const markdown = `
|
||||
# Test Document
|
||||
|
||||
Regular paragraph.
|
||||
|
||||
This should be treated as a regular paragraph, not code
|
||||
|
||||
\`inline code\` should work
|
||||
|
||||
\`\`\`javascript
|
||||
// This fenced code should work
|
||||
console.log('hello')
|
||||
\`\`\`
|
||||
|
||||
Another paragraph.
|
||||
`
|
||||
|
||||
const { container } = renderMarkdown(markdown)
|
||||
|
||||
// Verify only fenced code (pre element)
|
||||
expect(container.querySelectorAll('pre')).toHaveLength(1)
|
||||
|
||||
// Verify inline code
|
||||
const inlineCode = container.querySelector('code:not(pre code)')
|
||||
expect(inlineCode?.textContent).toBe('inline code')
|
||||
|
||||
// Verify fenced code
|
||||
const fencedCode = container.querySelector('pre code')
|
||||
expect(fencedCode?.textContent).toContain('console.log')
|
||||
|
||||
// Verify indented content becomes paragraph
|
||||
const paragraphs = container.querySelectorAll('p')
|
||||
const indentedParagraph = Array.from(paragraphs).find((p) =>
|
||||
p.textContent?.includes('This should be treated as a regular paragraph')
|
||||
)
|
||||
expect(indentedParagraph).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should handle indented code in nested structures', () => {
|
||||
const markdown = `
|
||||
> Blockquote with \`inline code\`
|
||||
>
|
||||
> This indented code in blockquote should become text
|
||||
|
||||
1. List item
|
||||
|
||||
This indented code in list should become text
|
||||
|
||||
* Bullet list
|
||||
* Nested item
|
||||
|
||||
More indented code to convert
|
||||
`
|
||||
|
||||
const { container } = renderMarkdown(markdown)
|
||||
|
||||
// Verify no indented code blocks
|
||||
expect(container.querySelectorAll('pre')).toHaveLength(0)
|
||||
|
||||
// Verify blockquote exists and contains converted text
|
||||
const blockquote = container.querySelector('blockquote')
|
||||
expect(blockquote?.textContent).toContain('This indented code in blockquote should become text')
|
||||
|
||||
// Verify lists exist
|
||||
const lists = container.querySelectorAll('ul, ol')
|
||||
expect(lists.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('should preserve other markdown elements when disabling constructs', () => {
|
||||
const markdown = `
|
||||
# Heading
|
||||
|
||||
Paragraph text.
|
||||
|
||||
Indented code to disable
|
||||
|
||||
[Link text](https://example.com)
|
||||
|
||||
\`\`\`
|
||||
Fenced code to keep
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
const { container } = renderMarkdown(markdown)
|
||||
|
||||
// Verify heading
|
||||
expect(container.querySelector('h1')?.textContent).toBe('Heading')
|
||||
|
||||
// Verify link
|
||||
const link = container.querySelector('a')
|
||||
expect(link?.textContent).toBe('Link text')
|
||||
expect(link?.getAttribute('href')).toBe('https://example.com')
|
||||
|
||||
// Verify only fenced code
|
||||
expect(container.querySelectorAll('pre')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should not affect markdown when no constructs are disabled', () => {
|
||||
const markdown = `
|
||||
This is indented code
|
||||
|
||||
\`inline code\`
|
||||
|
||||
\`\`\`javascript
|
||||
console.log('fenced')
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
const { container } = renderMarkdown(markdown, [])
|
||||
|
||||
// Should have indented code and fenced code
|
||||
expect(container.querySelectorAll('pre')).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('should handle markdown with only inline and fenced code', () => {
|
||||
const markdown = `
|
||||
Regular paragraph with \`inline code\`.
|
||||
|
||||
\`\`\`typescript
|
||||
function test(): string {
|
||||
return "hello";
|
||||
}
|
||||
\`\`\`
|
||||
`
|
||||
|
||||
const { container } = renderMarkdown(markdown)
|
||||
|
||||
// Should have only fenced code
|
||||
expect(container.querySelectorAll('pre')).toHaveLength(1)
|
||||
|
||||
// Verify fenced code content
|
||||
const fencedCode = container.querySelector('pre code')
|
||||
expect(fencedCode?.textContent).toContain('function test()')
|
||||
|
||||
// Verify inline code
|
||||
const inlineCode = container.querySelector('code:not(pre code)')
|
||||
expect(inlineCode?.textContent).toBe('inline code')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,107 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import remarkDisableConstructs from '../remarkDisableConstructs'
|
||||
|
||||
describe('remarkDisableConstructs', () => {
|
||||
let mockData: any
|
||||
let mockThis: any
|
||||
|
||||
beforeEach(() => {
|
||||
mockData = {}
|
||||
mockThis = {
|
||||
data: vi.fn().mockReturnValue(mockData)
|
||||
}
|
||||
})
|
||||
|
||||
describe('plugin creation', () => {
|
||||
it('should return a function when called', () => {
|
||||
const plugin = remarkDisableConstructs(['codeIndented'])
|
||||
|
||||
expect(typeof plugin).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('normal path', () => {
|
||||
it('should add micromarkExtensions for single construct', () => {
|
||||
const plugin = remarkDisableConstructs(['codeIndented'])
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData).toHaveProperty('micromarkExtensions')
|
||||
expect(Array.isArray(mockData.micromarkExtensions)).toBe(true)
|
||||
expect(mockData.micromarkExtensions).toHaveLength(1)
|
||||
expect(mockData.micromarkExtensions[0]).toEqual({
|
||||
disable: {
|
||||
null: ['codeIndented']
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle multiple constructs', () => {
|
||||
const constructs = ['codeIndented', 'autolink', 'htmlFlow']
|
||||
const plugin = remarkDisableConstructs(constructs)
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData.micromarkExtensions[0]).toEqual({
|
||||
disable: {
|
||||
null: constructs
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should not add extensions when empty array is provided', () => {
|
||||
const plugin = remarkDisableConstructs([])
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData).not.toHaveProperty('micromarkExtensions')
|
||||
})
|
||||
|
||||
it('should not add extensions when undefined is passed', () => {
|
||||
const plugin = remarkDisableConstructs()
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData).not.toHaveProperty('micromarkExtensions')
|
||||
})
|
||||
|
||||
it('should handle empty construct names', () => {
|
||||
const plugin = remarkDisableConstructs(['', ' '])
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData.micromarkExtensions[0]).toEqual({
|
||||
disable: {
|
||||
null: ['', ' ']
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle mixed valid and empty construct names', () => {
|
||||
const plugin = remarkDisableConstructs(['codeIndented', '', 'autolink'])
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData.micromarkExtensions[0]).toEqual({
|
||||
disable: {
|
||||
null: ['codeIndented', '', 'autolink']
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('interaction with existing data', () => {
|
||||
it('should append to existing micromarkExtensions', () => {
|
||||
const existingExtension = { some: 'extension' }
|
||||
mockData.micromarkExtensions = [existingExtension]
|
||||
|
||||
const plugin = remarkDisableConstructs(['codeIndented'])
|
||||
plugin.call(mockThis as any)
|
||||
|
||||
expect(mockData.micromarkExtensions).toHaveLength(2)
|
||||
expect(mockData.micromarkExtensions[0]).toBe(existingExtension)
|
||||
expect(mockData.micromarkExtensions[1]).toEqual({
|
||||
disable: {
|
||||
null: ['codeIndented']
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,53 @@
|
||||
import type { Plugin } from 'unified'
|
||||
|
||||
/**
|
||||
* Custom remark plugin to disable specific markdown constructs
|
||||
*
|
||||
* This plugin allows you to disable specific markdown constructs by passing
|
||||
* them as micromark extensions to the underlying parser.
|
||||
*
|
||||
* @see https://github.com/micromark/micromark
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Disable indented code blocks
|
||||
* remarkDisableConstructs(['codeIndented'])
|
||||
*
|
||||
* // Disable multiple constructs
|
||||
* remarkDisableConstructs(['codeIndented', 'autolink', 'htmlFlow'])
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper function to add values to plugin data
|
||||
* @param data - The plugin data object
|
||||
* @param field - The field name to add to
|
||||
* @param value - The value to add
|
||||
*/
|
||||
function add(data: any, field: string, value: unknown): void {
|
||||
const list = data[field] ? data[field] : (data[field] = [])
|
||||
list.push(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remark plugin to disable specific markdown constructs
|
||||
* @param constructs - Array of construct names to disable (e.g., ['codeIndented', 'autolink'])
|
||||
* @returns A remark plugin function
|
||||
*/
|
||||
function remarkDisableConstructs(constructs: string[] = []): Plugin<[], any, any> {
|
||||
return function () {
|
||||
const data = this.data()
|
||||
|
||||
if (constructs.length > 0) {
|
||||
const disableExtension = {
|
||||
disable: {
|
||||
null: constructs
|
||||
}
|
||||
}
|
||||
|
||||
add(data, 'micromarkExtensions', disableExtension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default remarkDisableConstructs
|
||||
@ -103,8 +103,8 @@ const AssistantPromptSettings: React.FC<Props> = ({ assistant, updateAssistant }
|
||||
</HStack>
|
||||
<TextAreaContainer>
|
||||
{showMarkdown ? (
|
||||
<MarkdownContainer onClick={() => setShowMarkdown(false)}>
|
||||
<ReactMarkdown className="markdown">{prompt}</ReactMarkdown>
|
||||
<MarkdownContainer className="markdown" onClick={() => setShowMarkdown(false)}>
|
||||
<ReactMarkdown>{prompt}</ReactMarkdown>
|
||||
<div style={{ height: '30px' }} />
|
||||
</MarkdownContainer>
|
||||
) : (
|
||||
|
||||
48
yarn.lock
48
yarn.lock
@ -5712,7 +5712,7 @@ __metadata:
|
||||
react-hotkeys-hook: "npm:^4.6.1"
|
||||
react-i18next: "npm:^14.1.2"
|
||||
react-infinite-scroll-component: "npm:^6.1.0"
|
||||
react-markdown: "npm:^9.0.1"
|
||||
react-markdown: "npm:^10.1.0"
|
||||
react-redux: "npm:^9.1.2"
|
||||
react-router: "npm:6"
|
||||
react-router-dom: "npm:6"
|
||||
@ -5721,10 +5721,10 @@ __metadata:
|
||||
redux: "npm:^5.0.1"
|
||||
redux-persist: "npm:^6.0.0"
|
||||
rehype-katex: "npm:^7.0.1"
|
||||
rehype-mathjax: "npm:^7.0.0"
|
||||
rehype-mathjax: "npm:^7.1.0"
|
||||
rehype-raw: "npm:^7.0.0"
|
||||
remark-cjk-friendly: "npm:^1.1.0"
|
||||
remark-gfm: "npm:^4.0.0"
|
||||
remark-cjk-friendly: "npm:^1.2.0"
|
||||
remark-gfm: "npm:^4.0.1"
|
||||
remark-math: "npm:^6.0.0"
|
||||
remove-markdown: "npm:^0.6.2"
|
||||
rollup-plugin-visualizer: "npm:^5.12.0"
|
||||
@ -12737,9 +12737,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-extension-cjk-friendly-util@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-extension-cjk-friendly-util@npm:1.1.0"
|
||||
"micromark-extension-cjk-friendly-util@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "micromark-extension-cjk-friendly-util@npm:2.0.0"
|
||||
dependencies:
|
||||
get-east-asian-width: "npm:^1.3.0"
|
||||
micromark-util-character: "npm:^2.0.0"
|
||||
@ -12747,16 +12747,16 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
micromark-util-types:
|
||||
optional: true
|
||||
checksum: 10c0/3ae1d4fd92f03a6c8e34e314c14a42b35cdd1bcbe043fceb1d2d45cd1a7b364e77643a3ca181910666cb11cc3606a1595fae9a15e87b0a4988fc57d5e4f65f67
|
||||
checksum: 10c0/194c799d88982ebf785e65a1c29cbded17d5dd3510a1769123ec30ddb7e256502b97753f63e8994d91ebafa1e9b96aa2dc2a90aa4e2f2072269b05652a412886
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-extension-cjk-friendly@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-extension-cjk-friendly@npm:1.1.0"
|
||||
"micromark-extension-cjk-friendly@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "micromark-extension-cjk-friendly@npm:1.2.0"
|
||||
dependencies:
|
||||
devlop: "npm:^1.0.0"
|
||||
micromark-extension-cjk-friendly-util: "npm:^1.1.0"
|
||||
micromark-extension-cjk-friendly-util: "npm:^2.0.0"
|
||||
micromark-util-chunked: "npm:^2.0.0"
|
||||
micromark-util-resolve-all: "npm:^2.0.0"
|
||||
micromark-util-symbol: "npm:^2.0.0"
|
||||
@ -12766,7 +12766,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
micromark-util-types:
|
||||
optional: true
|
||||
checksum: 10c0/95be6d8b4164b9b3b5281d77ed4f9337d95b2041ad4f7a775baa0d7f8ec495818101881eea2c7cc0ee4ee11738716899f20b3fbfbc2e6b80106544065d2ec04d
|
||||
checksum: 10c0/5be1841629310e21c803b64feb00453fb8ac939be80c2ff473d8b4486d8eca973347520912a6e4abeda5bea4ed8ef39d3db48c4bad8285dd380d9ed34417dd0d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -15611,9 +15611,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-markdown@npm:^9.0.1":
|
||||
version: 9.1.0
|
||||
resolution: "react-markdown@npm:9.1.0"
|
||||
"react-markdown@npm:^10.1.0":
|
||||
version: 10.1.0
|
||||
resolution: "react-markdown@npm:10.1.0"
|
||||
dependencies:
|
||||
"@types/hast": "npm:^3.0.0"
|
||||
"@types/mdast": "npm:^4.0.0"
|
||||
@ -15629,7 +15629,7 @@ __metadata:
|
||||
peerDependencies:
|
||||
"@types/react": ">=18"
|
||||
react: ">=18"
|
||||
checksum: 10c0/5bd645d39379f776d64588105f4060c390c3c8e4ff048552c9fa0ad31b756bb3ff7c11081542dc58d840ccf183a6dd4fd4d4edab44d8c24dee8b66551a5fd30d
|
||||
checksum: 10c0/4a5dc7d15ca6d05e9ee95318c1904f83b111a76f7588c44f50f1d54d4c97193b84e4f64c4b592057c989228238a2590306cedd0c4d398e75da49262b2b5ae1bf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -15915,7 +15915,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rehype-mathjax@npm:^7.0.0":
|
||||
"rehype-mathjax@npm:^7.1.0":
|
||||
version: 7.1.0
|
||||
resolution: "rehype-mathjax@npm:7.1.0"
|
||||
dependencies:
|
||||
@ -15942,18 +15942,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"remark-cjk-friendly@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "remark-cjk-friendly@npm:1.1.0"
|
||||
"remark-cjk-friendly@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "remark-cjk-friendly@npm:1.2.0"
|
||||
dependencies:
|
||||
micromark-extension-cjk-friendly: "npm:^1.1.0"
|
||||
micromark-extension-cjk-friendly: "npm:^1.2.0"
|
||||
peerDependencies:
|
||||
"@types/mdast": ^4.0.0
|
||||
unified: ^11.0.0
|
||||
peerDependenciesMeta:
|
||||
"@types/mdast":
|
||||
optional: true
|
||||
checksum: 10c0/ef43a4c404baaaa3e3d888ea68db8ffa101746faadb96d19d6b7ee8d00f0a025613c2e508527236961b226e41d8fb34f6cc6ac217ae8770fcbf47b9f496ab32a
|
||||
checksum: 10c0/ca7dc4fd50491693c4a84164650b30c3ae027cc7aa11b7a2e3811ab07ad0bf0c73484e37f9aed710bb68f95ca03cc540effe64cbe94bbc055b40e1aa951e2013
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -15967,7 +15967,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"remark-gfm@npm:^4.0.0":
|
||||
"remark-gfm@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "remark-gfm@npm:4.0.1"
|
||||
dependencies:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user