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;
+ });
}