import { useState } from "react";
import { Terminal, FileText } from "lucide-react";
import { useTerminalContext } from "../features/Terminal/useTerminalContext";
import { useAppDispatch, useAppSelector } from "../app/hooks";
import {
dismissEntry,
selectMostRecentExecution,
} from "../features/Terminal/terminalHistorySlice";
import { Button } from "./DesignSystem/Button";
import { CommandWithAI } from "./shared/CommandWithAI";
import {
normalizeQuotes,
copyToClipboard,
ExecutionResult,
// highlightCode,
} from "./shared/commandUtils";
interface CodeBlockProps {
children: string;
className?: string;
language?: string;
isShellInteractive?: boolean;
/** Called with the captured result after the Run button's command finishes. */
onResult?: (result: { output: string; exitCode: number }) => void;
}
export function CodeBlock({
children,
className = "",
language = "text",
isShellInteractive = false,
onResult,
}: CodeBlockProps) {
const [isExecuting, setIsExecuting] = useState(false);
const [copied, setCopied] = useState(false);
const { runCaptured, setPanelVisible, writeToPty, cwd } =
useTerminalContext();
const dispatch = useAppDispatch();
const code = children.trim();
const normalizedCode = isShellInteractive ? normalizeQuotes(code) : code;
// Use the selector to get the most recent execution result for this command (only for shell commands)
const executionResult = useAppSelector(
selectMostRecentExecution(normalizedCode),
);
console.log("executionResult", executionResult);
// Convert the execution result to the format expected by CommandWithAI
const result: ExecutionResult | null = executionResult
? {
output: executionResult.output,
exitCode: executionResult.exitCode,
timestamp: executionResult.timestamp,
duration: executionResult.duration,
}
: null;
// Check if the command is currently running
const isRunning = executionResult?.isRunning || false;
const handleExecute = async () => {
if (!isShellInteractive) return;
setIsExecuting(true);
setPanelVisible(true);
// give the terminal time to resize after opening the panel
await new Promise((resolve) => setTimeout(resolve, 100));
try {
const captured = await runCaptured(normalizedCode);
// The result shown inline comes from the selector; onResult is for
// callers that consume the output (e.g. a recipe capturing a value).
onResult?.(captured);
} catch (error) {
console.log("Error during execution:", error);
// Note: We don't set a local error result here since the selector will handle it
// The error will be recorded in the terminal history and picked up by the selector
} finally {
setIsExecuting(false);
}
};
const handleCopy = async () => {
const success = await copyToClipboard(code);
if (success) {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
const handleDismiss = () => {
if (!isShellInteractive) return;
if (executionResult?.id) {
console.log("dismissing", executionResult);
dispatch(dismissEntry(executionResult.id));
}
};
const handleWriteToTerminal = () => {
if (!isShellInteractive) return;
setPanelVisible(true);
writeToPty(normalizedCode);
};
// For interactive blocks, use the full CommandWithAI component
let headerButtons = null;
if (isShellInteractive) {
headerButtons = (
<>
>
);
}
return (
);
}