feat(topbar): aggregated notification bell (#9)
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled

This commit was merged in pull request #9.
This commit is contained in:
2026-06-15 00:20:25 +00:00
parent f739eedb47
commit e92c5c9236
3 changed files with 40 additions and 1 deletions
+18 -1
View File
@@ -18,7 +18,7 @@ import Documents from "./scenes/Documents";
import CommandBar from "./components/CommandBar";
import Toaster from "./components/Toaster";
import Console from "./components/Console";
import { Cmd, Home, Layers, HistoryIcon, Pulse, Refresh, Branch, Cog, User, Sun, Moon, Bot } from "./components/icons";
import { Cmd, Home, Layers, HistoryIcon, Pulse, Refresh, Branch, Cog, User, Sun, Moon, Bot, Bell } from "./components/icons";
import { useBackendHealth } from "./lib/useBackendHealth";
@@ -216,6 +216,23 @@ export default function App() {
<Refresh size={12} /> Refresh
</button>
)}
<button
className={`link-btn notif-btn${(chatUnreadCount + queueCount) > 0 ? " has-notif" : ""}`}
onClick={() => setScene(chatUnreadCount > 0 ? "chat" : "approvals")}
title={
chatUnreadCount + queueCount === 0
? "Nothing waiting for you"
: `${chatUnreadCount > 0 ? `${chatUnreadCount} unread message${chatUnreadCount === 1 ? "" : "s"}` : ""}${chatUnreadCount > 0 && queueCount > 0 ? " · " : ""}${queueCount > 0 ? `${queueCount} item${queueCount === 1 ? "" : "s"} in your queue` : ""}. Click to open ${chatUnreadCount > 0 ? "chat" : "approvals"}.`
}
aria-label={`Notifications: ${chatUnreadCount} unread, ${queueCount} queued`}
>
<Bell size={13} />
{(chatUnreadCount + queueCount) > 0 && (
<span className="notif-dot" aria-hidden>
{(chatUnreadCount + queueCount) > 99 ? "99+" : chatUnreadCount + queueCount}
</span>
)}
</button>
<button
className={`link-btn${consoleOpen ? " is-on" : ""}`}
onClick={() => setConsoleOpen(!consoleOpen)}
+1
View File
@@ -41,3 +41,4 @@ export const Layers = (p: P) => <Svg {...p}><path d="M12 3l9 5-9 5-9-5zM3 13l9 5
export const HistoryIcon = (p: P) => <Svg {...p}><path d="M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5M12 7v5l3 2" /></Svg>;
export const Home = (p: P) => <Svg {...p}><path d="M4 11l8-7 8 7v9h-5v-6h-6v6H4z" /></Svg>;
export const ChevronRight = (p: P) => <Svg {...p}><path d="M9 18l6-6-6-6" /></Svg>;
export const Bell = (p: P) => <Svg {...p}><path d="M6 8a6 6 0 0 1 12 0c0 7 3 7 3 9H3c0-2 3-2 3-9M10 21a2 2 0 0 0 4 0" /></Svg>;
+21
View File
@@ -2328,3 +2328,24 @@ select.studio-input { background: var(--bp-paper); }
font-family: inherit;
}
.wizard-form-input:disabled { opacity: 0.7; }
/* Aggregated topbar notification bell */
.notif-btn { position: relative; display: inline-flex; align-items: center; gap: 4px; }
.notif-btn.has-notif { color: var(--bp-amber, #d97a00); }
.notif-dot {
position: absolute;
top: -3px;
right: -4px;
min-width: 14px;
height: 14px;
padding: 0 4px;
border-radius: 7px;
background: var(--bp-amber, #d97a00);
color: #fff;
font-family: var(--bp-mono, monospace);
font-size: 9px;
line-height: 14px;
text-align: center;
font-weight: 700;
pointer-events: none;
}