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 }) => (
+
+ ),
+ 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 && (
+
+ )}
+
+
+
+
+
) : (