Use marked.js for Markdown and add React artifact renderer
- Markdown: swap regex converter for marked.js (full GFM support) - JSX/TSX: render in sandboxed iframe with React 18 + Babel standalone Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -475,6 +475,7 @@ async fn artifact_viewer(
|
||||
"html" | "htm" => "html",
|
||||
"md" | "markdown" => "markdown",
|
||||
"json" => "json",
|
||||
"jsx" | "tsx" => "react",
|
||||
_ => "code",
|
||||
};
|
||||
let escaped_name = meta.file_name.replace('<', "<").replace('>', ">");
|
||||
@@ -486,6 +487,7 @@ async fn artifact_viewer(
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<title>{escaped_name}</title>
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<style>
|
||||
:root {{ --bg: #1a1a2e; --panel: #16213e; --ink: #e0e0e0; --accent: #0f3460; --code-bg: #0d1117; }}
|
||||
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
|
||||
@@ -541,13 +543,19 @@ iframe {{ width: 100%; min-height: 80vh; border: 1px solid #333; border-radius:
|
||||
el.querySelector("iframe").srcdoc = text;
|
||||
}} else if (renderMode === "markdown") {{
|
||||
el.classList.add("md");
|
||||
el.innerHTML = simpleMarkdown(text);
|
||||
if (typeof marked !== "undefined") {{
|
||||
el.innerHTML = marked.parse(text);
|
||||
}} else {{
|
||||
el.innerHTML = "<pre>" + escapeHtml(text) + "</pre>";
|
||||
}}
|
||||
}} else if (renderMode === "json") {{
|
||||
try {{
|
||||
el.innerHTML = "<pre>" + escapeHtml(JSON.stringify(JSON.parse(text), null, 2)) + "</pre>";
|
||||
}} catch(_) {{
|
||||
el.innerHTML = "<pre>" + escapeHtml(text) + "</pre>";
|
||||
}}
|
||||
}} else if (renderMode === "react") {{
|
||||
renderReactArtifact(el, text);
|
||||
}} else {{
|
||||
el.innerHTML = "<pre>" + escapeHtml(text) + "</pre>";
|
||||
}}
|
||||
@@ -559,17 +567,39 @@ iframe {{ width: 100%; min-height: 80vh; border: 1px solid #333; border-radius:
|
||||
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
}}
|
||||
|
||||
function simpleMarkdown(md) {{
|
||||
return md
|
||||
.replace(/^### (.+)$/gm, "<h3>$1</h3>")
|
||||
.replace(/^## (.+)$/gm, "<h2>$1</h2>")
|
||||
.replace(/^# (.+)$/gm, "<h1>$1</h1>")
|
||||
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
|
||||
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
||||
.replace(/^- (.+)$/gm, "<li>$1</li>")
|
||||
.replace(/(<li>.*<\/li>)/s, "<ul>$1</ul>")
|
||||
.replace(/\n\n/g, "</p><p>")
|
||||
.replace(/^/, "<p>").replace(/$/, "</p>");
|
||||
function renderReactArtifact(container, source) {{
|
||||
const html = `<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"><\/script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"><\/script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js"><\/script>
|
||||
<style>*{{margin:0;padding:0;box-sizing:border-box}}body{{font-family:-apple-system,system-ui,sans-serif;padding:16px}}</style>
|
||||
</head><body>
|
||||
<div id="root"></div>
|
||||
<script type="text/babel" data-type="module">
|
||||
${{source}}
|
||||
|
||||
// Auto-mount: find the default export or last component
|
||||
const __moduleKeys = Object.keys(window).filter(k => /^[A-Z]/.test(k) && typeof window[k] === 'function');
|
||||
try {{
|
||||
// Try rendering the source as-is (may contain its own ReactDOM.render)
|
||||
const root = document.getElementById('root');
|
||||
if (!root.hasChildNodes()) {{
|
||||
// If nothing rendered, look for an App or default component
|
||||
const App = window.App || window.default || (typeof exports !== 'undefined' && exports.default);
|
||||
if (App) {{
|
||||
ReactDOM.createRoot(root).render(React.createElement(App));
|
||||
}}
|
||||
}}
|
||||
}} catch(e) {{
|
||||
document.getElementById('root').innerHTML = '<pre style="color:red">' + e.message + '</pre>';
|
||||
}}
|
||||
<\/script>
|
||||
</body></html>`;
|
||||
container.innerHTML = '<iframe sandbox="allow-scripts allow-same-origin" style="width:100%;min-height:80vh;border:1px solid #333;border-radius:6px;background:#fff" srcdoc=""></iframe>';
|
||||
container.querySelector("iframe").srcdoc = html;
|
||||
}}
|
||||
}})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user