import { c as _c } from "react-compiler-runtime"; import * as React from 'react'; import { useMemo } from 'react'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import { Ansi, Text } from '../../ink.js'; import { createHyperlink } from '../../utils/hyperlink.js'; import { jsonParse, jsonStringify } from '../../utils/slowOperations.js'; import { renderTruncatedContent } from '../../utils/terminal.js'; import { MessageResponse } from '../MessageResponse.js'; import { InVirtualListContext } from '../messageActions.js'; import { useExpandShellOutput } from './ExpandShellOutputContext.js'; export function tryFormatJson(line: string): string { try { const parsed = jsonParse(line); const stringified = jsonStringify(parsed); // Check if precision was lost during JSON round-trip // This happens when large integers exceed Number.MAX_SAFE_INTEGER // We normalize both strings by removing whitespace and unnecessary // escapes (\/ is valid but optional in JSON) for comparison const normalizedOriginal = line.replace(/\\\//g, '/').replace(/\s+/g, ''); const normalizedStringified = stringified.replace(/\s+/g, ''); if (normalizedOriginal !== normalizedStringified) { // Precision loss detected - return original line unformatted return line; } return jsonStringify(parsed, null, 2); } catch { return line; } } const MAX_JSON_FORMAT_LENGTH = 10_000; export function tryJsonFormatContent(content: string): string { if (content.length > MAX_JSON_FORMAT_LENGTH) { return content; } const allLines = content.split('\n'); return allLines.map(tryFormatJson).join('\n'); } // Match http(s) URLs inside JSON string values. Conservative: no quotes, // no whitespace, no trailing comma/brace that'd be JSON structure. const URL_IN_JSON = /https?:\/\/[^\s"'<>\\]+/g; export function linkifyUrlsInText(content: string): string { return content.replace(URL_IN_JSON, url => createHyperlink(url)); } export function OutputLine(t0) { const $ = _c(11); const { content, verbose, isError, isWarning, linkifyUrls } = t0; const { columns } = useTerminalSize(); const expandShellOutput = useExpandShellOutput(); const inVirtualList = React.useContext(InVirtualListContext); const shouldShowFull = verbose || expandShellOutput; let t1; if ($[0] !== columns || $[1] !== content || $[2] !== inVirtualList || $[3] !== linkifyUrls || $[4] !== shouldShowFull) { bb0: { let formatted = tryJsonFormatContent(content); if (linkifyUrls) { formatted = linkifyUrlsInText(formatted); } if (shouldShowFull) { t1 = stripUnderlineAnsi(formatted); break bb0; } t1 = stripUnderlineAnsi(renderTruncatedContent(formatted, columns, inVirtualList)); } $[0] = columns; $[1] = content; $[2] = inVirtualList; $[3] = linkifyUrls; $[4] = shouldShowFull; $[5] = t1; } else { t1 = $[5]; } const formattedContent = t1; const color = isError ? "error" : isWarning ? "warning" : undefined; let t2; if ($[6] !== formattedContent) { t2 = {formattedContent}; $[6] = formattedContent; $[7] = t2; } else { t2 = $[7]; } let t3; if ($[8] !== color || $[9] !== t2) { t3 = {t2}; $[8] = color; $[9] = t2; $[10] = t3; } else { t3 = $[10]; } return t3; } /** * Underline ANSI codes in particular tend to leak out for some reason. I wasn't * able to figure out why, or why emitting a reset ANSI code wasn't enough to * prevent them from leaking. I also didn't want to strip all ANSI codes with * stripAnsi(), because we used to do that and people complained about losing * all formatting. So we just strip the underline ANSI codes specifically. */ export function stripUnderlineAnsi(content: string): string { return content.replace( // eslint-disable-next-line no-control-regex /\u001b\[([0-9]+;)*4(;[0-9]+)*m|\u001b\[4(;[0-9]+)*m|\u001b\[([0-9]+;)*4m/g, ''); }