diff --git a/package-lock.json b/package-lock.json
index baa362a..97276e4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,6 +35,7 @@
"clsx": "^2.1.1",
"cors": "^2.8.5",
"discord.js": "^14.15.3",
+ "github-markdown-css": "^5.8.1",
"light-bolt11-decoder": "^3.1.1",
"lucide-react": "^0.441.0",
"next": "14.2.5",
@@ -47,6 +48,7 @@
"react": "^18",
"react-dom": "^18",
"reactflow": "^11.11.4",
+ "remark-breaks": "^4.0.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0",
@@ -57,6 +59,7 @@
"eslint": "^8",
"eslint-config-next": "14.2.5",
"postcss": "^8",
+ "prettier": "^3.2.5",
"tailwindcss": "^3.4.1"
}
},
@@ -8247,6 +8250,18 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
+ "node_modules/github-markdown-css": {
+ "version": "5.8.1",
+ "resolved": "https://registry.npmjs.org/github-markdown-css/-/github-markdown-css-5.8.1.tgz",
+ "integrity": "sha512-8G+PFvqigBQSWLQjyzgpa2ThD9bo7+kDsriUIidGcRhXgmcaAWUIpCZf8DavJgc+xifjbCG+GvMyWr0XMXmc7g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/github-slugger": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
@@ -10416,6 +10431,20 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-newline-to-break": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz",
+ "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-find-and-replace": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdast-util-phrasing": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
@@ -12145,6 +12174,22 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-format": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
@@ -12849,6 +12894,21 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/remark-breaks": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz",
+ "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-newline-to-break": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/remark-gfm": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz",
diff --git a/package.json b/package.json
index edaf6d3..55bb32a 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"clsx": "^2.1.1",
"cors": "^2.8.5",
"discord.js": "^14.15.3",
+ "github-markdown-css": "^5.8.1",
"light-bolt11-decoder": "^3.1.1",
"lucide-react": "^0.441.0",
"next": "14.2.5",
@@ -50,6 +51,7 @@
"react": "^18",
"react-dom": "^18",
"reactflow": "^11.11.4",
+ "remark-breaks": "^4.0.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0",
diff --git a/src/components/content/combined/CombinedDetails.js b/src/components/content/combined/CombinedDetails.js
index e254b7e..f3fbae9 100644
--- a/src/components/content/combined/CombinedDetails.js
+++ b/src/components/content/combined/CombinedDetails.js
@@ -12,11 +12,9 @@ import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscripti
import { getTotalFromZaps } from '@/utils/lightning';
import { useSession } from 'next-auth/react';
import useWindowWidth from '@/hooks/useWindowWidth';
-import dynamic from 'next/dynamic';
import { Toast } from 'primereact/toast';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), { ssr: false });
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const CombinedDetails = ({
processedEvent,
@@ -203,7 +201,7 @@ const CombinedDetails = ({
const renderContent = () => {
if (decryptedContent) {
- return ;
+ return ;
}
if (paidResource && !decryptedContent) {
@@ -231,7 +229,7 @@ const CombinedDetails = ({
}
if (processedEvent?.content) {
- return ;
+ return ;
}
return null;
diff --git a/src/components/content/courses/CombinedLesson.js b/src/components/content/courses/CombinedLesson.js
index af705ad..61f9854 100644
--- a/src/components/content/courses/CombinedLesson.js
+++ b/src/components/content/courses/CombinedLesson.js
@@ -7,7 +7,6 @@ import { useZapsQuery } from '@/hooks/nostrQueries/zaps/useZapsQuery';
import { nip19 } from 'nostr-tools';
import { Divider } from 'primereact/divider';
import { getTotalFromZaps } from '@/utils/lightning';
-import dynamic from 'next/dynamic';
import useWindowWidth from '@/hooks/useWindowWidth';
import appConfig from '@/config/appConfig';
import useTrackVideoLesson from '@/hooks/tracking/useTrackVideoLesson';
@@ -15,10 +14,7 @@ import { Menu } from 'primereact/menu';
import { Toast } from 'primereact/toast';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
import { useSession } from 'next-auth/react';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted }) => {
const [zapAmount, setZapAmount] = useState(0);
@@ -175,7 +171,7 @@ const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
if (isPaid && decryptionPerformed) {
return (
-
+
);
}
@@ -217,7 +213,7 @@ const CombinedLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
if (lesson?.content) {
return (
-
+
);
}
diff --git a/src/components/content/courses/CourseLesson.js b/src/components/content/courses/CourseLesson.js
index 3447cce..07230f3 100644
--- a/src/components/content/courses/CourseLesson.js
+++ b/src/components/content/courses/CourseLesson.js
@@ -4,7 +4,6 @@ import Image from 'next/image';
import { useImageProxy } from '@/hooks/useImageProxy';
import { getTotalFromZaps } from '@/utils/lightning';
import ZapDisplay from '@/components/zaps/ZapDisplay';
-import dynamic from 'next/dynamic';
import { useZapsQuery } from '@/hooks/nostrQueries/zaps/useZapsQuery';
import { Toast } from 'primereact/toast';
import useTrackDocumentLesson from '@/hooks/tracking/useTrackDocumentLesson';
@@ -13,10 +12,7 @@ import { nip19 } from 'nostr-tools';
import appConfig from '@/config/appConfig';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
import { useSession } from 'next-auth/react';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted }) => {
const [zapAmount, setZapAmount] = useState(0);
@@ -114,7 +110,7 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setComplete
const renderContent = () => {
if (isPaid && decryptionPerformed) {
- return ;
+ return ;
}
if (isPaid && !decryptionPerformed) {
return (
@@ -124,7 +120,7 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid, setComplete
);
}
if (lesson?.content) {
- return ;
+ return ;
}
return null;
};
diff --git a/src/components/content/courses/DocumentLesson.js b/src/components/content/courses/DocumentLesson.js
index 81e5664..017153a 100644
--- a/src/components/content/courses/DocumentLesson.js
+++ b/src/components/content/courses/DocumentLesson.js
@@ -7,17 +7,13 @@ import { useZapsQuery } from '@/hooks/nostrQueries/zaps/useZapsQuery';
import { nip19 } from 'nostr-tools';
import { Divider } from 'primereact/divider';
import { getTotalFromZaps } from '@/utils/lightning';
-import dynamic from 'next/dynamic';
import useWindowWidth from '@/hooks/useWindowWidth';
import appConfig from '@/config/appConfig';
import useTrackDocumentLesson from '@/hooks/tracking/useTrackDocumentLesson';
import { Toast } from 'primereact/toast';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
import { useSession } from 'next-auth/react';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted }) => {
const [zapAmount, setZapAmount] = useState(0);
@@ -118,7 +114,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
const renderContent = () => {
if (isPaid && decryptionPerformed) {
- return ;
+ return ;
}
if (isPaid && !decryptionPerformed) {
return (
@@ -133,7 +129,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid, setComple
);
}
if (lesson?.content) {
- return ;
+ return ;
}
return null;
};
diff --git a/src/components/content/courses/DraftCourseDetails.js b/src/components/content/courses/DraftCourseDetails.js
index b228908..8649443 100644
--- a/src/components/content/courses/DraftCourseDetails.js
+++ b/src/components/content/courses/DraftCourseDetails.js
@@ -4,7 +4,6 @@ import { useImageProxy } from '@/hooks/useImageProxy';
import { Tag } from 'primereact/tag';
import GenericButton from '@/components/buttons/GenericButton';
import Image from 'next/image';
-import dynamic from 'next/dynamic';
import axios from 'axios';
import { nip04, nip19 } from 'nostr-tools';
import { v4 as uuidv4 } from 'uuid';
@@ -18,10 +17,7 @@ import { validateEvent } from '@/utils/nostr';
import appConfig from '@/config/appConfig';
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
import 'primeicons/primeicons.css';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
export default function DraftCourseDetails({ processedEvent, draftId, lessons }) {
const [author, setAuthor] = useState(null);
@@ -467,7 +463,7 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
{processedEvent?.content && (
-
+
)}
diff --git a/src/components/content/courses/DraftCourseLesson.js b/src/components/content/courses/DraftCourseLesson.js
index d2886c5..1c1db42 100644
--- a/src/components/content/courses/DraftCourseLesson.js
+++ b/src/components/content/courses/DraftCourseLesson.js
@@ -6,11 +6,7 @@ import Image from 'next/image';
import { useImageProxy } from '@/hooks/useImageProxy';
import { formatDateTime, formatUnixTimestamp } from '@/utils/time';
import { useRouter } from 'next/router';
-import dynamic from 'next/dynamic';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const DraftCourseLesson = ({ lesson, course }) => {
const [isPublished, setIsPublished] = useState(false);
@@ -149,7 +145,7 @@ const DraftCourseLesson = ({ lesson, course }) => {
- {lesson?.content && }
+ {lesson?.content && }
);
diff --git a/src/components/content/courses/VideoLesson.js b/src/components/content/courses/VideoLesson.js
index e6ba7ff..9f59e46 100644
--- a/src/components/content/courses/VideoLesson.js
+++ b/src/components/content/courses/VideoLesson.js
@@ -6,7 +6,6 @@ import { useImageProxy } from '@/hooks/useImageProxy';
import { useZapsQuery } from '@/hooks/nostrQueries/zaps/useZapsQuery';
import { nip19 } from 'nostr-tools';
import { getTotalFromZaps } from '@/utils/lightning';
-import dynamic from 'next/dynamic';
import { Divider } from 'primereact/divider';
import appConfig from '@/config/appConfig';
import useWindowWidth from '@/hooks/useWindowWidth';
@@ -14,10 +13,7 @@ import useTrackVideoLesson from '@/hooks/tracking/useTrackVideoLesson';
import { Toast } from 'primereact/toast';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
import { useSession } from 'next-auth/react';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted }) => {
const [zapAmount, setZapAmount] = useState(0);
@@ -170,7 +166,7 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted
if (isPaid && decryptionPerformed) {
return (
-
+
);
} else if (isPaid && !decryptionPerformed) {
@@ -196,7 +192,7 @@ const VideoLesson = ({ lesson, course, decryptionPerformed, isPaid, setCompleted
} else if (lesson?.content) {
return (
-
+
);
}
diff --git a/src/components/content/documents/DocumentDetails.js b/src/components/content/documents/DocumentDetails.js
index 6d91805..290ab24 100644
--- a/src/components/content/documents/DocumentDetails.js
+++ b/src/components/content/documents/DocumentDetails.js
@@ -12,13 +12,9 @@ import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscripti
import { getTotalFromZaps } from '@/utils/lightning';
import { useSession } from 'next-auth/react';
import useWindowWidth from '@/hooks/useWindowWidth';
-import dynamic from 'next/dynamic';
import { Toast } from 'primereact/toast';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const DocumentDetails = ({
processedEvent,
@@ -211,7 +207,7 @@ const DocumentDetails = ({
const renderContent = () => {
if (decryptedContent) {
- return ;
+ return ;
}
if (paidResource && !decryptedContent) {
return (
@@ -237,7 +233,7 @@ const DocumentDetails = ({
);
}
if (processedEvent?.content) {
- return ;
+ return ;
}
return null;
};
diff --git a/src/components/content/videos/VideoDetails.js b/src/components/content/videos/VideoDetails.js
index f9219aa..a41dc4b 100644
--- a/src/components/content/videos/VideoDetails.js
+++ b/src/components/content/videos/VideoDetails.js
@@ -12,13 +12,9 @@ import { useZapsSubscription } from '@/hooks/nostrQueries/zaps/useZapsSubscripti
import { getTotalFromZaps } from '@/utils/lightning';
import { useSession } from 'next-auth/react';
import useWindowWidth from '@/hooks/useWindowWidth';
-import dynamic from 'next/dynamic';
import { Toast } from 'primereact/toast';
import MoreOptionsMenu from '@/components/ui/MoreOptionsMenu';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const VideoDetails = ({
processedEvent,
@@ -208,7 +204,7 @@ const VideoDetails = ({
const renderContent = () => {
if (decryptedContent) {
- return ;
+ return ;
}
if (paidResource && !decryptedContent) {
return (
@@ -241,7 +237,7 @@ const VideoDetails = ({
);
}
if (processedEvent?.content) {
- return ;
+ return ;
}
return null;
};
diff --git a/src/components/markdown/MarkdownDisplay.js b/src/components/markdown/MarkdownDisplay.js
new file mode 100644
index 0000000..ce8b13a
--- /dev/null
+++ b/src/components/markdown/MarkdownDisplay.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import ReactMarkdown from 'react-markdown';
+import rehypeRaw from 'rehype-raw';
+import remarkGfm from 'remark-gfm';
+import remarkBreaks from 'remark-breaks';
+import 'github-markdown-css/github-markdown-dark.css';
+
+const MarkdownDisplay = ({ content, className = "" }) => {
+ if (!content) return null;
+
+ return (
+
+
,
+ img: ({node, ...props}) =>
+ }}
+ >
+ {content}
+
+
+ );
+};
+
+export default MarkdownDisplay;
\ No newline at end of file
diff --git a/src/pages/course/[slug]/index.js b/src/pages/course/[slug]/index.js
index 931e858..6d5c71b 100644
--- a/src/pages/course/[slug]/index.js
+++ b/src/pages/course/[slug]/index.js
@@ -18,8 +18,7 @@ import ZapThreadsWrapper from '@/components/ZapThreadsWrapper';
import useWindowWidth from '@/hooks/useWindowWidth';
import MenuTab from '@/components/menutab/MenuTab';
import { Tag } from 'primereact/tag';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), { ssr: false });
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
const useCourseData = (ndk, fetchAuthor, router) => {
const [course, setCourse] = useState(null);
@@ -430,6 +429,7 @@ const Course = () => {
isPaid={paidCourse}
setCompleted={setCompleted}
/>
+
);
} else if (lesson.type === 'video' && !lesson.topics?.includes('document')) {
return (
@@ -456,7 +456,7 @@ const Course = () => {
return (
<>
-
+
{/* Tab navigation using MenuTab component */}
{
{/* Content tab content */}
{uniqueLessons.length > 0 && uniqueLessons[activeIndex] ? (
-
+
{renderLesson(uniqueLessons[activeIndex])}
) : (
-
+
Select a lesson from the sidebar to begin learning.
)}
{course?.content && (
-
{/* Lessons tab - only visible on mobile */}
-
+
Please use the sidebar to navigate lessons.
diff --git a/src/pages/draft/[slug]/index.js b/src/pages/draft/[slug]/index.js
index 84e709a..6ccdb3f 100644
--- a/src/pages/draft/[slug]/index.js
+++ b/src/pages/draft/[slug]/index.js
@@ -15,15 +15,11 @@ import { formatDateTime } from '@/utils/time';
import Image from 'next/image';
import useResponsiveImageDimensions from '@/hooks/useResponsiveImageDimensions';
import 'primeicons/primeicons.css';
-import dynamic from 'next/dynamic';
import { validateEvent } from '@/utils/nostr';
import appConfig from '@/config/appConfig';
import { useIsAdmin } from '@/hooks/useIsAdmin';
import { useEncryptContent } from '@/hooks/encryption/useEncryptContent';
-
-const MDDisplay = dynamic(() => import('@uiw/react-markdown-preview'), {
- ssr: false,
-});
+import MarkdownDisplay from '@/components/markdown/MarkdownDisplay';
export default function Draft() {
const [draft, setDraft] = useState(null);
@@ -433,7 +429,7 @@ export default function Draft() {
- {draft?.content && }
+ {draft?.content && }
);