diff --git a/apps/desktop/src/chat/components/context-bar.tsx b/apps/desktop/src/chat/components/context-bar.tsx index 73f5439a0d..d90e1784be 100644 --- a/apps/desktop/src/chat/components/context-bar.tsx +++ b/apps/desktop/src/chat/components/context-bar.tsx @@ -281,7 +281,7 @@ export function ContextBar({ [entities], ); - if (chips.length === 0 && !onAddEntity) { + if (chips.length === 0) { return null; } diff --git a/apps/desktop/src/chat/components/input/index.tsx b/apps/desktop/src/chat/components/input/index.tsx index 3ea3caa255..547263ece7 100644 --- a/apps/desktop/src/chat/components/input/index.tsx +++ b/apps/desktop/src/chat/components/input/index.tsx @@ -148,8 +148,14 @@ function Container({
{children} diff --git a/apps/desktop/src/chat/components/message/normal.tsx b/apps/desktop/src/chat/components/message/normal.tsx index 3a4695281a..a4bc42988c 100644 --- a/apps/desktop/src/chat/components/message/normal.tsx +++ b/apps/desktop/src/chat/components/message/normal.tsx @@ -113,7 +113,13 @@ function Part({ part }: { part: Part }) { } function Reasoning({ part }: { part: Extract }) { - const cleaned = part.text + const raw = part.text.trim(); + + if (!raw) { + return null; + } + + const cleaned = raw .replace(/[\n`*#"]/g, " ") .replace(/\s+/g, " ") .trim(); @@ -121,6 +127,10 @@ function Reasoning({ part }: { part: Extract }) { const streaming = part.state !== "done"; const title = streaming ? cleaned.slice(-150) : cleaned; + if (!title) { + return null; + } + return ( } diff --git a/apps/desktop/src/chat/components/shared.test.ts b/apps/desktop/src/chat/components/shared.test.ts new file mode 100644 index 0000000000..e955e2b1d6 --- /dev/null +++ b/apps/desktop/src/chat/components/shared.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, test } from "vitest"; + +import { hasRenderableContent } from "./shared"; + +describe("hasRenderableContent", () => { + test("returns false for blank reasoning-only messages", () => { + expect( + hasRenderableContent({ + id: "message-1", + role: "assistant", + parts: [{ type: "reasoning", text: " ", state: "done" }], + }), + ).toBe(false); + }); + + test("returns true for non-empty reasoning messages", () => { + expect( + hasRenderableContent({ + id: "message-2", + role: "assistant", + parts: [{ type: "reasoning", text: "Thinking", state: "done" }], + }), + ).toBe(true); + }); +}); diff --git a/apps/desktop/src/chat/components/shared.ts b/apps/desktop/src/chat/components/shared.ts index 274c1dffbf..9eac22ff04 100644 --- a/apps/desktop/src/chat/components/shared.ts +++ b/apps/desktop/src/chat/components/shared.ts @@ -1,5 +1,15 @@ import type { HyprUIMessage } from "~/chat/types"; export function hasRenderableContent(message: HyprUIMessage): boolean { - return message.parts.some((part) => part.type !== "step-start"); + return message.parts.some((part) => { + if (part.type === "step-start") { + return false; + } + + if (part.type === "reasoning") { + return part.text.trim().length > 0; + } + + return true; + }); }