@swarmclawai/swarmclaw 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +42 -7
  2. package/bin/swarmclaw.js +76 -16
  3. package/next.config.ts +11 -1
  4. package/package.json +4 -2
  5. package/public/screenshots/agents.png +0 -0
  6. package/public/screenshots/dashboard.png +0 -0
  7. package/public/screenshots/providers.png +0 -0
  8. package/public/screenshots/tasks.png +0 -0
  9. package/scripts/postinstall.mjs +18 -0
  10. package/src/app/api/chatrooms/[id]/chat/route.ts +410 -0
  11. package/src/app/api/chatrooms/[id]/members/route.ts +82 -0
  12. package/src/app/api/chatrooms/[id]/pins/route.ts +39 -0
  13. package/src/app/api/chatrooms/[id]/reactions/route.ts +42 -0
  14. package/src/app/api/chatrooms/[id]/route.ts +84 -0
  15. package/src/app/api/chatrooms/route.ts +50 -0
  16. package/src/app/api/credentials/route.ts +2 -3
  17. package/src/app/api/knowledge/[id]/route.ts +13 -2
  18. package/src/app/api/knowledge/route.ts +8 -1
  19. package/src/app/api/memory/route.ts +8 -0
  20. package/src/app/api/notifications/[id]/route.ts +27 -0
  21. package/src/app/api/notifications/route.ts +68 -0
  22. package/src/app/api/orchestrator/run/route.ts +1 -1
  23. package/src/app/api/plugins/install/route.ts +2 -2
  24. package/src/app/api/search/route.ts +155 -0
  25. package/src/app/api/sessions/[id]/chat/route.ts +2 -0
  26. package/src/app/api/sessions/[id]/edit-resend/route.ts +1 -1
  27. package/src/app/api/sessions/[id]/fork/route.ts +1 -1
  28. package/src/app/api/sessions/route.ts +3 -3
  29. package/src/app/api/settings/route.ts +9 -0
  30. package/src/app/api/setup/check-provider/route.ts +3 -16
  31. package/src/app/api/skills/[id]/route.ts +6 -0
  32. package/src/app/api/skills/route.ts +6 -0
  33. package/src/app/api/tasks/[id]/route.ts +20 -0
  34. package/src/app/api/tasks/bulk/route.ts +100 -0
  35. package/src/app/api/tasks/route.ts +1 -0
  36. package/src/app/api/usage/route.ts +45 -0
  37. package/src/app/api/webhooks/[id]/route.ts +15 -1
  38. package/src/app/globals.css +58 -15
  39. package/src/app/page.tsx +142 -13
  40. package/src/cli/index.js +42 -0
  41. package/src/cli/index.test.js +30 -0
  42. package/src/cli/spec.js +32 -0
  43. package/src/components/agents/agent-avatar.tsx +57 -10
  44. package/src/components/agents/agent-card.tsx +48 -15
  45. package/src/components/agents/agent-chat-list.tsx +123 -10
  46. package/src/components/agents/agent-list.tsx +50 -19
  47. package/src/components/agents/agent-sheet.tsx +56 -63
  48. package/src/components/auth/access-key-gate.tsx +10 -3
  49. package/src/components/auth/setup-wizard.tsx +2 -2
  50. package/src/components/auth/user-picker.tsx +31 -3
  51. package/src/components/chat/activity-moment.tsx +169 -0
  52. package/src/components/chat/chat-header.tsx +2 -0
  53. package/src/components/chat/chat-tool-toggles.tsx +1 -1
  54. package/src/components/chat/file-path-chip.tsx +125 -0
  55. package/src/components/chat/markdown-utils.ts +9 -0
  56. package/src/components/chat/message-bubble.tsx +46 -295
  57. package/src/components/chat/message-list.tsx +50 -1
  58. package/src/components/chat/streaming-bubble.tsx +36 -46
  59. package/src/components/chat/suggestions-bar.tsx +1 -1
  60. package/src/components/chat/thinking-indicator.tsx +72 -10
  61. package/src/components/chat/tool-call-bubble.tsx +66 -70
  62. package/src/components/chat/tool-request-banner.tsx +31 -7
  63. package/src/components/chat/transfer-agent-picker.tsx +63 -0
  64. package/src/components/chatrooms/agent-hover-card.tsx +124 -0
  65. package/src/components/chatrooms/chatroom-input.tsx +320 -0
  66. package/src/components/chatrooms/chatroom-list.tsx +123 -0
  67. package/src/components/chatrooms/chatroom-message.tsx +427 -0
  68. package/src/components/chatrooms/chatroom-sheet.tsx +215 -0
  69. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +134 -0
  70. package/src/components/chatrooms/chatroom-typing-bar.tsx +88 -0
  71. package/src/components/chatrooms/chatroom-view.tsx +344 -0
  72. package/src/components/chatrooms/reaction-picker.tsx +273 -0
  73. package/src/components/connectors/connector-sheet.tsx +34 -47
  74. package/src/components/home/home-view.tsx +501 -0
  75. package/src/components/input/chat-input.tsx +79 -41
  76. package/src/components/knowledge/knowledge-list.tsx +31 -1
  77. package/src/components/knowledge/knowledge-sheet.tsx +83 -2
  78. package/src/components/layout/app-layout.tsx +209 -83
  79. package/src/components/layout/mobile-header.tsx +2 -0
  80. package/src/components/layout/update-banner.tsx +2 -2
  81. package/src/components/logs/log-list.tsx +2 -2
  82. package/src/components/mcp-servers/mcp-server-sheet.tsx +1 -1
  83. package/src/components/memory/memory-agent-list.tsx +143 -0
  84. package/src/components/memory/memory-browser.tsx +205 -0
  85. package/src/components/memory/memory-card.tsx +34 -7
  86. package/src/components/memory/memory-detail.tsx +359 -120
  87. package/src/components/memory/memory-sheet.tsx +157 -23
  88. package/src/components/plugins/plugin-list.tsx +1 -1
  89. package/src/components/plugins/plugin-sheet.tsx +1 -1
  90. package/src/components/projects/project-detail.tsx +509 -0
  91. package/src/components/projects/project-list.tsx +195 -59
  92. package/src/components/providers/provider-list.tsx +2 -2
  93. package/src/components/providers/provider-sheet.tsx +3 -3
  94. package/src/components/schedules/schedule-card.tsx +3 -2
  95. package/src/components/schedules/schedule-list.tsx +1 -1
  96. package/src/components/schedules/schedule-sheet.tsx +25 -25
  97. package/src/components/secrets/secret-sheet.tsx +47 -24
  98. package/src/components/secrets/secrets-list.tsx +18 -8
  99. package/src/components/sessions/new-session-sheet.tsx +33 -65
  100. package/src/components/sessions/session-card.tsx +45 -14
  101. package/src/components/sessions/session-list.tsx +35 -18
  102. package/src/components/shared/agent-picker-list.tsx +90 -0
  103. package/src/components/shared/agent-switch-dialog.tsx +156 -0
  104. package/src/components/shared/attachment-chip.tsx +165 -0
  105. package/src/components/shared/avatar.tsx +10 -1
  106. package/src/components/shared/check-icon.tsx +12 -0
  107. package/src/components/shared/confirm-dialog.tsx +1 -1
  108. package/src/components/shared/empty-state.tsx +32 -0
  109. package/src/components/shared/file-preview.tsx +34 -0
  110. package/src/components/shared/form-styles.ts +2 -0
  111. package/src/components/shared/keyboard-shortcuts-dialog.tsx +116 -0
  112. package/src/components/shared/notification-center.tsx +223 -0
  113. package/src/components/shared/profile-sheet.tsx +115 -0
  114. package/src/components/shared/reply-quote.tsx +26 -0
  115. package/src/components/shared/search-dialog.tsx +296 -0
  116. package/src/components/shared/section-label.tsx +12 -0
  117. package/src/components/shared/settings/plugin-manager.tsx +1 -1
  118. package/src/components/shared/settings/section-providers.tsx +1 -1
  119. package/src/components/shared/settings/section-secrets.tsx +1 -1
  120. package/src/components/shared/settings/section-theme.tsx +95 -0
  121. package/src/components/shared/settings/section-user-preferences.tsx +39 -0
  122. package/src/components/shared/settings/settings-page.tsx +180 -27
  123. package/src/components/shared/settings/settings-sheet.tsx +9 -73
  124. package/src/components/shared/sheet-footer.tsx +33 -0
  125. package/src/components/skills/skill-list.tsx +61 -30
  126. package/src/components/skills/skill-sheet.tsx +81 -2
  127. package/src/components/tasks/task-board.tsx +448 -26
  128. package/src/components/tasks/task-card.tsx +46 -9
  129. package/src/components/tasks/task-column.tsx +62 -3
  130. package/src/components/tasks/task-list.tsx +12 -4
  131. package/src/components/tasks/task-sheet.tsx +89 -72
  132. package/src/components/ui/hover-card.tsx +52 -0
  133. package/src/components/usage/metrics-dashboard.tsx +78 -0
  134. package/src/components/usage/usage-list.tsx +1 -1
  135. package/src/components/webhooks/webhook-sheet.tsx +1 -1
  136. package/src/hooks/use-view-router.ts +69 -19
  137. package/src/instrumentation.ts +15 -1
  138. package/src/lib/chat.ts +2 -0
  139. package/src/lib/cron-human.ts +114 -0
  140. package/src/lib/memory.ts +3 -0
  141. package/src/lib/server/chat-execution.ts +24 -4
  142. package/src/lib/server/connectors/manager.ts +11 -0
  143. package/src/lib/server/context-manager.ts +225 -13
  144. package/src/lib/server/create-notification.ts +42 -0
  145. package/src/lib/server/daemon-state.ts +165 -10
  146. package/src/lib/server/execution-log.ts +1 -0
  147. package/src/lib/server/heartbeat-service.ts +40 -5
  148. package/src/lib/server/heartbeat-wake.ts +110 -0
  149. package/src/lib/server/langgraph-checkpoint.ts +1 -0
  150. package/src/lib/server/memory-consolidation.ts +92 -0
  151. package/src/lib/server/memory-db.ts +51 -6
  152. package/src/lib/server/openclaw-gateway.ts +9 -1
  153. package/src/lib/server/provider-health.ts +125 -0
  154. package/src/lib/server/queue.ts +5 -4
  155. package/src/lib/server/scheduler.ts +8 -0
  156. package/src/lib/server/session-run-manager.ts +4 -0
  157. package/src/lib/server/session-tools/chatroom.ts +136 -0
  158. package/src/lib/server/session-tools/context-mgmt.ts +36 -18
  159. package/src/lib/server/session-tools/index.ts +2 -0
  160. package/src/lib/server/session-tools/memory.ts +6 -1
  161. package/src/lib/server/storage.ts +80 -29
  162. package/src/lib/server/stream-agent-chat.ts +153 -47
  163. package/src/lib/server/system-events.ts +49 -0
  164. package/src/lib/server/ws-hub.ts +11 -0
  165. package/src/lib/soul-suggestions.ts +109 -0
  166. package/src/lib/tasks.ts +4 -1
  167. package/src/lib/view-routes.ts +36 -1
  168. package/src/lib/ws-client.ts +14 -4
  169. package/src/proxy.ts +79 -2
  170. package/src/stores/use-app-store.ts +94 -3
  171. package/src/stores/use-chat-store.ts +48 -3
  172. package/src/stores/use-chatroom-store.ts +276 -0
  173. package/src/types/index.ts +69 -2
@@ -2,7 +2,6 @@
2
2
 
3
3
  import { Component, useState, useEffect, useCallback } from 'react'
4
4
  import type { ReactNode, ErrorInfo } from 'react'
5
- import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
6
5
  import { useAppStore } from '@/stores/use-app-store'
7
6
  import { useMediaQuery } from '@/hooks/use-media-query'
8
7
  import { Avatar } from '@/components/shared/avatar'
@@ -12,9 +11,9 @@ import { AgentChatList } from '@/components/agents/agent-chat-list'
12
11
  import { AgentSheet } from '@/components/agents/agent-sheet'
13
12
  import { ScheduleList } from '@/components/schedules/schedule-list'
14
13
  import { ScheduleSheet } from '@/components/schedules/schedule-sheet'
15
- import { MemoryList } from '@/components/memory/memory-list'
14
+ import { MemoryAgentList } from '@/components/memory/memory-agent-list'
16
15
  import { MemorySheet } from '@/components/memory/memory-sheet'
17
- import { MemoryDetail } from '@/components/memory/memory-detail'
16
+ import { MemoryBrowser } from '@/components/memory/memory-browser'
18
17
  import { TaskList } from '@/components/tasks/task-list'
19
18
  import { TaskSheet } from '@/components/tasks/task-sheet'
20
19
  import { TaskBoard } from '@/components/tasks/task-board'
@@ -26,6 +25,10 @@ import { SkillList } from '@/components/skills/skill-list'
26
25
  import { SkillSheet } from '@/components/skills/skill-sheet'
27
26
  import { ConnectorList } from '@/components/connectors/connector-list'
28
27
  import { ConnectorSheet } from '@/components/connectors/connector-sheet'
28
+ import { ChatroomList } from '@/components/chatrooms/chatroom-list'
29
+ import { ChatroomView } from '@/components/chatrooms/chatroom-view'
30
+ import { ChatroomSheet } from '@/components/chatrooms/chatroom-sheet'
31
+ import { useChatroomStore } from '@/stores/use-chatroom-store'
29
32
  import { WebhookList } from '@/components/webhooks/webhook-list'
30
33
  import { WebhookSheet } from '@/components/webhooks/webhook-sheet'
31
34
  import { LogList } from '@/components/logs/log-list'
@@ -39,16 +42,26 @@ import { RunList } from '@/components/runs/run-list'
39
42
  import { ActivityFeed } from '@/components/activity/activity-feed'
40
43
  import { MetricsDashboard } from '@/components/usage/metrics-dashboard'
41
44
  import { ProjectList } from '@/components/projects/project-list'
45
+ import { ProjectDetail } from '@/components/projects/project-detail'
42
46
  import { ProjectSheet } from '@/components/projects/project-sheet'
47
+ import { SearchDialog } from '@/components/shared/search-dialog'
48
+ import { AgentSwitchDialog } from '@/components/shared/agent-switch-dialog'
49
+ import { KeyboardShortcutsDialog } from '@/components/shared/keyboard-shortcuts-dialog'
50
+ import { ProfileSheet } from '@/components/shared/profile-sheet'
51
+ import { HomeView } from '@/components/home/home-view'
43
52
  import { NetworkBanner } from './network-banner'
44
53
  import { UpdateBanner } from './update-banner'
45
54
  import { MobileHeader } from './mobile-header'
46
55
  import { DaemonIndicator } from './daemon-indicator'
56
+ import { NotificationCenter } from '@/components/shared/notification-center'
47
57
  import { ChatArea } from '@/components/chat/chat-area'
48
58
  import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
59
+ import { api } from '@/lib/api-client'
49
60
  import type { AppView } from '@/types'
50
61
 
51
62
  const RAIL_EXPANDED_KEY = 'sc_rail_expanded'
63
+ const STAR_NOTIFICATION_KEY = 'sc_star_notification_v1'
64
+ const GITHUB_REPO_URL = 'https://github.com/swarmclawai/swarmclaw'
52
65
 
53
66
  export function AppLayout() {
54
67
  const currentUser = useAppStore((s) => s.currentUser)
@@ -56,8 +69,6 @@ export function AppLayout() {
56
69
  const currentSessionId = useAppStore((s) => s.currentSessionId)
57
70
  const sidebarOpen = useAppStore((s) => s.sidebarOpen)
58
71
  const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
59
- const setUser = useAppStore((s) => s.setUser)
60
- const setCurrentSession = useAppStore((s) => s.setCurrentSession)
61
72
  const activeView = useAppStore((s) => s.activeView)
62
73
  const setActiveView = useAppStore((s) => s.setActiveView)
63
74
  const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
@@ -77,23 +88,25 @@ export function AppLayout() {
77
88
  const hasSelectedSession = !!(currentSessionId && sessions[currentSessionId])
78
89
  const pendingApprovalCount = Object.values(tasks).filter((t) => t.pendingApproval).length
79
90
 
91
+ const appSettings = useAppStore((s) => s.appSettings)
80
92
  const [agentViewMode, setAgentViewMode] = useState<'chat' | 'config'>('chat')
81
- const [shortcutsOpen, setShortcutsOpen] = useState(false)
93
+ const [profileSheetOpen, setProfileSheetOpen] = useState(false)
82
94
 
83
95
  const handleShortcutKey = useCallback((e: KeyboardEvent) => {
84
- // Ctrl+/ or Cmd+/
85
- if ((e.ctrlKey || e.metaKey) && e.key === '/') {
96
+ const mod = e.metaKey || e.ctrlKey
97
+ // Cmd+N / Ctrl+N new chat
98
+ if (mod && !e.shiftKey && e.key.toLowerCase() === 'n') {
86
99
  e.preventDefault()
87
- setShortcutsOpen((v) => !v)
100
+ const state = useAppStore.getState()
101
+ const allAgents = Object.values(state.agents).filter((a) => !a.trashedAt)
102
+ const target = allAgents.find((a) => a.id === 'default') || allAgents[0]
103
+ if (target) void state.setCurrentAgent(target.id)
88
104
  return
89
105
  }
90
- // ? key when not in an input/textarea/contenteditable
91
- if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) {
92
- const tag = (e.target as HTMLElement)?.tagName?.toLowerCase()
93
- const editable = (e.target as HTMLElement)?.isContentEditable
94
- if (tag === 'input' || tag === 'textarea' || editable) return
106
+ // Cmd+Shift+T / Ctrl+Shift+T jump to tasks
107
+ if (mod && e.shiftKey && e.key.toLowerCase() === 't') {
95
108
  e.preventDefault()
96
- setShortcutsOpen((v) => !v)
109
+ useAppStore.getState().setActiveView('tasks')
97
110
  }
98
111
  }, [])
99
112
 
@@ -102,6 +115,31 @@ export function AppLayout() {
102
115
  return () => window.removeEventListener('keydown', handleShortcutKey)
103
116
  }, [handleShortcutKey])
104
117
 
118
+ useEffect(() => {
119
+ if (typeof window === 'undefined') return
120
+ if (localStorage.getItem(STAR_NOTIFICATION_KEY)) return
121
+ localStorage.setItem(STAR_NOTIFICATION_KEY, '1')
122
+ void api('POST', '/notifications', {
123
+ type: 'info',
124
+ title: 'Enjoying SwarmClaw?',
125
+ message: 'If SwarmClaw helps your workflow, please star the GitHub repo to support the project.',
126
+ actionLabel: 'Star on GitHub',
127
+ actionUrl: GITHUB_REPO_URL,
128
+ entityType: 'support',
129
+ entityId: 'github-star',
130
+ }).then(() => {
131
+ void useAppStore.getState().loadNotifications()
132
+ }).catch(() => {})
133
+ }, [])
134
+
135
+ // Apply theme hue on mount/change
136
+ useEffect(() => {
137
+ const hue = appSettings.themeHue
138
+ if (hue) {
139
+ document.documentElement.style.setProperty('--neutral-tint', hue)
140
+ }
141
+ }, [appSettings.themeHue])
142
+
105
143
  const [railExpanded, setRailExpanded] = useState(() => {
106
144
  if (typeof window === 'undefined') return true
107
145
  const stored = localStorage.getItem(RAIL_EXPANDED_KEY)
@@ -115,8 +153,7 @@ export function AppLayout() {
115
153
  }
116
154
 
117
155
  const handleSwitchUser = () => {
118
- setUser(null)
119
- setCurrentSession(null)
156
+ setProfileSheetOpen(true)
120
157
  }
121
158
 
122
159
  const openNewSheet = () => {
@@ -127,6 +164,7 @@ export function AppLayout() {
127
164
  else if (activeView === 'providers') setProviderSheetOpen(true)
128
165
  else if (activeView === 'skills') setSkillSheetOpen(true)
129
166
  else if (activeView === 'connectors') setConnectorSheetOpen(true)
167
+ else if (activeView === 'chatrooms') useChatroomStore.getState().setChatroomSheetOpen(true)
130
168
  else if (activeView === 'webhooks') setWebhookSheetOpen(true)
131
169
  else if (activeView === 'mcp_servers') setMcpServerSheetOpen(true)
132
170
  else if (activeView === 'knowledge') setKnowledgeSheetOpen(true)
@@ -149,11 +187,14 @@ export function AppLayout() {
149
187
  const agents = useAppStore((s) => s.agents)
150
188
  const currentAgentId = useAppStore((s) => s.currentAgentId)
151
189
  const setCurrentAgent = useAppStore((s) => s.setCurrentAgent)
190
+ const defaultAgentId = appSettings.defaultAgentId && agents[appSettings.defaultAgentId]
191
+ ? appSettings.defaultAgentId
192
+ : Object.values(agents)[0]?.id || null
193
+ const isMainChat = activeView === 'agents' && currentAgentId === defaultAgentId
194
+
152
195
  const goToMainChat = async () => {
153
- // Navigate to default agent's chat thread
154
- const defaultAgent = agents['default'] || Object.values(agents)[0]
155
- if (defaultAgent) {
156
- await setCurrentAgent(defaultAgent.id)
196
+ if (defaultAgentId) {
197
+ await setCurrentAgent(defaultAgentId)
157
198
  }
158
199
  setActiveView('agents')
159
200
  setSidebarOpen(false)
@@ -167,7 +208,7 @@ export function AppLayout() {
167
208
  {/* Desktop: Navigation rail (expandable) */}
168
209
  {isDesktop && (
169
210
  <div
170
- className="shrink-0 bg-raised border-r border-white/[0.04] flex flex-col py-4 transition-all duration-200 overflow-hidden"
211
+ className="shrink-0 bg-raised border-r border-white/[0.04] flex flex-col py-4 transition-all duration-200 overflow-visible"
171
212
  style={{ width: railExpanded ? 180 : 60 }}
172
213
  >
173
214
  {/* Logo + collapse toggle */}
@@ -214,9 +255,9 @@ export function AppLayout() {
214
255
  <button
215
256
  onClick={goToMainChat}
216
257
  className={`w-full flex items-center gap-2.5 px-3 py-2.5 rounded-[10px] text-[13px] font-600 cursor-pointer transition-all
217
- ${activeView === 'agents' && currentAgentId && (currentAgentId === 'default' || currentAgentId === Object.keys(agents)[0])
218
- ? 'bg-[#6366F1]/15 border border-[#6366F1]/25 text-accent-bright'
219
- : 'bg-[#6366F1]/10 border border-[#6366F1]/20 text-accent-bright hover:bg-[#6366F1]/15'}`}
258
+ ${isMainChat
259
+ ? 'bg-accent-bright/15 border border-[#6366F1]/25 text-accent-bright'
260
+ : 'bg-accent-bright/10 border border-[#6366F1]/20 text-accent-bright hover:bg-accent-bright/15'}`}
220
261
  style={{ fontFamily: 'inherit' }}
221
262
  >
222
263
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
@@ -229,7 +270,7 @@ export function AppLayout() {
229
270
  <RailTooltip label="Main Chat" description="Your persistent assistant chat">
230
271
  <button
231
272
  onClick={goToMainChat}
232
- className={`rail-btn self-center mb-2 ${activeView === 'agents' && currentAgentId && (currentAgentId === 'default' || currentAgentId === Object.keys(agents)[0]) ? 'active' : ''}`}
273
+ className={`rail-btn self-center mb-2 ${isMainChat ? 'active' : ''}`}
233
274
  >
234
275
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
235
276
  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
@@ -238,13 +279,55 @@ export function AppLayout() {
238
279
  </RailTooltip>
239
280
  )}
240
281
 
282
+ {/* Search */}
283
+ {railExpanded ? (
284
+ <div className="px-3 mb-2">
285
+ <button
286
+ onClick={() => window.dispatchEvent(new CustomEvent('swarmclaw:open-search'))}
287
+ className="w-full flex items-center gap-2.5 px-3 py-2 rounded-[10px] text-[13px] font-500 cursor-pointer transition-all
288
+ bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] border-none"
289
+ style={{ fontFamily: 'inherit' }}
290
+ >
291
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
292
+ <circle cx="11" cy="11" r="8" /><line x1="21" y1="21" x2="16.65" y2="16.65" />
293
+ </svg>
294
+ Search
295
+ <kbd className="ml-auto px-1.5 py-0.5 rounded-[5px] bg-white/[0.06] border border-white/[0.08] text-[10px] font-mono text-text-3">
296
+ ⌘K
297
+ </kbd>
298
+ </button>
299
+ </div>
300
+ ) : (
301
+ <RailTooltip label="Search" description="Search across all entities (⌘K)">
302
+ <button
303
+ onClick={() => window.dispatchEvent(new CustomEvent('swarmclaw:open-search'))}
304
+ className="rail-btn self-center mb-2"
305
+ >
306
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
307
+ <circle cx="11" cy="11" r="8" /><line x1="21" y1="21" x2="16.65" y2="16.65" />
308
+ </svg>
309
+ </button>
310
+ </RailTooltip>
311
+ )}
312
+
241
313
  {/* Nav items */}
242
314
  <div className={`flex flex-col gap-0.5 ${railExpanded ? 'px-3' : 'items-center'}`}>
315
+ <NavItem view="home" label="Home" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('home')}>
316
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
317
+ <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /><polyline points="9 22 9 12 15 12 15 22" />
318
+ </svg>
319
+ </NavItem>
243
320
  <NavItem view="agents" label="Agents" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('agents')}>
244
321
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
245
322
  <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
246
323
  </svg>
247
324
  </NavItem>
325
+ <NavItem view="chatrooms" label="Chatrooms" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('chatrooms')}>
326
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
327
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
328
+ <path d="M8 10h8" /><path d="M8 14h4" />
329
+ </svg>
330
+ </NavItem>
248
331
  <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
249
332
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
250
333
  <path d="M2 20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8l-7-7H4a2 2 0 0 0-2 2v17Z" /><path d="M14 2v7h7" />
@@ -362,30 +445,53 @@ export function AppLayout() {
362
445
  </a>
363
446
  </RailTooltip>
364
447
  )}
365
- {railExpanded && <DaemonIndicator />}
366
448
  {railExpanded ? (
367
- <button
368
- onClick={() => handleNavClick('settings')}
449
+ <a
450
+ href={GITHUB_REPO_URL}
451
+ target="_blank"
452
+ rel="noopener noreferrer"
369
453
  className="w-full flex items-center gap-2.5 px-3 py-2 rounded-[10px] text-[13px] font-500 cursor-pointer transition-all
370
- bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] border-none"
454
+ bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] no-underline"
371
455
  style={{ fontFamily: 'inherit' }}
372
456
  >
373
457
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="shrink-0">
374
- <circle cx="12" cy="12" r="3" />
375
- <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
458
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
376
459
  </svg>
377
- Settings
378
- </button>
460
+ Star on GitHub
461
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" className="ml-auto opacity-40">
462
+ <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /><polyline points="15 3 21 3 21 9" /><line x1="10" y1="14" x2="21" y2="3" />
463
+ </svg>
464
+ </a>
379
465
  ) : (
380
- <RailTooltip label="Settings" description="API keys, providers & app config">
381
- <button onClick={() => handleNavClick('settings')} className="rail-btn">
466
+ <RailTooltip label="Star on GitHub" description="Support SwarmClaw with a GitHub star">
467
+ <a
468
+ href={GITHUB_REPO_URL}
469
+ target="_blank"
470
+ rel="noopener noreferrer"
471
+ className="rail-btn"
472
+ >
382
473
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
383
- <circle cx="12" cy="12" r="3" />
384
- <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
474
+ <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
385
475
  </svg>
386
- </button>
476
+ </a>
477
+ </RailTooltip>
478
+ )}
479
+ {railExpanded && <DaemonIndicator />}
480
+ {railExpanded ? (
481
+ <NotificationCenter variant="row" align="left" direction="up" />
482
+ ) : (
483
+ <RailTooltip label="Notifications" description="View system notifications">
484
+ <div className="rail-btn flex items-center justify-center">
485
+ <NotificationCenter align="left" direction="up" />
486
+ </div>
387
487
  </RailTooltip>
388
488
  )}
489
+ <NavItem view="settings" label="Settings" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('settings')}>
490
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
491
+ <circle cx="12" cy="12" r="3" />
492
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
493
+ </svg>
494
+ </NavItem>
389
495
 
390
496
  {railExpanded ? (
391
497
  <button
@@ -394,13 +500,13 @@ export function AppLayout() {
394
500
  bg-transparent hover:bg-white/[0.04] border-none"
395
501
  style={{ fontFamily: 'inherit' }}
396
502
  >
397
- <Avatar user={currentUser!} size="sm" />
503
+ <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
398
504
  <span className="text-[13px] font-500 text-text-2 capitalize truncate">{currentUser}</span>
399
505
  </button>
400
506
  ) : (
401
- <RailTooltip label="Switch User" description="Sign in as a different user">
507
+ <RailTooltip label="Profile" description="Edit your profile">
402
508
  <button onClick={handleSwitchUser} className="mt-2 bg-transparent border-none cursor-pointer shrink-0">
403
- <Avatar user={currentUser!} size="sm" />
509
+ <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
404
510
  </button>
405
511
  </RailTooltip>
406
512
  )}
@@ -419,7 +525,7 @@ export function AppLayout() {
419
525
  {activeView === 'logs' || activeView === 'usage' || activeView === 'runs' ? null : activeView === 'memory' ? (
420
526
  <button
421
527
  onClick={() => useAppStore.getState().setMemorySheetOpen(true)}
422
- className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-[#6366F1]/15 transition-all cursor-pointer"
528
+ className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer"
423
529
  style={{ fontFamily: 'inherit' }}
424
530
  >
425
531
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
@@ -430,7 +536,7 @@ export function AppLayout() {
430
536
  ) : (
431
537
  <button
432
538
  onClick={openNewSheet}
433
- className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-[#6366F1]/15 transition-all cursor-pointer"
539
+ className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer"
434
540
  style={{ fontFamily: 'inherit' }}
435
541
  >
436
542
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
@@ -460,7 +566,7 @@ export function AppLayout() {
460
566
  </>
461
567
  )}
462
568
  {activeView === 'schedules' && <ScheduleList inSidebar />}
463
- {activeView === 'memory' && <MemoryList inSidebar />}
569
+ {activeView === 'memory' && <MemoryAgentList />}
464
570
  {activeView === 'tasks' && <TaskList inSidebar />}
465
571
  {activeView === 'secrets' && <SecretsList inSidebar />}
466
572
  {activeView === 'providers' && <ProviderList inSidebar />}
@@ -495,19 +601,19 @@ export function AppLayout() {
495
601
  <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
496
602
  </svg>
497
603
  </a>
498
- <button onClick={() => handleNavClick('settings')} className="rail-btn">
604
+ <button onClick={() => handleNavClick('settings')} className={`rail-btn ${activeView === 'settings' ? 'active' : ''}`}>
499
605
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
500
606
  <circle cx="12" cy="12" r="3" />
501
607
  <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
502
608
  </svg>
503
609
  </button>
504
610
  <button onClick={handleSwitchUser} className="bg-transparent border-none cursor-pointer shrink-0">
505
- <Avatar user={currentUser!} size="sm" />
611
+ <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
506
612
  </button>
507
613
  </div>
508
614
  {/* View selector tabs */}
509
615
  <div className="flex px-4 py-2 gap-1 shrink-0 flex-wrap">
510
- {(['agents', 'schedules', 'memory', 'tasks', 'secrets', 'providers', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins', 'usage', 'runs', 'logs'] as AppView[]).map((v) => (
616
+ {(['agents', 'chatrooms', 'schedules', 'memory', 'tasks', 'secrets', 'providers', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins', 'usage', 'runs', 'logs'] as AppView[]).map((v) => (
511
617
  <button
512
618
  key={v}
513
619
  onClick={() => setActiveView(v)}
@@ -528,7 +634,7 @@ export function AppLayout() {
528
634
  setSidebarOpen(false)
529
635
  openNewSheet()
530
636
  }}
531
- className="w-full py-3 rounded-[12px] border-none bg-[#6366F1] text-white text-[14px] font-600 cursor-pointer
637
+ className="w-full py-3 rounded-[12px] border-none bg-accent-bright text-white text-[14px] font-600 cursor-pointer
532
638
  hover:brightness-110 active:scale-[0.98] transition-all
533
639
  shadow-[0_2px_12px_rgba(99,102,241,0.15)]"
534
640
  style={{ fontFamily: 'inherit' }}
@@ -556,7 +662,7 @@ export function AppLayout() {
556
662
  </>
557
663
  )}
558
664
  {activeView === 'schedules' && <ScheduleList inSidebar />}
559
- {activeView === 'memory' && <MemoryList inSidebar onSelect={() => setSidebarOpen(false)} />}
665
+ {activeView === 'memory' && <MemoryAgentList />}
560
666
  {activeView === 'tasks' && <TaskList inSidebar />}
561
667
  {activeView === 'secrets' && <SecretsList inSidebar />}
562
668
  {activeView === 'providers' && <ProviderList inSidebar />}
@@ -566,7 +672,6 @@ export function AppLayout() {
566
672
  {activeView === 'mcp_servers' && <McpServerList />}
567
673
  {activeView === 'knowledge' && <KnowledgeList />}
568
674
  {activeView === 'plugins' && <PluginList inSidebar />}
569
- {activeView === 'projects' && <ProjectList />}
570
675
  {activeView === 'runs' && <RunList />}
571
676
  {activeView === 'logs' && <LogList />}
572
677
  </div>
@@ -577,7 +682,9 @@ export function AppLayout() {
577
682
  <ErrorBoundary>
578
683
  <div className="flex-1 flex flex-col h-full min-w-0 bg-bg">
579
684
  {!isDesktop && <MobileHeader />}
580
- {activeView === 'agents' && hasSelectedSession ? (
685
+ {activeView === 'home' ? (
686
+ <HomeView />
687
+ ) : activeView === 'agents' && hasSelectedSession ? (
581
688
  <ChatArea />
582
689
  ) : activeView === 'agents' ? (
583
690
  <div className="flex-1 flex flex-col">
@@ -599,11 +706,38 @@ export function AppLayout() {
599
706
  ) : activeView === 'tasks' && isDesktop ? (
600
707
  <TaskBoard />
601
708
  ) : activeView === 'memory' ? (
602
- <MemoryDetail />
709
+ <MemoryBrowser />
603
710
  ) : activeView === 'activity' ? (
604
711
  <ActivityFeed />
605
712
  ) : activeView === 'usage' ? (
606
713
  <MetricsDashboard />
714
+ ) : activeView === 'chatrooms' ? (
715
+ <div className="flex-1 flex h-full min-w-0">
716
+ <div className="w-[280px] shrink-0 border-r border-white/[0.06] flex flex-col">
717
+ <div className="flex items-center px-4 pt-4 pb-2 shrink-0">
718
+ <h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] flex-1">Chatrooms</h2>
719
+ <button
720
+ onClick={() => { useChatroomStore.getState().setEditingChatroomId(null); useChatroomStore.getState().setChatroomSheetOpen(true) }}
721
+ className="flex items-center gap-1 px-2 py-1 rounded-[6px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer"
722
+ style={{ fontFamily: 'inherit' }}
723
+ >
724
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
725
+ <line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
726
+ </svg>
727
+ New
728
+ </button>
729
+ </div>
730
+ <ChatroomList />
731
+ </div>
732
+ <ChatroomView />
733
+ </div>
734
+ ) : activeView === 'projects' ? (
735
+ <div className="flex-1 flex h-full min-w-0">
736
+ <div className="w-[280px] shrink-0 border-r border-white/[0.06] flex flex-col">
737
+ <ProjectList />
738
+ </div>
739
+ <ProjectDetail />
740
+ </div>
607
741
  ) : activeView === 'settings' ? (
608
742
  <SettingsPage />
609
743
  ) : !sidebarOpen && FULL_WIDTH_VIEWS.has(activeView) ? (
@@ -615,13 +749,13 @@ export function AppLayout() {
615
749
  {activeView !== 'runs' && activeView !== 'logs' && (
616
750
  <button
617
751
  onClick={openNewSheet}
618
- className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-[#6366F1]/15 transition-all cursor-pointer"
752
+ className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer"
619
753
  style={{ fontFamily: 'inherit' }}
620
754
  >
621
755
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
622
756
  <line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
623
757
  </svg>
624
- {activeView === 'schedules' ? 'Schedule' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : activeView === 'plugins' ? 'Plugin' : activeView === 'projects' ? 'Project' : 'New'}
758
+ {activeView === 'schedules' ? 'Schedule' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : activeView === 'plugins' ? 'Plugin' : 'New'}
625
759
  </button>
626
760
  )}
627
761
  </div>
@@ -634,7 +768,6 @@ export function AppLayout() {
634
768
  {activeView === 'mcp_servers' && <McpServerList />}
635
769
  {activeView === 'knowledge' && <KnowledgeList />}
636
770
  {activeView === 'plugins' && <PluginList />}
637
- {activeView === 'projects' && <ProjectList />}
638
771
  {activeView === 'runs' && <RunList />}
639
772
  {activeView === 'logs' && <LogList />}
640
773
  </div>
@@ -644,6 +777,9 @@ export function AppLayout() {
644
777
  </div>
645
778
  </ErrorBoundary>
646
779
 
780
+ <SearchDialog />
781
+ <AgentSwitchDialog />
782
+ <KeyboardShortcutsDialog />
647
783
  <AgentSheet />
648
784
  <ScheduleSheet />
649
785
  <MemorySheet />
@@ -652,32 +788,14 @@ export function AppLayout() {
652
788
  <ProviderSheet />
653
789
  <SkillSheet />
654
790
  <ConnectorSheet />
791
+ <ChatroomSheet />
655
792
  <WebhookSheet />
656
793
  <McpServerSheet />
657
794
  <KnowledgeSheet />
658
795
  <PluginSheet />
659
796
  <ProjectSheet />
797
+ <ProfileSheet open={profileSheetOpen} onClose={() => setProfileSheetOpen(false)} />
660
798
 
661
- <Dialog open={shortcutsOpen} onOpenChange={setShortcutsOpen}>
662
- <DialogContent className="sm:max-w-[380px] bg-raised border-white/[0.08]">
663
- <DialogHeader>
664
- <DialogTitle className="text-text">Keyboard Shortcuts</DialogTitle>
665
- </DialogHeader>
666
- <div className="space-y-3 py-2">
667
- {([
668
- ['Enter', 'Send message'],
669
- ['Shift + Enter', 'New line'],
670
- ['Ctrl + F', 'Search in chat'],
671
- ['Ctrl + /', 'Show shortcuts'],
672
- ] as const).map(([keys, desc]) => (
673
- <div key={keys} className="flex items-center justify-between">
674
- <span className="text-[13px] text-text-2">{desc}</span>
675
- <kbd className="px-2 py-1 rounded-[6px] bg-white/[0.06] border border-white/[0.08] text-[11px] font-mono text-text-3">{keys}</kbd>
676
- </div>
677
- ))}
678
- </div>
679
- </DialogContent>
680
- </Dialog>
681
799
  </div>
682
800
  )
683
801
  }
@@ -716,7 +834,7 @@ class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boole
716
834
  </p>
717
835
  <button
718
836
  onClick={() => window.location.reload()}
719
- className="inline-flex items-center gap-2 px-6 py-3 rounded-[12px] border-none bg-[#6366F1] text-white text-[14px] font-600 cursor-pointer
837
+ className="inline-flex items-center gap-2 px-6 py-3 rounded-[12px] border-none bg-accent-bright text-white text-[14px] font-600 cursor-pointer
720
838
  hover:brightness-110 active:scale-[0.97] transition-all shadow-[0_4px_16px_rgba(99,102,241,0.2)]"
721
839
  style={{ fontFamily: 'inherit' }}
722
840
  >
@@ -736,7 +854,9 @@ class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boole
736
854
  }
737
855
 
738
856
  const VIEW_DESCRIPTIONS: Record<AppView, string> = {
857
+ home: 'Dashboard overview',
739
858
  agents: 'Chat with & configure your AI agents',
859
+ chatrooms: 'Multi-agent collaborative chatrooms',
740
860
  schedules: 'Automated task schedules',
741
861
  memory: 'Long-term agent memory store',
742
862
  tasks: 'Task board for orchestrator jobs',
@@ -757,12 +877,18 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
757
877
  }
758
878
 
759
879
  const FULL_WIDTH_VIEWS = new Set<AppView>([
760
- 'schedules', 'secrets', 'providers', 'skills',
880
+ 'home', 'chatrooms', 'schedules', 'secrets', 'providers', 'skills',
761
881
  'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins',
762
- 'usage', 'runs', 'logs', 'settings', 'projects', 'activity',
882
+ 'usage', 'runs', 'logs', 'settings', 'activity', 'projects',
763
883
  ])
764
884
 
765
- const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; title: string; description: string; features: string[] }> = {
885
+ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: string; title: string; description: string; features: string[] }> = {
886
+ chatrooms: {
887
+ icon: 'message-square',
888
+ title: 'Chatrooms',
889
+ description: 'Multi-agent chatrooms for collaborative conversations. Add agents and use @mentions to trigger responses.',
890
+ features: ['Create chatrooms with multiple AI agents', 'Use @AgentName to trigger specific agents', '@all mentions trigger all agents sequentially', 'Agents can chain by mentioning each other'],
891
+ },
766
892
  schedules: {
767
893
  icon: 'clock',
768
894
  title: 'Schedules',
@@ -868,8 +994,8 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; titl
868
994
  }
869
995
 
870
996
  function ViewEmptyState({ view }: { view: AppView }) {
871
- if (view === 'agents') return null
872
- const config = VIEW_EMPTY_STATES[view as Exclude<AppView, 'agents'>]
997
+ if (view === 'agents' || view === 'home') return null
998
+ const config = VIEW_EMPTY_STATES[view as Exclude<AppView, 'agents' | 'home'>]
873
999
  if (!config) return null
874
1000
 
875
1001
  return (
@@ -1056,7 +1182,7 @@ function DesktopEmptyState({ userName }: { userName: string | null }) {
1056
1182
  </p>
1057
1183
  <button
1058
1184
  onClick={() => setNewSessionOpen(true)}
1059
- className="inline-flex items-center gap-2.5 px-12 py-4 rounded-[16px] border-none bg-[#6366F1] text-white text-[16px] font-display font-600
1185
+ className="inline-flex items-center gap-2.5 px-12 py-4 rounded-[16px] border-none bg-accent-bright text-white text-[16px] font-display font-600
1060
1186
  cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
1061
1187
  shadow-[0_6px_28px_rgba(99,102,241,0.3)]"
1062
1188
  style={{ fontFamily: 'inherit' }}
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useAppStore } from '@/stores/use-app-store'
4
4
  import { IconButton } from '@/components/shared/icon-button'
5
+ import { NotificationCenter } from '@/components/shared/notification-center'
5
6
 
6
7
  export function MobileHeader() {
7
8
  const toggleSidebar = useAppStore((s) => s.toggleSidebar)
@@ -26,6 +27,7 @@ export function MobileHeader() {
26
27
  <span className="font-700">SwarmClaw</span>
27
28
  )}
28
29
  </h1>
30
+ <NotificationCenter />
29
31
  </header>
30
32
  )
31
33
  }
@@ -63,7 +63,7 @@ export function UpdateBanner() {
63
63
  if (dismissed === version.remoteSha && updateState === 'idle') return null
64
64
 
65
65
  return (
66
- <div className="px-4 py-1.5 border-b border-white/[0.04] text-[10px] flex items-center gap-2 shrink-0 bg-[#6366F1]/[0.04]">
66
+ <div className="px-4 py-1.5 border-b border-white/[0.04] text-[10px] flex items-center gap-2 shrink-0 bg-accent-bright/[0.04]">
67
67
  {updateState === 'idle' && (
68
68
  <>
69
69
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-accent-bright shrink-0">
@@ -74,7 +74,7 @@ export function UpdateBanner() {
74
74
  </span>
75
75
  <button
76
76
  onClick={handleUpdate}
77
- className="text-[10px] font-600 text-accent-bright hover:text-white bg-[#6366F1]/20 hover:bg-[#6366F1]/30 px-2 py-0.5 rounded-[6px] border-none cursor-pointer transition-all shrink-0"
77
+ className="text-[10px] font-600 text-accent-bright hover:text-white bg-accent-bright/20 hover:bg-accent-bright/30 px-2 py-0.5 rounded-[6px] border-none cursor-pointer transition-all shrink-0"
78
78
  style={{ fontFamily: 'inherit' }}
79
79
  >
80
80
  Update