diff --git a/packages/elements/__tests__/reasoning.test.tsx b/packages/elements/__tests__/reasoning.test.tsx index e874d554..498b9ad0 100644 --- a/packages/elements/__tests__/reasoning.test.tsx +++ b/packages/elements/__tests__/reasoning.test.tsx @@ -183,6 +183,47 @@ describe("reasoningTrigger", () => { expect(screen.getByText("Thought for a few seconds")).toBeInTheDocument(); }); + it("does not warn when duration is set after streaming ends", () => { + const spy = vi.spyOn(console, "warn").mockImplementation(vi.fn()); + const { rerender } = render( + + + + ); + + rerender( + + + + ); + + expect(screen.getByText("Thought for 3 seconds")).toBeInTheDocument(); + expect(spy).not.toHaveBeenCalled(); + spy.mockRestore(); + }); + + it("prefers the duration prop over the measured duration", () => { + vi.useFakeTimers(); + const { rerender } = render( + + + + ); + + act(() => { + vi.advanceTimersByTime(2000); + }); + + rerender( + + + + ); + + expect(screen.getByText("Thought for 9 seconds")).toBeInTheDocument(); + vi.useRealTimers(); + }); + it("renders custom children", () => { render( diff --git a/packages/elements/src/reasoning.tsx b/packages/elements/src/reasoning.tsx index 4789ee2a..d465c7dd 100644 --- a/packages/elements/src/reasoning.tsx +++ b/packages/elements/src/reasoning.tsx @@ -75,10 +75,13 @@ export const Reasoning = memo( onChange: onOpenChange, prop: open, }); - const [duration, setDuration] = useControllableState({ - defaultProp: undefined, - prop: durationProp, - }); + // Kept in plain state: `duration` has no change callback, and + // `useControllableState` warns when `durationProp` goes from undefined + // (while streaming) to a number (once reasoning finishes). + const [measuredDuration, setDuration] = useState( + undefined + ); + const duration = durationProp ?? measuredDuration; const hasEverStreamedRef = useRef(isStreaming); const [hasAutoClosed, setHasAutoClosed] = useState(false);