fix(client): pass the thinking preference into ReasoningCompact

`showThinking` is an app-global display preference the group only reads,
and `ToolCallGroup` already receives it from its host. `ReasoningCompact`
still reached for `showThinkingAtom` itself, coupling a grouped thought
to `~/store` and blocking the move to a feature-owned workspace. It
takes the preference as a required prop now, from the value the group
already holds.
This commit is contained in:
Marco Beretta 2026-09-06 18:52:31 +02:00
parent 6e16282b5c
commit 77e905be2c
No known key found for this signature in database
GPG key ID: D918033D8E74CC11
3 changed files with 40 additions and 5 deletions

View file

@ -273,6 +273,10 @@ Reasoning.displayName = 'Reasoning';
type ReasoningCompactProps = {
reasoning: string;
label: string;
/** The host's thoughts-visible preference, supplied by the group rather
* than read from the app store, so a grouped thought stays movable with
* the feature that renders it. */
showThinking: boolean;
isAfterTool?: boolean;
/** True while this is the reasoning part currently streaming. Drives the
* collapsed grok-style live peek. */
@ -287,11 +291,16 @@ type ReasoningCompactProps = {
* a floating collapse + copy bar inside the rounded content panel.
*/
export const ReasoningCompact = memo(
({ reasoning, label, isAfterTool = false, isStreaming = false }: ReasoningCompactProps) => {
({
reasoning,
label,
showThinking,
isAfterTool = false,
isStreaming = false,
}: ReasoningCompactProps) => {
const contentId = useId();
const localize = useLocalize();
const fontSize = useAtomValue(fontSizeAtom);
const showThinking = useAtomValue(showThinkingAtom);
const [isExpanded, setIsExpanded] = useState(showThinking);
const [isBarVisible, setIsBarVisible] = useState(false);
const [isCopied, setIsCopied] = useState(false);

View file

@ -36,7 +36,7 @@ jest.mock('lucide-react', () => ({
describe('ReasoningCompact', () => {
it('reveals its own Thoughts chevron instantly on row hover or focus', () => {
render(<ReasoningCompact reasoning="A useful thought" label="Thoughts" />);
render(<ReasoningCompact reasoning="A useful thought" label="Thoughts" showThinking={false} />);
const button = screen.getByRole('button', { name: 'Thoughts' });
const chevron = screen.getByTestId('thoughts-chevron');
@ -55,7 +55,14 @@ describe('ReasoningCompact', () => {
});
it('removes the extra top margin when Thoughts follows a tool', () => {
render(<ReasoningCompact reasoning="A useful thought" label="Thoughts" isAfterTool />);
render(
<ReasoningCompact
reasoning="A useful thought"
label="Thoughts"
isAfterTool
showThinking={false}
/>,
);
const row = screen
.getByRole('button', { name: 'Thoughts' })
@ -69,9 +76,27 @@ describe('ReasoningCompact', () => {
/** Collapsed is the default whenever thoughts are hidden, and a streaming
* THINK part re-renders on every delta, so keeping the whole text mounted
* behind the invisible panel cost layout work for nothing. */
render(<ReasoningCompact reasoning="A long stream of reasoning" label="Thoughts" />);
render(
<ReasoningCompact
reasoning="A long stream of reasoning"
label="Thoughts"
showThinking={false}
/>,
);
expect(screen.getByRole('button', { name: 'Thoughts' })).toBeInTheDocument();
expect(screen.queryByText('A long stream of reasoning')).not.toBeInTheDocument();
});
it('opens from the host preference rather than the app store', () => {
render(
<ReasoningCompact
reasoning="A long stream of reasoning"
label="Thoughts"
showThinking={true}
/>,
);
expect(screen.getByText('A long stream of reasoning')).toBeInTheDocument();
});
});

View file

@ -672,6 +672,7 @@ export default function ToolCallGroup({
key={`reasoning-${idx}`}
reasoning={reasoning}
label={label}
showThinking={showThinking}
isAfterTool={isAfterTool}
isStreaming={streaming}
/>