e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import type { UIEvent } from "react";
|
|
import { useMemo, useRef } from "react";
|
|
import { highlight } from "@/lib/highlight";
|
|
|
|
type CodeEditorProps = {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
language?: string;
|
|
ariaLabel: string;
|
|
className?: string;
|
|
};
|
|
|
|
export function CodeEditor({
|
|
value,
|
|
onChange,
|
|
language = "zero",
|
|
ariaLabel,
|
|
className = "",
|
|
}: CodeEditorProps) {
|
|
const preRef = useRef<HTMLPreElement | null>(null);
|
|
|
|
const html = useMemo(() => {
|
|
const trailingNewline = value.endsWith("\n") ? " " : "";
|
|
return highlight(value + trailingNewline, language);
|
|
}, [value, language]);
|
|
|
|
function handleScroll(event: UIEvent<HTMLTextAreaElement>) {
|
|
const pre = preRef.current;
|
|
if (!pre) return;
|
|
pre.scrollTop = event.currentTarget.scrollTop;
|
|
pre.scrollLeft = event.currentTarget.scrollLeft;
|
|
}
|
|
|
|
return (
|
|
<div className={`relative h-full w-full ${className}`}>
|
|
<pre
|
|
ref={preRef}
|
|
aria-hidden="true"
|
|
className="pointer-events-none absolute inset-0 m-0 overflow-hidden whitespace-pre p-6 font-mono text-[0.9375rem] leading-[1.7] text-code-fg"
|
|
>
|
|
<code dangerouslySetInnerHTML={{ __html: html }} />
|
|
</pre>
|
|
<textarea
|
|
value={value}
|
|
onChange={(event) => onChange(event.target.value)}
|
|
onScroll={handleScroll}
|
|
spellCheck={false}
|
|
autoCorrect="off"
|
|
autoCapitalize="off"
|
|
autoComplete="off"
|
|
aria-label={ariaLabel}
|
|
className="relative block h-full w-full resize-none whitespace-pre border-0 bg-transparent p-6 font-mono text-[0.9375rem] leading-[1.7] text-transparent caret-fg outline-none"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|