| import dedent from 'dedent'; |
|
|
| const markdownRenderer = dedent(`import React, { useEffect, useState } from 'react'; |
| import Markdown from 'marked-react'; |
| |
| interface MarkdownRendererProps { |
| content: string; |
| } |
| |
| const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content }) => { |
| return ( |
| <div |
| className="markdown-body" |
| style={{ |
| padding: '2rem', |
| margin: '1rem', |
| minHeight: '100vh' |
| }} |
| > |
| <Markdown gfm={true} breaks={true}>{content}</Markdown> |
| </div> |
| ); |
| }; |
| |
| export default MarkdownRenderer;`); |
|
|
| const wrapMarkdownRenderer = (content: string) => { |
| |
| const normalizedContent = content.replace(/^( {2})(-|\d+\.)/gm, ' $2'); |
|
|
| |
| const escapedContent = normalizedContent |
| .replace(/\\/g, '\\\\') |
| .replace(/`/g, '\\`') |
| .replace(/\$/g, '\\$'); |
|
|
| return dedent(`import React from 'react'; |
| import MarkdownRenderer from '/components/ui/MarkdownRenderer'; |
| |
| const App = () => { |
| return <MarkdownRenderer content={\`${escapedContent}\`} />; |
| }; |
| |
| export default App; |
| `); |
| }; |
|
|
| const markdownCSS = ` |
| /* GitHub Markdown CSS - Light theme base */ |
| .markdown-body { |
| -ms-text-size-adjust: 100%; |
| -webkit-text-size-adjust: 100%; |
| line-height: 1.5; |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif; |
| font-size: 16px; |
| line-height: 1.5; |
| word-wrap: break-word; |
| color: #24292f; |
| background-color: #ffffff; |
| } |
| |
| .markdown-body h1, .markdown-body h2 { |
| border-bottom: 1px solid #d0d7de; |
| margin: 0.6em 0; |
| } |
| |
| .markdown-body h1 { font-size: 2em; margin: 0.67em 0; } |
| .markdown-body h2 { font-size: 1.5em; } |
| .markdown-body h3 { font-size: 1.25em; } |
| .markdown-body h4 { font-size: 1em; } |
| .markdown-body h5 { font-size: 0.875em; } |
| .markdown-body h6 { font-size: 0.85em; } |
| |
| .markdown-body ul, .markdown-body ol { |
| list-style: revert !important; |
| padding-left: 2em !important; |
| margin-top: 0; |
| margin-bottom: 16px; |
| } |
| |
| .markdown-body ul { list-style-type: disc !important; } |
| .markdown-body ol { list-style-type: decimal !important; } |
| .markdown-body ul ul { list-style-type: circle !important; } |
| .markdown-body ul ul ul { list-style-type: square !important; } |
| |
| .markdown-body li { margin-top: 0.25em; } |
| |
| .markdown-body li:has(> input[type="checkbox"]) { |
| list-style-type: none !important; |
| } |
| |
| .markdown-body li > input[type="checkbox"] { |
| margin-right: 0.75em; |
| margin-left: -1.5em; |
| vertical-align: middle; |
| pointer-events: none; |
| width: 16px; |
| height: 16px; |
| } |
| |
| .markdown-body .task-list-item { |
| list-style-type: none !important; |
| } |
| |
| .markdown-body .task-list-item > input[type="checkbox"] { |
| margin-right: 0.75em; |
| margin-left: -1.5em; |
| vertical-align: middle; |
| pointer-events: none; |
| width: 16px; |
| height: 16px; |
| } |
| |
| .markdown-body code { |
| padding: 0.2em 0.4em; |
| margin: 0; |
| font-size: 85%; |
| border-radius: 6px; |
| background-color: rgba(175, 184, 193, 0.2); |
| color: #24292f; |
| font-family: ui-monospace, monospace; |
| white-space: pre-wrap; |
| } |
| |
| .markdown-body pre { |
| padding: 16px; |
| overflow: auto; |
| font-size: 85%; |
| line-height: 1.45; |
| border-radius: 6px; |
| margin-top: 0; |
| margin-bottom: 16px; |
| background-color: #f6f8fa; |
| color: #24292f; |
| } |
| |
| .markdown-body pre code { |
| display: inline-block; |
| padding: 0; |
| margin: 0; |
| overflow: visible; |
| line-height: inherit; |
| word-wrap: normal; |
| background-color: transparent; |
| border: 0; |
| } |
| |
| .markdown-body a { |
| text-decoration: none; |
| color: #0969da; |
| } |
| |
| .markdown-body a:hover { |
| text-decoration: underline; |
| } |
| |
| .markdown-body table { |
| border-spacing: 0; |
| border-collapse: collapse; |
| display: block; |
| width: max-content; |
| max-width: 100%; |
| overflow: auto; |
| } |
| |
| .markdown-body table thead { |
| background-color: #f6f8fa; |
| } |
| |
| .markdown-body table th, .markdown-body table td { |
| padding: 6px 13px; |
| border: 1px solid #d0d7de; |
| } |
| |
| .markdown-body blockquote { |
| padding: 0 1em; |
| border-left: 0.25em solid #d0d7de; |
| margin: 0 0 16px 0; |
| color: #57606a; |
| } |
| |
| .markdown-body hr { |
| height: 0.25em; |
| padding: 0; |
| margin: 24px 0; |
| border: 0; |
| background-color: #d0d7de; |
| } |
| |
| .markdown-body img { |
| max-width: 100%; |
| box-sizing: content-box; |
| } |
| |
| /* Dark theme */ |
| @media (prefers-color-scheme: dark) { |
| .markdown-body { |
| color: #c9d1d9; |
| background-color: #0d1117; |
| } |
| |
| .markdown-body h1, .markdown-body h2 { |
| border-bottom-color: #21262d; |
| } |
| |
| .markdown-body code { |
| background-color: rgba(110, 118, 129, 0.4); |
| color: #c9d1d9; |
| } |
| |
| .markdown-body pre { |
| background-color: #161b22; |
| color: #c9d1d9; |
| } |
| |
| .markdown-body a { |
| color: #58a6ff; |
| } |
| |
| .markdown-body table thead { |
| background-color: #161b22; |
| } |
| |
| .markdown-body table th, .markdown-body table td { |
| border-color: #30363d; |
| } |
| |
| .markdown-body blockquote { |
| border-left-color: #3b434b; |
| color: #8b949e; |
| } |
| |
| .markdown-body hr { |
| background-color: #21262d; |
| } |
| } |
| `; |
|
|
| export const getMarkdownFiles = (content: string) => { |
| return { |
| 'content.md': content || '# No content provided', |
| 'App.tsx': wrapMarkdownRenderer(content), |
| 'index.tsx': dedent(`import React, { StrictMode } from "react"; |
| import { createRoot } from "react-dom/client"; |
| import "./styles.css"; |
| import "./markdown.css"; |
| |
| import App from "./App"; |
| |
| const root = createRoot(document.getElementById("root")); |
| root.render(<App />); |
| ;`), |
| '/components/ui/MarkdownRenderer.tsx': markdownRenderer, |
| 'markdown.css': markdownCSS, |
| }; |
| }; |
|
|