49 lines
2.0 KiB
TypeScript
49 lines
2.0 KiB
TypeScript
import { useEffect, useRef } from "react"
|
|
import { EditorState } from "@codemirror/state"
|
|
import { EditorView, lineNumbers } from "@codemirror/view"
|
|
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"
|
|
import { javascript } from "@codemirror/lang-javascript"
|
|
import { tags as t } from "@lezer/highlight"
|
|
|
|
const highlight = HighlightStyle.define([
|
|
{ tag: [t.keyword, t.modifier, t.operatorKeyword], color: "var(--ds-accent)" },
|
|
{ tag: [t.propertyName], color: "var(--ds-accent)" },
|
|
{ tag: [t.string, t.regexp], color: "var(--ds-ok)" },
|
|
{ tag: [t.number, t.bool, t.null], color: "var(--ds-warn)" },
|
|
{ tag: [t.comment], color: "var(--ds-text-3)", fontStyle: "italic" },
|
|
])
|
|
|
|
const theme = EditorView.theme({
|
|
"&": { backgroundColor: "var(--ds-bg)", color: "var(--ds-text-2)", fontSize: "12px" },
|
|
".cm-content": { fontFamily: "var(--font-data, ui-monospace, monospace)", padding: "6px 0" },
|
|
".cm-gutters": { backgroundColor: "var(--ds-bg)", color: "var(--ds-text-3)", border: "none" },
|
|
".cm-scroller": { overflow: "auto", maxHeight: "360px" },
|
|
})
|
|
|
|
/**
|
|
* Read-only CodeMirror view. JSON is valid JavaScript, so the JS grammar
|
|
* highlights it; `lang="text"` skips highlighting for plain log lines.
|
|
*/
|
|
export const CodeBlock = ({ code, lang = "json", label }: { code: string; lang?: "json" | "text"; label: string }) => {
|
|
const host = useRef<HTMLDivElement>(null)
|
|
useEffect(() => {
|
|
if (!host.current) return
|
|
const view = new EditorView({
|
|
parent: host.current,
|
|
state: EditorState.create({
|
|
doc: code,
|
|
extensions: [
|
|
EditorState.readOnly.of(true),
|
|
EditorView.editable.of(false),
|
|
EditorView.lineWrapping,
|
|
lineNumbers(),
|
|
theme,
|
|
...(lang === "json" ? [javascript(), syntaxHighlighting(highlight)] : []),
|
|
],
|
|
}),
|
|
})
|
|
return () => view.destroy()
|
|
}, [code, lang])
|
|
return <div ref={host} role="group" aria-label={label} className="overflow-hidden rounded-[10px] border border-ds-line" />
|
|
}
|