2 Commits
Author SHA1 Message Date
janvanwanandClaude Opus 5 fe5d18afa7 feat(ui): bulk-clear conversations, fold Start/Stop AI into the status dot
- Clear button beside Export deletes every conversation currently listed. It
  respects the search filter, so it doubles as "delete these matches"; the
  confirm text names which of the two is about to happen. Starts a fresh
  conversation id if the active one was among those deleted.
- The separate Start/Stop AI button is gone. The status dot was already
  colour-coding the same state and sitting right above it, so clicking it now
  toggles Ollama and the tooltip carries the phase text ("loading model…"),
  which is where the eye already is during the wait.
- Active nav/conversation items are marked with a blue left border and
  weighted text instead of a filled background, and the sidebar is wider so
  conversation titles stop truncating.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:39:30 -05:00
janvanwanandClaude Opus 5 4bfa979ee8 fix(panel): suppress the nm-applet tray icon like the other replaced applets
network-manager-gnome autostarts its own systray icon from /etc/xdg/autostart,
which duplicates the network genmon (plugin-13) already in the panel. Override
it with a Hidden=true user-level .desktop, the same way blueman-applet and
nm-tray are already handled, and copy it into place from the panel installer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:39:30 -05:00
3 changed files with 63 additions and 32 deletions
+3
View File
@@ -26,6 +26,9 @@ ln -sf "$NEXUS/management/autostart/nexus-popup.desktop" \
"$AUTOSTART/nexus-popup.desktop"
cp -f "$NEXUS/management/autostart/nm-tray.desktop" "$AUTOSTART/nm-tray.desktop"
cp -f "$NEXUS/management/autostart/nm-tray-autostart.desktop" "$AUTOSTART/nm-tray-autostart.desktop"
# Same for nm-applet (network-manager-gnome), which ships its own systray icon
# from /etc/xdg/autostart — the network genmon (plugin-13) replaces it.
cp -f "$NEXUS/management/autostart/nm-applet.desktop" "$AUTOSTART/nm-applet.desktop"
# Suppress blueman-applet autostart (both the system blueman.desktop and the
# user blueman-applet.desktop). blueman always forces its own tray icon, which
# the panel renders too small; the Bluetooth genmon (plugin-16) replaces it and
+51 -32
View File
@@ -154,6 +154,21 @@ function App() {
}
};
// Bulk delete of everything currently listed (respects the search filter).
const clearConversations = async () => {
const n = conversations.length;
if (!n) return;
const what = search ? `${n} matching conversation${n > 1 ? "s" : ""}` : `all ${n} conversation${n > 1 ? "s" : ""}`;
if (!window.confirm(`Delete ${what}? This cannot be undone.`)) return;
const ids = conversations.map(c => c.id);
await Promise.all(
ids.map(id => fetch(`${API_BASE}/conversations/${id}`, { method: "DELETE" }).catch(err =>
console.error("Failed to delete conversation:", err)))
);
if (ids.includes(activeConversationId)) setActiveConversationId(crypto.randomUUID());
loadConversations(search);
};
const navItems = [
{ key: "chatbot", icon: "💬", label: "Chat" },
{ key: "playbook", icon: "📖", label: "Playbooks" },
@@ -168,7 +183,7 @@ function App() {
<div style={{ background: "#111", color: "#eee", height: "100vh", overflow: "hidden", fontFamily: "system-ui", display: "flex" }}>
{/* Sidebar */}
<aside style={{
width: "220px",
width: "400px",
flexShrink: 0,
background: "#0a0a0a",
borderRight: "1px solid #333",
@@ -201,7 +216,7 @@ function App() {
borderRadius: "50%",
background: status === "Offline" || ollamaStatus === "unavailable" ? "#f44336"
: ollamaStatus === "running" ? "#4caf50" : "#ff9800",
cursor: "pointer",
cursor: (ollamaBusy || ollamaStatus === "unavailable" || status === "Offline") ? "not-allowed" : "pointer",
transition: "all 0.3s"
}}
onMouseEnter={() => {
@@ -209,7 +224,11 @@ function App() {
setShowStatusTooltip(true);
}}
onMouseLeave={() => setShowStatusTooltip(false)}
title="Hover to refresh"
onClick={() => {
if (ollamaBusy || ollamaStatus === "unavailable" || status === "Offline") return;
toggleOllama();
}}
title={ollamaStatus === "unavailable" ? "Ollama binary not found" : "Click to start/stop the AI"}
/>
{showStatusTooltip && (
<div style={{
@@ -230,35 +249,15 @@ function App() {
Synapse: <strong>{status}</strong>
</div>
<div style={{ color: ollamaStatus === "unavailable" ? "#f44336" : ollamaStatus === "running" ? "#4caf50" : "#aaa" }}>
Ollama: <strong>{ollamaStatus}</strong>
{/* Naming the phase matters: nearly all of the wait is the model
being read off disk (GBs), not the server booting. */}
Ollama: <strong>{ollamaBusy ? (ollamaStatus === "running" ? "stopping…" : "loading model…") : ollamaStatus}</strong>
</div>
</div>
)}
</div>
{/* Manual AI start/stop — Ollama is off until the user turns it on */}
<button
onClick={toggleOllama}
disabled={ollamaBusy || ollamaStatus === "unavailable" || status === "Offline"}
title={ollamaStatus === "unavailable" ? "Ollama binary not found" : "Start or stop the AI"}
style={{
width: "100%",
padding: "0.4rem 0.6rem",
background: ollamaStatus === "running" ? "#2a1a1a" : "#152a15",
color: ollamaStatus === "running" ? "#ff8a80" : "#8aff8a",
border: "1px solid " + (ollamaStatus === "running" ? "#5a2a2a" : "#2a5a2a"),
borderRadius: "6px",
cursor: (ollamaBusy || ollamaStatus === "unavailable" || status === "Offline") ? "not-allowed" : "pointer",
fontSize: "0.78rem",
opacity: (ollamaStatus === "unavailable" || status === "Offline") ? 0.5 : 1,
}}
>
{/* Naming the phase matters: nearly all of the wait is the model
being read off disk (GBs), not the server booting. "…" for a
minute reads as hung. */}
{ollamaBusy ? (ollamaStatus === "running" ? "Stopping…" : "Loading model…")
: ollamaStatus === "running" ? "⏹ Stop AI" : "▶ Start AI"}
</button>
{/* Start/Stop AI moved onto the status dot above — click it to toggle. */}
<nav style={{ display: "flex", flexDirection: "column", width: "100%", marginTop: "0.1rem" }}>
{navItems.map(item => {
@@ -269,9 +268,11 @@ function App() {
onClick={() => setCurrentPage(item.key)}
style={{
padding: "0.32rem 0.5rem",
background: isActive ? "#1c2733" : "transparent",
color: isActive ? "#fff" : "#bbb",
background: "transparent",
color: isActive ? "#4aa3e0" : "#bbb",
fontWeight: isActive ? 600 : 400,
border: "none",
borderLeft: `2px solid ${isActive ? "#007acc" : "transparent"}`,
borderRadius: "5px",
cursor: "pointer",
fontSize: "0.8rem",
@@ -330,6 +331,22 @@ function App() {
>
Export
</button>
<button
onClick={clearConversations}
title={search ? "Delete the conversations matching this search" : "Delete all conversations"}
disabled={conversations.length === 0}
style={{
padding: "0.2rem 0.5rem",
background: "#161616",
color: conversations.length === 0 ? "#555" : "#c66",
border: "1px solid #2a2a2a",
borderRadius: "6px",
cursor: conversations.length === 0 ? "default" : "pointer",
fontSize: "0.75rem",
}}
>
🗑 Clear
</button>
<button
onClick={startNewChat}
title="New chat"
@@ -385,8 +402,9 @@ function App() {
marginBottom: "0.3rem",
borderRadius: "6px",
cursor: "pointer",
background: isActive ? "#1c2a3a" : (isHover ? "#161616" : "transparent"),
border: `1px solid ${isActive ? "#2a4a6a" : "transparent"}`,
background: isHover ? "#161616" : "transparent",
border: "1px solid transparent",
borderLeft: `2px solid ${isActive ? "#007acc" : "transparent"}`,
display: "flex",
alignItems: "center",
gap: "0.4rem",
@@ -395,7 +413,8 @@ function App() {
<div style={{ flexGrow: 1, minWidth: 0 }}>
<div style={{
fontSize: "0.82rem",
color: isActive ? "#eee" : "#ccc",
color: isActive ? "#4aa3e0" : "#ccc",
fontWeight: isActive ? 600 : 400,
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
+9
View File
@@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=Network
Comment=Manage your network connections
Icon=nm-device-wireless
TryExec=nm-applet
Exec=nm-applet
Hidden=true
X-XFCE-Autostart-Override=true