@swarmclawai/swarmclaw 0.4.0 → 0.5.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.
- package/README.md +21 -4
- package/bin/server-cmd.js +28 -19
- package/next.config.ts +13 -0
- package/package.json +3 -1
- package/src/app/api/agents/[id]/route.ts +39 -22
- package/src/app/api/agents/[id]/thread/route.ts +2 -2
- package/src/app/api/agents/route.ts +3 -2
- package/src/app/api/agents/trash/route.ts +44 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/connectors/[id]/route.ts +17 -7
- package/src/app/api/connectors/[id]/webhook/route.ts +103 -0
- package/src/app/api/connectors/route.ts +6 -3
- package/src/app/api/credentials/[id]/route.ts +2 -1
- package/src/app/api/credentials/route.ts +2 -2
- package/src/app/api/documents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +8 -0
- package/src/app/api/knowledge/[id]/route.ts +5 -4
- package/src/app/api/knowledge/upload/route.ts +2 -2
- package/src/app/api/mcp-servers/[id]/route.ts +11 -14
- package/src/app/api/mcp-servers/[id]/test/route.ts +2 -1
- package/src/app/api/mcp-servers/[id]/tools/route.ts +2 -1
- package/src/app/api/mcp-servers/route.ts +2 -2
- package/src/app/api/memory/[id]/route.ts +9 -8
- package/src/app/api/memory/route.ts +2 -2
- package/src/app/api/memory-images/[filename]/route.ts +2 -1
- package/src/app/api/openclaw/agent-files/route.ts +57 -0
- package/src/app/api/openclaw/approvals/route.ts +46 -0
- package/src/app/api/openclaw/config-sync/route.ts +33 -0
- package/src/app/api/openclaw/cron/route.ts +52 -0
- package/src/app/api/openclaw/directory/route.ts +27 -0
- package/src/app/api/openclaw/discover/route.ts +62 -0
- package/src/app/api/openclaw/dotenv-keys/route.ts +18 -0
- package/src/app/api/openclaw/exec-config/route.ts +41 -0
- package/src/app/api/openclaw/gateway/route.ts +72 -0
- package/src/app/api/openclaw/history/route.ts +109 -0
- package/src/app/api/openclaw/media/route.ts +53 -0
- package/src/app/api/openclaw/models/route.ts +12 -0
- package/src/app/api/openclaw/permissions/route.ts +39 -0
- package/src/app/api/openclaw/sandbox-env/route.ts +69 -0
- package/src/app/api/openclaw/skills/install/route.ts +32 -0
- package/src/app/api/openclaw/skills/remove/route.ts +24 -0
- package/src/app/api/openclaw/skills/route.ts +82 -0
- package/src/app/api/openclaw/sync/route.ts +31 -0
- package/src/app/api/orchestrator/run/route.ts +2 -2
- package/src/app/api/projects/[id]/route.ts +55 -0
- package/src/app/api/projects/route.ts +27 -0
- package/src/app/api/providers/[id]/models/route.ts +2 -1
- package/src/app/api/providers/[id]/route.ts +13 -15
- package/src/app/api/providers/route.ts +2 -2
- package/src/app/api/schedules/[id]/route.ts +16 -18
- package/src/app/api/schedules/[id]/run/route.ts +4 -3
- package/src/app/api/schedules/route.ts +2 -2
- package/src/app/api/secrets/[id]/route.ts +16 -17
- package/src/app/api/secrets/route.ts +2 -2
- package/src/app/api/sessions/[id]/clear/route.ts +2 -1
- package/src/app/api/sessions/[id]/deploy/route.ts +2 -1
- package/src/app/api/sessions/[id]/devserver/route.ts +2 -1
- package/src/app/api/sessions/[id]/edit-resend/route.ts +22 -0
- package/src/app/api/sessions/[id]/fork/route.ts +44 -0
- package/src/app/api/sessions/[id]/messages/route.ts +20 -2
- package/src/app/api/sessions/[id]/retry/route.ts +2 -1
- package/src/app/api/sessions/[id]/route.ts +14 -4
- package/src/app/api/sessions/route.ts +8 -4
- package/src/app/api/skills/[id]/route.ts +23 -21
- package/src/app/api/skills/import/route.ts +2 -2
- package/src/app/api/skills/route.ts +2 -2
- package/src/app/api/tasks/[id]/approve/route.ts +2 -1
- package/src/app/api/tasks/[id]/route.ts +6 -5
- package/src/app/api/tasks/route.ts +2 -2
- package/src/app/api/tts/stream/route.ts +48 -0
- package/src/app/api/upload/route.ts +2 -2
- package/src/app/api/uploads/[filename]/route.ts +4 -1
- package/src/app/api/webhooks/[id]/route.ts +29 -31
- package/src/app/api/webhooks/route.ts +2 -2
- package/src/app/globals.css +14 -0
- package/src/app/layout.tsx +5 -20
- package/src/app/page.tsx +3 -24
- package/src/cli/index.js +60 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/spec.js +42 -0
- package/src/components/agents/agent-avatar.tsx +45 -0
- package/src/components/agents/agent-card.tsx +19 -5
- package/src/components/agents/agent-chat-list.tsx +31 -24
- package/src/components/agents/agent-files-editor.tsx +185 -0
- package/src/components/agents/agent-list.tsx +84 -3
- package/src/components/agents/agent-sheet.tsx +147 -14
- package/src/components/agents/cron-job-form.tsx +137 -0
- package/src/components/agents/exec-config-panel.tsx +147 -0
- package/src/components/agents/inspector-panel.tsx +310 -0
- package/src/components/agents/openclaw-skills-panel.tsx +230 -0
- package/src/components/agents/permission-preset-selector.tsx +79 -0
- package/src/components/agents/personality-builder.tsx +111 -0
- package/src/components/agents/sandbox-env-panel.tsx +72 -0
- package/src/components/agents/skill-install-dialog.tsx +102 -0
- package/src/components/agents/trash-list.tsx +109 -0
- package/src/components/chat/chat-area.tsx +41 -6
- package/src/components/chat/chat-header.tsx +305 -29
- package/src/components/chat/chat-preview-panel.tsx +113 -0
- package/src/components/chat/exec-approval-card.tsx +89 -0
- package/src/components/chat/message-bubble.tsx +218 -36
- package/src/components/chat/message-list.tsx +135 -31
- package/src/components/chat/streaming-bubble.tsx +59 -10
- package/src/components/chat/suggestions-bar.tsx +74 -0
- package/src/components/chat/thinking-indicator.tsx +20 -6
- package/src/components/chat/tool-call-bubble.tsx +98 -19
- package/src/components/chat/tool-request-banner.tsx +20 -2
- package/src/components/chat/trace-block.tsx +103 -0
- package/src/components/chat/voice-overlay.tsx +80 -0
- package/src/components/connectors/connector-list.tsx +6 -2
- package/src/components/connectors/connector-sheet.tsx +31 -7
- package/src/components/layout/app-layout.tsx +47 -25
- package/src/components/projects/project-list.tsx +123 -0
- package/src/components/projects/project-sheet.tsx +135 -0
- package/src/components/schedules/schedule-list.tsx +3 -1
- package/src/components/sessions/new-session-sheet.tsx +6 -6
- package/src/components/sessions/session-card.tsx +1 -1
- package/src/components/sessions/session-list.tsx +7 -7
- package/src/components/settings/gateway-connection-panel.tsx +278 -0
- package/src/components/shared/avatar.tsx +13 -2
- package/src/components/shared/connector-platform-icon.tsx +4 -0
- package/src/components/shared/settings/section-heartbeat.tsx +1 -1
- package/src/components/shared/settings/section-orchestrator.tsx +1 -2
- package/src/components/shared/settings/section-web-search.tsx +56 -0
- package/src/components/shared/settings/settings-page.tsx +74 -0
- package/src/components/skills/skill-list.tsx +2 -1
- package/src/components/tasks/task-board.tsx +1 -1
- package/src/components/tasks/task-list.tsx +5 -2
- package/src/components/tasks/task-sheet.tsx +12 -12
- package/src/hooks/use-continuous-speech.ts +181 -0
- package/src/hooks/use-openclaw-gateway.ts +63 -0
- package/src/hooks/use-view-router.ts +52 -0
- package/src/hooks/use-voice-conversation.ts +80 -0
- package/src/lib/id.ts +6 -0
- package/src/lib/notification-sounds.ts +58 -0
- package/src/lib/personality-parser.ts +97 -0
- package/src/lib/projects.ts +13 -0
- package/src/lib/provider-sets.ts +5 -0
- package/src/lib/providers/anthropic.ts +14 -1
- package/src/lib/providers/index.ts +6 -0
- package/src/lib/providers/ollama.ts +9 -1
- package/src/lib/providers/openai.ts +9 -1
- package/src/lib/providers/openclaw.ts +28 -2
- package/src/lib/runtime-loop.ts +2 -2
- package/src/lib/server/api-routes.test.ts +5 -6
- package/src/lib/server/build-llm.ts +17 -4
- package/src/lib/server/chat-execution.ts +82 -6
- package/src/lib/server/collection-helpers.ts +54 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +217 -0
- package/src/lib/server/connectors/bluebubbles.ts +360 -0
- package/src/lib/server/connectors/connector-routing.test.ts +1 -1
- package/src/lib/server/connectors/googlechat.ts +51 -8
- package/src/lib/server/connectors/manager.ts +424 -13
- package/src/lib/server/connectors/media.ts +2 -2
- package/src/lib/server/connectors/openclaw.ts +65 -0
- package/src/lib/server/connectors/pairing.test.ts +99 -0
- package/src/lib/server/connectors/pairing.ts +256 -0
- package/src/lib/server/connectors/signal.ts +1 -0
- package/src/lib/server/connectors/teams.ts +5 -5
- package/src/lib/server/connectors/types.ts +10 -0
- package/src/lib/server/daemon-state.ts +11 -0
- package/src/lib/server/execution-log.ts +3 -3
- package/src/lib/server/heartbeat-service.ts +1 -1
- package/src/lib/server/knowledge-db.test.ts +2 -33
- package/src/lib/server/main-agent-loop.ts +8 -9
- package/src/lib/server/main-session.ts +21 -0
- package/src/lib/server/memory-db.ts +6 -6
- package/src/lib/server/openclaw-approvals.ts +105 -0
- package/src/lib/server/openclaw-config-sync.ts +107 -0
- package/src/lib/server/openclaw-exec-config.ts +52 -0
- package/src/lib/server/openclaw-gateway.ts +291 -0
- package/src/lib/server/openclaw-history-merge.ts +36 -0
- package/src/lib/server/openclaw-models.ts +56 -0
- package/src/lib/server/openclaw-permission-presets.ts +64 -0
- package/src/lib/server/openclaw-sync.ts +497 -0
- package/src/lib/server/orchestrator-lg.ts +30 -9
- package/src/lib/server/orchestrator.ts +4 -4
- package/src/lib/server/process-manager.ts +2 -2
- package/src/lib/server/queue.ts +24 -11
- package/src/lib/server/scheduler.ts +2 -2
- package/src/lib/server/session-mailbox.ts +2 -2
- package/src/lib/server/session-run-manager.ts +2 -2
- package/src/lib/server/session-tools/connector.ts +53 -6
- package/src/lib/server/session-tools/crud.ts +3 -3
- package/src/lib/server/session-tools/delegate.ts +22 -6
- package/src/lib/server/session-tools/file.ts +192 -19
- package/src/lib/server/session-tools/index.ts +4 -2
- package/src/lib/server/session-tools/memory.ts +2 -2
- package/src/lib/server/session-tools/openclaw-nodes.ts +112 -0
- package/src/lib/server/session-tools/sandbox.ts +33 -0
- package/src/lib/server/session-tools/search-providers.ts +277 -0
- package/src/lib/server/session-tools/session-info.ts +2 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +2 -2
- package/src/lib/server/session-tools/shell.ts +1 -1
- package/src/lib/server/session-tools/web.ts +53 -72
- package/src/lib/server/storage.ts +74 -11
- package/src/lib/server/stream-agent-chat.ts +53 -4
- package/src/lib/server/suggestions.ts +20 -0
- package/src/lib/server/task-result.test.ts +44 -0
- package/src/lib/server/task-result.ts +14 -0
- package/src/lib/server/ws-hub.ts +14 -0
- package/src/lib/tool-definitions.ts +5 -3
- package/src/lib/tts-stream.ts +130 -0
- package/src/lib/view-routes.ts +28 -0
- package/src/proxy.ts +3 -0
- package/src/stores/use-app-store.ts +80 -1
- package/src/stores/use-approval-store.ts +78 -0
- package/src/stores/use-chat-store.ts +162 -6
- package/src/types/index.ts +154 -3
- package/tsconfig.json +13 -4
|
@@ -6,7 +6,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/u
|
|
|
6
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
7
7
|
import { useMediaQuery } from '@/hooks/use-media-query'
|
|
8
8
|
import { Avatar } from '@/components/shared/avatar'
|
|
9
|
-
import {
|
|
9
|
+
import { SettingsPage } from '@/components/shared/settings/settings-page'
|
|
10
10
|
import { AgentList } from '@/components/agents/agent-list'
|
|
11
11
|
import { AgentChatList } from '@/components/agents/agent-chat-list'
|
|
12
12
|
import { AgentSheet } from '@/components/agents/agent-sheet'
|
|
@@ -37,6 +37,8 @@ import { PluginList } from '@/components/plugins/plugin-list'
|
|
|
37
37
|
import { PluginSheet } from '@/components/plugins/plugin-sheet'
|
|
38
38
|
import { UsageList } from '@/components/usage/usage-list'
|
|
39
39
|
import { RunList } from '@/components/runs/run-list'
|
|
40
|
+
import { ProjectList } from '@/components/projects/project-list'
|
|
41
|
+
import { ProjectSheet } from '@/components/projects/project-sheet'
|
|
40
42
|
import { NetworkBanner } from './network-banner'
|
|
41
43
|
import { UpdateBanner } from './update-banner'
|
|
42
44
|
import { MobileHeader } from './mobile-header'
|
|
@@ -53,7 +55,6 @@ export function AppLayout() {
|
|
|
53
55
|
const currentSessionId = useAppStore((s) => s.currentSessionId)
|
|
54
56
|
const sidebarOpen = useAppStore((s) => s.sidebarOpen)
|
|
55
57
|
const setSidebarOpen = useAppStore((s) => s.setSidebarOpen)
|
|
56
|
-
const setSettingsOpen = useAppStore((s) => s.setSettingsOpen)
|
|
57
58
|
const setUser = useAppStore((s) => s.setUser)
|
|
58
59
|
const setCurrentSession = useAppStore((s) => s.setCurrentSession)
|
|
59
60
|
const activeView = useAppStore((s) => s.activeView)
|
|
@@ -69,6 +70,7 @@ export function AppLayout() {
|
|
|
69
70
|
const setMcpServerSheetOpen = useAppStore((s) => s.setMcpServerSheetOpen)
|
|
70
71
|
const setKnowledgeSheetOpen = useAppStore((s) => s.setKnowledgeSheetOpen)
|
|
71
72
|
const setPluginSheetOpen = useAppStore((s) => s.setPluginSheetOpen)
|
|
73
|
+
const setProjectSheetOpen = useAppStore((s) => s.setProjectSheetOpen)
|
|
72
74
|
const tasks = useAppStore((s) => s.tasks)
|
|
73
75
|
const isDesktop = useMediaQuery('(min-width: 768px)')
|
|
74
76
|
const hasSelectedSession = !!(currentSessionId && sessions[currentSessionId])
|
|
@@ -128,6 +130,7 @@ export function AppLayout() {
|
|
|
128
130
|
else if (activeView === 'mcp_servers') setMcpServerSheetOpen(true)
|
|
129
131
|
else if (activeView === 'knowledge') setKnowledgeSheetOpen(true)
|
|
130
132
|
else if (activeView === 'plugins') setPluginSheetOpen(true)
|
|
133
|
+
else if (activeView === 'projects') setProjectSheetOpen(true)
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
const handleNavClick = (view: AppView) => {
|
|
@@ -145,15 +148,11 @@ export function AppLayout() {
|
|
|
145
148
|
const agents = useAppStore((s) => s.agents)
|
|
146
149
|
const currentAgentId = useAppStore((s) => s.currentAgentId)
|
|
147
150
|
const setCurrentAgent = useAppStore((s) => s.setCurrentAgent)
|
|
148
|
-
const mainSession = Object.values(sessions).find((s) => s.name === '__main__' && s.user === currentUser)
|
|
149
|
-
|
|
150
151
|
const goToMainChat = async () => {
|
|
151
152
|
// Navigate to default agent's chat thread
|
|
152
153
|
const defaultAgent = agents['default'] || Object.values(agents)[0]
|
|
153
154
|
if (defaultAgent) {
|
|
154
155
|
await setCurrentAgent(defaultAgent.id)
|
|
155
|
-
} else if (mainSession) {
|
|
156
|
-
setCurrentSession(mainSession.id)
|
|
157
156
|
}
|
|
158
157
|
setActiveView('agents')
|
|
159
158
|
setSidebarOpen(false)
|
|
@@ -245,6 +244,11 @@ export function AppLayout() {
|
|
|
245
244
|
<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
245
|
</svg>
|
|
247
246
|
</NavItem>
|
|
247
|
+
<NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
|
|
248
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
249
|
+
<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" />
|
|
250
|
+
</svg>
|
|
251
|
+
</NavItem>
|
|
248
252
|
<NavItem view="schedules" label="Schedules" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('schedules')}>
|
|
249
253
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
250
254
|
<circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
|
|
@@ -355,7 +359,7 @@ export function AppLayout() {
|
|
|
355
359
|
{railExpanded && <DaemonIndicator />}
|
|
356
360
|
{railExpanded ? (
|
|
357
361
|
<button
|
|
358
|
-
onClick={() =>
|
|
362
|
+
onClick={() => handleNavClick('settings')}
|
|
359
363
|
className="w-full flex items-center gap-2.5 px-3 py-2 rounded-[10px] text-[13px] font-500 cursor-pointer transition-all
|
|
360
364
|
bg-transparent text-text-3 hover:text-text hover:bg-white/[0.04] border-none"
|
|
361
365
|
style={{ fontFamily: 'inherit' }}
|
|
@@ -368,7 +372,7 @@ export function AppLayout() {
|
|
|
368
372
|
</button>
|
|
369
373
|
) : (
|
|
370
374
|
<RailTooltip label="Settings" description="API keys, providers & app config">
|
|
371
|
-
<button onClick={() =>
|
|
375
|
+
<button onClick={() => handleNavClick('settings')} className="rail-btn">
|
|
372
376
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
373
377
|
<circle cx="12" cy="12" r="3" />
|
|
374
378
|
<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" />
|
|
@@ -486,7 +490,7 @@ export function AppLayout() {
|
|
|
486
490
|
<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" />
|
|
487
491
|
</svg>
|
|
488
492
|
</a>
|
|
489
|
-
<button onClick={() =>
|
|
493
|
+
<button onClick={() => handleNavClick('settings')} className="rail-btn">
|
|
490
494
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
491
495
|
<circle cx="12" cy="12" r="3" />
|
|
492
496
|
<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" />
|
|
@@ -512,7 +516,7 @@ export function AppLayout() {
|
|
|
512
516
|
</button>
|
|
513
517
|
))}
|
|
514
518
|
</div>
|
|
515
|
-
{activeView !== 'logs' && activeView !== 'usage' && activeView !== 'runs' && (
|
|
519
|
+
{activeView !== 'logs' && activeView !== 'usage' && activeView !== 'runs' && activeView !== 'settings' && (
|
|
516
520
|
<div className="px-4 py-2.5 shrink-0">
|
|
517
521
|
<button
|
|
518
522
|
onClick={() => {
|
|
@@ -524,7 +528,7 @@ export function AppLayout() {
|
|
|
524
528
|
shadow-[0_2px_12px_rgba(99,102,241,0.15)]"
|
|
525
529
|
style={{ fontFamily: 'inherit' }}
|
|
526
530
|
>
|
|
527
|
-
+ New {activeView === 'agents' ? 'Agent' : activeView === 'schedules' ? 'Schedule' : activeView === 'tasks' ? 'Task' : 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' : 'Entry'}
|
|
531
|
+
+ New {activeView === 'agents' ? 'Agent' : activeView === 'schedules' ? 'Schedule' : activeView === 'tasks' ? 'Task' : 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' : 'Entry'}
|
|
528
532
|
</button>
|
|
529
533
|
</div>
|
|
530
534
|
)}
|
|
@@ -557,6 +561,7 @@ export function AppLayout() {
|
|
|
557
561
|
{activeView === 'mcp_servers' && <McpServerList />}
|
|
558
562
|
{activeView === 'knowledge' && <KnowledgeList />}
|
|
559
563
|
{activeView === 'plugins' && <PluginList inSidebar />}
|
|
564
|
+
{activeView === 'projects' && <ProjectList />}
|
|
560
565
|
{activeView === 'usage' && <UsageList />}
|
|
561
566
|
{activeView === 'runs' && <RunList />}
|
|
562
567
|
{activeView === 'logs' && <LogList />}
|
|
@@ -591,6 +596,8 @@ export function AppLayout() {
|
|
|
591
596
|
<TaskBoard />
|
|
592
597
|
) : activeView === 'memory' ? (
|
|
593
598
|
<MemoryDetail />
|
|
599
|
+
) : activeView === 'settings' ? (
|
|
600
|
+
<SettingsPage />
|
|
594
601
|
) : !sidebarOpen && FULL_WIDTH_VIEWS.has(activeView) ? (
|
|
595
602
|
<div className="flex-1 flex flex-col h-full">
|
|
596
603
|
<div className="flex items-center px-6 pt-5 pb-3 shrink-0">
|
|
@@ -606,7 +613,7 @@ export function AppLayout() {
|
|
|
606
613
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
|
607
614
|
<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
|
|
608
615
|
</svg>
|
|
609
|
-
{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'}
|
|
616
|
+
{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'}
|
|
610
617
|
</button>
|
|
611
618
|
)}
|
|
612
619
|
</div>
|
|
@@ -619,6 +626,7 @@ export function AppLayout() {
|
|
|
619
626
|
{activeView === 'mcp_servers' && <McpServerList />}
|
|
620
627
|
{activeView === 'knowledge' && <KnowledgeList />}
|
|
621
628
|
{activeView === 'plugins' && <PluginList />}
|
|
629
|
+
{activeView === 'projects' && <ProjectList />}
|
|
622
630
|
{activeView === 'usage' && <UsageList />}
|
|
623
631
|
{activeView === 'runs' && <RunList />}
|
|
624
632
|
{activeView === 'logs' && <LogList />}
|
|
@@ -629,7 +637,6 @@ export function AppLayout() {
|
|
|
629
637
|
</div>
|
|
630
638
|
</ErrorBoundary>
|
|
631
639
|
|
|
632
|
-
<SettingsSheet />
|
|
633
640
|
<AgentSheet />
|
|
634
641
|
<ScheduleSheet />
|
|
635
642
|
<MemorySheet />
|
|
@@ -642,6 +649,7 @@ export function AppLayout() {
|
|
|
642
649
|
<McpServerSheet />
|
|
643
650
|
<KnowledgeSheet />
|
|
644
651
|
<PluginSheet />
|
|
652
|
+
<ProjectSheet />
|
|
645
653
|
|
|
646
654
|
<Dialog open={shortcutsOpen} onOpenChange={setShortcutsOpen}>
|
|
647
655
|
<DialogContent className="sm:max-w-[380px] bg-raised border-white/[0.08]">
|
|
@@ -735,13 +743,15 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
|
|
|
735
743
|
logs: 'Application logs & error tracking',
|
|
736
744
|
plugins: 'Extend agent capabilities with custom plugins',
|
|
737
745
|
usage: 'Token usage analytics & cost tracking',
|
|
738
|
-
runs: 'Live
|
|
746
|
+
runs: 'Live run monitoring & history',
|
|
747
|
+
settings: 'Manage providers, API keys & orchestrator engine',
|
|
748
|
+
projects: 'Group agents, tasks & schedules into projects',
|
|
739
749
|
}
|
|
740
750
|
|
|
741
751
|
const FULL_WIDTH_VIEWS = new Set<AppView>([
|
|
742
752
|
'schedules', 'secrets', 'providers', 'skills',
|
|
743
753
|
'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins',
|
|
744
|
-
'usage', 'runs', 'logs',
|
|
754
|
+
'usage', 'runs', 'logs', 'settings', 'projects',
|
|
745
755
|
])
|
|
746
756
|
|
|
747
757
|
const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; title: string; description: string; features: string[] }> = {
|
|
@@ -754,14 +764,14 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; titl
|
|
|
754
764
|
memory: {
|
|
755
765
|
icon: 'database',
|
|
756
766
|
title: 'Memory',
|
|
757
|
-
description: 'Long-term memory store for AI agents. Orchestrators can store and retrieve knowledge across
|
|
758
|
-
features: ['Agents store findings and learnings automatically', 'Full-text search across all stored memories', 'Organized by categories and agents', 'Persists across
|
|
767
|
+
description: 'Long-term memory store for AI agents. Orchestrators can store and retrieve knowledge across conversations.',
|
|
768
|
+
features: ['Agents store findings and learnings automatically', 'Full-text search across all stored memories', 'Organized by categories and agents', 'Persists across conversations for continuity'],
|
|
759
769
|
},
|
|
760
770
|
tasks: {
|
|
761
771
|
icon: 'clipboard',
|
|
762
772
|
title: 'Task Board',
|
|
763
773
|
description: 'A Trello-style board for managing orchestrator jobs. Create tasks, assign them to orchestrators, and track progress.',
|
|
764
|
-
features: ['Kanban columns: Backlog, Queued, Running, Completed, Failed', 'Assign tasks to specific orchestrator agents', 'Sequential queue ensures orchestrators don\'t conflict', 'View results and
|
|
774
|
+
features: ['Kanban columns: Backlog, Queued, Running, Completed, Failed', 'Assign tasks to specific orchestrator agents', 'Sequential queue ensures orchestrators don\'t conflict', 'View results and logs for completed tasks'],
|
|
765
775
|
},
|
|
766
776
|
secrets: {
|
|
767
777
|
icon: 'lock',
|
|
@@ -785,7 +795,7 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; titl
|
|
|
785
795
|
icon: 'link',
|
|
786
796
|
title: 'Connectors',
|
|
787
797
|
description: 'Bridge chat platforms to your AI agents. Receive messages from Discord, Telegram, Slack, or WhatsApp and route them to agents.',
|
|
788
|
-
features: ['Connect Discord, Telegram, Slack, or WhatsApp bots', 'Route incoming messages to any agent', 'Each platform channel gets its own
|
|
798
|
+
features: ['Connect Discord, Telegram, Slack, or WhatsApp bots', 'Route incoming messages to any agent', 'Each platform channel gets its own chat thread', 'Start and stop connectors from the UI'],
|
|
789
799
|
},
|
|
790
800
|
webhooks: {
|
|
791
801
|
icon: 'webhook',
|
|
@@ -796,7 +806,7 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; titl
|
|
|
796
806
|
mcp_servers: {
|
|
797
807
|
icon: 'server',
|
|
798
808
|
title: 'MCP Servers',
|
|
799
|
-
description: 'Connect agents to external MCP (Model Context Protocol) servers, injecting their tools into
|
|
809
|
+
description: 'Connect agents to external MCP (Model Context Protocol) servers, injecting their tools into agent chats.',
|
|
800
810
|
features: ['Configure stdio, SSE, or streamable HTTP transports', 'Test connections and discover available tools', 'Assign MCP servers to specific agents', 'Tools appear alongside built-in tools in chat'],
|
|
801
811
|
},
|
|
802
812
|
knowledge: {
|
|
@@ -820,15 +830,27 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents'>, { icon: string; titl
|
|
|
820
830
|
usage: {
|
|
821
831
|
icon: 'bar-chart',
|
|
822
832
|
title: 'Usage',
|
|
823
|
-
description: 'Track token usage and costs across all providers and
|
|
824
|
-
features: ['Per-provider cost breakdown', 'Token usage over time', '
|
|
833
|
+
description: 'Track token usage and costs across all providers and agents.',
|
|
834
|
+
features: ['Per-provider cost breakdown', 'Token usage over time', 'Per-agent cost tracking', 'Export usage data'],
|
|
825
835
|
},
|
|
826
836
|
runs: {
|
|
827
837
|
icon: 'activity',
|
|
828
838
|
title: 'Runs',
|
|
829
|
-
description: 'View the
|
|
839
|
+
description: 'View the run queue and execution history.',
|
|
830
840
|
features: ['Monitor queued and running tasks', 'View run results and errors', 'Cancel pending runs', 'Automatic retry tracking'],
|
|
831
841
|
},
|
|
842
|
+
settings: {
|
|
843
|
+
icon: 'settings',
|
|
844
|
+
title: 'Settings',
|
|
845
|
+
description: 'Manage providers, API keys & orchestrator engine.',
|
|
846
|
+
features: ['Configure LLM providers', 'Manage API credentials', 'Tune orchestrator settings', 'Set up voice & embedding'],
|
|
847
|
+
},
|
|
848
|
+
projects: {
|
|
849
|
+
icon: 'folder',
|
|
850
|
+
title: 'Projects',
|
|
851
|
+
description: 'Organize your work into projects. Group agents, tasks, and schedules under a common scope.',
|
|
852
|
+
features: ['Create named projects with color badges', 'Assign agents and tasks to projects', 'Filter sidebar views by project', 'Global view when no filter is active'],
|
|
853
|
+
},
|
|
832
854
|
}
|
|
833
855
|
|
|
834
856
|
function ViewEmptyState({ view }: { view: AppView }) {
|
|
@@ -1016,7 +1038,7 @@ function DesktopEmptyState({ userName }: { userName: string | null }) {
|
|
|
1016
1038
|
<span className="text-text-2">What would you like to do?</span>
|
|
1017
1039
|
</h1>
|
|
1018
1040
|
<p className="text-[15px] text-text-3 mb-12">
|
|
1019
|
-
Create a new
|
|
1041
|
+
Create a new chat to start chatting
|
|
1020
1042
|
</p>
|
|
1021
1043
|
<button
|
|
1022
1044
|
onClick={() => setNewSessionOpen(true)}
|
|
@@ -1029,7 +1051,7 @@ function DesktopEmptyState({ userName }: { userName: string | null }) {
|
|
|
1029
1051
|
<line x1="12" y1="5" x2="12" y2="19" />
|
|
1030
1052
|
<line x1="5" y1="12" x2="19" y2="12" />
|
|
1031
1053
|
</svg>
|
|
1032
|
-
New
|
|
1054
|
+
New Chat
|
|
1033
1055
|
</button>
|
|
1034
1056
|
</div>
|
|
1035
1057
|
</div>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
4
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
|
|
6
|
+
export function ProjectList() {
|
|
7
|
+
const projects = useAppStore((s) => s.projects)
|
|
8
|
+
const loadProjects = useAppStore((s) => s.loadProjects)
|
|
9
|
+
const agents = useAppStore((s) => s.agents)
|
|
10
|
+
const tasks = useAppStore((s) => s.tasks)
|
|
11
|
+
const setProjectSheetOpen = useAppStore((s) => s.setProjectSheetOpen)
|
|
12
|
+
const setEditingProjectId = useAppStore((s) => s.setEditingProjectId)
|
|
13
|
+
const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
|
|
14
|
+
const setActiveProjectFilter = useAppStore((s) => s.setActiveProjectFilter)
|
|
15
|
+
const [search, setSearch] = useState('')
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18
|
+
useEffect(() => { loadProjects() }, [])
|
|
19
|
+
|
|
20
|
+
const filtered = useMemo(() => {
|
|
21
|
+
return Object.values(projects)
|
|
22
|
+
.filter((p) => {
|
|
23
|
+
if (search && !p.name.toLowerCase().includes(search.toLowerCase())) return false
|
|
24
|
+
return true
|
|
25
|
+
})
|
|
26
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
27
|
+
}, [projects, search])
|
|
28
|
+
|
|
29
|
+
const entityCounts = useMemo(() => {
|
|
30
|
+
const counts: Record<string, { agents: number; tasks: number }> = {}
|
|
31
|
+
for (const p of Object.values(projects)) {
|
|
32
|
+
counts[p.id] = { agents: 0, tasks: 0 }
|
|
33
|
+
}
|
|
34
|
+
for (const a of Object.values(agents)) {
|
|
35
|
+
if (a.projectId && counts[a.projectId]) counts[a.projectId].agents++
|
|
36
|
+
}
|
|
37
|
+
for (const t of Object.values(tasks)) {
|
|
38
|
+
if (t.projectId && counts[t.projectId]) counts[t.projectId].tasks++
|
|
39
|
+
}
|
|
40
|
+
return counts
|
|
41
|
+
}, [projects, agents, tasks])
|
|
42
|
+
|
|
43
|
+
if (!filtered.length && !search) {
|
|
44
|
+
return (
|
|
45
|
+
<div className="flex-1 flex flex-col items-center justify-center gap-4 text-text-3 p-8 text-center">
|
|
46
|
+
<div className="w-12 h-12 rounded-[14px] bg-accent-soft flex items-center justify-center mb-1">
|
|
47
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-accent-bright">
|
|
48
|
+
<path d="M2 20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8l-7-7H4a2 2 0 0 0-2 2v17Z" />
|
|
49
|
+
<path d="M14 2v7h7" />
|
|
50
|
+
</svg>
|
|
51
|
+
</div>
|
|
52
|
+
<p className="font-display text-[15px] font-600 text-text-2">No projects yet</p>
|
|
53
|
+
<p className="text-[13px] text-text-3/50">Group agents, tasks, and schedules into projects</p>
|
|
54
|
+
<button
|
|
55
|
+
onClick={() => { setEditingProjectId(null); setProjectSheetOpen(true) }}
|
|
56
|
+
className="inline-flex items-center gap-1.5 px-4 py-2 text-[13px] font-500 text-white bg-accent rounded-lg hover:bg-accent-bright transition-colors"
|
|
57
|
+
>
|
|
58
|
+
<span className="text-lg leading-none">+</span> New Project
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className="flex-1 flex flex-col h-full overflow-y-auto">
|
|
66
|
+
<div className="p-4 pb-0">
|
|
67
|
+
<div className="flex items-center gap-2 mb-4">
|
|
68
|
+
<input
|
|
69
|
+
type="text"
|
|
70
|
+
value={search}
|
|
71
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
72
|
+
placeholder="Search projects..."
|
|
73
|
+
className="flex-1 px-3 py-2 rounded-lg bg-white/[0.06] border border-white/[0.06] text-[13px] text-text-1 placeholder:text-text-3/40 focus:outline-none focus:border-accent/40"
|
|
74
|
+
style={{ fontFamily: 'inherit' }}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="flex-1 overflow-y-auto px-4 pb-4 space-y-2">
|
|
79
|
+
{filtered.map((project) => {
|
|
80
|
+
const counts = entityCounts[project.id] || { agents: 0, tasks: 0 }
|
|
81
|
+
const isActive = activeProjectFilter === project.id
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
key={project.id}
|
|
85
|
+
className={`group relative p-4 rounded-xl border transition-colors cursor-pointer ${
|
|
86
|
+
isActive
|
|
87
|
+
? 'bg-accent/10 border-accent/30'
|
|
88
|
+
: 'bg-white/[0.03] border-white/[0.06] hover:bg-white/[0.06]'
|
|
89
|
+
}`}
|
|
90
|
+
onClick={() => setActiveProjectFilter(isActive ? null : project.id)}
|
|
91
|
+
>
|
|
92
|
+
<div className="flex items-start justify-between gap-3">
|
|
93
|
+
<div className="flex items-center gap-2.5 min-w-0">
|
|
94
|
+
{project.color && (
|
|
95
|
+
<div className="w-3 h-3 rounded-full shrink-0" style={{ backgroundColor: project.color }} />
|
|
96
|
+
)}
|
|
97
|
+
<div className="min-w-0">
|
|
98
|
+
<div className="font-display text-[14px] font-600 text-text-1 truncate">{project.name}</div>
|
|
99
|
+
{project.description && (
|
|
100
|
+
<p className="text-[12px] text-text-3/60 mt-0.5 line-clamp-2">{project.description}</p>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<button
|
|
105
|
+
onClick={(e) => { e.stopPropagation(); setEditingProjectId(project.id); setProjectSheetOpen(true) }}
|
|
106
|
+
className="opacity-0 group-hover:opacity-100 p-1.5 rounded-md hover:bg-white/[0.08] transition-all text-text-3/50 hover:text-text-2"
|
|
107
|
+
>
|
|
108
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
109
|
+
<path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" />
|
|
110
|
+
</svg>
|
|
111
|
+
</button>
|
|
112
|
+
</div>
|
|
113
|
+
<div className="flex items-center gap-3 mt-2.5 text-[11px] text-text-3/50">
|
|
114
|
+
<span>{counts.agents} agent{counts.agents !== 1 ? 's' : ''}</span>
|
|
115
|
+
<span>{counts.tasks} task{counts.tasks !== 1 ? 's' : ''}</span>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
)
|
|
119
|
+
})}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import { createProject, updateProject, deleteProject } from '@/lib/projects'
|
|
6
|
+
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
7
|
+
import { toast } from 'sonner'
|
|
8
|
+
|
|
9
|
+
const PROJECT_COLORS = [
|
|
10
|
+
'#EF4444', '#F97316', '#EAB308', '#22C55E', '#06B6D4',
|
|
11
|
+
'#3B82F6', '#8B5CF6', '#EC4899', '#6B7280',
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const inputClass = 'w-full px-3 py-2.5 rounded-lg bg-white/[0.06] border border-white/[0.06] text-[13px] text-text-1 placeholder:text-text-3/40 focus:outline-none focus:border-accent/40 transition-colors'
|
|
15
|
+
|
|
16
|
+
export function ProjectSheet() {
|
|
17
|
+
const open = useAppStore((s) => s.projectSheetOpen)
|
|
18
|
+
const setOpen = useAppStore((s) => s.setProjectSheetOpen)
|
|
19
|
+
const editingId = useAppStore((s) => s.editingProjectId)
|
|
20
|
+
const setEditingId = useAppStore((s) => s.setEditingProjectId)
|
|
21
|
+
const projects = useAppStore((s) => s.projects)
|
|
22
|
+
const loadProjects = useAppStore((s) => s.loadProjects)
|
|
23
|
+
|
|
24
|
+
const [name, setName] = useState('')
|
|
25
|
+
const [description, setDescription] = useState('')
|
|
26
|
+
const [color, setColor] = useState<string | undefined>(undefined)
|
|
27
|
+
|
|
28
|
+
const editing = editingId ? projects[editingId] : null
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (open) {
|
|
32
|
+
if (editing) {
|
|
33
|
+
setName(editing.name)
|
|
34
|
+
setDescription(editing.description)
|
|
35
|
+
setColor(editing.color)
|
|
36
|
+
} else {
|
|
37
|
+
setName('')
|
|
38
|
+
setDescription('')
|
|
39
|
+
setColor(PROJECT_COLORS[0])
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
|
+
}, [open, editingId])
|
|
44
|
+
|
|
45
|
+
const onClose = () => {
|
|
46
|
+
setOpen(false)
|
|
47
|
+
setEditingId(null)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleSave = async () => {
|
|
51
|
+
const data = {
|
|
52
|
+
name: name.trim() || 'Unnamed Project',
|
|
53
|
+
description,
|
|
54
|
+
color,
|
|
55
|
+
}
|
|
56
|
+
if (editing) {
|
|
57
|
+
await updateProject(editing.id, data)
|
|
58
|
+
} else {
|
|
59
|
+
await createProject(data)
|
|
60
|
+
}
|
|
61
|
+
await loadProjects()
|
|
62
|
+
onClose()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const handleDelete = async () => {
|
|
66
|
+
if (editing) {
|
|
67
|
+
await deleteProject(editing.id)
|
|
68
|
+
await loadProjects()
|
|
69
|
+
onClose()
|
|
70
|
+
toast.success('Project deleted')
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<BottomSheet open={open} onClose={onClose}>
|
|
76
|
+
<h2 className="font-display text-[18px] font-700 text-text mb-6">{editing ? 'Edit Project' : 'New Project'}</h2>
|
|
77
|
+
<div className="mb-6">
|
|
78
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Name</label>
|
|
79
|
+
<input
|
|
80
|
+
type="text"
|
|
81
|
+
value={name}
|
|
82
|
+
onChange={(e) => setName(e.target.value)}
|
|
83
|
+
placeholder="e.g. Marketing Site"
|
|
84
|
+
className={inputClass}
|
|
85
|
+
style={{ fontFamily: 'inherit' }}
|
|
86
|
+
autoFocus
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className="mb-6">
|
|
91
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Description</label>
|
|
92
|
+
<textarea
|
|
93
|
+
value={description}
|
|
94
|
+
onChange={(e) => setDescription(e.target.value)}
|
|
95
|
+
placeholder="What is this project about?"
|
|
96
|
+
className={inputClass + ' min-h-[80px] resize-y'}
|
|
97
|
+
style={{ fontFamily: 'inherit' }}
|
|
98
|
+
rows={3}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div className="mb-8">
|
|
103
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Color</label>
|
|
104
|
+
<div className="flex items-center gap-2">
|
|
105
|
+
{PROJECT_COLORS.map((c) => (
|
|
106
|
+
<button
|
|
107
|
+
key={c}
|
|
108
|
+
type="button"
|
|
109
|
+
onClick={() => setColor(c)}
|
|
110
|
+
className={`w-7 h-7 rounded-full transition-all ${color === c ? 'ring-2 ring-offset-2 ring-offset-surface ring-accent scale-110' : 'hover:scale-105'}`}
|
|
111
|
+
style={{ backgroundColor: c }}
|
|
112
|
+
/>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div className="flex items-center gap-3">
|
|
118
|
+
<button
|
|
119
|
+
onClick={handleSave}
|
|
120
|
+
className="flex-1 py-2.5 rounded-lg bg-accent text-white text-[13px] font-600 hover:bg-accent-bright transition-colors"
|
|
121
|
+
>
|
|
122
|
+
{editing ? 'Update' : 'Create'} Project
|
|
123
|
+
</button>
|
|
124
|
+
{editing && (
|
|
125
|
+
<button
|
|
126
|
+
onClick={handleDelete}
|
|
127
|
+
className="px-4 py-2.5 rounded-lg bg-red-500/10 text-red-400 text-[13px] font-600 hover:bg-red-500/20 transition-colors"
|
|
128
|
+
>
|
|
129
|
+
Delete
|
|
130
|
+
</button>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
</BottomSheet>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
@@ -12,6 +12,7 @@ export function ScheduleList({ inSidebar }: Props) {
|
|
|
12
12
|
const schedules = useAppStore((s) => s.schedules)
|
|
13
13
|
const loadSchedules = useAppStore((s) => s.loadSchedules)
|
|
14
14
|
const setScheduleSheetOpen = useAppStore((s) => s.setScheduleSheetOpen)
|
|
15
|
+
const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
|
|
15
16
|
const [search, setSearch] = useState('')
|
|
16
17
|
|
|
17
18
|
useEffect(() => { loadSchedules() }, [])
|
|
@@ -20,10 +21,11 @@ export function ScheduleList({ inSidebar }: Props) {
|
|
|
20
21
|
return Object.values(schedules)
|
|
21
22
|
.filter((s) => {
|
|
22
23
|
if (search && !s.name.toLowerCase().includes(search.toLowerCase())) return false
|
|
24
|
+
if (activeProjectFilter && s.projectId !== activeProjectFilter) return false
|
|
23
25
|
return true
|
|
24
26
|
})
|
|
25
27
|
.sort((a, b) => b.createdAt - a.createdAt)
|
|
26
|
-
}, [schedules, search])
|
|
28
|
+
}, [schedules, search, activeProjectFilter])
|
|
27
29
|
|
|
28
30
|
if (!filtered.length && !search) {
|
|
29
31
|
return (
|
|
@@ -129,7 +129,7 @@ export function NewSessionSheet() {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
const handleCreate = async () => {
|
|
132
|
-
const sessionName = name.trim() || 'New
|
|
132
|
+
const sessionName = name.trim() || 'New Chat'
|
|
133
133
|
const cwd = selectedDir || ''
|
|
134
134
|
const resolvedCredentialId = currentProvider?.requiresApiKey
|
|
135
135
|
? credentialId
|
|
@@ -174,14 +174,14 @@ export function NewSessionSheet() {
|
|
|
174
174
|
<BottomSheet open={open} onClose={onClose} wide>
|
|
175
175
|
{/* Header */}
|
|
176
176
|
<div className="mb-10">
|
|
177
|
-
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-2">New
|
|
178
|
-
<p className="text-[14px] text-text-3">Configure your AI
|
|
177
|
+
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-2">New Chat</h2>
|
|
178
|
+
<p className="text-[14px] text-text-3">Configure your AI chat</p>
|
|
179
179
|
</div>
|
|
180
180
|
|
|
181
181
|
{/* Name */}
|
|
182
182
|
<div className="mb-8">
|
|
183
183
|
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
|
|
184
|
-
|
|
184
|
+
Chat Name
|
|
185
185
|
</label>
|
|
186
186
|
<input
|
|
187
187
|
type="text"
|
|
@@ -373,7 +373,7 @@ export function NewSessionSheet() {
|
|
|
373
373
|
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
374
374
|
Tools <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
375
375
|
</label>
|
|
376
|
-
<p className="text-[12px] text-text-3/60 mb-3">Allow this model to execute commands and access files in the
|
|
376
|
+
<p className="text-[12px] text-text-3/60 mb-3">Allow this model to execute commands and access files in the working directory.</p>
|
|
377
377
|
<div className="flex flex-wrap gap-2.5">
|
|
378
378
|
{([
|
|
379
379
|
{ id: 'shell' as SessionTool, label: 'Shell' },
|
|
@@ -469,7 +469,7 @@ export function NewSessionSheet() {
|
|
|
469
469
|
shadow-[0_4px_20px_rgba(99,102,241,0.25)] hover:brightness-110"
|
|
470
470
|
style={{ fontFamily: 'inherit' }}
|
|
471
471
|
>
|
|
472
|
-
Create
|
|
472
|
+
Create Chat
|
|
473
473
|
</button>
|
|
474
474
|
</div>
|
|
475
475
|
</BottomSheet>
|
|
@@ -117,7 +117,7 @@ export function SessionCard({ session, active, onClick }: Props) {
|
|
|
117
117
|
onClick={handleDelete}
|
|
118
118
|
className="shrink-0 opacity-0 group-hover/card:opacity-100 transition-opacity duration-150
|
|
119
119
|
text-text-3 hover:text-red-400 p-0.5 -mr-1 cursor-pointer bg-transparent border-none"
|
|
120
|
-
title="Delete
|
|
120
|
+
title="Delete chat"
|
|
121
121
|
>
|
|
122
122
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
123
123
|
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|