Skip to content

Commit d50f722

Browse files
refactor: replace ReactMarkdown with TipTapView for enhanced markdown rendering in DecisionSummary
1 parent b9ff687 commit d50f722

File tree

3 files changed

+67
-31
lines changed

3 files changed

+67
-31
lines changed

components/decision-summary.tsx

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,7 @@ import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/com
22
import { Decision } from "@/lib/domain/Decision"
33
import { Stakeholder } from "@/lib/domain/Stakeholder"
44
import { StakeholderRoleGroups } from "@/components/stakeholders/StakeholderRoleGroups"
5-
import ReactMarkdown from 'react-markdown'
6-
import { ReactNode } from 'react'
7-
8-
// Define proper types for the ReactMarkdown components
9-
interface ReactMarkdownProps {
10-
children?: ReactNode;
11-
className?: string;
12-
}
13-
14-
// Components for custom rendering
15-
const MarkdownComponents = {
16-
// Override how strong (bold) is rendered
17-
strong: ({ children, ...props }: ReactMarkdownProps) => <span className="font-bold" {...props}>{children}</span>,
18-
19-
// Override how emphasis (italic) is rendered
20-
em: ({ children, ...props }: ReactMarkdownProps) => <span className="italic" {...props}>{children}</span>,
21-
22-
// Override how lists are rendered
23-
ul: ({ children, ...props }: ReactMarkdownProps) => <ul className="list-disc ml-5 my-2" {...props}>{children}</ul>,
24-
ol: ({ children, ...props }: ReactMarkdownProps) => <ol className="list-decimal ml-5 my-2" {...props}>{children}</ol>,
25-
li: ({ children, ...props }: ReactMarkdownProps) => <li className="my-1" {...props}>{children}</li>,
26-
27-
// Override how paragraphs are rendered
28-
p: ({ children, ...props }: ReactMarkdownProps) => <p className="my-2" {...props}>{children}</p>
29-
};
5+
import { TipTapView } from '@/components/tiptap-view'
306

317
interface DecisionSummaryProps {
328
decision: Decision
@@ -60,7 +36,7 @@ export function DecisionSummary({
6036
<div className="space-y-2">
6137
<h3 className="text-muted-foreground">Description</h3>
6238
<div className="prose prose-sm dark:prose-invert max-w-none">
63-
<ReactMarkdown components={MarkdownComponents}>{decision.description || ''}</ReactMarkdown>
39+
<TipTapView content={decision.description || ''} />
6440
</div>
6541
</div>
6642

@@ -80,7 +56,7 @@ export function DecisionSummary({
8056
<div className="space-y-2">
8157
<h3 className="text-muted-foreground">Decision</h3>
8258
<div className="rounded-md bg-muted p-4 prose prose-sm dark:prose-invert max-w-none">
83-
<ReactMarkdown components={MarkdownComponents}>{decision.decision || "No decision recorded"}</ReactMarkdown>
59+
<TipTapView content={decision.decision || "No decision recorded"} />
8460
</div>
8561
</div>
8662

components/tiptap-editor.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@ export function TipTapEditor({ content, onChange, className = '' }: TipTapEditor
4444
transformCopiedText: true
4545
})
4646
],
47-
content: {
48-
type: 'doc',
49-
content: content ? [{ type: 'paragraph', content: [{ type: 'text', text: content }] }] : [],
50-
},
47+
content: rawMarkdown,
5148
onUpdate: ({ editor }) => {
5249
// Use the markdown extension to get markdown content
5350
const markdown = editor.storage.markdown.getMarkdown();

components/tiptap-view.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use client'
2+
3+
import * as React from 'react'
4+
import { useEditor, EditorContent } from '@tiptap/react'
5+
import StarterKit from '@tiptap/starter-kit'
6+
import { Markdown } from 'tiptap-markdown'
7+
8+
interface TipTapViewProps {
9+
content: string
10+
className?: string
11+
}
12+
13+
export function TipTapView({ content, className = '' }: TipTapViewProps) {
14+
const editor = useEditor({
15+
extensions: [
16+
StarterKit.configure({
17+
bold: {
18+
HTMLAttributes: {
19+
class: 'font-bold'
20+
}
21+
},
22+
heading: {
23+
levels: [1, 2, 3]
24+
}
25+
}),
26+
Markdown.configure({
27+
html: false,
28+
transformPastedText: true,
29+
transformCopiedText: true
30+
})
31+
],
32+
content,
33+
editable: false,
34+
editorProps: {
35+
attributes: {
36+
class: 'prose prose-sm dark:prose-invert focus:outline-none max-w-none p-4'
37+
}
38+
}
39+
});
40+
41+
// Update editor content when content prop changes
42+
React.useEffect(() => {
43+
if (editor && content !== undefined) {
44+
// Only update if content actually changed
45+
if (editor.storage.markdown.getMarkdown() !== content) {
46+
editor.commands.setContent(content);
47+
}
48+
}
49+
}, [editor, content]);
50+
51+
if (!editor) {
52+
return null;
53+
}
54+
55+
return (
56+
<div className={`[&_.ProseMirror]:focus-visible:outline-none [&_.ProseMirror]:focus-visible:ring-0 ${className}`}>
57+
<EditorContent
58+
editor={editor}
59+
className="[&_ul]:list-disc [&_ul]:ml-4 [&_ol]:list-decimal [&_ol]:ml-4 [&_h1]:text-3xl [&_h1]:font-bold [&_h1]:mb-4 [&_h2]:text-2xl [&_h2]:font-bold [&_h2]:mb-3 [&_h3]:text-xl [&_h3]:font-bold [&_h3]:mb-2"
60+
/>
61+
</div>
62+
)
63+
}

0 commit comments

Comments
 (0)