Files
CodeNomad/src/components/markdown.tsx
Shantur Rathore 3c5c4755b8 Add logs tab with real-time server output and consolidate syntax highlighting
- Implement dedicated Logs tab showing stdout/stderr from OpenCode server
- Add log level parsing (INFO, ERROR, WARN, DEBUG) with color coding
- Stream logs from main process to renderer via IPC events
- Persist scroll position and auto-scroll state per instance
- Synchronize instance IDs between renderer and main process
- Consolidate syntax highlighting to single shared highlighter instance
- Optimize markdown rendering with global highlighter initialization
- Fix code block copy button to always appear on right side
- Enable debug logging with --print-logs --log-level DEBUG flags
2025-10-23 11:14:35 +01:00

74 lines
2.5 KiB
TypeScript

import { createEffect, createSignal, Show } from "solid-js"
import { renderMarkdown } from "../lib/markdown"
interface MarkdownProps {
content: string
isDark?: boolean
}
export function Markdown(props: MarkdownProps) {
const [html, setHtml] = createSignal("")
let containerRef: HTMLDivElement | undefined
createEffect(async () => {
const rendered = await renderMarkdown(props.content)
setHtml(rendered)
})
createEffect(() => {
const currentHtml = html()
if (containerRef && currentHtml) {
setTimeout(() => {
const codeBlocks = containerRef?.querySelectorAll(".markdown-code-block")
codeBlocks?.forEach((block) => {
const existing = block.querySelector(".code-block-header")
if (existing) return
const lang = block.getAttribute("data-language")
const encodedCode = block.getAttribute("data-code")
const header = document.createElement("div")
header.className = "code-block-header"
const languageSpan = lang
? `<span class="code-block-language">${lang}</span>`
: '<span class="code-block-language"></span>'
header.innerHTML = `
${languageSpan}
<button class="code-block-copy" data-code="${encodedCode || ""}">
<svg class="copy-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
<span class="copy-text">Copy</span>
</button>
`
block.insertBefore(header, block.firstChild)
const button = header.querySelector(".code-block-copy")
if (button) {
button.addEventListener("click", async () => {
const code = button.getAttribute("data-code")
if (code) {
const decodedCode = decodeURIComponent(code)
await navigator.clipboard.writeText(decodedCode)
const copyText = button.querySelector(".copy-text")
if (copyText) {
copyText.textContent = "Copied!"
setTimeout(() => {
copyText.textContent = "Copy"
}, 2000)
}
}
})
}
})
}, 0)
}
})
return <div ref={containerRef} class="prose prose-sm dark:prose-invert max-w-none" innerHTML={html()} />
}