Render unsupported Markdown drafts in preview mode (#33733)

- Preserve source syntax while allowing preview and PDF export
- Keep Source available for editing and reserve fallback styling for neutral guidance
This commit is contained in:
Jinjing
2026-10-25 20:53:41 -07:00
committed by GitHub
parent ad1684435e
commit 4597d3f249
6 changed files with 60 additions and 7 deletions
@@ -286,10 +286,12 @@ describe('inline Markdown render classification', () => {
expect(classifiers.exceedsSizeLimit).not.toHaveBeenCalled()
})
it('preserves unsupported and oversized rich-mode fallbacks', () => {
it('renders unsupported rich drafts in preview while keeping oversized drafts in source', () => {
const unsupported = renderEditPath({ content: '[reference]: https://example.com' })
expect(unsupported.model.canExportMarkdownToPdf).toBe(false)
expect(unsupported.view.container.innerHTML).toContain('data-editor-view="preview"')
expect(unsupported.view.container.innerHTML).not.toContain('data-editor-view="source"')
expect(unsupported.model.canExportMarkdownToPdf).toBe(true)
expect(unsupported.view.getByText('Reference links require source mode.')).toBeTruthy()
expect(unsupported.view.queryByText('Open anyway')).toBeNull()
unsupported.view.unmount()
@@ -300,4 +302,15 @@ describe('inline Markdown render classification', () => {
expect(oversized.view.getByText(/File is larger than the .* rich editing limit/)).toBeTruthy()
expect(oversized.view.getByText('Open anyway')).toBeTruthy()
})
it('keeps unsupported drafts editable when Source is selected', () => {
const source = renderEditPath({
content: '[reference]: https://example.com',
viewMode: 'source'
})
expect(source.view.container.innerHTML).toContain('data-editor-view="source"')
expect(source.view.container.innerHTML).not.toContain('data-editor-view="preview"')
expect(classifiers.getUnsupportedMessage).not.toHaveBeenCalled()
})
})
@@ -142,7 +142,7 @@ export function EditorMarkdownFileSurface({
return (
<div className="flex h-full min-h-0 flex-col">
{shouldExplainRichFallback ? (
<div className="border-b border-border/60 bg-amber-500/10 px-3 py-2 text-xs text-amber-950 dark:text-amber-100">
<div className="border-b border-border/60 bg-muted/40 px-3 py-2 text-xs text-muted-foreground">
{richModeUnsupportedMessage}
</div>
) : null}
@@ -55,6 +55,35 @@ function parseCjkFriendlyMarkdown(markdown: string) {
}
describe('MarkdownPreviewBody', () => {
it('renders forward reference links in research prose and tables without exposing definitions', () => {
const url = 'https://github.com/example/app/blob/revision/src/app/(mobile)/language.tsx#L95'
const markdown = [
'# Language picker research',
'',
'Sources: [Picker][picker], [Automatic][], and [Settings].',
'',
'| App | Evidence |',
'| --- | --- |',
'| Example | [Picker][picker] |',
'',
`[picker]: ${url} "Mobile picker"`,
'[automatic]: https://example.com/automatic',
'[settings]: ./settings.md'
].join('\n')
const html = renderToStaticMarkup(<MarkdownPreviewBody content={markdown} components={{}} />)
expect(html).toContain('<h1 id="language-picker-research">Language picker research</h1>')
expect(html.split(`href="${url}"`)).toHaveLength(3)
expect(html).toContain('title="Mobile picker"')
expect(html).toContain('<a href="https://example.com/automatic">Automatic</a>')
expect(html).toContain('<a href="./settings.md">Settings</a>')
expect(html).toContain('<table>')
expect(html).not.toContain('[picker]')
expect(html).not.toContain('[automatic]')
expect(html).not.toContain('[settings]')
})
it.each(cjkEmphasisExamples)('renders emphasis for $name', ({ markdown, expectedHtml }) => {
const html = renderToStaticMarkup(<MarkdownPreviewBody content={markdown} components={{}} />)
@@ -163,7 +163,8 @@ describe('getEditorPanelRenderModel markdown export affordance', () => {
editorDrafts: { '/repo/README.md': '[example]: https://example.com' }
})
expect(model.canExportMarkdownToPdf).toBe(false)
expect(model.inlineMarkdownRenderState?.renderMode).toBe('preview')
expect(model.canExportMarkdownToPdf).toBe(true)
})
it('disables rich export when a multibyte character crosses the byte limit', () => {
@@ -32,13 +32,23 @@ describe('getMarkdownRenderMode', () => {
).toBe('preview')
})
it('falls back to source mode when rich editing is unsupported', () => {
it('renders a preview when rich editing cannot preserve the source', () => {
expect(
getMarkdownRenderMode({
exceedsRichModeSizeLimit: false,
hasRichModeUnsupportedContent: true,
viewMode: 'rich'
})
).toBe('preview')
})
it('keeps oversized unsupported documents in source mode', () => {
expect(
getMarkdownRenderMode({
exceedsRichModeSizeLimit: true,
hasRichModeUnsupportedContent: true,
viewMode: 'rich'
})
).toBe('source')
})
@@ -30,6 +30,6 @@ export function getMarkdownRenderMode({
return 'source'
}
// Unsafe rich documents need an editable source fallback; read-only preview has its own explicit mode.
return hasRichModeUnsupportedContent ? 'source' : 'rich-editor'
// Preview preserves unsupported syntax; Source remains available for editing.
return hasRichModeUnsupportedContent ? 'preview' : 'rich-editor'
}