mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-02 02:09:03 +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
c9f94a3b15
commit
ed0bb7fd16
@ -190,7 +190,7 @@
|
|||||||
"react-hotkeys-hook": "^4.6.1",
|
"react-hotkeys-hook": "^4.6.1",
|
||||||
"react-i18next": "^14.1.2",
|
"react-i18next": "^14.1.2",
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^10.1.0",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-router": "6",
|
"react-router": "6",
|
||||||
"react-router-dom": "6",
|
"react-router-dom": "6",
|
||||||
@ -199,10 +199,10 @@
|
|||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
"rehype-mathjax": "^7.0.0",
|
"rehype-mathjax": "^7.1.0",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-cjk-friendly": "^1.1.0",
|
"remark-cjk-friendly": "^1.2.0",
|
||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.1",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remove-markdown": "^0.6.2",
|
"remove-markdown": "^0.6.2",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
|
|||||||
@ -41,11 +41,10 @@ const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
|||||||
return (
|
return (
|
||||||
<EditorContainer style={{ height }}>
|
<EditorContainer style={{ height }}>
|
||||||
<InputArea value={inputValue} onChange={handleChange} placeholder={placeholder} autoFocus={autoFocus} />
|
<InputArea value={inputValue} onChange={handleChange} placeholder={placeholder} autoFocus={autoFocus} />
|
||||||
<PreviewArea>
|
<PreviewArea className="markdown">
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm, remarkCjkFriendly, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkCjkFriendly, remarkMath]}
|
||||||
rehypePlugins={[rehypeRaw, rehypeKatex]}
|
rehypePlugins={[rehypeRaw, rehypeKatex]}>
|
||||||
className="markdown">
|
|
||||||
{inputValue || t('settings.provider.notes.markdown_editor_default_value')}
|
{inputValue || t('settings.provider.notes.markdown_editor_default_value')}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
</PreviewArea>
|
</PreviewArea>
|
||||||
|
|||||||
@ -75,8 +75,8 @@ const AgentsPage: FC = () => {
|
|||||||
{agent.description && <AgentDescription>{agent.description}</AgentDescription>}
|
{agent.description && <AgentDescription>{agent.description}</AgentDescription>}
|
||||||
|
|
||||||
{agent.prompt && (
|
{agent.prompt && (
|
||||||
<AgentPrompt>
|
<AgentPrompt className="markdown">
|
||||||
<ReactMarkdown className="markdown">{agent.prompt}</ReactMarkdown>{' '}
|
<ReactMarkdown>{agent.prompt}</ReactMarkdown>
|
||||||
</AgentPrompt>
|
</AgentPrompt>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import remarkMath from 'remark-math'
|
|||||||
|
|
||||||
import CodeBlock from './CodeBlock'
|
import CodeBlock from './CodeBlock'
|
||||||
import Link from './Link'
|
import Link from './Link'
|
||||||
|
import remarkDisableConstructs from './plugins/remarkDisableConstructs'
|
||||||
import Table from './Table'
|
import Table from './Table'
|
||||||
|
|
||||||
const ALLOWED_ELEMENTS =
|
const ALLOWED_ELEMENTS =
|
||||||
@ -40,7 +41,7 @@ const Markdown: FC<Props> = ({ block }) => {
|
|||||||
const { mathEngine } = useSettings()
|
const { mathEngine } = useSettings()
|
||||||
|
|
||||||
const remarkPlugins = useMemo(() => {
|
const remarkPlugins = useMemo(() => {
|
||||||
const plugins = [remarkGfm, remarkCjkFriendly]
|
const plugins = [remarkGfm, remarkCjkFriendly, remarkDisableConstructs(['codeIndented'])]
|
||||||
if (mathEngine !== 'none') {
|
if (mathEngine !== 'none') {
|
||||||
plugins.push(remarkMath)
|
plugins.push(remarkMath)
|
||||||
}
|
}
|
||||||
@ -105,20 +106,21 @@ const Markdown: FC<Props> = ({ block }) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMarkdown
|
<div className="markdown">
|
||||||
rehypePlugins={rehypePlugins}
|
<ReactMarkdown
|
||||||
remarkPlugins={remarkPlugins}
|
rehypePlugins={rehypePlugins}
|
||||||
className="markdown"
|
remarkPlugins={remarkPlugins}
|
||||||
components={components}
|
components={components}
|
||||||
disallowedElements={DISALLOWED_ELEMENTS}
|
disallowedElements={DISALLOWED_ELEMENTS}
|
||||||
urlTransform={urlTransform}
|
urlTransform={urlTransform}
|
||||||
remarkRehypeOptions={{
|
remarkRehypeOptions={{
|
||||||
footnoteLabel: t('common.footnotes'),
|
footnoteLabel: t('common.footnotes'),
|
||||||
footnoteLabelTagName: 'h4',
|
footnoteLabelTagName: 'h4',
|
||||||
footnoteBackContent: ' '
|
footnoteBackContent: ' '
|
||||||
}}>
|
}}>
|
||||||
{messageContent}
|
{messageContent}
|
||||||
</ReactMarkdown>
|
</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-mathjax', () => ({ __esModule: true, default: vi.fn() }))
|
||||||
vi.mock('rehype-raw', () => ({ __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
|
// Mock ReactMarkdown with realistic rendering
|
||||||
vi.mock('react-markdown', () => ({
|
vi.mock('react-markdown', () => ({
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
@ -162,12 +168,16 @@ describe('Markdown', () => {
|
|||||||
describe('rendering', () => {
|
describe('rendering', () => {
|
||||||
it('should render markdown content with correct structure', () => {
|
it('should render markdown content with correct structure', () => {
|
||||||
const block = createMainTextBlock({ content: 'Test content' })
|
const block = createMainTextBlock({ content: 'Test content' })
|
||||||
render(<Markdown block={block} />)
|
const { container } = render(<Markdown block={block} />)
|
||||||
|
|
||||||
const markdown = screen.getByTestId('markdown-content')
|
// Check that the outer container has the markdown class
|
||||||
expect(markdown).toBeInTheDocument()
|
const markdownContainer = container.querySelector('.markdown')
|
||||||
expect(markdown).toHaveClass('markdown')
|
expect(markdownContainer).toBeInTheDocument()
|
||||||
expect(markdown).toHaveTextContent('Test content')
|
|
||||||
|
// 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', () => {
|
it('should handle empty content gracefully', () => {
|
||||||
|
|||||||
@ -3,55 +3,58 @@
|
|||||||
exports[`Markdown > rendering > should match snapshot 1`] = `
|
exports[`Markdown > rendering > should match snapshot 1`] = `
|
||||||
<div
|
<div
|
||||||
class="markdown"
|
class="markdown"
|
||||||
data-testid="markdown-content"
|
|
||||||
>
|
>
|
||||||
# Test Markdown
|
<div
|
||||||
|
data-testid="markdown-content"
|
||||||
|
>
|
||||||
|
# Test Markdown
|
||||||
|
|
||||||
This is **bold** text.
|
This is **bold** text.
|
||||||
<span
|
<span
|
||||||
data-testid="has-link-component"
|
data-testid="has-link-component"
|
||||||
>
|
|
||||||
link
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
data-testid="has-code-component"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-id="code-block-1"
|
|
||||||
data-testid="code-block"
|
|
||||||
>
|
>
|
||||||
<code>
|
link
|
||||||
test code
|
</span>
|
||||||
</code>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
data-testid="has-table-component"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
data-block-id="test-block-1"
|
data-testid="has-code-component"
|
||||||
data-testid="table-component"
|
|
||||||
>
|
>
|
||||||
<table>
|
<div
|
||||||
test table
|
data-id="code-block-1"
|
||||||
</table>
|
data-testid="code-block"
|
||||||
<button
|
|
||||||
data-testid="copy-table-button"
|
|
||||||
type="button"
|
|
||||||
>
|
>
|
||||||
Copy Table
|
<code>
|
||||||
</button>
|
test code
|
||||||
|
</code>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</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>
|
</div>
|
||||||
<span
|
|
||||||
data-testid="has-img-component"
|
|
||||||
>
|
|
||||||
img
|
|
||||||
</span>
|
|
||||||
</div>
|
</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>
|
</HStack>
|
||||||
<TextAreaContainer>
|
<TextAreaContainer>
|
||||||
{showMarkdown ? (
|
{showMarkdown ? (
|
||||||
<MarkdownContainer onClick={() => setShowMarkdown(false)}>
|
<MarkdownContainer className="markdown" onClick={() => setShowMarkdown(false)}>
|
||||||
<ReactMarkdown className="markdown">{prompt}</ReactMarkdown>
|
<ReactMarkdown>{prompt}</ReactMarkdown>
|
||||||
<div style={{ height: '30px' }} />
|
<div style={{ height: '30px' }} />
|
||||||
</MarkdownContainer>
|
</MarkdownContainer>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
48
yarn.lock
48
yarn.lock
@ -5712,7 +5712,7 @@ __metadata:
|
|||||||
react-hotkeys-hook: "npm:^4.6.1"
|
react-hotkeys-hook: "npm:^4.6.1"
|
||||||
react-i18next: "npm:^14.1.2"
|
react-i18next: "npm:^14.1.2"
|
||||||
react-infinite-scroll-component: "npm:^6.1.0"
|
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-redux: "npm:^9.1.2"
|
||||||
react-router: "npm:6"
|
react-router: "npm:6"
|
||||||
react-router-dom: "npm:6"
|
react-router-dom: "npm:6"
|
||||||
@ -5721,10 +5721,10 @@ __metadata:
|
|||||||
redux: "npm:^5.0.1"
|
redux: "npm:^5.0.1"
|
||||||
redux-persist: "npm:^6.0.0"
|
redux-persist: "npm:^6.0.0"
|
||||||
rehype-katex: "npm:^7.0.1"
|
rehype-katex: "npm:^7.0.1"
|
||||||
rehype-mathjax: "npm:^7.0.0"
|
rehype-mathjax: "npm:^7.1.0"
|
||||||
rehype-raw: "npm:^7.0.0"
|
rehype-raw: "npm:^7.0.0"
|
||||||
remark-cjk-friendly: "npm:^1.1.0"
|
remark-cjk-friendly: "npm:^1.2.0"
|
||||||
remark-gfm: "npm:^4.0.0"
|
remark-gfm: "npm:^4.0.1"
|
||||||
remark-math: "npm:^6.0.0"
|
remark-math: "npm:^6.0.0"
|
||||||
remove-markdown: "npm:^0.6.2"
|
remove-markdown: "npm:^0.6.2"
|
||||||
rollup-plugin-visualizer: "npm:^5.12.0"
|
rollup-plugin-visualizer: "npm:^5.12.0"
|
||||||
@ -12737,9 +12737,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"micromark-extension-cjk-friendly-util@npm:^1.1.0":
|
"micromark-extension-cjk-friendly-util@npm:^2.0.0":
|
||||||
version: 1.1.0
|
version: 2.0.0
|
||||||
resolution: "micromark-extension-cjk-friendly-util@npm:1.1.0"
|
resolution: "micromark-extension-cjk-friendly-util@npm:2.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
get-east-asian-width: "npm:^1.3.0"
|
get-east-asian-width: "npm:^1.3.0"
|
||||||
micromark-util-character: "npm:^2.0.0"
|
micromark-util-character: "npm:^2.0.0"
|
||||||
@ -12747,16 +12747,16 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
micromark-util-types:
|
micromark-util-types:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/3ae1d4fd92f03a6c8e34e314c14a42b35cdd1bcbe043fceb1d2d45cd1a7b364e77643a3ca181910666cb11cc3606a1595fae9a15e87b0a4988fc57d5e4f65f67
|
checksum: 10c0/194c799d88982ebf785e65a1c29cbded17d5dd3510a1769123ec30ddb7e256502b97753f63e8994d91ebafa1e9b96aa2dc2a90aa4e2f2072269b05652a412886
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"micromark-extension-cjk-friendly@npm:^1.1.0":
|
"micromark-extension-cjk-friendly@npm:^1.2.0":
|
||||||
version: 1.1.0
|
version: 1.2.0
|
||||||
resolution: "micromark-extension-cjk-friendly@npm:1.1.0"
|
resolution: "micromark-extension-cjk-friendly@npm:1.2.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
devlop: "npm:^1.0.0"
|
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-chunked: "npm:^2.0.0"
|
||||||
micromark-util-resolve-all: "npm:^2.0.0"
|
micromark-util-resolve-all: "npm:^2.0.0"
|
||||||
micromark-util-symbol: "npm:^2.0.0"
|
micromark-util-symbol: "npm:^2.0.0"
|
||||||
@ -12766,7 +12766,7 @@ __metadata:
|
|||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
micromark-util-types:
|
micromark-util-types:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/95be6d8b4164b9b3b5281d77ed4f9337d95b2041ad4f7a775baa0d7f8ec495818101881eea2c7cc0ee4ee11738716899f20b3fbfbc2e6b80106544065d2ec04d
|
checksum: 10c0/5be1841629310e21c803b64feb00453fb8ac939be80c2ff473d8b4486d8eca973347520912a6e4abeda5bea4ed8ef39d3db48c4bad8285dd380d9ed34417dd0d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -15611,9 +15611,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-markdown@npm:^9.0.1":
|
"react-markdown@npm:^10.1.0":
|
||||||
version: 9.1.0
|
version: 10.1.0
|
||||||
resolution: "react-markdown@npm:9.1.0"
|
resolution: "react-markdown@npm:10.1.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/hast": "npm:^3.0.0"
|
"@types/hast": "npm:^3.0.0"
|
||||||
"@types/mdast": "npm:^4.0.0"
|
"@types/mdast": "npm:^4.0.0"
|
||||||
@ -15629,7 +15629,7 @@ __metadata:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@types/react": ">=18"
|
"@types/react": ">=18"
|
||||||
react: ">=18"
|
react: ">=18"
|
||||||
checksum: 10c0/5bd645d39379f776d64588105f4060c390c3c8e4ff048552c9fa0ad31b756bb3ff7c11081542dc58d840ccf183a6dd4fd4d4edab44d8c24dee8b66551a5fd30d
|
checksum: 10c0/4a5dc7d15ca6d05e9ee95318c1904f83b111a76f7588c44f50f1d54d4c97193b84e4f64c4b592057c989228238a2590306cedd0c4d398e75da49262b2b5ae1bf
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -15915,7 +15915,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"rehype-mathjax@npm:^7.0.0":
|
"rehype-mathjax@npm:^7.1.0":
|
||||||
version: 7.1.0
|
version: 7.1.0
|
||||||
resolution: "rehype-mathjax@npm:7.1.0"
|
resolution: "rehype-mathjax@npm:7.1.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15942,18 +15942,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"remark-cjk-friendly@npm:^1.1.0":
|
"remark-cjk-friendly@npm:^1.2.0":
|
||||||
version: 1.1.0
|
version: 1.2.0
|
||||||
resolution: "remark-cjk-friendly@npm:1.1.0"
|
resolution: "remark-cjk-friendly@npm:1.2.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
micromark-extension-cjk-friendly: "npm:^1.1.0"
|
micromark-extension-cjk-friendly: "npm:^1.2.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@types/mdast": ^4.0.0
|
"@types/mdast": ^4.0.0
|
||||||
unified: ^11.0.0
|
unified: ^11.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
"@types/mdast":
|
"@types/mdast":
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/ef43a4c404baaaa3e3d888ea68db8ffa101746faadb96d19d6b7ee8d00f0a025613c2e508527236961b226e41d8fb34f6cc6ac217ae8770fcbf47b9f496ab32a
|
checksum: 10c0/ca7dc4fd50491693c4a84164650b30c3ae027cc7aa11b7a2e3811ab07ad0bf0c73484e37f9aed710bb68f95ca03cc540effe64cbe94bbc055b40e1aa951e2013
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -15967,7 +15967,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"remark-gfm@npm:^4.0.0":
|
"remark-gfm@npm:^4.0.1":
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
resolution: "remark-gfm@npm:4.0.1"
|
resolution: "remark-gfm@npm:4.0.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user