diff --git a/apps/web/src/appSettings.ts b/apps/web/src/appSettings.ts index 4dbd29e66..fe6eb6957 100644 --- a/apps/web/src/appSettings.ts +++ b/apps/web/src/appSettings.ts @@ -88,6 +88,7 @@ export const AppSettingsSchema = Schema.Struct({ sidebarAccentProjectNames: Schema.Boolean.pipe(withDefaults(() => true)), sidebarAccentColorOverride: Schema.optional(Schema.String.check(Schema.isMaxLength(64))), sidebarAccentBgColorOverride: Schema.optional(Schema.String.check(Schema.isMaxLength(64))), + showReasoningContent: Schema.Boolean.pipe(withDefaults(() => false)), showStitchBorder: Schema.Boolean.pipe(withDefaults(() => true)), customCodexModels: Schema.Array(Schema.String).pipe(withDefaults(() => [])), customClaudeModels: Schema.Array(Schema.String).pipe(withDefaults(() => [])), diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 23418dc3b..bd52ad92c 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -380,6 +380,7 @@ export default function ChatView({ threadId }: ChatViewProps) { const { settings } = useAppSettings(); const setStickyComposerModel = useComposerDraftStore((store) => store.setStickyModel); const timestampFormat = settings.timestampFormat; + const showReasoningContent = settings.showReasoningContent; const navigate = useNavigate(); const activeProjectId = threads.find((t) => t.id === threadId)?.projectId ?? null; const previewOpen = usePreviewStateStore((state) => @@ -4741,6 +4742,7 @@ export default function ChatView({ threadId }: ChatViewProps) { onImageExpand={onExpandTimelineImage} markdownCwd={gitCwd ?? undefined} resolvedTheme={resolvedTheme} + showReasoningContent={showReasoningContent} timestampFormat={timestampFormat} workspaceRoot={activeProject?.cwd ?? undefined} shortcutGuides={chatShortcutGuides} diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx index 4d8125479..7d5627ca9 100644 --- a/apps/web/src/components/chat/MessagesTimeline.test.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx @@ -99,6 +99,7 @@ describe("MessagesTimeline", () => { onImageExpand={() => {}} markdownCwd={undefined} resolvedTheme="light" + showReasoningContent={false} timestampFormat="locale" workspaceRoot={undefined} onRemoveQueuedMessage={() => {}} @@ -146,6 +147,7 @@ describe("MessagesTimeline", () => { onImageExpand={() => {}} markdownCwd={undefined} resolvedTheme="light" + showReasoningContent={false} timestampFormat="locale" workspaceRoot={undefined} onRemoveQueuedMessage={() => {}} @@ -180,6 +182,7 @@ describe("MessagesTimeline", () => { onImageExpand={() => {}} markdownCwd={undefined} resolvedTheme="light" + showReasoningContent={false} timestampFormat="locale" workspaceRoot={undefined} onRemoveQueuedMessage={() => {}} diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index 3c543d486..25adf1f1a 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -85,6 +85,7 @@ interface MessagesTimelineProps { onImageExpand: (preview: ExpandedImagePreview) => void; markdownCwd: string | undefined; resolvedTheme: "light" | "dark"; + showReasoningContent: boolean; timestampFormat: TimestampFormat; workspaceRoot: string | undefined; onRemoveQueuedMessage: (messageId: MessageId) => void; @@ -111,6 +112,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({ onImageExpand, markdownCwd, resolvedTheme, + showReasoningContent, timestampFormat, workspaceRoot, onRemoveQueuedMessage, @@ -372,6 +374,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({ key={`work-row:${subGroup.entries[0]!.id}`} workEntry={subGroup.entries[0]!} resolvedTheme={resolvedTheme} + showReasoningContent={showReasoningContent} /> ) : ( ), )} @@ -1072,8 +1076,9 @@ function groupConsecutiveWorkEntries(entries: TimelineWorkEntry[]): ConsecutiveW const SimpleWorkEntryRow = memo(function SimpleWorkEntryRow(props: { workEntry: TimelineWorkEntry; resolvedTheme: "light" | "dark"; + showReasoningContent: boolean; }) { - const { workEntry, resolvedTheme } = props; + const { workEntry, resolvedTheme, showReasoningContent } = props; const iconConfig = workToneIcon(workEntry.tone); const EntryIcon = workEntryIcon(workEntry); const heading = toolWorkEntryHeading(workEntry); @@ -1081,6 +1086,8 @@ const SimpleWorkEntryRow = memo(function SimpleWorkEntryRow(props: { const hasChangedFiles = (workEntry.changedFiles?.length ?? 0) > 0; const previewIsChangedFiles = hasChangedFiles && !workEntry.command && !workEntry.detail; const hasDiffData = workEntry.diffData != null && workEntry.itemType === "file_change"; + const isReasoningWithDetail = + showReasoningContent && workEntry.label === "Reasoning update" && !!workEntry.detail; return (
@@ -1105,6 +1112,13 @@ const SimpleWorkEntryRow = memo(function SimpleWorkEntryRow(props: {

+ {isReasoningWithDetail && ( +
+

+ {workEntry.detail} +

+
+ )} {hasDiffData ? (
@@ -1140,8 +1154,9 @@ const CollapsedWorkEntryGroup = memo(function CollapsedWorkEntryGroup(props: { heading: string; entries: TimelineWorkEntry[]; resolvedTheme: "light" | "dark"; + showReasoningContent: boolean; }) { - const { heading, entries, resolvedTheme } = props; + const { heading, entries, resolvedTheme, showReasoningContent } = props; const [isExpanded, setIsExpanded] = useState(false); const firstEntry = entries[0]!; const EntryIcon = workEntryIcon(firstEntry); @@ -1177,10 +1192,17 @@ const CollapsedWorkEntryGroup = memo(function CollapsedWorkEntryGroup(props: { {entries.map((entry) => { const preview = workEntryPreview(entry); const hasDiff = entry.diffData != null && entry.itemType === "file_change"; + const showReasoningDetail = + showReasoningContent && entry.label === "Reasoning update" && !!entry.detail; return (
-

- {preview ?? heading} +

+ {showReasoningDetail ? entry.detail : (preview ?? heading)}

{hasDiff && (
diff --git a/apps/web/src/routes/_chat.settings.tsx b/apps/web/src/routes/_chat.settings.tsx index 8662b5e44..190c0bb55 100644 --- a/apps/web/src/routes/_chat.settings.tsx +++ b/apps/web/src/routes/_chat.settings.tsx @@ -422,6 +422,9 @@ function SettingsRouteView() { ...(settings.enableAssistantStreaming !== defaults.enableAssistantStreaming ? ["Assistant output"] : []), + ...(settings.showReasoningContent !== defaults.showReasoningContent + ? ["Reasoning content"] + : []), ...(settings.showAuthFailuresAsErrors !== defaults.showAuthFailuresAsErrors ? ["Auth failure errors"] : []), @@ -1131,6 +1134,34 @@ function SettingsRouteView() { } /> + + updateSettings({ + showReasoningContent: defaults.showReasoningContent, + }) + } + /> + ) : null + } + control={ + + updateSettings({ + showReasoningContent: Boolean(checked), + }) + } + aria-label="Show reasoning content in work log" + /> + } + /> +