authorgravatar for saiansh2525@gmail.comYash Singh <saiansh2525@gmail.com> 2026-10-03 18:46:17-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 18:46:17-05:00
logf786ff3548aef904b7bdd47f263e8704ba20ca4b
tree0940d2ef928bb43ceacb91745b9cbb284337e842
parentc47f4263f927a68645270c2892d8fa18ba7dcb3b
signature Signed by PGP key B5690EEEBB952194

fix(web): highlight tool inputs and remove nested work log indentation (#15384)


3 files changed, 54 insertions(+), 9 deletions(-)

apps/mobile/src/features/threads/thread-work-log.tsx+1-1
......@@ -1009,7 +1009,7 @@ const ThreadWorkLogRow = memo(function ThreadWorkLogRow(
10091009 entering={WORK_LOG_DETAIL_ENTER_TRANSITION}
10101010 exiting={WORK_LOG_DETAIL_EXIT_TRANSITION}
10111011 layout={WORK_LOG_LAYOUT_TRANSITION}
1012 className={reasoning ? "ml-7 py-1" : "ml-7 border-l border-border pb-1 pl-3 pt-0.5"}
1012 className={reasoning ? "ml-7 py-1" : "pb-1 pt-0.5"}
10131013 >
10141014 {row.workEntry.questionAnswer ? (
10151015 <QuestionAnswerHistory
apps/web/src/components/chat/V2ItemInspector.tsx+50-5
......@@ -5,12 +5,16 @@ import type {
55 ThreadId,
66} from "@t3tools/contracts";
77import { ExternalLinkIcon, GitBranchIcon, RotateCcwIcon } from "lucide-react";
8import { memo } from "react";
8import { memo, Suspense, use, useMemo } from "react";
99
10import { useTheme } from "../../hooks/useTheme";
11import { resolveDiffThemeName } from "../../lib/diffRendering";
12import { getSyntaxHighlighterPromise } from "../../lib/syntaxHighlighting";
1013import { useV2ItemSupport } from "../../state/v2ItemSupport";
1114import { formatWorkspaceRelativePath } from "../../filePathDisplay";
1215import { Button } from "../ui/button";
1316import ChatMarkdown from "../ChatMarkdown";
17import { RenderErrorBoundary } from "../RenderErrorBoundary";
1418import { resolveExternalWebLinkHref } from "./externalLinkContextMenu";
1519
1620interface V2ItemInspectorProps {
......@@ -26,12 +30,53 @@ interface V2ItemInspectorProps {
2630 }) => void;
2731}
2832
29function StructuredValue({ value }: { readonly value: unknown }) {
33function JsonTokens({ text }: { readonly text: string }) {
34 const { resolvedTheme } = useTheme();
35 const highlighter = use(getSyntaxHighlighterPromise("json"));
36 const { tokens } = useMemo(
37 () =>
38 highlighter.codeToTokens(text, { lang: "json", theme: resolveDiffThemeName(resolvedTheme) }),
39 [highlighter, text, resolvedTheme],
40 );
41 return tokens.flatMap((line, lineIndex) => [
42 lineIndex > 0 ? "\n" : "",
43 ...line.map((token) => (
44 <span key={token.offset} style={{ color: token.color }}>
45 {token.content}
46 </span>
47 )),
48 ]);
49}
50
51function StructuredValue({
52 value,
53 highlightJson = false,
54}: {
55 readonly value: unknown;
56 readonly highlightJson?: boolean;
57}) {
3058 const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
59 const isJson = useMemo(() => {
60 if (!highlightJson || !text) return false;
61 try {
62 JSON.parse(text);
63 return true;
64 } catch {
65 return false;
66 }
67 }, [highlightJson, text]);
3168 if (!text) return null;
3269 return (
3370 <pre className="max-h-80 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border/50 bg-background/60 p-2 font-mono text-2xs leading-relaxed text-muted-foreground select-text">
34 {text}
71 {isJson ? (
72 <RenderErrorBoundary fallback={text}>
73 <Suspense fallback={text}>
74 <JsonTokens text={text} />
75 </Suspense>
76 </RenderErrorBoundary>
77 ) : (
78 text
79 )}
3580 </pre>
3681 );
3782}
......@@ -61,7 +106,7 @@ export const V2ItemInspector = memo(function V2ItemInspector(props: V2ItemInspec
61106
62107 {item.type === "command_execution" ? (
63108 <div className="space-y-2">
64 <StructuredValue value={item.input} />
109 <StructuredValue value={item.input} highlightJson />
65110 {item.exitCode !== undefined ? (
66111 <p className={item.exitCode === 0 ? "text-success" : "text-destructive"}>
67112 Process exited with code {item.exitCode}
......@@ -158,7 +203,7 @@ export const V2ItemInspector = memo(function V2ItemInspector(props: V2ItemInspec
158203 <p className="mb-1 text-3xs font-medium tracking-wide uppercase text-muted-foreground">
159204 Input
160205 </p>
161 <StructuredValue value={item.input} />
206 <StructuredValue value={item.input} highlightJson />
162207 </div>
163208 ) : null}
164209
apps/web/src/components/chat/WorkLog.tsx+3-3
......@@ -64,7 +64,7 @@ function WorkLogLine({ icon, label, trailing, wrapLabel }: RowContent) {
6464}
6565
6666const interactionClassName =
67 "cursor-pointer hover:bg-accent/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/70";
67 "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/70";
6868
6969function useRowClassName(interactive: boolean) {
7070 const grouped = use(GroupedRows);
......@@ -119,9 +119,9 @@ export function WorkLogDetails({
119119 return (
120120 <div
121121 className={cn(
122 "ms-7 cursor-auto",
122 "cursor-auto",
123123 kind === "text"
124 ? "flex max-h-96 flex-col gap-3 overflow-auto px-0.5 py-1 select-text"
124 ? "ms-7 flex max-h-96 flex-col gap-3 overflow-auto px-0.5 py-1 select-text"
125125 : kind === "panel"
126126 ? "mt-1 rounded-md bg-muted/40 px-3 py-2"
127127 : "mt-1",