From a44e225f5ee578b72ed5e4a620bd6b2de3283b51 Mon Sep 17 00:00:00 2001 From: Cyther Date: Sun, 12 Jul 2026 12:31:36 +0400 Subject: [PATCH] feat: render markdown files visually instead of showing raw content --- src/components/common/MarkdownRenderer.tsx | 33 ++++++++++++++++++++++ src/pages/project/FileViewer.tsx | 27 ++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/components/common/MarkdownRenderer.tsx diff --git a/src/components/common/MarkdownRenderer.tsx b/src/components/common/MarkdownRenderer.tsx new file mode 100644 index 0000000..384d8b1 --- /dev/null +++ b/src/components/common/MarkdownRenderer.tsx @@ -0,0 +1,33 @@ +import Markdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { cn } from '../../utils/cn'; + +interface MarkdownRendererProps { + content: string; + className?: string; +} + +export default function MarkdownRenderer({ content, className }: MarkdownRendererProps) { + return ( +
+ ( + + ), + img: ({ node: _, ...props }) => ( + {props.alt + ), + table: ({ node: _, ...props }) => ( +
+ + + ), + }} + > + {content} + + + ); +} diff --git a/src/pages/project/FileViewer.tsx b/src/pages/project/FileViewer.tsx index 27433aa..381bb07 100644 --- a/src/pages/project/FileViewer.tsx +++ b/src/pages/project/FileViewer.tsx @@ -1,10 +1,11 @@ import { useState } from 'react'; import { useOutletContext, useParams, useLocation, Link, useNavigate } from 'react-router-dom'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { ChevronRight, ArrowLeft, Pencil } from 'lucide-react'; +import { ChevronRight, ArrowLeft, Pencil, FileText } from 'lucide-react'; import { Button } from '../../components/ui/button'; import { Skeleton } from '../../components/ui/skeleton'; import FileViewerComponent from '../../components/code/FileViewer'; +import MarkdownRenderer from '../../components/common/MarkdownRenderer'; import InlineEditor from '../../components/repository/InlineEditor'; import ErrorMessage from '../../components/common/ErrorMessage'; import PermGate from '../../components/common/PermGate'; @@ -60,12 +61,13 @@ export default function FileViewer() { } })(); - // Detect if binary const isBinary = file && ( file.encoding !== 'base64' || (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico', 'bmp'].includes(ext)) ); + const isMarkdown = ext === 'md' || ext === 'mdx' || ext === 'markdown' || ext === 'mdown'; + const rawUrl = file ? `${api.client.base}/projects/${id}/repository/files/${encodeURIComponent(filePath)}/raw?ref=${ref}` : undefined; @@ -192,6 +194,27 @@ export default function FileViewer() { onSave={handleSave} onCancel={() => setIsEditing(false)} /> + ) : isMarkdown ? ( +
+
+
+ + {file.file_name} + ยท + {(new TextEncoder().encode(content).length / 1024).toFixed(1)} KB +
+ {rawUrl && ( + + )} +
+
+ +
+
) : (