6f41fa7cbe
- Install @phosphor-icons/react package - Create centralized Icon component with size/weight/color variants - Create icon registry with 34 icons across 5 categories - Replace all raw Unicode symbols with proper icon components - Add icons to navigation, buttons, status indicators, git operations - Add icon CSS with consistent sizing and spacing - Fix type definitions for Phosphor icon compatibility Quality gates: typecheck ✓, lint ✓, build ✓ (375KB bundle)
78 lines
1.9 KiB
TypeScript
78 lines
1.9 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
|
|
import { Icon } from "./icon";
|
|
import { highlightCode, loadLanguage } from "../utils/language";
|
|
|
|
interface SyntaxHighlighterProps {
|
|
code: string;
|
|
language: string;
|
|
showLineNumbers?: boolean;
|
|
}
|
|
|
|
export const SyntaxHighlighter: React.FC<SyntaxHighlighterProps> = ({
|
|
code,
|
|
language,
|
|
showLineNumbers = true,
|
|
}) => {
|
|
const [highlighted, setHighlighted] = useState(">");
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const highlight = async () => {
|
|
await loadLanguage(language);
|
|
setHighlighted(highlightCode(code, language));
|
|
};
|
|
void highlight();
|
|
}, [code, language]);
|
|
|
|
const handleCopy = async () => {
|
|
await navigator.clipboard.writeText(code);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
};
|
|
|
|
const lines = code.split("\n");
|
|
|
|
return (
|
|
<div className="syntax-highlighter">
|
|
<div className="highlighter-toolbar">
|
|
<span className="language-badge">{language}</span>
|
|
<button
|
|
className="copy-button"
|
|
onClick={handleCopy}
|
|
type="button"
|
|
>
|
|
{copied ? (
|
|
<>
|
|
<Icon name="success" size="sm" />
|
|
Copied!
|
|
</>
|
|
) : (
|
|
<>
|
|
<Icon name="copy" size="sm" />
|
|
Copy
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
<div className="code-container">
|
|
{showLineNumbers && (
|
|
<div className="line-numbers">
|
|
{lines.map((_, i) => (
|
|
<div key={i} className="line-number">
|
|
{i + 1}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
<pre className="code-block">
|
|
<code
|
|
className={`language-${language}`}
|
|
dangerouslySetInnerHTML={{ __html: highlighted }}
|
|
/>
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|