import React from "react";
import { Text, View } from "react-native";
import { StyleSheet } from "react-native-unistyles";
import { isWeb } from "@/constants/platform";
import { CODE_SURFACE_DATASET } from "@/styles/code-surface";
import { syntaxTokenStyleFor } from "@/styles/syntax-token-styles";
import type { KeyedLine, KeyedToken } from "@/utils/highlight-cache";
// Line height kept in sync with the plain `scrollText` style in
// tool-call-details so a highlighted block lines up with an unhighlighted
// fallback and blank lines keep their height.
const CODE_LINE_HEIGHT = 18;
const ZERO_WIDTH = "";
interface HighlightedLinesProps {
lines: KeyedLine[];
// 1-based line number of the first line; when set, a line-number gutter is
// rendered (used by Read, which carries a server-normalized offset).
startLine?: number;
}
function ContentLine({ line }: { line: KeyedLine }) {
return (
{line.tokens.length === 0
? ZERO_WIDTH
: line.tokens.map(({ key, token }: KeyedToken) => (
{token.text}
))}
);
}
const GutteredLine = React.memo(function GutteredLine({
line,
lineNumber,
digits,
}: {
line: KeyedLine;
lineNumber: number;
digits: number;
}) {
return (
{String(lineNumber).padStart(digits)}
);
});
// Renders pre-tokenized lines (from the shared highlight cache), optionally with
// a line-number gutter. Callers decide whether to highlight at all, so the
// expensive size-cap / unsupported-language fallback stays a single plain Text.
export function HighlightedLines({ lines, startLine }: HighlightedLinesProps) {
if (startLine === undefined) {
return (
{lines.map((line) => (
))}
);
}
const lastLineNumber = startLine + lines.length - 1;
const digits = Math.max(2, String(lastLineNumber).length);
return (
{lines.map((line, index) => (
))}
);
}
const styles = StyleSheet.create((theme) => ({
row: {
flexDirection: "row",
minHeight: CODE_LINE_HEIGHT,
},
gutterText: {
fontFamily: theme.fontFamily.mono,
fontSize: theme.fontSize.code,
lineHeight: CODE_LINE_HEIGHT,
color: theme.colors.foregroundMuted,
opacity: 0.6,
userSelect: "none",
flexShrink: 0,
},
lineText: {
fontFamily: theme.fontFamily.mono,
fontSize: theme.fontSize.code,
color: theme.colors.foreground,
lineHeight: CODE_LINE_HEIGHT,
...(isWeb
? {
whiteSpace: "pre",
overflowWrap: "normal",
}
: null),
},
}));