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(...@@ -1009,7 +1009,7 @@ const ThreadWorkLogRow = memo(function ThreadWorkLogRow(
1009 entering={WORK_LOG_DETAIL_ENTER_TRANSITION}1009 entering={WORK_LOG_DETAIL_ENTER_TRANSITION}
1010 exiting={WORK_LOG_DETAIL_EXIT_TRANSITION}1010 exiting={WORK_LOG_DETAIL_EXIT_TRANSITION}
1011 layout={WORK_LOG_LAYOUT_TRANSITION}1011 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"}
1013 >1013 >
1014 {row.workEntry.questionAnswer ? (1014 {row.workEntry.questionAnswer ? (
1015 <QuestionAnswerHistory1015 <QuestionAnswerHistory
apps/web/src/components/chat/V2ItemInspector.tsx+50-5
...@@ -5,12 +5,16 @@ import type {...@@ -5,12 +5,16 @@ import type {
5 ThreadId,5 ThreadId,
6} from "@t3tools/contracts";6} from "@t3tools/contracts";
7import { ExternalLinkIcon, GitBranchIcon, RotateCcwIcon } from "lucide-react";7import { 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";
10import { useV2ItemSupport } from "../../state/v2ItemSupport";13import { useV2ItemSupport } from "../../state/v2ItemSupport";
11import { formatWorkspaceRelativePath } from "../../filePathDisplay";14import { formatWorkspaceRelativePath } from "../../filePathDisplay";
12import { Button } from "../ui/button";15import { Button } from "../ui/button";
13import ChatMarkdown from "../ChatMarkdown";16import ChatMarkdown from "../ChatMarkdown";
17import { RenderErrorBoundary } from "../RenderErrorBoundary";
14import { resolveExternalWebLinkHref } from "./externalLinkContextMenu";18import { resolveExternalWebLinkHref } from "./externalLinkContextMenu";
1519
16interface V2ItemInspectorProps {20interface V2ItemInspectorProps {
...@@ -26,12 +30,53 @@ interface V2ItemInspectorProps {...@@ -26,12 +30,53 @@ interface V2ItemInspectorProps {
26 }) => void;30 }) => void;
27}31}
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}) {
30 const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);58 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]);
31 if (!text) return null;68 if (!text) return null;
32 return (69 return (
33 <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">70 <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 )}
35 </pre>80 </pre>
36 );81 );
37}82}
...@@ -61,7 +106,7 @@ export const V2ItemInspector = memo(function V2ItemInspector(props: V2ItemInspec...@@ -61,7 +106,7 @@ export const V2ItemInspector = memo(function V2ItemInspector(props: V2ItemInspec
61106
62 {item.type === "command_execution" ? (107 {item.type === "command_execution" ? (
63 <div className="space-y-2">108 <div className="space-y-2">
64 <StructuredValue value={item.input} />109 <StructuredValue value={item.input} highlightJson />
65 {item.exitCode !== undefined ? (110 {item.exitCode !== undefined ? (
66 <p className={item.exitCode === 0 ? "text-success" : "text-destructive"}>111 <p className={item.exitCode === 0 ? "text-success" : "text-destructive"}>
67 Process exited with code {item.exitCode}112 Process exited with code {item.exitCode}
...@@ -158,7 +203,7 @@ export const V2ItemInspector = memo(function V2ItemInspector(props: V2ItemInspec...@@ -158,7 +203,7 @@ export const V2ItemInspector = memo(function V2ItemInspector(props: V2ItemInspec
158 <p className="mb-1 text-3xs font-medium tracking-wide uppercase text-muted-foreground">203 <p className="mb-1 text-3xs font-medium tracking-wide uppercase text-muted-foreground">
159 Input204 Input
160 </p>205 </p>
161 <StructuredValue value={item.input} />206 <StructuredValue value={item.input} highlightJson />
162 </div>207 </div>
163 ) : null}208 ) : null}
164209
apps/web/src/components/chat/WorkLog.tsx+3-3
...@@ -64,7 +64,7 @@ function WorkLogLine({ icon, label, trailing, wrapLabel }: RowContent) {...@@ -64,7 +64,7 @@ function WorkLogLine({ icon, label, trailing, wrapLabel }: RowContent) {
64}64}
6565
66const interactionClassName =66const 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
69function useRowClassName(interactive: boolean) {69function useRowClassName(interactive: boolean) {
70 const grouped = use(GroupedRows);70 const grouped = use(GroupedRows);
...@@ -119,9 +119,9 @@ export function WorkLogDetails({...@@ -119,9 +119,9 @@ export function WorkLogDetails({
119 return (119 return (
120 <div120 <div
121 className={cn(121 className={cn(
122 "ms-7 cursor-auto",122 "cursor-auto",
123 kind === "text"123 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"
125 : kind === "panel"125 : kind === "panel"
126 ? "mt-1 rounded-md bg-muted/40 px-3 py-2"126 ? "mt-1 rounded-md bg-muted/40 px-3 py-2"
127 : "mt-1",127 : "mt-1",