@swarmclawai/swarmclaw 0.5.3 → 0.6.2

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 (224) hide show
  1. package/README.md +53 -9
  2. package/bin/server-cmd.js +1 -0
  3. package/bin/swarmclaw.js +76 -16
  4. package/next.config.ts +11 -1
  5. package/package.json +5 -2
  6. package/scripts/postinstall.mjs +18 -0
  7. package/src/app/api/canvas/[sessionId]/route.ts +31 -0
  8. package/src/app/api/chatrooms/[id]/chat/route.ts +284 -0
  9. package/src/app/api/chatrooms/[id]/members/route.ts +82 -0
  10. package/src/app/api/chatrooms/[id]/pins/route.ts +39 -0
  11. package/src/app/api/chatrooms/[id]/reactions/route.ts +42 -0
  12. package/src/app/api/chatrooms/[id]/route.ts +84 -0
  13. package/src/app/api/chatrooms/route.ts +50 -0
  14. package/src/app/api/connectors/[id]/route.ts +1 -0
  15. package/src/app/api/connectors/route.ts +2 -1
  16. package/src/app/api/credentials/route.ts +2 -3
  17. package/src/app/api/files/open/route.ts +43 -0
  18. package/src/app/api/knowledge/[id]/route.ts +13 -2
  19. package/src/app/api/knowledge/route.ts +8 -1
  20. package/src/app/api/memory/route.ts +8 -0
  21. package/src/app/api/notifications/route.ts +4 -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 +53 -1
  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/[id]/messages/route.ts +70 -2
  29. package/src/app/api/sessions/[id]/route.ts +4 -0
  30. package/src/app/api/sessions/route.ts +3 -3
  31. package/src/app/api/settings/route.ts +9 -0
  32. package/src/app/api/setup/check-provider/route.ts +3 -16
  33. package/src/app/api/skills/[id]/route.ts +6 -0
  34. package/src/app/api/skills/route.ts +6 -0
  35. package/src/app/api/tasks/[id]/route.ts +12 -0
  36. package/src/app/api/tasks/bulk/route.ts +100 -0
  37. package/src/app/api/tasks/metrics/route.ts +101 -0
  38. package/src/app/api/tasks/route.ts +18 -2
  39. package/src/app/api/tts/route.ts +3 -2
  40. package/src/app/api/tts/stream/route.ts +3 -2
  41. package/src/app/api/uploads/[filename]/route.ts +19 -34
  42. package/src/app/api/uploads/route.ts +94 -0
  43. package/src/app/api/webhooks/[id]/route.ts +15 -1
  44. package/src/app/globals.css +63 -15
  45. package/src/app/page.tsx +142 -13
  46. package/src/cli/index.js +40 -1
  47. package/src/cli/index.test.js +30 -0
  48. package/src/cli/spec.js +42 -0
  49. package/src/components/agents/agent-avatar.tsx +57 -10
  50. package/src/components/agents/agent-card.tsx +50 -17
  51. package/src/components/agents/agent-chat-list.tsx +148 -12
  52. package/src/components/agents/agent-list.tsx +50 -19
  53. package/src/components/agents/agent-sheet.tsx +120 -65
  54. package/src/components/agents/inspector-panel.tsx +81 -6
  55. package/src/components/agents/openclaw-skills-panel.tsx +32 -3
  56. package/src/components/agents/personality-builder.tsx +42 -14
  57. package/src/components/agents/soul-library-picker.tsx +89 -0
  58. package/src/components/auth/access-key-gate.tsx +10 -3
  59. package/src/components/auth/setup-wizard.tsx +2 -2
  60. package/src/components/auth/user-picker.tsx +31 -3
  61. package/src/components/canvas/canvas-panel.tsx +96 -0
  62. package/src/components/chat/activity-moment.tsx +173 -0
  63. package/src/components/chat/chat-area.tsx +46 -22
  64. package/src/components/chat/chat-header.tsx +457 -286
  65. package/src/components/chat/chat-preview-panel.tsx +1 -2
  66. package/src/components/chat/chat-tool-toggles.tsx +1 -1
  67. package/src/components/chat/delegation-banner.tsx +371 -0
  68. package/src/components/chat/file-path-chip.tsx +146 -0
  69. package/src/components/chat/heartbeat-history-panel.tsx +269 -0
  70. package/src/components/chat/markdown-utils.ts +9 -0
  71. package/src/components/chat/message-bubble.tsx +356 -315
  72. package/src/components/chat/message-list.tsx +230 -8
  73. package/src/components/chat/streaming-bubble.tsx +104 -47
  74. package/src/components/chat/suggestions-bar.tsx +1 -1
  75. package/src/components/chat/thinking-indicator.tsx +72 -10
  76. package/src/components/chat/tool-call-bubble.tsx +111 -73
  77. package/src/components/chat/tool-request-banner.tsx +31 -7
  78. package/src/components/chat/transfer-agent-picker.tsx +63 -0
  79. package/src/components/chatrooms/agent-hover-card.tsx +124 -0
  80. package/src/components/chatrooms/chatroom-input.tsx +320 -0
  81. package/src/components/chatrooms/chatroom-list.tsx +130 -0
  82. package/src/components/chatrooms/chatroom-message.tsx +432 -0
  83. package/src/components/chatrooms/chatroom-sheet.tsx +215 -0
  84. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +134 -0
  85. package/src/components/chatrooms/chatroom-typing-bar.tsx +88 -0
  86. package/src/components/chatrooms/chatroom-view.tsx +344 -0
  87. package/src/components/chatrooms/reaction-picker.tsx +273 -0
  88. package/src/components/connectors/connector-list.tsx +168 -90
  89. package/src/components/connectors/connector-sheet.tsx +95 -56
  90. package/src/components/home/home-view.tsx +501 -0
  91. package/src/components/input/chat-input.tsx +107 -43
  92. package/src/components/knowledge/knowledge-list.tsx +31 -1
  93. package/src/components/knowledge/knowledge-sheet.tsx +83 -2
  94. package/src/components/layout/app-layout.tsx +194 -97
  95. package/src/components/layout/update-banner.tsx +2 -2
  96. package/src/components/logs/log-list.tsx +2 -2
  97. package/src/components/mcp-servers/mcp-server-sheet.tsx +1 -1
  98. package/src/components/memory/memory-agent-list.tsx +143 -0
  99. package/src/components/memory/memory-browser.tsx +205 -0
  100. package/src/components/memory/memory-card.tsx +34 -7
  101. package/src/components/memory/memory-detail.tsx +359 -120
  102. package/src/components/memory/memory-sheet.tsx +157 -23
  103. package/src/components/plugins/plugin-list.tsx +1 -1
  104. package/src/components/plugins/plugin-sheet.tsx +1 -1
  105. package/src/components/projects/project-detail.tsx +509 -0
  106. package/src/components/projects/project-list.tsx +195 -59
  107. package/src/components/providers/provider-list.tsx +2 -2
  108. package/src/components/providers/provider-sheet.tsx +3 -3
  109. package/src/components/schedules/schedule-card.tsx +1 -1
  110. package/src/components/schedules/schedule-list.tsx +1 -1
  111. package/src/components/schedules/schedule-sheet.tsx +259 -126
  112. package/src/components/secrets/secret-sheet.tsx +47 -24
  113. package/src/components/secrets/secrets-list.tsx +18 -8
  114. package/src/components/sessions/new-session-sheet.tsx +33 -65
  115. package/src/components/sessions/session-card.tsx +45 -14
  116. package/src/components/sessions/session-list.tsx +35 -18
  117. package/src/components/settings/gateway-disconnect-overlay.tsx +80 -0
  118. package/src/components/shared/agent-picker-list.tsx +90 -0
  119. package/src/components/shared/agent-switch-dialog.tsx +156 -0
  120. package/src/components/shared/attachment-chip.tsx +165 -0
  121. package/src/components/shared/avatar.tsx +10 -1
  122. package/src/components/shared/chatroom-picker-list.tsx +61 -0
  123. package/src/components/shared/check-icon.tsx +12 -0
  124. package/src/components/shared/confirm-dialog.tsx +1 -1
  125. package/src/components/shared/connector-platform-icon.tsx +51 -4
  126. package/src/components/shared/empty-state.tsx +32 -0
  127. package/src/components/shared/file-preview.tsx +34 -0
  128. package/src/components/shared/form-styles.ts +2 -0
  129. package/src/components/shared/icon-button.tsx +16 -2
  130. package/src/components/shared/keyboard-shortcuts-dialog.tsx +116 -0
  131. package/src/components/shared/notification-center.tsx +44 -6
  132. package/src/components/shared/profile-sheet.tsx +115 -0
  133. package/src/components/shared/reply-quote.tsx +26 -0
  134. package/src/components/shared/search-dialog.tsx +31 -15
  135. package/src/components/shared/section-label.tsx +12 -0
  136. package/src/components/shared/settings/plugin-manager.tsx +1 -1
  137. package/src/components/shared/settings/section-embedding.tsx +48 -13
  138. package/src/components/shared/settings/section-orchestrator.tsx +46 -15
  139. package/src/components/shared/settings/section-providers.tsx +1 -1
  140. package/src/components/shared/settings/section-secrets.tsx +1 -1
  141. package/src/components/shared/settings/section-storage.tsx +206 -0
  142. package/src/components/shared/settings/section-theme.tsx +95 -0
  143. package/src/components/shared/settings/section-user-preferences.tsx +57 -0
  144. package/src/components/shared/settings/section-voice.tsx +42 -21
  145. package/src/components/shared/settings/section-web-search.tsx +30 -6
  146. package/src/components/shared/settings/settings-page.tsx +182 -27
  147. package/src/components/shared/settings/settings-sheet.tsx +9 -73
  148. package/src/components/shared/settings/storage-browser.tsx +259 -0
  149. package/src/components/shared/sheet-footer.tsx +33 -0
  150. package/src/components/skills/skill-list.tsx +61 -30
  151. package/src/components/skills/skill-sheet.tsx +81 -2
  152. package/src/components/tasks/task-board.tsx +448 -26
  153. package/src/components/tasks/task-card.tsx +59 -9
  154. package/src/components/tasks/task-column.tsx +62 -3
  155. package/src/components/tasks/task-list.tsx +12 -4
  156. package/src/components/tasks/task-sheet.tsx +416 -74
  157. package/src/components/ui/hover-card.tsx +52 -0
  158. package/src/components/usage/metrics-dashboard.tsx +90 -6
  159. package/src/components/usage/usage-list.tsx +1 -1
  160. package/src/components/webhooks/webhook-sheet.tsx +1 -1
  161. package/src/hooks/use-continuous-speech.ts +10 -4
  162. package/src/hooks/use-view-router.ts +69 -19
  163. package/src/hooks/use-voice-conversation.ts +53 -10
  164. package/src/hooks/use-ws.ts +4 -2
  165. package/src/instrumentation.ts +15 -1
  166. package/src/lib/chat.ts +2 -0
  167. package/src/lib/memory.ts +3 -0
  168. package/src/lib/providers/anthropic.ts +13 -7
  169. package/src/lib/providers/index.ts +1 -0
  170. package/src/lib/providers/openai.ts +13 -7
  171. package/src/lib/server/chat-execution.ts +75 -15
  172. package/src/lib/server/chatroom-helpers.ts +146 -0
  173. package/src/lib/server/connectors/manager.ts +229 -7
  174. package/src/lib/server/context-manager.ts +225 -13
  175. package/src/lib/server/create-notification.ts +14 -2
  176. package/src/lib/server/daemon-state.ts +157 -10
  177. package/src/lib/server/execution-log.ts +1 -0
  178. package/src/lib/server/heartbeat-service.ts +48 -6
  179. package/src/lib/server/heartbeat-wake.ts +110 -0
  180. package/src/lib/server/langgraph-checkpoint.ts +1 -0
  181. package/src/lib/server/main-agent-loop.ts +1 -1
  182. package/src/lib/server/memory-consolidation.ts +105 -0
  183. package/src/lib/server/memory-db.ts +183 -10
  184. package/src/lib/server/mime.ts +51 -0
  185. package/src/lib/server/openclaw-gateway.ts +9 -1
  186. package/src/lib/server/orchestrator-lg.ts +2 -0
  187. package/src/lib/server/orchestrator.ts +5 -2
  188. package/src/lib/server/playwright-proxy.mjs +2 -3
  189. package/src/lib/server/prompt-runtime-context.ts +53 -0
  190. package/src/lib/server/provider-health.ts +125 -0
  191. package/src/lib/server/queue.ts +56 -10
  192. package/src/lib/server/scheduler.ts +8 -0
  193. package/src/lib/server/session-run-manager.ts +4 -0
  194. package/src/lib/server/session-tools/canvas.ts +67 -0
  195. package/src/lib/server/session-tools/chatroom.ts +136 -0
  196. package/src/lib/server/session-tools/connector.ts +83 -9
  197. package/src/lib/server/session-tools/context-mgmt.ts +36 -18
  198. package/src/lib/server/session-tools/crud.ts +21 -0
  199. package/src/lib/server/session-tools/delegate.ts +68 -4
  200. package/src/lib/server/session-tools/git.ts +71 -0
  201. package/src/lib/server/session-tools/http.ts +57 -0
  202. package/src/lib/server/session-tools/index.ts +10 -0
  203. package/src/lib/server/session-tools/memory.ts +7 -1
  204. package/src/lib/server/session-tools/search-providers.ts +16 -8
  205. package/src/lib/server/session-tools/subagent.ts +106 -0
  206. package/src/lib/server/session-tools/web.ts +115 -4
  207. package/src/lib/server/storage.ts +53 -29
  208. package/src/lib/server/stream-agent-chat.ts +185 -57
  209. package/src/lib/server/system-events.ts +49 -0
  210. package/src/lib/server/task-mention.ts +41 -0
  211. package/src/lib/server/ws-hub.ts +11 -0
  212. package/src/lib/sessions.ts +10 -0
  213. package/src/lib/soul-library.ts +103 -0
  214. package/src/lib/soul-suggestions.ts +109 -0
  215. package/src/lib/task-dedupe.ts +26 -0
  216. package/src/lib/tasks.ts +4 -1
  217. package/src/lib/tool-definitions.ts +2 -0
  218. package/src/lib/tts.ts +2 -2
  219. package/src/lib/view-routes.ts +36 -1
  220. package/src/lib/ws-client.ts +14 -4
  221. package/src/stores/use-app-store.ts +41 -3
  222. package/src/stores/use-chat-store.ts +113 -5
  223. package/src/stores/use-chatroom-store.ts +276 -0
  224. package/src/types/index.ts +88 -4
@@ -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,18 +42,27 @@ 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'
43
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'
44
52
  import { NetworkBanner } from './network-banner'
45
53
  import { UpdateBanner } from './update-banner'
46
54
  import { MobileHeader } from './mobile-header'
47
55
  import { DaemonIndicator } from './daemon-indicator'
48
56
  import { NotificationCenter } from '@/components/shared/notification-center'
49
57
  import { ChatArea } from '@/components/chat/chat-area'
58
+ import { CanvasPanel } from '@/components/canvas/canvas-panel'
50
59
  import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
60
+ import { api } from '@/lib/api-client'
51
61
  import type { AppView } from '@/types'
52
62
 
53
63
  const RAIL_EXPANDED_KEY = 'sc_rail_expanded'
64
+ const STAR_NOTIFICATION_KEY = 'sc_star_notification_v1'
65
+ const GITHUB_REPO_URL = 'https://github.com/swarmclawai/swarmclaw'
54
66
 
55
67
  export function AppLayout() {
56
68
  const currentUser = useAppStore((s) => s.currentUser)
@@ -58,8 +70,6 @@ export function AppLayout() {
58
70
  const currentSessionId = useAppStore((s) => s.currentSessionId)
59
71
  const sidebarOpen = useAppStore((s) => s.sidebarOpen)
60
72
  const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
61
- const setUser = useAppStore((s) => s.setUser)
62
- const setCurrentSession = useAppStore((s) => s.setCurrentSession)
63
73
  const activeView = useAppStore((s) => s.activeView)
64
74
  const setActiveView = useAppStore((s) => s.setActiveView)
65
75
  const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
@@ -79,23 +89,26 @@ export function AppLayout() {
79
89
  const hasSelectedSession = !!(currentSessionId && sessions[currentSessionId])
80
90
  const pendingApprovalCount = Object.values(tasks).filter((t) => t.pendingApproval).length
81
91
 
92
+ const appSettings = useAppStore((s) => s.appSettings)
82
93
  const [agentViewMode, setAgentViewMode] = useState<'chat' | 'config'>('chat')
83
- const [shortcutsOpen, setShortcutsOpen] = useState(false)
94
+ const [profileSheetOpen, setProfileSheetOpen] = useState(false)
95
+ const [canvasDismissedFor, setCanvasDismissedFor] = useState<string | null>(null)
84
96
 
85
97
  const handleShortcutKey = useCallback((e: KeyboardEvent) => {
86
- // Ctrl+/ or Cmd+/
87
- if ((e.ctrlKey || e.metaKey) && e.key === '/') {
98
+ const mod = e.metaKey || e.ctrlKey
99
+ // Cmd+N / Ctrl+N new chat
100
+ if (mod && !e.shiftKey && e.key.toLowerCase() === 'n') {
88
101
  e.preventDefault()
89
- setShortcutsOpen((v) => !v)
102
+ const state = useAppStore.getState()
103
+ const allAgents = Object.values(state.agents).filter((a) => !a.trashedAt)
104
+ const target = allAgents.find((a) => a.id === 'default') || allAgents[0]
105
+ if (target) void state.setCurrentAgent(target.id)
90
106
  return
91
107
  }
92
- // ? key when not in an input/textarea/contenteditable
93
- if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) {
94
- const tag = (e.target as HTMLElement)?.tagName?.toLowerCase()
95
- const editable = (e.target as HTMLElement)?.isContentEditable
96
- if (tag === 'input' || tag === 'textarea' || editable) return
108
+ // Cmd+Shift+T / Ctrl+Shift+T jump to tasks
109
+ if (mod && e.shiftKey && e.key.toLowerCase() === 't') {
97
110
  e.preventDefault()
98
- setShortcutsOpen((v) => !v)
111
+ useAppStore.getState().setActiveView('tasks')
99
112
  }
100
113
  }, [])
101
114
 
@@ -104,6 +117,31 @@ export function AppLayout() {
104
117
  return () => window.removeEventListener('keydown', handleShortcutKey)
105
118
  }, [handleShortcutKey])
106
119
 
120
+ useEffect(() => {
121
+ if (typeof window === 'undefined') return
122
+ if (localStorage.getItem(STAR_NOTIFICATION_KEY)) return
123
+ localStorage.setItem(STAR_NOTIFICATION_KEY, '1')
124
+ void api('POST', '/notifications', {
125
+ type: 'info',
126
+ title: 'Enjoying SwarmClaw?',
127
+ message: 'If SwarmClaw helps your workflow, please star the GitHub repo to support the project.',
128
+ actionLabel: 'Star on GitHub',
129
+ actionUrl: GITHUB_REPO_URL,
130
+ entityType: 'support',
131
+ entityId: 'github-star',
132
+ }).then(() => {
133
+ void useAppStore.getState().loadNotifications()
134
+ }).catch(() => {})
135
+ }, [])
136
+
137
+ // Apply theme hue on mount/change
138
+ useEffect(() => {
139
+ const hue = appSettings.themeHue
140
+ if (hue) {
141
+ document.documentElement.style.setProperty('--neutral-tint', hue)
142
+ }
143
+ }, [appSettings.themeHue])
144
+
107
145
  const [railExpanded, setRailExpanded] = useState(() => {
108
146
  if (typeof window === 'undefined') return true
109
147
  const stored = localStorage.getItem(RAIL_EXPANDED_KEY)
@@ -117,8 +155,7 @@ export function AppLayout() {
117
155
  }
118
156
 
119
157
  const handleSwitchUser = () => {
120
- setUser(null)
121
- setCurrentSession(null)
158
+ setProfileSheetOpen(true)
122
159
  }
123
160
 
124
161
  const openNewSheet = () => {
@@ -129,6 +166,7 @@ export function AppLayout() {
129
166
  else if (activeView === 'providers') setProviderSheetOpen(true)
130
167
  else if (activeView === 'skills') setSkillSheetOpen(true)
131
168
  else if (activeView === 'connectors') setConnectorSheetOpen(true)
169
+ else if (activeView === 'chatrooms') useChatroomStore.getState().setChatroomSheetOpen(true)
132
170
  else if (activeView === 'webhooks') setWebhookSheetOpen(true)
133
171
  else if (activeView === 'mcp_servers') setMcpServerSheetOpen(true)
134
172
  else if (activeView === 'knowledge') setKnowledgeSheetOpen(true)
@@ -151,11 +189,18 @@ export function AppLayout() {
151
189
  const agents = useAppStore((s) => s.agents)
152
190
  const currentAgentId = useAppStore((s) => s.currentAgentId)
153
191
  const setCurrentAgent = useAppStore((s) => s.setCurrentAgent)
192
+ const defaultAgentId = appSettings.defaultAgentId && agents[appSettings.defaultAgentId]
193
+ ? appSettings.defaultAgentId
194
+ : Object.values(agents)[0]?.id || null
195
+ const isMainChat = activeView === 'agents' && currentAgentId === defaultAgentId
196
+
197
+ const currentSession = currentSessionId ? sessions[currentSessionId] : null
198
+ const hasCanvas = !!(currentSession?.canvasContent && canvasDismissedFor !== currentSessionId)
199
+ const canvasAgentName = currentSession?.agentId && agents[currentSession.agentId] ? agents[currentSession.agentId].name : undefined
200
+
154
201
  const goToMainChat = async () => {
155
- // Navigate to default agent's chat thread
156
- const defaultAgent = agents['default'] || Object.values(agents)[0]
157
- if (defaultAgent) {
158
- await setCurrentAgent(defaultAgent.id)
202
+ if (defaultAgentId) {
203
+ await setCurrentAgent(defaultAgentId)
159
204
  }
160
205
  setActiveView('agents')
161
206
  setSidebarOpen(false)
@@ -169,7 +214,7 @@ export function AppLayout() {
169
214
  {/* Desktop: Navigation rail (expandable) */}
170
215
  {isDesktop && (
171
216
  <div
172
- className="shrink-0 bg-raised border-r border-white/[0.04] flex flex-col py-4 transition-all duration-200 overflow-hidden"
217
+ className="shrink-0 bg-raised border-r border-white/[0.04] flex flex-col py-4 transition-all duration-200 overflow-visible"
173
218
  style={{ width: railExpanded ? 180 : 60 }}
174
219
  >
175
220
  {/* Logo + collapse toggle */}
@@ -216,9 +261,9 @@ export function AppLayout() {
216
261
  <button
217
262
  onClick={goToMainChat}
218
263
  className={`w-full flex items-center gap-2.5 px-3 py-2.5 rounded-[10px] text-[13px] font-600 cursor-pointer transition-all
219
- ${activeView === 'agents' && currentAgentId && (currentAgentId === 'default' || currentAgentId === Object.keys(agents)[0])
220
- ? 'bg-[#6366F1]/15 border border-[#6366F1]/25 text-accent-bright'
221
- : 'bg-[#6366F1]/10 border border-[#6366F1]/20 text-accent-bright hover:bg-[#6366F1]/15'}`}
264
+ ${isMainChat
265
+ ? 'bg-accent-bright/15 border border-[#6366F1]/25 text-accent-bright'
266
+ : 'bg-accent-bright/10 border border-[#6366F1]/20 text-accent-bright hover:bg-accent-bright/15'}`}
222
267
  style={{ fontFamily: 'inherit' }}
223
268
  >
224
269
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
@@ -231,7 +276,7 @@ export function AppLayout() {
231
276
  <RailTooltip label="Main Chat" description="Your persistent assistant chat">
232
277
  <button
233
278
  onClick={goToMainChat}
234
- className={`rail-btn self-center mb-2 ${activeView === 'agents' && currentAgentId && (currentAgentId === 'default' || currentAgentId === Object.keys(agents)[0]) ? 'active' : ''}`}
279
+ className={`rail-btn self-center mb-2 ${isMainChat ? 'active' : ''}`}
235
280
  >
236
281
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
237
282
  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
@@ -273,11 +318,22 @@ export function AppLayout() {
273
318
 
274
319
  {/* Nav items */}
275
320
  <div className={`flex flex-col gap-0.5 ${railExpanded ? 'px-3' : 'items-center'}`}>
321
+ <NavItem view="home" label="Home" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('home')}>
322
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
323
+ <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" />
324
+ </svg>
325
+ </NavItem>
276
326
  <NavItem view="agents" label="Agents" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('agents')}>
277
327
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
278
328
  <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
279
329
  </svg>
280
330
  </NavItem>
331
+ <NavItem view="chatrooms" label="Chatrooms" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('chatrooms')}>
332
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
333
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
334
+ <path d="M8 10h8" /><path d="M8 14h4" />
335
+ </svg>
336
+ </NavItem>
281
337
  <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
282
338
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
283
339
  <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" />
@@ -395,42 +451,53 @@ export function AppLayout() {
395
451
  </a>
396
452
  </RailTooltip>
397
453
  )}
398
- {railExpanded && <DaemonIndicator />}
399
- {railExpanded ? (
400
- <div className="flex items-center gap-1 px-3 py-1">
401
- <span className="text-[12px] font-500 text-text-3 flex-1">Alerts</span>
402
- <NotificationCenter />
403
- </div>
404
- ) : (
405
- <RailTooltip label="Notifications" description="View system notifications">
406
- <div className="rail-btn flex items-center justify-center">
407
- <NotificationCenter />
408
- </div>
409
- </RailTooltip>
410
- )}
411
454
  {railExpanded ? (
412
- <button
413
- onClick={() => handleNavClick('settings')}
455
+ <a
456
+ href={GITHUB_REPO_URL}
457
+ target="_blank"
458
+ rel="noopener noreferrer"
414
459
  className="w-full flex items-center gap-2.5 px-3 py-2 rounded-[10px] text-[13px] font-500 cursor-pointer transition-all
415
- bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] border-none"
460
+ bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] no-underline"
416
461
  style={{ fontFamily: 'inherit' }}
417
462
  >
418
463
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="shrink-0">
419
- <circle cx="12" cy="12" r="3" />
420
- <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" />
464
+ <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" />
421
465
  </svg>
422
- Settings
423
- </button>
466
+ Star on GitHub
467
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" className="ml-auto opacity-40">
468
+ <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" />
469
+ </svg>
470
+ </a>
424
471
  ) : (
425
- <RailTooltip label="Settings" description="API keys, providers & app config">
426
- <button onClick={() => handleNavClick('settings')} className="rail-btn">
472
+ <RailTooltip label="Star on GitHub" description="Support SwarmClaw with a GitHub star">
473
+ <a
474
+ href={GITHUB_REPO_URL}
475
+ target="_blank"
476
+ rel="noopener noreferrer"
477
+ className="rail-btn"
478
+ >
427
479
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
428
- <circle cx="12" cy="12" r="3" />
429
- <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" />
480
+ <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" />
430
481
  </svg>
431
- </button>
482
+ </a>
483
+ </RailTooltip>
484
+ )}
485
+ {railExpanded && <DaemonIndicator />}
486
+ {railExpanded ? (
487
+ <NotificationCenter variant="row" align="left" direction="up" />
488
+ ) : (
489
+ <RailTooltip label="Notifications" description="View system notifications">
490
+ <div className="rail-btn flex items-center justify-center">
491
+ <NotificationCenter align="left" direction="up" />
492
+ </div>
432
493
  </RailTooltip>
433
494
  )}
495
+ <NavItem view="settings" label="Settings" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('settings')}>
496
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
497
+ <circle cx="12" cy="12" r="3" />
498
+ <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" />
499
+ </svg>
500
+ </NavItem>
434
501
 
435
502
  {railExpanded ? (
436
503
  <button
@@ -439,13 +506,13 @@ export function AppLayout() {
439
506
  bg-transparent hover:bg-white/[0.04] border-none"
440
507
  style={{ fontFamily: 'inherit' }}
441
508
  >
442
- <Avatar user={currentUser!} size="sm" />
509
+ <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
443
510
  <span className="text-[13px] font-500 text-text-2 capitalize truncate">{currentUser}</span>
444
511
  </button>
445
512
  ) : (
446
- <RailTooltip label="Switch User" description="Sign in as a different user">
513
+ <RailTooltip label="Profile" description="Edit your profile">
447
514
  <button onClick={handleSwitchUser} className="mt-2 bg-transparent border-none cursor-pointer shrink-0">
448
- <Avatar user={currentUser!} size="sm" />
515
+ <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
449
516
  </button>
450
517
  </RailTooltip>
451
518
  )}
@@ -464,7 +531,7 @@ export function AppLayout() {
464
531
  {activeView === 'logs' || activeView === 'usage' || activeView === 'runs' ? null : activeView === 'memory' ? (
465
532
  <button
466
533
  onClick={() => useAppStore.getState().setMemorySheetOpen(true)}
467
- 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"
534
+ 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"
468
535
  style={{ fontFamily: 'inherit' }}
469
536
  >
470
537
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
@@ -475,7 +542,7 @@ export function AppLayout() {
475
542
  ) : (
476
543
  <button
477
544
  onClick={openNewSheet}
478
- 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"
545
+ 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"
479
546
  style={{ fontFamily: 'inherit' }}
480
547
  >
481
548
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
@@ -505,7 +572,7 @@ export function AppLayout() {
505
572
  </>
506
573
  )}
507
574
  {activeView === 'schedules' && <ScheduleList inSidebar />}
508
- {activeView === 'memory' && <MemoryList inSidebar />}
575
+ {activeView === 'memory' && <MemoryAgentList />}
509
576
  {activeView === 'tasks' && <TaskList inSidebar />}
510
577
  {activeView === 'secrets' && <SecretsList inSidebar />}
511
578
  {activeView === 'providers' && <ProviderList inSidebar />}
@@ -540,19 +607,19 @@ export function AppLayout() {
540
607
  <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" />
541
608
  </svg>
542
609
  </a>
543
- <button onClick={() => handleNavClick('settings')} className="rail-btn">
610
+ <button onClick={() => handleNavClick('settings')} className={`rail-btn ${activeView === 'settings' ? 'active' : ''}`}>
544
611
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
545
612
  <circle cx="12" cy="12" r="3" />
546
613
  <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" />
547
614
  </svg>
548
615
  </button>
549
616
  <button onClick={handleSwitchUser} className="bg-transparent border-none cursor-pointer shrink-0">
550
- <Avatar user={currentUser!} size="sm" />
617
+ <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
551
618
  </button>
552
619
  </div>
553
620
  {/* View selector tabs */}
554
621
  <div className="flex px-4 py-2 gap-1 shrink-0 flex-wrap">
555
- {(['agents', 'schedules', 'memory', 'tasks', 'secrets', 'providers', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins', 'usage', 'runs', 'logs'] as AppView[]).map((v) => (
622
+ {(['agents', 'chatrooms', 'schedules', 'memory', 'tasks', 'secrets', 'providers', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins', 'usage', 'runs', 'logs'] as AppView[]).map((v) => (
556
623
  <button
557
624
  key={v}
558
625
  onClick={() => setActiveView(v)}
@@ -573,7 +640,7 @@ export function AppLayout() {
573
640
  setSidebarOpen(false)
574
641
  openNewSheet()
575
642
  }}
576
- className="w-full py-3 rounded-[12px] border-none bg-[#6366F1] text-white text-[14px] font-600 cursor-pointer
643
+ className="w-full py-3 rounded-[12px] border-none bg-accent-bright text-white text-[14px] font-600 cursor-pointer
577
644
  hover:brightness-110 active:scale-[0.98] transition-all
578
645
  shadow-[0_2px_12px_rgba(99,102,241,0.15)]"
579
646
  style={{ fontFamily: 'inherit' }}
@@ -601,7 +668,7 @@ export function AppLayout() {
601
668
  </>
602
669
  )}
603
670
  {activeView === 'schedules' && <ScheduleList inSidebar />}
604
- {activeView === 'memory' && <MemoryList inSidebar onSelect={() => setSidebarOpen(false)} />}
671
+ {activeView === 'memory' && <MemoryAgentList />}
605
672
  {activeView === 'tasks' && <TaskList inSidebar />}
606
673
  {activeView === 'secrets' && <SecretsList inSidebar />}
607
674
  {activeView === 'providers' && <ProviderList inSidebar />}
@@ -611,7 +678,6 @@ export function AppLayout() {
611
678
  {activeView === 'mcp_servers' && <McpServerList />}
612
679
  {activeView === 'knowledge' && <KnowledgeList />}
613
680
  {activeView === 'plugins' && <PluginList inSidebar />}
614
- {activeView === 'projects' && <ProjectList />}
615
681
  {activeView === 'runs' && <RunList />}
616
682
  {activeView === 'logs' && <LogList />}
617
683
  </div>
@@ -620,10 +686,23 @@ export function AppLayout() {
620
686
 
621
687
  {/* Main content */}
622
688
  <ErrorBoundary>
623
- <div className="flex-1 flex flex-col h-full min-w-0 bg-bg">
689
+ <div className="flex-1 flex flex-col h-full min-h-0 min-w-0 bg-bg">
624
690
  {!isDesktop && <MobileHeader />}
625
- {activeView === 'agents' && hasSelectedSession ? (
626
- <ChatArea />
691
+ {activeView === 'home' ? (
692
+ <HomeView />
693
+ ) : activeView === 'agents' && hasSelectedSession ? (
694
+ <div className="flex-1 flex h-full min-h-0 min-w-0">
695
+ <div className="flex-1 min-h-0 min-w-0 overflow-hidden">
696
+ <ChatArea />
697
+ </div>
698
+ {hasCanvas && currentSessionId && (
699
+ <CanvasPanel
700
+ sessionId={currentSessionId}
701
+ agentName={canvasAgentName}
702
+ onClose={() => setCanvasDismissedFor(currentSessionId)}
703
+ />
704
+ )}
705
+ </div>
627
706
  ) : activeView === 'agents' ? (
628
707
  <div className="flex-1 flex flex-col">
629
708
  {!isDesktop ? (
@@ -644,11 +723,38 @@ export function AppLayout() {
644
723
  ) : activeView === 'tasks' && isDesktop ? (
645
724
  <TaskBoard />
646
725
  ) : activeView === 'memory' ? (
647
- <MemoryDetail />
726
+ <MemoryBrowser />
648
727
  ) : activeView === 'activity' ? (
649
728
  <ActivityFeed />
650
729
  ) : activeView === 'usage' ? (
651
730
  <MetricsDashboard />
731
+ ) : activeView === 'chatrooms' ? (
732
+ <div className="flex-1 flex h-full min-w-0">
733
+ <div className="w-[280px] shrink-0 border-r border-white/[0.06] flex flex-col">
734
+ <div className="flex items-center px-4 pt-4 pb-2 shrink-0">
735
+ <h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] flex-1">Chatrooms</h2>
736
+ <button
737
+ onClick={() => { useChatroomStore.getState().setEditingChatroomId(null); useChatroomStore.getState().setChatroomSheetOpen(true) }}
738
+ 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"
739
+ style={{ fontFamily: 'inherit' }}
740
+ >
741
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
742
+ <line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
743
+ </svg>
744
+ New
745
+ </button>
746
+ </div>
747
+ <ChatroomList />
748
+ </div>
749
+ <ChatroomView />
750
+ </div>
751
+ ) : activeView === 'projects' ? (
752
+ <div className="flex-1 flex h-full min-w-0">
753
+ <div className="w-[280px] shrink-0 border-r border-white/[0.06] flex flex-col">
754
+ <ProjectList />
755
+ </div>
756
+ <ProjectDetail />
757
+ </div>
652
758
  ) : activeView === 'settings' ? (
653
759
  <SettingsPage />
654
760
  ) : !sidebarOpen && FULL_WIDTH_VIEWS.has(activeView) ? (
@@ -660,13 +766,13 @@ export function AppLayout() {
660
766
  {activeView !== 'runs' && activeView !== 'logs' && (
661
767
  <button
662
768
  onClick={openNewSheet}
663
- 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"
769
+ 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"
664
770
  style={{ fontFamily: 'inherit' }}
665
771
  >
666
772
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
667
773
  <line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
668
774
  </svg>
669
- {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'}
775
+ {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'}
670
776
  </button>
671
777
  )}
672
778
  </div>
@@ -679,7 +785,6 @@ export function AppLayout() {
679
785
  {activeView === 'mcp_servers' && <McpServerList />}
680
786
  {activeView === 'knowledge' && <KnowledgeList />}
681
787
  {activeView === 'plugins' && <PluginList />}
682
- {activeView === 'projects' && <ProjectList />}
683
788
  {activeView === 'runs' && <RunList />}
684
789
  {activeView === 'logs' && <LogList />}
685
790
  </div>
@@ -690,6 +795,8 @@ export function AppLayout() {
690
795
  </ErrorBoundary>
691
796
 
692
797
  <SearchDialog />
798
+ <AgentSwitchDialog />
799
+ <KeyboardShortcutsDialog />
693
800
  <AgentSheet />
694
801
  <ScheduleSheet />
695
802
  <MemorySheet />
@@ -698,32 +805,14 @@ export function AppLayout() {
698
805
  <ProviderSheet />
699
806
  <SkillSheet />
700
807
  <ConnectorSheet />
808
+ <ChatroomSheet />
701
809
  <WebhookSheet />
702
810
  <McpServerSheet />
703
811
  <KnowledgeSheet />
704
812
  <PluginSheet />
705
813
  <ProjectSheet />
814
+ <ProfileSheet open={profileSheetOpen} onClose={() => setProfileSheetOpen(false)} />
706
815
 
707
- <Dialog open={shortcutsOpen} onOpenChange={setShortcutsOpen}>
708
- <DialogContent className="sm:max-w-[380px] bg-raised border-white/[0.08]">
709
- <DialogHeader>
710
- <DialogTitle className="text-text">Keyboard Shortcuts</DialogTitle>
711
- </DialogHeader>
712
- <div className="space-y-3 py-2">
713
- {([
714
- ['Enter', 'Send message'],
715
- ['Shift + Enter', 'New line'],
716
- ['Ctrl + F', 'Search in chat'],
717
- ['Ctrl + /', 'Show shortcuts'],
718
- ] as const).map(([keys, desc]) => (
719
- <div key={keys} className="flex items-center justify-between">
720
- <span className="text-[13px] text-text-2">{desc}</span>
721
- <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>
722
- </div>
723
- ))}
724
- </div>
725
- </DialogContent>
726
- </Dialog>
727
816
  </div>
728
817
  )
729
818
  }
@@ -762,7 +851,7 @@ class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boole
762
851
  </p>
763
852
  <button
764
853
  onClick={() => window.location.reload()}
765
- 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
854
+ 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
766
855
  hover:brightness-110 active:scale-[0.97] transition-all shadow-[0_4px_16px_rgba(99,102,241,0.2)]"
767
856
  style={{ fontFamily: 'inherit' }}
768
857
  >
@@ -782,7 +871,9 @@ class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boole
782
871
  }
783
872
 
784
873
  const VIEW_DESCRIPTIONS: Record<AppView, string> = {
874
+ home: 'Dashboard overview',
785
875
  agents: 'Chat with & configure your AI agents',
876
+ chatrooms: 'Multi-agent collaborative chatrooms',
786
877
  schedules: 'Automated task schedules',
787
878
  memory: 'Long-term agent memory store',
788
879
  tasks: 'Task board for orchestrator jobs',
@@ -803,12 +894,18 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
803
894
  }
804
895
 
805
896
  const FULL_WIDTH_VIEWS = new Set<AppView>([
806
- 'schedules', 'secrets', 'providers', 'skills',
897
+ 'home', 'chatrooms', 'schedules', 'secrets', 'providers', 'skills',
807
898
  'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins',
808
- 'usage', 'runs', 'logs', 'settings', 'projects', 'activity',
899
+ 'usage', 'runs', 'logs', 'settings', 'activity', 'projects',
809
900
  ])
810
901
 
811
- const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; title: string; description: string; features: string[] }> = {
902
+ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: string; title: string; description: string; features: string[] }> = {
903
+ chatrooms: {
904
+ icon: 'message-square',
905
+ title: 'Chatrooms',
906
+ description: 'Multi-agent chatrooms for collaborative conversations. Add agents and use @mentions to trigger responses.',
907
+ 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'],
908
+ },
812
909
  schedules: {
813
910
  icon: 'clock',
814
911
  title: 'Schedules',
@@ -914,8 +1011,8 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; titl
914
1011
  }
915
1012
 
916
1013
  function ViewEmptyState({ view }: { view: AppView }) {
917
- if (view === 'agents') return null
918
- const config = VIEW_EMPTY_STATES[view as Exclude<AppView, 'agents'>]
1014
+ if (view === 'agents' || view === 'home') return null
1015
+ const config = VIEW_EMPTY_STATES[view as Exclude<AppView, 'agents' | 'home'>]
919
1016
  if (!config) return null
920
1017
 
921
1018
  return (
@@ -1102,7 +1199,7 @@ function DesktopEmptyState({ userName }: { userName: string | null }) {
1102
1199
  </p>
1103
1200
  <button
1104
1201
  onClick={() => setNewSessionOpen(true)}
1105
- 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
1202
+ 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
1106
1203
  cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
1107
1204
  shadow-[0_6px_28px_rgba(99,102,241,0.3)]"
1108
1205
  style={{ fontFamily: 'inherit' }}
@@ -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
@@ -162,7 +162,7 @@ export function LogList() {
162
162
  <button
163
163
  key={i}
164
164
  onClick={() => { setLevelFilter(f.levels); setSearch(f.search) }}
165
- className="group flex items-center gap-1 px-2 py-1 rounded-[6px] text-[10px] font-600 cursor-pointer transition-all border-none bg-accent-soft text-accent-bright hover:bg-[#6366F1]/15"
165
+ className="group flex items-center gap-1 px-2 py-1 rounded-[6px] text-[10px] font-600 cursor-pointer transition-all border-none bg-accent-soft text-accent-bright hover:bg-accent-bright/15"
166
166
  >
167
167
  {f.name}
168
168
  <span
@@ -337,7 +337,7 @@ export function LogList() {
337
337
  <button
338
338
  onClick={handleCreateTask}
339
339
  disabled={creatingTask}
340
- className="px-5 py-3 rounded-[14px] border-none bg-[#6366F1] text-white text-[14px] font-600
340
+ className="px-5 py-3 rounded-[14px] border-none bg-accent-bright text-white text-[14px] font-600
341
341
  cursor-pointer active:scale-[0.97] disabled:opacity-40 transition-all
342
342
  shadow-[0_4px_20px_rgba(99,102,241,0.25)] hover:brightness-110 shrink-0"
343
343
  style={{ fontFamily: 'inherit' }}