This commit is contained in:
30
package-lock.json
generated
30
package-lock.json
generated
@@ -29,6 +29,7 @@
|
||||
"react-syntax-highlighter": "^16.1.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"yarn": "^1.22.22",
|
||||
"zustand": "^5.0.9"
|
||||
@@ -6275,6 +6276,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",
|
||||
@@ -7970,6 +7985,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.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"react-syntax-highlighter": "^16.1.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"yarn": "^1.22.22",
|
||||
"zustand": "^5.0.9"
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
import React, { useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkBreaks from 'remark-breaks'; // 엔터 한 번으로 줄바꿈 되도록 추가
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import { Copy, Check, Terminal, ExternalLink } from 'lucide-react';
|
||||
import { clsx } from 'clsx';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
|
||||
interface MarkdownRendererProps {
|
||||
content: string;
|
||||
@@ -18,7 +19,8 @@ export default function MarkdownRenderer({ content }: MarkdownRendererProps) {
|
||||
return (
|
||||
<div className="markdown-content">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
// remarkBreaks: 일반 텍스트에서 엔터 한 번을 <br/>로 변환해줍니다.
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
rehypePlugins={[rehypeSanitize, rehypeSlug]}
|
||||
components={{
|
||||
// 1. 코드 블록 커스텀
|
||||
@@ -43,7 +45,7 @@ export default function MarkdownRenderer({ content }: MarkdownRendererProps) {
|
||||
);
|
||||
},
|
||||
|
||||
// 2. 인용구
|
||||
// 2. 인용구 (Blockquote)
|
||||
blockquote({ children }) {
|
||||
return (
|
||||
<blockquote className="border-l-4 border-blue-500 bg-blue-50 pl-4 py-3 my-6 text-gray-700 rounded-r-lg italic shadow-sm">
|
||||
@@ -82,29 +84,27 @@ export default function MarkdownRenderer({ content }: MarkdownRendererProps) {
|
||||
return <thead className="text-xs text-gray-700 uppercase bg-gray-50 border-b border-gray-200">{children}</thead>;
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-6 py-3 font-bold text-gray-900">{children}</th>;
|
||||
return <th className="px-6 py-3 font-bold text-gray-900 whitespace-nowrap">{children}</th>;
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-6 py-4 border-b border-gray-100 whitespace-pre-wrap">{children}</td>;
|
||||
},
|
||||
|
||||
// 5. 이미지 (비율 유지 및 중앙 정렬)
|
||||
// 5. 이미지 (figure 태그 사용으로 시멘틱 개선)
|
||||
img({ src, alt }) {
|
||||
return (
|
||||
// 🛠️ [Fix] flex-col 추가: 이미지와 캡션을 세로로 정렬
|
||||
// items-center 추가: 가로축 중앙 정렬
|
||||
<span className="block my-8 flex flex-col items-center justify-center">
|
||||
<figure className="block my-8 flex flex-col items-center justify-center">
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="rounded-xl shadow-lg border border-gray-100 max-w-full h-auto max-h-[700px] mx-auto hover:scale-[1.01] transition-transform duration-300"
|
||||
className="rounded-xl shadow-lg border border-gray-100 max-w-full h-auto max-h-[700px] mx-auto hover:scale-[1.01] transition-transform duration-300 object-contain"
|
||||
loading="lazy"
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
{alt && <span className="block text-center text-sm text-gray-400 mt-2 w-full">{alt}</span>}
|
||||
</span>
|
||||
{alt && <figcaption className="text-center text-sm text-gray-400 mt-2 w-full">{alt}</figcaption>}
|
||||
</figure>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -123,19 +123,24 @@ export default function MarkdownRenderer({ content }: MarkdownRendererProps) {
|
||||
);
|
||||
},
|
||||
li({ children }) {
|
||||
return <li className="pl-1">{children}</li>;
|
||||
// p 태그가 내부에 생길 경우 margin 상쇄를 위해 items-start 등 조정
|
||||
return <li className="pl-1 leading-relaxed">{children}</li>;
|
||||
},
|
||||
|
||||
// 7. 헤딩 스타일 (🛠️ 수정: ...props를 전달해야 id가 붙어서 목차 이동이 작동함)
|
||||
// 7. 헤딩 스타일
|
||||
h1({ children, ...props }: any) {
|
||||
return <h1 className="text-3xl font-extrabold mt-12 mb-6 pb-4 border-b border-gray-100 text-gray-900" {...props}>{children}</h1>;
|
||||
return <h1 className="text-3xl font-extrabold mt-12 mb-6 pb-4 border-b border-gray-100 text-gray-900 scroll-mt-20" {...props}>{children}</h1>;
|
||||
},
|
||||
h2({ children, ...props }: any) {
|
||||
return <h2 className="text-2xl font-bold mt-10 mb-5 pb-2 text-gray-800" {...props}>{children}</h2>;
|
||||
return <h2 className="text-2xl font-bold mt-10 mb-5 pb-2 text-gray-800 scroll-mt-20" {...props}>{children}</h2>;
|
||||
},
|
||||
h3({ children, ...props }: any) {
|
||||
return <h3 className="text-xl font-bold mt-8 mb-4 text-gray-800 flex items-center gap-2 before:content-[''] before:w-1.5 before:h-6 before:bg-blue-500 before:rounded-full before:mr-1" {...props}>{children}</h3>;
|
||||
return <h3 className="text-xl font-bold mt-8 mb-4 text-gray-800 flex items-center gap-2 before:content-[''] before:w-1.5 before:h-6 before:bg-blue-500 before:rounded-full before:mr-1 scroll-mt-20" {...props}>{children}</h3>;
|
||||
},
|
||||
// p 태그 스타일 추가 (일반 텍스트 가독성)
|
||||
p({ children }) {
|
||||
return <p className="mb-4 leading-7 text-gray-700">{children}</p>;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
|
||||
17
yarn.lock
17
yarn.lock
@@ -2796,6 +2796,14 @@ mdast-util-mdxjs-esm@^2.0.0:
|
||||
mdast-util-from-markdown "^2.0.0"
|
||||
mdast-util-to-markdown "^2.0.0"
|
||||
|
||||
mdast-util-newline-to-break@^2.0.0:
|
||||
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==
|
||||
dependencies:
|
||||
"@types/mdast" "^4.0.0"
|
||||
mdast-util-find-and-replace "^3.0.0"
|
||||
|
||||
mdast-util-phrasing@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz"
|
||||
@@ -3689,6 +3697,15 @@ rehype@~13.0.0:
|
||||
rehype-stringify "^10.0.0"
|
||||
unified "^11.0.0"
|
||||
|
||||
remark-breaks@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz"
|
||||
integrity sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==
|
||||
dependencies:
|
||||
"@types/mdast" "^4.0.0"
|
||||
mdast-util-newline-to-break "^2.0.0"
|
||||
unified "^11.0.0"
|
||||
|
||||
remark-gfm@^4.0.1, remark-gfm@~4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz"
|
||||
|
||||
Reference in New Issue
Block a user