diff --git a/src/renderer/src/components/editor/EditorContent.markdown-classification.test.tsx b/src/renderer/src/components/editor/EditorContent.markdown-classification.test.tsx index 28e004e29f4..5edae2ca1a5 100644 --- a/src/renderer/src/components/editor/EditorContent.markdown-classification.test.tsx +++ b/src/renderer/src/components/editor/EditorContent.markdown-classification.test.tsx @@ -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() + }) }) diff --git a/src/renderer/src/components/editor/EditorMarkdownFileSurface.tsx b/src/renderer/src/components/editor/EditorMarkdownFileSurface.tsx index b3777113d0a..4bb4c04d65a 100644 --- a/src/renderer/src/components/editor/EditorMarkdownFileSurface.tsx +++ b/src/renderer/src/components/editor/EditorMarkdownFileSurface.tsx @@ -142,7 +142,7 @@ export function EditorMarkdownFileSurface({ return (
{shouldExplainRichFallback ? ( -
+
{richModeUnsupportedMessage}
) : null} diff --git a/src/renderer/src/components/editor/MarkdownPreviewBody.test.tsx b/src/renderer/src/components/editor/MarkdownPreviewBody.test.tsx index 5554dba47ff..c47301fde90 100644 --- a/src/renderer/src/components/editor/MarkdownPreviewBody.test.tsx +++ b/src/renderer/src/components/editor/MarkdownPreviewBody.test.tsx @@ -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() + + expect(html).toContain('

Language picker research

') + expect(html.split(`href="${url}"`)).toHaveLength(3) + expect(html).toContain('title="Mobile picker"') + expect(html).toContain('Automatic') + expect(html).toContain('Settings') + expect(html).toContain('') + 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() diff --git a/src/renderer/src/components/editor/editor-panel-render-model.test.ts b/src/renderer/src/components/editor/editor-panel-render-model.test.ts index 8a117cd8a30..95deda13558 100644 --- a/src/renderer/src/components/editor/editor-panel-render-model.test.ts +++ b/src/renderer/src/components/editor/editor-panel-render-model.test.ts @@ -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', () => { diff --git a/src/renderer/src/components/editor/markdown-render-mode.test.ts b/src/renderer/src/components/editor/markdown-render-mode.test.ts index f5ee3477c0a..77aef5e7590 100644 --- a/src/renderer/src/components/editor/markdown-render-mode.test.ts +++ b/src/renderer/src/components/editor/markdown-render-mode.test.ts @@ -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') }) diff --git a/src/renderer/src/components/editor/markdown-render-mode.ts b/src/renderer/src/components/editor/markdown-render-mode.ts index 163fcc9ddfa..e123aaaba92 100644 --- a/src/renderer/src/components/editor/markdown-render-mode.ts +++ b/src/renderer/src/components/editor/markdown-render-mode.ts @@ -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' }