import type { ToolCallDiffTarget } from "../../diffPanelStore"; import type { EnvironmentId, OrchestrationV2ProjectedTurnItem } from "@t3tools/contracts"; import { isJsonToolText, toolCallLines, turnItemDetailRevision, turnItemNeedsDetailFetch, turnItemOutputImages, turnItemOutputText, } from "@t3tools/client-runtime/work-log/item-detail"; import { formatDiffCount } from "@t3tools/client-runtime/diff-count"; import { isJavaScriptReplTool } from "@t3tools/shared/toolActivity"; import { ExternalLinkIcon } from "lucide-react"; import { memo, type ReactNode, Suspense, use } from "react"; import { useRenderedAppearance } from "../../hooks/useTheme"; import { cn } from "../../lib/utils"; import { resolveDiffThemeName } from "../../lib/diffRendering"; import { getSyntaxHighlighterPromise } from "../../lib/syntaxHighlighting"; import { useTurnItemDetail } from "../../state/queries"; import { formatWorkspaceRelativePath } from "../../filePathDisplay"; import { Button } from "../ui/button"; import ChatMarkdown from "../ChatMarkdown"; import { ChatMarkdownAssetImage } from "./ChatMarkdownMedia"; import { RenderErrorBoundary } from "../RenderErrorBoundary"; import { CommandOutputPanel, useCommandOutputStreaming } from "./CommandOutputPanel"; import { resolveExternalWebLinkHref } from "./externalLinkContextMenu"; import type { ExpandedImagePreview } from "./ExpandedImagePreview"; import { ShellCommandBlock } from "./ShellCommandBlock"; import type { TurnItemSource } from "./timelineRowContext"; interface V2ItemInspectorProps { readonly projectedItem: OrchestrationV2ProjectedTurnItem; readonly environmentId: EnvironmentId; readonly cwd?: string | undefined; readonly workspaceRoot?: string | undefined; readonly onOpenToolDiff?: ((target: ToolCallDiffTarget) => void) | undefined; readonly onImageExpand?: ((preview: ExpandedImagePreview) => void) | undefined; readonly source?: TurnItemSource | undefined; } function SyntaxTokens({ text, language, }: { readonly text: string; readonly language: "json" | "typescript" | "html"; }) { const resolvedTheme = useRenderedAppearance(); const highlighter = use(getSyntaxHighlighterPromise(language)); const { tokens } = highlighter.codeToTokens(text, { lang: language, theme: resolveDiffThemeName(resolvedTheme), }); return tokens.flatMap((line, lineIndex) => [ lineIndex > 0 ? "\n" : "", ...line.map((token) => ( {token.content} )), ]); } const monoClassName = "font-mono text-(length:--font-size-code,var(--text-2xs)) leading-relaxed whitespace-pre-wrap break-words select-text"; function StructuredValue({ value, highlightJson = false, language, }: { readonly value: unknown; readonly highlightJson?: boolean; readonly language?: "typescript" | "html" | "json"; }) { const text = typeof value === "string" ? value : JSON.stringify(value, null, 2); const isJson = highlightJson && isJsonToolText(text); if (!text) return null; return (
{language || isJson ? (
) : (
text
)}
);
}
function useFetchedTurnItem(
projectedItem: OrchestrationV2ProjectedTurnItem,
environmentId: EnvironmentId,
source: TurnItemSource | undefined,
skip = false,
) {
const wireItem = projectedItem.item;
const fetches = !skip && turnItemNeedsDetailFetch(wireItem);
const detail = useTurnItemDetail(
fetches
? {
environmentId,
threadId: projectedItem.sourceThreadId,
itemId: projectedItem.sourceItemId,
revision: turnItemDetailRevision(wireItem),
}
: null,
source?.detail,
);
const fetchedItem = detail.data?.item;
const item = fetchedItem?.type === wireItem.type ? fetchedItem : wireItem;
return {
item,
output: {
output: turnItemOutputText(item),
images: turnItemOutputImages(item),
environmentId,
pending: item === wireItem && detail.isPending,
error:
item !== wireItem
? null
: detail.data?.item === null
? "Output is no longer available."
: detail.error,
empty: fetches && item !== wireItem,
},
};
}
interface ToolOutputState {
readonly output: string | null;
readonly images: ReturnType{result.preview}
) : null}{result.title ?? result.url ?? "Search result"}
)} {result.snippet ?{result.snippet}
: null}