[Haik]: (moved logo out of island) (fixed notification derefrencing issues when u swap dashboards) (fixed chat header update issue where itd get stuck in the completed state)

This commit is contained in:
haikdc
2026-03-20 21:37:27 -07:00
parent a8aff60bca
commit c0afabb964
5 changed files with 95 additions and 33 deletions
+7
View File
@@ -1285,6 +1285,13 @@ class AgentManager:
})
session.sdk_session_id = None
session.status = "running"
await ws_manager.send_to_session(session_id, "agent:status", {
"session_id": session_id,
"status": "running",
"session": session.model_dump(mode="json"),
})
task = asyncio.create_task(self._run_agent_loop(
session_id, new_content,
images=target_msg.images,
+29 -30
View File
@@ -3,6 +3,7 @@ import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip';
import SearchIcon from '@mui/icons-material/Search';
import StopCircleOutlinedIcon from '@mui/icons-material/StopCircleOutlined';
import CloseIcon from '@mui/icons-material/Close';
import { motion, AnimatePresence } from 'framer-motion';
@@ -217,7 +218,7 @@ const DynamicIsland: React.FC = () => {
);
const trackedAgents: TrackedAgent[] = useMemo(() => {
return trackedIds
const agents = trackedIds
.map((id): TrackedAgent | null => {
const session = sessions[id];
if (session && session.status !== 'draft') {
@@ -230,7 +231,19 @@ const DynamicIsland: React.FC = () => {
return null;
})
.filter((a): a is TrackedAgent => a !== null);
}, [trackedIds, sessions, history]);
const trackedIdSet = new Set(trackedIds);
for (const g of groups) {
if (!trackedIdSet.has(g.sessionId)) {
const session = sessions[g.sessionId];
if (session && session.status !== 'draft') {
agents.push({ id: g.sessionId, name: session.name, status: session.status, dashboardId: session.dashboard_id });
}
}
}
return agents;
}, [trackedIds, sessions, history, groups]);
const activeAgents = useMemo(
() => trackedAgents.filter((a) => a.status === 'running' || a.status === 'waiting_approval'),
@@ -317,9 +330,9 @@ const DynamicIsland: React.FC = () => {
// ---- Styling — uses the same neutral palette as the rest of the UI ----
const islandWidth = islandState === 'idle'
? 126
? 200
: islandState === 'compact'
? 220
? 210
: 400;
const islandBorderRadius = islandState === 'expanded' ? 14 : 50;
@@ -413,7 +426,7 @@ const DynamicIsland: React.FC = () => {
};
// ---------------------------------------------------------------------------
// Idle pill
// Idle pill — disabled search bar
// ---------------------------------------------------------------------------
const IdlePill: React.FC<{ c: ReturnType<typeof useClaudeTokens> }> = ({ c }) => (
@@ -423,40 +436,32 @@ const IdlePill: React.FC<{ c: ReturnType<typeof useClaudeTokens> }> = ({ c }) =>
exit={{ opacity: 0, scale: 0.92 }}
transition={{ duration: 0.2 }}
>
<motion.div
animate={{ scale: [1, 1.006, 1] }}
transition={{ repeat: Infinity, duration: 4, ease: 'easeInOut' }}
>
<Tooltip title="Coming soon" arrow placement="bottom">
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 0.6,
px: 1.5,
gap: 0.75,
px: 1.25,
height: 24,
userSelect: 'none',
cursor: 'default',
}}
>
<Box
component="img"
src="./logo.png"
alt="OpenSwarm"
sx={{ width: 13, height: 13, borderRadius: 0.5, opacity: 0.6 }}
/>
<SearchIcon sx={{ fontSize: 13, color: c.text.ghost, flexShrink: 0 }} />
<Typography
sx={{
color: c.text.tertiary,
fontSize: '0.68rem',
fontWeight: 500,
letterSpacing: 0.3,
color: c.text.ghost,
fontSize: '0.66rem',
fontWeight: 400,
lineHeight: 1,
whiteSpace: 'nowrap',
}}
>
OpenSwarm
Search...
</Typography>
</Box>
</motion.div>
</Tooltip>
</motion.div>
);
@@ -512,12 +517,6 @@ const CompactPill: React.FC<{
}}
/>
)}
<Box
component="img"
src="./logo.png"
alt=""
sx={{ width: 11, height: 11, borderRadius: 0.25, opacity: 0.45, flexShrink: 0 }}
/>
</Box>
</motion.div>
);
@@ -351,6 +351,36 @@ const AppShell: React.FC = () => {
</Tooltip>
<DynamicIsland />
<Box sx={{ flex: 1 }} />
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 0.75,
pr: 1.5,
WebkitAppRegion: 'no-drag',
}}
>
<Box
component="img"
src="./logo.png"
alt="OpenSwarm"
sx={{ width: 16, height: 16, borderRadius: 0.5, opacity: 0.6 }}
/>
<Typography
sx={{
color: c.text.tertiary,
fontSize: '0.72rem',
fontWeight: 500,
letterSpacing: 0.3,
lineHeight: 1,
}}
>
OpenSwarm
</Typography>
</Box>
</Box>
{showUpdateBanner && (
+28 -2
View File
@@ -520,7 +520,13 @@ const agentsSlice = createSlice({
},
updateSession(state, action: PayloadAction<AgentSession>) {
if (state.history[action.payload.id]) return;
if (state.history[action.payload.id]) {
if (action.payload.status === 'running') {
delete state.history[action.payload.id];
} else {
return;
}
}
const existing = state.sessions[action.payload.id];
state.sessions[action.payload.id] = {
...action.payload,
@@ -720,8 +726,9 @@ const agentsSlice = createSlice({
.addCase(fetchSessions.fulfilled, (state, action) => {
state.loading = false;
const sessions: Record<string, AgentSession> = {};
const trackedSet = new Set(state.trackedNotificationIds);
for (const [id, existing] of Object.entries(state.sessions)) {
if (existing.status === 'draft') sessions[id] = existing;
if (existing.status === 'draft' || trackedSet.has(id)) sessions[id] = existing;
}
for (const s of action.payload) {
const existing = state.sessions[s.id];
@@ -732,6 +739,11 @@ const agentsSlice = createSlice({
};
}
state.sessions = sessions;
for (const s of action.payload) {
if ((s.status === 'running' || s.status === 'waiting_approval') && !state.trackedNotificationIds.includes(s.id)) {
state.trackedNotificationIds.push(s.id);
}
}
})
.addCase(fetchSessions.rejected, (state) => {
state.loading = false;
@@ -783,6 +795,18 @@ const agentsSlice = createSlice({
session.system_prompt = action.payload.systemPrompt;
}
})
.addCase(sendMessage.pending, (state, action) => {
const session = state.sessions[action.meta.arg.sessionId];
if (session) {
session.status = 'running';
}
})
.addCase(editMessage.pending, (state, action) => {
const session = state.sessions[action.meta.arg.sessionId];
if (session) {
session.status = 'running';
}
})
.addCase(stopAgent.fulfilled, (state, action) => {
const session = state.sessions[action.payload];
if (session) {
@@ -829,6 +853,7 @@ const agentsSlice = createSlice({
state.activeSessionId = null;
}
state.expandedSessionIds = state.expandedSessionIds.filter((id) => id !== sessionId);
state.trackedNotificationIds = state.trackedNotificationIds.filter((id) => id !== sessionId);
})
.addCase(closeSession.rejected, (state, action) => {
const sessionId = action.meta.arg.sessionId;
@@ -851,6 +876,7 @@ const agentsSlice = createSlice({
state.activeSessionId = null;
}
state.expandedSessionIds = state.expandedSessionIds.filter((id) => id !== sessionId);
state.trackedNotificationIds = state.trackedNotificationIds.filter((id) => id !== sessionId);
})
.addCase(deleteSession.fulfilled, (state, action) => {
const sessionId = action.payload;
File diff suppressed because one or more lines are too long